diff --git a/.bzrignore b/.bzrignore index 17fee4a6fc1..d3e2393aa8d 100644 --- a/.bzrignore +++ b/.bzrignore @@ -253,6 +253,7 @@ depcomp extra/comp_err extra/my_print_defaults extra/mysql_install +extra/mysql_waitpid extra/perror extra/replace extra/resolve_stack_dump @@ -350,6 +351,7 @@ libmysqld/opt_range.cc libmysqld/opt_sum.cc libmysqld/password.c libmysqld/procedure.cc +libmysqld/protocol.cc libmysqld/records.cc libmysqld/repl_failsafe.cc libmysqld/set_var.cc @@ -569,6 +571,7 @@ support-files/mysql-log-rotate support-files/mysql.server support-files/mysql.spec tags +test_xml tests/client_test tmp/* tools/my_vsnprintf.c @@ -579,6 +582,12 @@ vio/test-ssl vio/test-sslclient vio/test-sslserver vio/viotest-ssl -libmysqld/protocol.cc -test_xml -extra/mysql_waitpid +comon.h +emacs.h +fcns.c +fcns.h +help.h +help.c +vi.h +include/readline/readline.h +cmd-line-utils/libedit/common.h diff --git a/BUILD/compile-alpha-cxx b/BUILD/compile-alpha-cxx index 3e6eee9a0d6..a342d927868 100755 --- a/BUILD/compile-alpha-cxx +++ b/BUILD/compile-alpha-cxx @@ -4,7 +4,7 @@ make -k clean /bin/rm -f */.deps/*.P config.cache innobase/config.cache bdb/build_unix/config.cache mysql-*.tar.gz aclocal; autoheader; aclocal; automake; autoconf -CC=ccc CFLAGS="-fast" CXX=cxx CXXFLAGS="-fast -noexceptions -nortti" ./configure --prefix=/usr/local/mysql --disable-shared --with-extra-charsets=complex --enable-thread-safe-client --with-mysqld-ldflags=-non_shared --with-client-ldflags=-non_shared --without-extra-tools +CC=ccc CFLAGS="-fast" CXX=cxx CXXFLAGS="-fast -noexceptions -nortti" ./configure --prefix=/usr/local/mysql --disable-shared --with-extra-charsets=complex --enable-thread-safe-client --with-mysqld-ldflags=-non_shared --with-client-ldflags=-non_shared --without-extra-tools --disable-dependency-tracking make -j2 find . -name ".deps" | xargs rm -r diff --git a/BitKeeper/etc/logging_ok b/BitKeeper/etc/logging_ok index 562191618ad..c5970cdd45d 100644 --- a/BitKeeper/etc/logging_ok +++ b/BitKeeper/etc/logging_ok @@ -17,6 +17,7 @@ bell@sanja.is.com.ua bk@admin.bk davida@isil.mysql.com gluh@gluh.(none) +gluh@gluh.mysql.r18.ru heikki@donna.mysql.fi heikki@hundin.mysql.fi heikki@rescue. diff --git a/Makefile.am b/Makefile.am index 87d1c7b5b71..7949e7be776 100644 --- a/Makefile.am +++ b/Makefile.am @@ -21,10 +21,10 @@ AUTOMAKE_OPTIONS = foreign # These are built from source in the Docs directory EXTRA_DIST = INSTALL-SOURCE README \ COPYING COPYING.LIB -SUBDIRS = include @docs_dirs@ @readline_dir@ \ +SUBDIRS = . include @docs_dirs@ @readline_dir@ \ @thread_dirs@ pstack @sql_client_dirs@ \ - @sql_server_dirs@ @libmysqld_dirs@ scripts man \ - tests BUILD os2 \ + @sql_server_dirs@ scripts man tests \ + BUILD os2 libmysql_r @libmysqld_dirs@ \ @bench_dirs@ support-files @fs_dirs@ @tools_dirs@ # Relink after clean diff --git a/VC++Files/client/mysqlclient.dsp b/VC++Files/client/mysqlclient.dsp index 7bc38eb913a..316f8dd245e 100644 --- a/VC++Files/client/mysqlclient.dsp +++ b/VC++Files/client/mysqlclient.dsp @@ -116,6 +116,10 @@ SOURCE="..\strings\ctype-big5.c" # End Source File # Begin Source File +SOURCE="..\strings\ctype-bin.c" +# End Source File +# Begin Source File + SOURCE="..\strings\ctype-czech.c" # End Source File # Begin Source File @@ -204,10 +208,6 @@ SOURCE=..\mysys\mf_cache.c # End Source File # Begin Source File -SOURCE=..\mysys\mf_casecnv.c -# End Source File -# Begin Source File - SOURCE=..\mysys\mf_dirname.c # End Source File # Begin Source File @@ -422,6 +422,10 @@ SOURCE=.\select_test.c # End Source File # Begin Source File +SOURCE=..\mysys\sha1.c +# End Source File +# Begin Source File + SOURCE=.\sql_string.cpp # End Source File # Begin Source File diff --git a/VC++Files/libmysql/libmysql.def b/VC++Files/libmysql/libmysql.def index 3ddbe28bc83..e7959078390 100644 --- a/VC++Files/libmysql/libmysql.def +++ b/VC++Files/libmysql/libmysql.def @@ -59,7 +59,6 @@ EXPORTS list_add list_delete max_allowed_packet - my_casecmp my_init my_end my_strdup diff --git a/VC++Files/libmysql/libmysql.dsp b/VC++Files/libmysql/libmysql.dsp index 6b86c69917c..46b7b422f9d 100644 --- a/VC++Files/libmysql/libmysql.dsp +++ b/VC++Files/libmysql/libmysql.dsp @@ -127,6 +127,10 @@ SOURCE="..\strings\ctype-big5.c" # End Source File # Begin Source File +SOURCE="..\strings\ctype-bin.c" +# End Source File +# Begin Source File + SOURCE="..\strings\ctype-czech.c" # End Source File # Begin Source File @@ -219,10 +223,6 @@ SOURCE=..\strings\longlong2str.c # End Source File # Begin Source File -SOURCE=..\mysys\mf_casecnv.c -# End Source File -# Begin Source File - SOURCE=..\mysys\mf_dirname.c # End Source File # Begin Source File @@ -396,6 +396,10 @@ SOURCE=..\client\select_test.c # End Source File # Begin Source File +SOURCE=..\mysys\sha1.c +# End Source File +# Begin Source File + SOURCE=..\client\sql_string.cpp # End Source File # Begin Source File diff --git a/VC++Files/mysys/mysys.dsp b/VC++Files/mysys/mysys.dsp index fb92fce0e2e..aaf9980a883 100644 --- a/VC++Files/mysys/mysys.dsp +++ b/VC++Files/mysys/mysys.dsp @@ -162,10 +162,6 @@ SOURCE=.\mf_cache.c # End Source File # Begin Source File -SOURCE=.\mf_casecnv.c -# End Source File -# Begin Source File - SOURCE=.\mf_dirname.c # End Source File # Begin Source File @@ -234,6 +230,10 @@ SOURCE=.\mf_strip.c # End Source File # Begin Source File +SOURCE=.\mf_tempdir.c +# End Source File +# Begin Source File + SOURCE=.\mf_tempfile.c # End Source File # Begin Source File diff --git a/VC++Files/sql/mysqld.dsp b/VC++Files/sql/mysqld.dsp index acb1df0c343..9d7514f10bb 100644 --- a/VC++Files/sql/mysqld.dsp +++ b/VC++Files/sql/mysqld.dsp @@ -488,6 +488,10 @@ SOURCE=.\item_func.cpp # End Source File # Begin Source File +SOURCE=.\item_row.cpp +# End Source File +# Begin Source File + SOURCE=.\item_strfunc.cpp !IF "$(CFG)" == "mysqld - Win32 Release" @@ -678,26 +682,6 @@ SOURCE=.\mysqld.cpp # End Source File # Begin Source File -SOURCE=.\net_pkg.cpp - -!IF "$(CFG)" == "mysqld - Win32 Release" - -!ELSEIF "$(CFG)" == "mysqld - Win32 Debug" - -# ADD CPP /G5 -# SUBTRACT CPP /YX /Yc /Yu - -!ELSEIF "$(CFG)" == "mysqld - Win32 nt" - -!ELSEIF "$(CFG)" == "mysqld - Win32 Max nt" - -!ELSEIF "$(CFG)" == "mysqld - Win32 Max" - -!ENDIF - -# End Source File -# Begin Source File - SOURCE=.\net_serv.cpp # End Source File # Begin Source File @@ -963,6 +947,10 @@ SOURCE=.\sql_handler.cpp # End Source File # Begin Source File +SOURCE=.\sql_help.cpp +# End Source File +# Begin Source File + SOURCE=.\sql_insert.cpp !IF "$(CFG)" == "mysqld - Win32 Release" diff --git a/VC++Files/strings/strings.dsp b/VC++Files/strings/strings.dsp index 1433b8d05f6..bf3ac1831e8 100644 --- a/VC++Files/strings/strings.dsp +++ b/VC++Files/strings/strings.dsp @@ -104,6 +104,10 @@ SOURCE=.\bmove512.c # End Source File # Begin Source File +SOURCE=".\ctype-bin.c" +# End Source File +# Begin Source File + SOURCE=".\ctype-big5.c" # End Source File # Begin Source File @@ -168,6 +172,18 @@ SOURCE=.\str2int.c # End Source File # Begin Source File +SOURCE=.\strcend.c +# End Source File +# Begin Source File + +SOURCE=.\strend.c +# End Source File +# Begin Source File + +SOURCE=.\strfill.c +# End Source File +# Begin Source File + SOURCE=.\Strings.asm !IF "$(CFG)" == "strings - Win32 Release" @@ -199,6 +215,19 @@ InputName=Strings # End Source File # Begin Source File +SOURCE=.\strmake.c +# End Source File +# Begin Source File + +SOURCE=.\strmov.c +# End Source File +# Begin Source File + +SOURCE=.\strnmov.c +# End Source File +# Begin Source File + + SOURCE=.\strtol.c # End Source File # Begin Source File diff --git a/acconfig.h b/acconfig.h index e969a635a5b..426ef45f6cd 100644 --- a/acconfig.h +++ b/acconfig.h @@ -318,6 +318,22 @@ /* READLINE: */ #undef VOID_SIGHANDLER +/* used libedit interface (can we dereference result of rl_completion_entry_function?) */ +#undef USE_LIBEDIT_INTERFACE + +/* used new readline interface (does rl_completion_func_t and rl_compentry_func_t defined?) */ +#undef USE_NEW_READLINE_INTERFACE + +/* macro for libedit */ +#undef HAVE_VIS_H +#undef HAVE_FGETLN +#undef HAVE_ISSETUGID +#undef HAVE_STRLCPY +#undef HAVE_GETLINE +#undef HAVE_FLOCKFILE +#undef HAVE_SYS_TYPES_H +#undef HAVE_SYS_CDEFS_H + /* Leave that blank line there!! Autoheader needs it. If you're adding to this file, keep in mind: diff --git a/acinclude.m4 b/acinclude.m4 index 15f08e44c27..970620d9be7 100644 --- a/acinclude.m4 +++ b/acinclude.m4 @@ -1,5 +1,45 @@ # Local macros for automake & autoconf +AC_DEFUN(MYSQL_CHECK_LIBEDIT_INTERFACE,[ + AC_CACHE_CHECK([libedit variant of rl_completion_entry_function], mysql_cv_libedit_interface, + AC_TRY_COMPILE( + [ + #include "stdio.h" + #include "readline/readline.h" + ], + [ + char res= *(*rl_completion_entry_function)(0,0); + completion_matches(0,0); + ], + [ + mysql_cv_libedit_interface=yes + AC_DEFINE_UNQUOTED(USE_LIBEDIT_INTERFACE) + ], + [mysql_cv_libedit_interface=no] + ) + ) +]) + +AC_DEFUN(MYSQL_CHECK_NEW_RL_INTERFACE,[ + AC_CACHE_CHECK([defined rl_compentry_func_t and rl_completion_func_t], mysql_cv_new_rl_interface, + AC_TRY_COMPILE( + [ + #include "stdio.h" + #include "readline/readline.h" + ], + [ + rl_completion_func_t *func1= (rl_completion_func_t*)0; + rl_compentry_func_t *func2= (rl_compentry_func_t*)0; + ], + [ + mysql_cv_new_rl_interface=yes + AC_DEFINE_UNQUOTED(USE_NEW_READLINE_INTERFACE) + ], + [mysql_cv_new_rl_interface=no] + ) + ) +]) + # A local version of AC_CHECK_SIZEOF that includes sys/types.h dnl MYSQL_CHECK_SIZEOF(TYPE [, CROSS-SIZE]) AC_DEFUN(MYSQL_CHECK_SIZEOF, diff --git a/client/client_priv.h b/client/client_priv.h index eb4473cb10f..b6bfc253854 100644 --- a/client/client_priv.h +++ b/client/client_priv.h @@ -38,4 +38,5 @@ enum options { OPT_CHARSETS_DIR=256, OPT_DEFAULT_CHARSET, OPT_SSL_KEY, OPT_SSL_CERT, OPT_SSL_CA, OPT_SSL_CAPATH, OPT_SSL_CIPHER, OPT_SHUTDOWN_TIMEOUT, OPT_LOCAL_INFILE, OPT_PROMPT, OPT_IGN_LINES,OPT_TRANSACTION,OPT_MYSQL_PROTOCOL, - OPT_SHARED_MEMORY_BASE_NAME, OPT_FRM }; + OPT_SHARED_MEMORY_BASE_NAME, OPT_FRM, OPT_SKIP_OPTIMIZATION, + OPT_COMPATIBLE }; diff --git a/client/insert_test.c b/client/insert_test.c index d8ca71bffce..e4eb852af52 100644 --- a/client/insert_test.c +++ b/client/insert_test.c @@ -34,6 +34,7 @@ int main(int argc, char **argv) exit(1); } + mysql_init(&mysql); if (!(sock = mysql_real_connect(&mysql,NULL,NULL,NULL,argv[1],0,NULL,0))) { fprintf(stderr,"Couldn't connect to engine!\n%s\n",mysql_error(&mysql)); diff --git a/client/mysql.cc b/client/mysql.cc index f13d601ee42..b7bda97a8f8 100644 --- a/client/mysql.cc +++ b/client/mysql.cc @@ -80,6 +80,8 @@ extern "C" { #if defined( __WIN__) || defined(OS2) #include #else +// readline 4.2 has own __P +#undef __P #include #define HAVE_READLINE #endif @@ -273,9 +275,9 @@ static const char *server_default_groups[]= { "server", "embedded", "mysql_SERVER", 0 }; #ifdef HAVE_READLINE -extern "C" void add_history(char *command); /* From readline directory */ -extern "C" int read_history(char *command); -extern "C" int write_history(char *command); +extern "C" int add_history(const char *command); /* From readline directory */ +extern "C" int read_history(const char *command); +extern "C" int write_history(const char *command); static void initialize_readline (char *name); #endif @@ -1081,8 +1083,11 @@ static char **new_mysql_completion (const char *text, int start, int end); if not. */ -char *no_completion (const char *text __attribute__ ((unused)), - int ) +#if defined(USE_NEW_READLINE_INTERFACE) || defined(USE_LIBEDIT_INTERFACE) +char *no_completion(const char*,int) +#else +int no_completion() +#endif { return 0; /* No filename completion */ } @@ -1093,12 +1098,15 @@ static void initialize_readline (char *name) rl_readline_name = name; /* Tell the completer that we want a crack first. */ -#if RL_READLINE_VERSION > 0x0400 - rl_attempted_completion_function = &new_mysql_completion; - rl_completion_entry_function= &no_completion; +#if defined(USE_NEW_READLINE_INTERFACE) + rl_attempted_completion_function= (rl_completion_func_t*)&new_mysql_completion; + rl_completion_entry_function= (rl_compentry_func_t*)&no_completion; +#elif defined(USE_LIBEDIT_INTERFACE) + rl_attempted_completion_function= (CPPFunction*)&new_mysql_completion; + rl_completion_entry_function= (CPFunction*)&no_completion; #else - rl_attempted_completion_function =(CPPFunction *)new_mysql_completion; - rl_completion_entry_function= (Function *)no_completion; + rl_attempted_completion_function= (CPPFunction*)&new_mysql_completion; + rl_completion_entry_function= (Function*)&no_completion; #endif } @@ -1114,7 +1122,7 @@ static char **new_mysql_completion (const char *text, int end __attribute__((unused))) { if (!status.batch && !quick) -#if RL_READLINE_VERSION > 0x0400 +#if defined(USE_NEW_READLINE_INTERFACE) return rl_completion_matches(text, new_command_generator); #else return completion_matches((char *)text, (CPFunction *)new_command_generator); @@ -2387,14 +2395,12 @@ com_use(String *buffer __attribute__((unused)), char *line) items in the array to zero first. */ -enum quote_type { NO_QUOTE, SQUOTE, DQUOTE, BTICK }; - char *get_arg(char *line, my_bool get_next_arg) { char *ptr; my_bool quoted= 0, valid_arg= 0; uint count= 0; - enum quote_type qtype= NO_QUOTE; + char qtype= 0; ptr= line; if (get_next_arg) @@ -2415,10 +2421,9 @@ char *get_arg(char *line, my_bool get_next_arg) } while (my_isspace(system_charset_info, *ptr)) ptr++; - if ((*ptr == '\'' && (qtype= SQUOTE)) || - (*ptr == '\"' && (qtype= DQUOTE)) || - (*ptr == '`' && (qtype= BTICK))) + if (*ptr == '\'' || *ptr == '\"' || *ptr == '`') { + qtype= *ptr; quoted= 1; ptr++; } @@ -2435,10 +2440,7 @@ char *get_arg(char *line, my_bool get_next_arg) ptr= line; ptr+= count; } - else if ((!quoted && *ptr == ' ') || - (*ptr == '\'' && qtype == SQUOTE) || - (*ptr == '\"' && qtype == DQUOTE) || - (*ptr == '`' && qtype == BTICK)) + else if ((!quoted && *ptr == ' ') || (quoted && *ptr == qtype)) { *ptr= 0; break; diff --git a/client/mysqlbinlog.cc b/client/mysqlbinlog.cc index 4cf86eb31c7..6dbd79e9053 100644 --- a/client/mysqlbinlog.cc +++ b/client/mysqlbinlog.cc @@ -56,6 +56,9 @@ static short binlog_flags = 0; static MYSQL* mysql = NULL; static const char* table = 0; +static bool use_local_load= 0; +static const char* dirname_for_local_load= 0; + static void dump_local_log_entries(const char* logname); static void dump_remote_log_entries(const char* logname); static void dump_log_entries(const char* logname); @@ -64,6 +67,129 @@ static void dump_remote_table(NET* net, const char* db, const char* table); static void die(const char* fmt, ...); static MYSQL* safe_connect(); +class Load_log_processor { + char target_dir_name[MY_NFILE]; + int target_dir_name_len; + DYNAMIC_ARRAY file_names; + + const char* create_file(Create_file_log_event *ce) + { + const char *bname= ce->fname + ce->fname_len -1; + while (bname>ce->fname && bname[-1]!=FN_LIBCHAR) + bname--; + + uint blen= ce->fname_len - (bname-ce->fname); + uint full_len= target_dir_name_len + blen; + char *tmp; + if (!(tmp= my_malloc(full_len + 9 + 1,MYF(MY_WME))) || + set_dynamic(&file_names,(gptr)&ce,ce->file_id)) + { + die("Could not construct local filename %s%s",target_dir_name,bname); + return 0; + } + + char *ptr= tmp; + memcpy(ptr,target_dir_name,target_dir_name_len); + ptr+= target_dir_name_len; + memcpy(ptr,bname,blen); + ptr+= blen; + sprintf(ptr,"-%08x",ce->file_id); + + ce->set_fname_outside_temp_buf(tmp,full_len); + + return tmp; + } + + void append_to_file(const char* fname, int flags, + gptr data, uint size) + { + FILE *file; + if(!(file= my_fopen(fname,flags,MYF(MY_WME)))) + exit(1); + if (my_fwrite(file,data,size,MYF(MY_WME|MY_NABP))) + exit(1); + if (my_fclose(file,MYF(MY_WME))) + exit(1); + } + +public: + + Load_log_processor() + { + init_dynamic_array(&file_names,sizeof(Create_file_log_event*), + 100,100 CALLER_INFO); + } + + ~Load_log_processor() + { + destroy(); + delete_dynamic(&file_names); + } + + void init_by_dir_name(const char *atarget_dir_name) + { + char *end= strmov(target_dir_name,atarget_dir_name); + if (end[-1]!=FN_LIBCHAR) + *end++= FN_LIBCHAR; + target_dir_name_len= end-target_dir_name; + } + void init_by_file_name(const char *file_name) + { + int len= strlen(file_name); + const char *end= file_name + len - 1; + while (end>file_name && *end!=FN_LIBCHAR) + end--; + if (*end!=FN_LIBCHAR) + target_dir_name_len= 0; + else + { + target_dir_name_len= end - file_name + 1; + memmove(target_dir_name,file_name,target_dir_name_len); + } + } + void init_by_cur_dir() + { + target_dir_name_len= 0; + } + void destroy() + { + Create_file_log_event **ptr= (Create_file_log_event**)file_names.buffer; + Create_file_log_event **end= ptr + file_names.elements; + for (; ptrfname,MYF(MY_WME)); + delete *ptr; + *ptr= 0; + } + } + } + Create_file_log_event *grab_event(uint file_id) + { + Create_file_log_event **ptr= + (Create_file_log_event**)file_names.buffer + file_id; + Create_file_log_event *res= *ptr; + *ptr= 0; + return res; + } + void process(Create_file_log_event *ce) + { + const char *fname= create_file(ce); + append_to_file (fname,O_CREAT|O_BINARY,ce->block,ce->block_len); + } + void process(Append_block_log_event *ae) + { + if (ae->file_id >= file_names.elements) + die("Skiped CreateFile event for file_id: %u",ae->file_id); + Create_file_log_event* ce= + *((Create_file_log_event**)file_names.buffer + ae->file_id); + append_to_file(ce->fname,O_APPEND|O_BINARY,ae->block,ae->block_len); + } +}; + +Load_log_processor load_processor; + static struct my_option my_long_options[] = { #ifndef DBUG_OFF @@ -97,6 +223,9 @@ static struct my_option my_long_options[] = {"user", 'u', "Connect to the remote server as username", (gptr*) &user, (gptr*) &user, 0, GET_STR_ALLOC, REQUIRED_ARG, 0, 0, 0, 0, 0, 0}, + {"local-load", 'l', "Prepare files for local load in directory", + (gptr*) &dirname_for_local_load, (gptr*) &dirname_for_local_load, 0, + GET_STR_ALLOC, OPT_ARG, 0, 0, 0, 0, 0, 0}, {"version", 'V', "Print version and exit.", 0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0} @@ -209,6 +338,9 @@ get_one_option(int optid, const struct my_option *opt __attribute__((unused)), case 'V': print_version(); exit(0); + case 'l': + use_local_load= 1; + break; case '?': usage(); exit(0); @@ -420,6 +552,8 @@ static void dump_local_log_entries(const char* logname) MYF(MY_WME | MY_NABP))) exit(1); old_format = check_header(file); + if (use_local_load && !dirname_for_local_load) + load_processor.init_by_file_name(logname); } else { @@ -441,6 +575,8 @@ static void dump_local_log_entries(const char* logname) } file->pos_in_file=position; file->seek_not_done=0; + if (use_local_load && !dirname_for_local_load) + load_processor.init_by_cur_dir(); } if (!position) @@ -495,11 +631,43 @@ Could not read entry at offset %s : Error in log format or read error", } if (!short_form) fprintf(result_file, "# at %s\n",llstr(old_off,llbuff)); - - ev->print(result_file, short_form, last_db); + + if (!use_local_load) + ev->print(result_file, short_form, last_db); + else + { + switch(ev->get_type_code()) + { + case CREATE_FILE_EVENT: + { + Create_file_log_event* ce= (Create_file_log_event*)ev; + ce->print(result_file, short_form, last_db,true); + load_processor.process(ce); + ev= 0; + break; + } + case APPEND_BLOCK_EVENT: + ev->print(result_file, short_form, last_db); + load_processor.process((Append_block_log_event*)ev); + break; + case EXEC_LOAD_EVENT: + { + ev->print(result_file, short_form, last_db); + Execute_load_log_event *exv= (Execute_load_log_event*)ev; + Create_file_log_event *ce= load_processor.grab_event(exv->file_id); + ce->print(result_file, short_form, last_db,true); + my_free((char*)ce->fname,MYF(MY_WME)); + delete ce; + break; + } + default: + ev->print(result_file, short_form, last_db); + } + } } rec_count++; - delete ev; + if (ev) + delete ev; } if (fd >= 0) my_close(fd, MYF(MY_WME)); @@ -520,6 +688,8 @@ int main(int argc, char** argv) if (use_remote) mysql = safe_connect(); + if (dirname_for_local_load) + load_processor.init_by_dir_name(dirname_for_local_load); if (table) { diff --git a/client/mysqldump.c b/client/mysqldump.c index 9534cc68ed4..5eabce9b038 100644 --- a/client/mysqldump.c +++ b/client/mysqldump.c @@ -36,7 +36,7 @@ ** Added --single-transaction option 06/06/2002 by Peter Zaitsev */ -#define DUMP_VERSION "9.07" +#define DUMP_VERSION "10.0" #include #include @@ -69,21 +69,25 @@ static char *add_load_option(char *ptr, const char *object, const char *statement); +static ulong find_set(TYPELIB *lib, const char *x, uint length, + char **err_pos, uint *err_len); static char *field_escape(char *to,const char *from,uint length); -static my_bool verbose=0,tFlag=0,cFlag=0,dFlag=0,quick=0, extended_insert = 0, - lock_tables=0,ignore_errors=0,flush_logs=0,replace=0, - ignore=0,opt_drop=0,opt_keywords=0,opt_lock=0,opt_compress=0, - opt_delayed=0,create_options=0,opt_quoted=0,opt_databases=0, +static my_bool verbose=0,tFlag=0,cFlag=0,dFlag=0,quick= 1, extended_insert= 1, + lock_tables=1,ignore_errors=0,flush_logs=0,replace=0, + ignore=0,opt_drop=1,opt_keywords=0,opt_lock=1,opt_compress=0, + opt_delayed=0,create_options=1,opt_quoted=0,opt_databases=0, opt_alldbs=0,opt_create_db=0,opt_first_slave=0, - opt_autocommit=0,opt_master_data,opt_disable_keys=0,opt_xml=0, + opt_autocommit=0,opt_master_data,opt_disable_keys=1,opt_xml=0, tty_password=0,opt_single_transaction=0; static MYSQL mysql_connection,*sock=0; static char insert_pat[12 * 1024],*opt_password=0,*current_user=0, *current_host=0,*path=0,*fields_terminated=0, *lines_terminated=0, *enclosed=0, *opt_enclosed=0, *escaped=0, - *where=0, *default_charset; -static uint opt_mysql_port=0; + *where=0, *default_charset, *opt_compatible_mode_str= 0, + *err_ptr= 0; +static ulong opt_compatible_mode= 0; +static uint opt_mysql_port= 0, err_len= 0; static my_string opt_mysql_unix_port=0; static int first_error=0; extern ulong net_buffer_length; @@ -93,7 +97,17 @@ FILE *md_result_file; #ifdef HAVE_SMEM static char *shared_memory_base_name=0; #endif -static uint opt_protocol=0; +static uint opt_protocol= 0; + +const char *compatible_mode_names[]= +{ + "MYSQL323", "MYSQL40", "POSTGRESQL", "ORACLE", "MSSQL", "DB2", + "SAPDB", "NO_KEY_OPTIONS", "NO_TABLE_OPTIONS", "NO_FIELD_OPTIONS", + NullS +}; +TYPELIB compatible_mode_typelib= {array_elements(compatible_mode_names) - 1, + "", compatible_mode_names}; + static struct my_option my_long_options[] = { @@ -102,13 +116,13 @@ static struct my_option my_long_options[] = (gptr*) &opt_alldbs, (gptr*) &opt_alldbs, 0, GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0}, {"all", 'a', "Include all MySQL specific create options.", - (gptr*) &create_options, (gptr*) &create_options, 0, GET_BOOL, NO_ARG, 0, + (gptr*) &create_options, (gptr*) &create_options, 0, GET_BOOL, NO_ARG, 1, 0, 0, 0, 0, 0}, {"add-drop-table", OPT_DROP, "Add a 'drop table' before each create.", - (gptr*) &opt_drop, (gptr*) &opt_drop, 0, GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, + (gptr*) &opt_drop, (gptr*) &opt_drop, 0, GET_BOOL, NO_ARG, 1, 0, 0, 0, 0, 0}, {"add-locks", OPT_LOCKS, "Add locks around insert statements.", - (gptr*) &opt_lock, (gptr*) &opt_lock, 0, GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, + (gptr*) &opt_lock, (gptr*) &opt_lock, 0, GET_BOOL, NO_ARG, 1, 0, 0, 0, 0, 0}, {"allow-keywords", OPT_KEYWORDS, "Allow creation of column names that are keywords.", (gptr*) &opt_keywords, @@ -116,6 +130,10 @@ static struct my_option my_long_options[] = {"character-sets-dir", OPT_CHARSETS_DIR, "Directory where character sets are", (gptr*) &charsets_dir, (gptr*) &charsets_dir, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0}, + {"compatible", OPT_COMPATIBLE, + "Change the dump to be compatible with a given mode. By default tables are dumped without any restrictions. Legal modes are: mysql323, mysql40, postgresql, oracle, mssql, db2, sapdb, no_key_options, no_table_options, no_field_options. One can use several modes separated by commas. Note: Requires MySQL server version 4.1.0 or higher. This option does a no operation on earlier server versions.", + (gptr*) &opt_compatible_mode_str, (gptr*) &opt_compatible_mode_str, 0, + GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0}, {"complete-insert", 'c', "Use complete insert statements.", (gptr*) &cFlag, (gptr*) &cFlag, 0, GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0}, {"compress", 'C', "Use compression in server/client protocol.", @@ -135,11 +153,11 @@ static struct my_option my_long_options[] = 0, 0}, {"disable-keys", 'K', "'/*!40000 ALTER TABLE tb_name DISABLE KEYS */; and '/*!40000 ALTER TABLE tb_name ENABLE KEYS */; will be put in the output.", (gptr*) &opt_disable_keys, - (gptr*) &opt_disable_keys, 0, GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0}, + (gptr*) &opt_disable_keys, 0, GET_BOOL, NO_ARG, 1, 0, 0, 0, 0, 0}, {"extended-insert", 'e', "Allows utilization of the new, much faster INSERT syntax.", (gptr*) &extended_insert, (gptr*) &extended_insert, 0, GET_BOOL, NO_ARG, - 0, 0, 0, 0, 0, 0}, + 1, 0, 0, 0, 0, 0}, {"fields-terminated-by", OPT_FTB, "Fields in the textfile are terminated by ...", (gptr*) &fields_terminated, (gptr*) &fields_terminated, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0}, @@ -168,7 +186,7 @@ static struct my_option my_long_options[] = (gptr*) &lines_terminated, (gptr*) &lines_terminated, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0}, {"lock-tables", 'l', "Lock all tables for read.", (gptr*) &lock_tables, - (gptr*) &lock_tables, 0, GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0}, + (gptr*) &lock_tables, 0, GET_BOOL, NO_ARG, 1, 0, 0, 0, 0, 0}, {"master-data", OPT_MASTER_DATA, "This will cause the master position and filename to be appended to your output. This will automagically enable --first-slave.", 0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0}, @@ -178,8 +196,8 @@ static struct my_option my_long_options[] = 0, 0, 0, 0, 0, 0}, {"single-transaction", OPT_TRANSACTION, "Dump all tables in single transaction to get consistent snapshot. Mutually exclusive with --lock-tables.", - (gptr*) &opt_single_transaction, (gptr*) &opt_single_transaction, 0, GET_BOOL, NO_ARG, - 0, 0, 0, 0, 0, 0}, + (gptr*) &opt_single_transaction, (gptr*) &opt_single_transaction, 0, + GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0}, {"no-create-db", 'n', "'CREATE DATABASE /*!32312 IF NOT EXISTS*/ db_name;' will not be put in the output. The above line will be added otherwise, if --databases or --all-databases option was given.}", (gptr*) &opt_create_db, (gptr*) &opt_create_db, 0, GET_BOOL, NO_ARG, 0, 0, @@ -192,7 +210,7 @@ static struct my_option my_long_options[] = "Change the value of a variable. Please note that this option is deprecated; you can set variables directly with --variable-name=value.", 0, 0, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0}, {"opt", OPT_OPTIMIZE, - "Same as --add-drop-table --add-locks --all --quick --extended-insert --lock-tables --disable-keys", + "Same as --add-drop-table --add-locks --all --quick --extended-insert --lock-tables --disable-keys. Enabled by default, disable with --skip-opt.", 0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0}, {"password", 'p', "Password to use when connecting to server. If password is not given it's solicited on the tty.", @@ -207,7 +225,7 @@ static struct my_option my_long_options[] = {"protocol", OPT_MYSQL_PROTOCOL, "The protocol of connection (tcp,socket,pipe,memory)", 0, 0, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0}, {"quick", 'q', "Don't buffer query, dump directly to stdout.", - (gptr*) &quick, (gptr*) &quick, 0, GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0}, + (gptr*) &quick, (gptr*) &quick, 0, GET_BOOL, NO_ARG, 1, 0, 0, 0, 0, 0}, {"quote-names",'Q', "Quote table and column names with a `", (gptr*) &opt_quoted, (gptr*) &opt_quoted, 0, GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0}, @@ -219,6 +237,9 @@ static struct my_option my_long_options[] = "Base name of shared memory", (gptr*) &shared_memory_base_name, (gptr*) &shared_memory_base_name, 0, GET_STR_ALLOC, REQUIRED_ARG, 0, 0, 0, 0, 0, 0}, #endif + {"skip-opt", OPT_SKIP_OPTIMIZATION, + "Disable --opt. Disables --add-locks, --all, --quick, --extended-insert, --lock-tables and --disable-keys.", + 0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0}, {"socket", 'S', "Socket file to use for connection.", (gptr*) &opt_mysql_unix_port, (gptr*) &opt_mysql_unix_port, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0}, @@ -322,6 +343,7 @@ static void write_header(FILE *sql_file, char *db_name) return; } /* write_header */ + static void write_footer(FILE *sql_file) { if (opt_xml) @@ -329,6 +351,7 @@ static void write_footer(FILE *sql_file) fputs("\n", sql_file); } /* write_footer */ + static my_bool get_one_option(int optid, const struct my_option *opt __attribute__((unused)), char *argument) @@ -378,27 +401,48 @@ get_one_option(int optid, const struct my_option *opt __attribute__((unused)), usage(); exit(0); case (int) OPT_OPTIMIZE: - extended_insert=opt_drop=opt_lock=quick=create_options=opt_disable_keys= - lock_tables=1; + extended_insert= opt_drop= opt_lock= quick= create_options= + opt_disable_keys= lock_tables= 1; if (opt_single_transaction) lock_tables=0; break; + case (int) OPT_SKIP_OPTIMIZATION: + extended_insert= opt_drop= opt_lock= quick= create_options= + opt_disable_keys= lock_tables= 0; + break; case (int) OPT_TABLES: opt_databases=0; break; - case OPT_MYSQL_PROTOCOL: - { - if ((opt_protocol = find_type(argument, &sql_protocol_typelib,0)) == ~(ulong) 0) - { - fprintf(stderr, "Unknown option to protocol: %s\n", argument); - exit(1); + case (int) OPT_COMPATIBLE: + { + char buff[255]; + + opt_quoted= 1; + opt_compatible_mode_str= argument; + opt_compatible_mode= find_set(&compatible_mode_typelib, + argument, strlen(argument), + &err_ptr, &err_len); + if (err_len) + { + strmake(buff, err_ptr, min(sizeof(buff), err_len)); + fprintf(stderr, "Invalid mode to --compatible: %s\n", buff); + exit(1); + } + break; + } + case (int) OPT_MYSQL_PROTOCOL: + { + if ((opt_protocol= find_type(argument, &sql_protocol_typelib, 0)) + == ~(ulong) 0) + { + fprintf(stderr, "Unknown option to protocol: %s\n", argument); + exit(1); + } + break; } - break; - } } return 0; } - static int get_options(int *argc, char ***argv) { int ho_error; @@ -418,13 +462,8 @@ static int get_options(int *argc, char ***argv) "%s: You must use option --tab with --fields-...\n", my_progname); return(1); } - - if (opt_single_transaction && lock_tables) - { - fprintf(stderr, "%s: You can't use --lock-tables and --single-transaction at the same time.\n", my_progname); - return(1); - } - + if (opt_single_transaction) + lock_tables= 0; if (enclosed && opt_enclosed) { fprintf(stderr, "%s: You can't use ..enclosed.. and ..optionally-enclosed.. at the same time.\n", my_progname); @@ -604,6 +643,31 @@ static uint getTableStructure(char *table, char* db) /* Make an sql-file, if path was given iow. option -T was given */ char buff[20+FN_REFLEN]; + if (opt_compatible_mode) + { + char *end; + uint i; + + sprintf(buff, "/*!41000 SET @@sql_mode=\""); + end= strend(buff); + for (i= 0; opt_compatible_mode; opt_compatible_mode>>= 1, i++) + { + if (opt_compatible_mode & 1) + { + end= strmov(end, compatible_mode_names[i]); + end= strmov(end, ","); + } + } + end= strmov(--end, "\" */"); + if (mysql_query(sock, buff)) + { + fprintf(stderr, "%s: Can't set the compatible mode '%s' (%s)\n", + my_progname, table, mysql_error(sock)); + safe_exit(EX_MYSQLERR); + DBUG_RETURN(0); + } + } + sprintf(buff,"show create table `%s`",table); if (mysql_query(sock, buff)) { @@ -1399,6 +1463,48 @@ static int dump_selected_tables(char *db, char **table_names, int tables) } /* dump_selected_tables */ + +static ulong find_set(TYPELIB *lib, const char *x, uint length, + char **err_pos, uint *err_len) +{ + const char *end= x + length; + ulong found= 0; + uint find; + char buff[255]; + + *err_pos= 0; // No error yet + while (end > x && my_isspace(system_charset_info, end[-1])) + end--; + + *err_len= 0; + if (x != end) + { + const char *start= x; + for (;;) + { + const char *pos= start; + uint var_len; + + for (; pos != end && *pos != ','; pos++) ; + var_len= (uint) (pos - start); + strmake(buff, start, min(sizeof(buff), var_len)); + find= find_type(buff, lib, var_len); + if (!find) + { + *err_pos= (char*) start; + *err_len= var_len; + } + else + found|= ((longlong) 1 << (find - 1)); + if (pos == end) + break; + start= pos + 1; + } + } + return found; +} + + /* Print a value with a prefix on file */ static void print_value(FILE *file, MYSQL_RES *result, MYSQL_ROW row, const char *prefix, const char *name, diff --git a/client/mysqltest.c b/client/mysqltest.c index 4fd7ad9759d..c938ec5eec0 100644 --- a/client/mysqltest.c +++ b/client/mysqltest.c @@ -42,7 +42,7 @@ **********************************************************************/ -#define MTEST_VERSION "1.26" +#define MTEST_VERSION "1.27" #include #include @@ -684,7 +684,7 @@ int open_file(const char* name) if (*cur_file && cur_file == file_stack_end) die("Source directives are nesting too deep"); - if (!(*(cur_file+1) = my_fopen(buff, O_RDONLY, MYF(MY_WME)))) + if (!(*(cur_file+1) = my_fopen(buff, O_RDONLY | O_BINARY, MYF(MY_WME)))) die(NullS); cur_file++; *++lineno=1; @@ -1925,7 +1925,7 @@ get_one_option(int optid, const struct my_option *opt __attribute__((unused)), argument= buff; } fn_format(buff, argument, "", "", 4); - if (!(*++cur_file = my_fopen(buff, O_RDONLY, MYF(MY_WME)))) + if (!(*++cur_file = my_fopen(buff, O_RDONLY | O_BINARY, MYF(MY_WME)))) die("Could not open %s: errno = %d", argument, errno); break; } diff --git a/cmd-line-utils/libedit/Makefile.am b/cmd-line-utils/libedit/Makefile.am new file mode 100644 index 00000000000..4c8d055df06 --- /dev/null +++ b/cmd-line-utils/libedit/Makefile.am @@ -0,0 +1,86 @@ +## 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=vi.c emacs.c common.c +AHDR=vi.h emacs.h common.h + +INCLUDES = -I$(top_srcdir)/include -I$(srcdir)/../.. -I.. + +noinst_LIBRARIES = liblibedit.a + +liblibedit_a_SOURCES = chared.c el.c fgetln.c history.c map.c \ + prompt.c readline.c search.c \ + strlcpy.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 + +pkginclude_HEADERS = readline/readline.h + +noinst_HEADERS = chared.h el.h histedit.h key.h \ + parse.h refresh.h sig.h sys.h \ + tokenizer.h compat.h fgetln.h \ + hist.h map.h prompt.h search.h \ + strlcpy.h term.h tty.h + +DEFS = -DUNDEF_THREADS_HACK -DHAVE_CONFIG_H -DNO_KILL_INTR + +vi.h: vi.c makelist + sh ./makelist -h ./vi.c > $@.tmp && \ + mv $@.tmp $@ + +emacs.h: emacs.c makelist + sh ./makelist -h ./emacs.c > $@.tmp && \ + mv $@.tmp $@ + +common.h: common.c makelist + sh ./makelist -h ./common.c > $@.tmp && \ + mv $@.tmp $@ + +help.c: ${ASRC} makelist + sh ./makelist -bc ${ASRC} > $@.tmp && \ + mv $@.tmp $@ + +help.h: ${ASRC} makelist + sh ./makelist -bh ${ASRC} > $@.tmp && \ + mv $@.tmp $@ + +fcns.h: ${AHDR} makelist + sh ./makelist -fh ${AHDR} > $@.tmp && \ + mv $@.tmp $@ + +fcns.c: ${AHDR} fcns.h makelist + sh ./makelist -fc ${AHDR} > $@.tmp && \ + mv $@.tmp $@ + +#%.o: vi.h emacs.h common.h help.h fcns.h +#objects := $(patsubst %.c,%.o,$(wildcard *.c)) +#$(objects): vi.h emacs.h + +chared.o: vi.h emacs.h common.h help.h fcns.h +el.o: vi.h emacs.h common.h help.h fcns.h +fgetln.o: vi.h emacs.h common.h help.h fcns.h +history.o: vi.h emacs.h common.h help.h fcns.h +map.o: vi.h emacs.h common.h help.h fcns.h +prompt.o: vi.h emacs.h common.h help.h fcns.h +readline.o: vi.h emacs.h common.h help.h fcns.h +search.o: vi.h emacs.h common.h help.h fcns.h +strlcpy.o: vi.h emacs.h common.h help.h fcns.h +tokenizer.o: vi.h emacs.h common.h help.h fcns.h +vi.o: vi.h emacs.h common.h help.h fcns.h +common.o: vi.h emacs.h common.h help.h fcns.h +emacs.o: vi.h emacs.h common.h help.h fcns.h +hist.o: vi.h emacs.h common.h help.h fcns.h +key.o: vi.h emacs.h common.h help.h fcns.h +parse.o: vi.h emacs.h common.h help.h fcns.h +read.o: vi.h emacs.h common.h help.h fcns.h +refresh.o: vi.h emacs.h common.h help.h fcns.h +sig.o: vi.h emacs.h common.h help.h fcns.h +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.% diff --git a/cmd-line-utils/libedit/TEST/test.c b/cmd-line-utils/libedit/TEST/test.c new file mode 100644 index 00000000000..605341eac62 --- /dev/null +++ b/cmd-line-utils/libedit/TEST/test.c @@ -0,0 +1,269 @@ +/* $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 new file mode 100644 index 00000000000..6ac051c3bb0 --- /dev/null +++ b/cmd-line-utils/libedit/chared.c @@ -0,0 +1,690 @@ +/* $NetBSD: chared.c,v 1.14 2001/05/17 01:02:17 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. + */ + +#include "compat.h" + +/* + * chared.c: Character editor utilities + */ +#include "sys.h" + +#include +#include "el.h" + +/* value to leave unused in line buffer */ +#define EL_LEAVE 2 + +/* cv_undo(): + * Handle state for the vi undo command + */ +protected void +cv_undo(EditLine *el,int action, size_t size, char *ptr) +{ + c_undo_t *vu = &el->el_chared.c_undo; + vu->action = action; + vu->ptr = ptr; + vu->isize = size; + (void) memcpy(vu->buf, vu->ptr, size); +#ifdef DEBUG_UNDO + (void) fprintf(el->el_errfile, "Undo buffer \"%s\" size = +%d -%d\n", + vu->ptr, vu->isize, vu->dsize); +#endif +} + + +/* c_insert(): + * Insert num characters + */ +protected void +c_insert(EditLine *el, int num) +{ + char *cp; + + if (el->el_line.lastchar + num >= el->el_line.limit) + return; /* can't go past end of buffer */ + + if (el->el_line.cursor < el->el_line.lastchar) { + /* if I must move chars */ + for (cp = el->el_line.lastchar; cp >= el->el_line.cursor; cp--) + cp[num] = *cp; + } + el->el_line.lastchar += num; +} + + +/* c_delafter(): + * Delete num characters after the cursor + */ +protected void +c_delafter(EditLine *el, int num) +{ + + if (el->el_line.cursor + num > el->el_line.lastchar) + num = el->el_line.lastchar - el->el_line.cursor; + + if (num > 0) { + char *cp; + + if (el->el_map.current != el->el_map.emacs) + cv_undo(el, INSERT, (size_t)num, el->el_line.cursor); + + for (cp = el->el_line.cursor; cp <= el->el_line.lastchar; cp++) + *cp = cp[num]; + + el->el_line.lastchar -= num; + } +} + + +/* c_delbefore(): + * Delete num characters before the cursor + */ +protected void +c_delbefore(EditLine *el, int num) +{ + + if (el->el_line.cursor - num < el->el_line.buffer) + num = el->el_line.cursor - el->el_line.buffer; + + if (num > 0) { + char *cp; + + if (el->el_map.current != el->el_map.emacs) + cv_undo(el, INSERT, (size_t)num, + el->el_line.cursor - num); + + for (cp = el->el_line.cursor - num; + cp <= el->el_line.lastchar; + cp++) + *cp = cp[num]; + + el->el_line.lastchar -= num; + } +} + + +/* ce__isword(): + * Return if p is part of a word according to emacs + */ +protected int +ce__isword(int p) +{ + return (isalpha(p) || isdigit(p) || strchr("*?_-.[]~=", p) != NULL); +} + + +/* cv__isword(): + * Return if p is part of a word according to vi + */ +protected int +cv__isword(int p) +{ + return (!isspace(p)); +} + + +/* c__prev_word(): + * Find the previous word + */ +protected char * +c__prev_word(char *p, char *low, int n, int (*wtest)(int)) +{ + p--; + + while (n--) { + while ((p >= low) && !(*wtest)((unsigned char) *p)) + p--; + while ((p >= low) && (*wtest)((unsigned char) *p)) + p--; + } + + /* cp now points to one character before the word */ + p++; + if (p < low) + p = low; + /* cp now points where we want it */ + return (p); +} + + +/* c__next_word(): + * Find the next word + */ +protected char * +c__next_word(char *p, char *high, int n, int (*wtest)(int)) +{ + while (n--) { + while ((p < high) && !(*wtest)((unsigned char) *p)) + p++; + while ((p < high) && (*wtest)((unsigned char) *p)) + p++; + } + if (p > high) + p = high; + /* p now points where we want it */ + return (p); +} + +/* cv_next_word(): + * Find the next word vi style + */ +protected char * +cv_next_word(EditLine *el, char *p, char *high, int n, int (*wtest)(int)) +{ + int test; + + while (n--) { + test = (*wtest)((unsigned char) *p); + while ((p < high) && (*wtest)((unsigned char) *p) == test) + p++; + /* + * vi historically deletes with cw only the word preserving the + * trailing whitespace! This is not what 'w' does.. + */ + if (el->el_chared.c_vcmd.action != (DELETE|INSERT)) + while ((p < high) && isspace((unsigned char) *p)) + p++; + } + + /* p now points where we want it */ + if (p > high) + return (high); + else + return (p); +} + + +/* cv_prev_word(): + * Find the previous word vi style + */ +protected char * +cv_prev_word(EditLine *el, char *p, char *low, int n, int (*wtest)(int)) +{ + int test; + + while (n--) { + p--; + /* + * vi historically deletes with cb only the word preserving the + * leading whitespace! This is not what 'b' does.. + */ + if (el->el_chared.c_vcmd.action != (DELETE|INSERT)) + while ((p > low) && isspace((unsigned char) *p)) + p--; + test = (*wtest)((unsigned char) *p); + while ((p >= low) && (*wtest)((unsigned char) *p) == test) + p--; + p++; + while (isspace((unsigned char) *p)) + p++; + } + + /* p now points where we want it */ + if (p < low) + return (low); + else + return (p); +} + + +#ifdef notdef +/* c__number(): + * Ignore character p points to, return number appearing after that. + * A '$' by itself means a big number; "$-" is for negative; '^' means 1. + * Return p pointing to last char used. + */ +protected char * +c__number( + char *p, /* character position */ + int *num, /* Return value */ + int dval) /* dval is the number to subtract from like $-3 */ +{ + int i; + int sign = 1; + + if (*++p == '^') { + *num = 1; + return (p); + } + if (*p == '$') { + if (*++p != '-') { + *num = 0x7fffffff; /* Handle $ */ + return (--p); + } + sign = -1; /* Handle $- */ + ++p; + } + for (i = 0; isdigit((unsigned char) *p); i = 10 * i + *p++ - '0') + continue; + *num = (sign < 0 ? dval - i : i); + return (--p); +} +#endif + +/* cv_delfini(): + * Finish vi delete action + */ +protected void +cv_delfini(EditLine *el) +{ + int size; + int oaction; + + if (el->el_chared.c_vcmd.action & INSERT) + el->el_map.current = el->el_map.key; + + oaction = el->el_chared.c_vcmd.action; + el->el_chared.c_vcmd.action = NOP; + + if (el->el_chared.c_vcmd.pos == 0) + return; + + + if (el->el_line.cursor > el->el_chared.c_vcmd.pos) { + size = (int) (el->el_line.cursor - el->el_chared.c_vcmd.pos); + c_delbefore(el, size); + el->el_line.cursor = el->el_chared.c_vcmd.pos; + re_refresh_cursor(el); + } else if (el->el_line.cursor < el->el_chared.c_vcmd.pos) { + size = (int)(el->el_chared.c_vcmd.pos - el->el_line.cursor); + c_delafter(el, size); + } else { + size = 1; + c_delafter(el, size); + } + switch (oaction) { + case DELETE|INSERT: + el->el_chared.c_undo.action = DELETE|INSERT; + break; + case DELETE: + el->el_chared.c_undo.action = INSERT; + break; + case NOP: + case INSERT: + default: + EL_ABORT((el->el_errfile, "Bad oaction %d\n", oaction)); + break; + } + + + el->el_chared.c_undo.ptr = el->el_line.cursor; + el->el_chared.c_undo.dsize = size; +} + + +#ifdef notdef +/* ce__endword(): + * Go to the end of this word according to emacs + */ +protected char * +ce__endword(char *p, char *high, int n) +{ + p++; + + while (n--) { + while ((p < high) && isspace((unsigned char) *p)) + p++; + while ((p < high) && !isspace((unsigned char) *p)) + p++; + } + + p--; + return (p); +} +#endif + + +/* cv__endword(): + * Go to the end of this word according to vi + */ +protected char * +cv__endword(char *p, char *high, int n) +{ + p++; + + while (n--) { + while ((p < high) && isspace((unsigned char) *p)) + p++; + + if (isalnum((unsigned char) *p)) + while ((p < high) && isalnum((unsigned char) *p)) + p++; + else + while ((p < high) && !(isspace((unsigned char) *p) || + isalnum((unsigned char) *p))) + p++; + } + p--; + return (p); +} + +/* ch_init(): + * Initialize the character editor + */ +protected int +ch_init(EditLine *el) +{ + el->el_line.buffer = (char *) el_malloc(EL_BUFSIZ); + if (el->el_line.buffer == NULL) + return (-1); + + (void) memset(el->el_line.buffer, 0, EL_BUFSIZ); + el->el_line.cursor = el->el_line.buffer; + el->el_line.lastchar = el->el_line.buffer; + el->el_line.limit = &el->el_line.buffer[EL_BUFSIZ - 2]; + + el->el_chared.c_undo.buf = (char *) el_malloc(EL_BUFSIZ); + if (el->el_chared.c_undo.buf == NULL) + return (-1); + (void) memset(el->el_chared.c_undo.buf, 0, EL_BUFSIZ); + el->el_chared.c_undo.action = NOP; + el->el_chared.c_undo.isize = 0; + el->el_chared.c_undo.dsize = 0; + el->el_chared.c_undo.ptr = el->el_line.buffer; + + el->el_chared.c_vcmd.action = NOP; + el->el_chared.c_vcmd.pos = el->el_line.buffer; + el->el_chared.c_vcmd.ins = el->el_line.buffer; + + el->el_chared.c_kill.buf = (char *) el_malloc(EL_BUFSIZ); + if (el->el_chared.c_kill.buf == NULL) + return (-1); + (void) memset(el->el_chared.c_kill.buf, 0, EL_BUFSIZ); + el->el_chared.c_kill.mark = el->el_line.buffer; + el->el_chared.c_kill.last = el->el_chared.c_kill.buf; + + el->el_map.current = el->el_map.key; + + el->el_state.inputmode = MODE_INSERT; /* XXX: save a default */ + el->el_state.doingarg = 0; + el->el_state.metanext = 0; + el->el_state.argument = 1; + el->el_state.lastcmd = ED_UNASSIGNED; + + el->el_chared.c_macro.nline = NULL; + el->el_chared.c_macro.level = -1; + el->el_chared.c_macro.macro = (char **) el_malloc(EL_MAXMACRO * + sizeof(char *)); + if (el->el_chared.c_macro.macro == NULL) + return (-1); + return (0); +} + +/* ch_reset(): + * Reset the character editor + */ +protected void +ch_reset(EditLine *el) +{ + el->el_line.cursor = el->el_line.buffer; + el->el_line.lastchar = el->el_line.buffer; + + el->el_chared.c_undo.action = NOP; + el->el_chared.c_undo.isize = 0; + el->el_chared.c_undo.dsize = 0; + el->el_chared.c_undo.ptr = el->el_line.buffer; + + el->el_chared.c_vcmd.action = NOP; + el->el_chared.c_vcmd.pos = el->el_line.buffer; + el->el_chared.c_vcmd.ins = el->el_line.buffer; + + el->el_chared.c_kill.mark = el->el_line.buffer; + + el->el_map.current = el->el_map.key; + + el->el_state.inputmode = MODE_INSERT; /* XXX: save a default */ + el->el_state.doingarg = 0; + el->el_state.metanext = 0; + el->el_state.argument = 1; + el->el_state.lastcmd = ED_UNASSIGNED; + + el->el_chared.c_macro.level = -1; + + el->el_history.eventno = 0; +} + +/* ch_enlargebufs(): + * Enlarge line buffer to be able to hold twice as much characters. + * Returns 1 if successful, 0 if not. + */ +protected int +ch_enlargebufs(el, addlen) + EditLine *el; + size_t addlen; +{ + size_t sz, newsz; + char *newbuffer, *oldbuf, *oldkbuf; + + sz = el->el_line.limit - el->el_line.buffer + EL_LEAVE; + newsz = sz * 2; + /* + * If newly required length is longer than current buffer, we need + * to make the buffer big enough to hold both old and new stuff. + */ + if (addlen > sz) { + while(newsz - sz < addlen) + newsz *= 2; + } + + /* + * Reallocate line buffer. + */ + newbuffer = el_realloc(el->el_line.buffer, newsz); + if (!newbuffer) + return 0; + + /* zero the newly added memory, leave old data in */ + (void) memset(&newbuffer[sz], 0, newsz - sz); + + oldbuf = el->el_line.buffer; + + el->el_line.buffer = newbuffer; + el->el_line.cursor = newbuffer + (el->el_line.cursor - oldbuf); + el->el_line.lastchar = newbuffer + (el->el_line.lastchar - oldbuf); + el->el_line.limit = &newbuffer[newsz - EL_LEAVE]; + + /* + * Reallocate kill buffer. + */ + newbuffer = el_realloc(el->el_chared.c_kill.buf, newsz); + if (!newbuffer) + return 0; + + /* zero the newly added memory, leave old data in */ + (void) memset(&newbuffer[sz], 0, newsz - sz); + + oldkbuf = el->el_chared.c_kill.buf; + + el->el_chared.c_kill.buf = newbuffer; + el->el_chared.c_kill.last = newbuffer + + (el->el_chared.c_kill.last - oldkbuf); + el->el_chared.c_kill.mark = el->el_line.buffer + + (el->el_chared.c_kill.mark - oldbuf); + + /* + * Reallocate undo buffer. + */ + newbuffer = el_realloc(el->el_chared.c_undo.buf, newsz); + if (!newbuffer) + return 0; + + /* zero the newly added memory, leave old data in */ + (void) memset(&newbuffer[sz], 0, newsz - sz); + + el->el_chared.c_undo.ptr = el->el_line.buffer + + (el->el_chared.c_undo.ptr - oldbuf); + el->el_chared.c_undo.buf = newbuffer; + + if (!hist_enlargebuf(el, sz, newsz)) + return 0; + + return 1; +} + +/* ch_end(): + * Free the data structures used by the editor + */ +protected void +ch_end(EditLine *el) +{ + el_free((ptr_t) el->el_line.buffer); + el->el_line.buffer = NULL; + el->el_line.limit = NULL; + el_free((ptr_t) el->el_chared.c_undo.buf); + el->el_chared.c_undo.buf = NULL; + el_free((ptr_t) el->el_chared.c_kill.buf); + el->el_chared.c_kill.buf = NULL; + el_free((ptr_t) el->el_chared.c_macro.macro); + el->el_chared.c_macro.macro = NULL; + ch_reset(el); +} + + +/* el_insertstr(): + * Insert string at cursorI + */ +public int +el_insertstr(EditLine *el, const char *s) +{ + size_t len; + + if ((len = strlen(s)) == 0) + return (-1); + if (el->el_line.lastchar + len >= el->el_line.limit) { + if (!ch_enlargebufs(el, len)) + return (-1); + } + + c_insert(el, (int)len); + while (*s) + *el->el_line.cursor++ = *s++; + return (0); +} + + +/* el_deletestr(): + * Delete num characters before the cursor + */ +public void +el_deletestr(EditLine *el, int n) +{ + if (n <= 0) + return; + + if (el->el_line.cursor < &el->el_line.buffer[n]) + return; + + c_delbefore(el, n); /* delete before dot */ + el->el_line.cursor -= n; + if (el->el_line.cursor < el->el_line.buffer) + el->el_line.cursor = el->el_line.buffer; +} + +/* c_gets(): + * Get a string + */ +protected int +c_gets(EditLine *el, char *buf) +{ + char ch; + int len = 0; + + for (ch = 0; ch == 0;) { + if (el_getc(el, &ch) != 1) + return (ed_end_of_file(el, 0)); + switch (ch) { + case 0010: /* Delete and backspace */ + case 0177: + if (len > 1) { + *el->el_line.cursor-- = '\0'; + el->el_line.lastchar = el->el_line.cursor; + buf[len--] = '\0'; + } else { + el->el_line.buffer[0] = '\0'; + el->el_line.lastchar = el->el_line.buffer; + el->el_line.cursor = el->el_line.buffer; + return (CC_REFRESH); + } + re_refresh(el); + ch = 0; + break; + + case 0033: /* ESC */ + case '\r': /* Newline */ + case '\n': + break; + + default: + if (len >= EL_BUFSIZ) + term_beep(el); + else { + buf[len++] = ch; + *el->el_line.cursor++ = ch; + el->el_line.lastchar = el->el_line.cursor; + } + re_refresh(el); + ch = 0; + break; + } + } + buf[len] = ch; + return (len); +} + + +/* c_hpos(): + * Return the current horizontal position of the cursor + */ +protected int +c_hpos(EditLine *el) +{ + char *ptr; + + /* + * Find how many characters till the beginning of this line. + */ + if (el->el_line.cursor == el->el_line.buffer) + return (0); + else { + for (ptr = el->el_line.cursor - 1; + ptr >= el->el_line.buffer && *ptr != '\n'; + ptr--) + continue; + return (el->el_line.cursor - ptr - 1); + } +} diff --git a/cmd-line-utils/libedit/chared.h b/cmd-line-utils/libedit/chared.h new file mode 100644 index 00000000000..2eb9ad32886 --- /dev/null +++ b/cmd-line-utils/libedit/chared.h @@ -0,0 +1,159 @@ +/* $NetBSD: chared.h,v 1.6 2001/01/10 07:45:41 jdolecek 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. + * + * @(#)chared.h 8.1 (Berkeley) 6/4/93 + */ + +/* + * el.chared.h: Character editor interface + */ +#ifndef _h_el_chared +#define _h_el_chared + +#include +#include + +#include "histedit.h" + +#define EL_MAXMACRO 10 + +/* + * This is a 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. + * + * On the other hand we really don't want to move the cursor, because + * all the editing commands don't include the character under the cursor. + * Probably the best fix is to make all the editing commands aware of + * this fact. + */ +#define VI_MOVE + + +typedef struct c_macro_t { + int level; + char **macro; + char *nline; +} c_macro_t; + +/* + * Undo information for both vi and emacs + */ +typedef struct c_undo_t { + int action; + size_t isize; + size_t dsize; + char *ptr; + char *buf; +} c_undo_t; + +/* + * Current action information for vi + */ +typedef struct c_vcmd_t { + int action; + char *pos; + char *ins; +} c_vcmd_t; + +/* + * Kill buffer for emacs + */ +typedef struct c_kill_t { + char *buf; + char *last; + char *mark; +} c_kill_t; + +/* + * Note that we use both data structures because the user can bind + * commands from both editors! + */ +typedef struct el_chared_t { + c_undo_t c_undo; + c_kill_t c_kill; + c_vcmd_t c_vcmd; + c_macro_t c_macro; +} el_chared_t; + + +#define STReof "^D\b\b" +#define STRQQ "\"\"" + +#define isglob(a) (strchr("*[]?", (a)) != NULL) +#define isword(a) (isprint(a)) + +#define NOP 0x00 +#define DELETE 0x01 +#define INSERT 0x02 +#define CHANGE 0x04 + +#define CHAR_FWD 0 +#define CHAR_BACK 1 + +#define MODE_INSERT 0 +#define MODE_REPLACE 1 +#define MODE_REPLACE_1 2 + +#include "common.h" +#include "vi.h" +#include "emacs.h" +#include "search.h" +#include "fcns.h" + + +protected int cv__isword(int); +protected void cv_delfini(EditLine *); +protected char *cv__endword(char *, char *, int); +protected int ce__isword(int); +protected void cv_undo(EditLine *, int, size_t, char *); +protected char *cv_next_word(EditLine*, char *, char *, int, int (*)(int)); +protected char *cv_prev_word(EditLine*, char *, char *, int, int (*)(int)); +protected char *c__next_word(char *, char *, int, int (*)(int)); +protected char *c__prev_word(char *, char *, int, int (*)(int)); +protected void c_insert(EditLine *, int); +protected void c_delbefore(EditLine *, int); +protected void c_delafter(EditLine *, int); +protected int c_gets(EditLine *, char *); +protected int c_hpos(EditLine *); + +protected int ch_init(EditLine *); +protected void ch_reset(EditLine *); +protected int ch_enlargebufs __P((EditLine *, size_t)); +protected void ch_end(EditLine *); + +#endif /* _h_el_chared */ diff --git a/cmd-line-utils/libedit/common.c b/cmd-line-utils/libedit/common.c new file mode 100644 index 00000000000..9ac6af9ac1b --- /dev/null +++ b/cmd-line-utils/libedit/common.c @@ -0,0 +1,954 @@ +/* $NetBSD: common.c,v 1.10 2001/01/10 07:45:41 jdolecek 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 "compat.h" + +/* + * common.c: Common Editor functions + */ +#include "sys.h" +#include "el.h" + +/* ed_end_of_file(): + * Indicate end of file + * [^D] + */ +protected el_action_t +/*ARGSUSED*/ +ed_end_of_file(EditLine *el, int c __attribute__((unused))) +{ + + re_goto_bottom(el); + *el->el_line.lastchar = '\0'; + return (CC_EOF); +} + + +/* ed_insert(): + * Add character to the line + * Insert a character [bound to all insert keys] + */ +protected el_action_t +ed_insert(EditLine *el, int c) +{ + int i; + + if (c == '\0') + return (CC_ERROR); + + if (el->el_line.lastchar + el->el_state.argument >= + el->el_line.limit) { + /* end of buffer space, try to allocate more */ + if (!ch_enlargebufs(el, (size_t) el->el_state.argument)) + return CC_ERROR; /* error allocating more */ + } + + if (el->el_state.argument == 1) { + if (el->el_state.inputmode != MODE_INSERT) { + el->el_chared.c_undo.buf[el->el_chared.c_undo.isize++] = + *el->el_line.cursor; + el->el_chared.c_undo.buf[el->el_chared.c_undo.isize] = + '\0'; + c_delafter(el, 1); + } + c_insert(el, 1); + + *el->el_line.cursor++ = c; + el->el_state.doingarg = 0; /* just in case */ + re_fastaddc(el); /* fast refresh for one char. */ + } else { + if (el->el_state.inputmode != MODE_INSERT) { + for (i = 0; i < el->el_state.argument; i++) + el->el_chared.c_undo.buf[el->el_chared.c_undo.isize++] = + el->el_line.cursor[i]; + + el->el_chared.c_undo.buf[el->el_chared.c_undo.isize] = + '\0'; + c_delafter(el, el->el_state.argument); + } + c_insert(el, el->el_state.argument); + + while (el->el_state.argument--) + *el->el_line.cursor++ = c; + re_refresh(el); + } + + if (el->el_state.inputmode == MODE_REPLACE_1) + (void) vi_command_mode(el, 0); + + return (CC_NORM); +} + + +/* ed_delete_prev_word(): + * Delete from beginning of current word to cursor + * [M-^?] [^W] + */ +protected el_action_t +/*ARGSUSED*/ +ed_delete_prev_word(EditLine *el, int c __attribute__((unused))) +{ + char *cp, *p, *kp; + + if (el->el_line.cursor == el->el_line.buffer) + return (CC_ERROR); + + cp = c__prev_word(el->el_line.cursor, el->el_line.buffer, + el->el_state.argument, ce__isword); + + for (p = cp, kp = el->el_chared.c_kill.buf; p < el->el_line.cursor; p++) + *kp++ = *p; + el->el_chared.c_kill.last = kp; + + c_delbefore(el, el->el_line.cursor - cp); /* delete before dot */ + el->el_line.cursor = cp; + if (el->el_line.cursor < el->el_line.buffer) + el->el_line.cursor = el->el_line.buffer; /* bounds check */ + return (CC_REFRESH); +} + + +/* ed_delete_next_char(): + * Delete character under cursor + * [^D] [x] + */ +protected el_action_t +/*ARGSUSED*/ +ed_delete_next_char(EditLine *el, int c __attribute__((unused))) +{ +#ifdef notdef /* XXX */ +#define EL el->el_line + (void) fprintf(el->el_errlfile, + "\nD(b: %x(%s) c: %x(%s) last: %x(%s) limit: %x(%s)\n", + EL.buffer, EL.buffer, EL.cursor, EL.cursor, EL.lastchar, + EL.lastchar, EL.limit, EL.limit); +#endif + if (el->el_line.cursor == el->el_line.lastchar) { + /* if I'm at the end */ + if (el->el_map.type == MAP_VI) { + if (el->el_line.cursor == el->el_line.buffer) { + /* if I'm also at the beginning */ +#ifdef KSHVI + return (CC_ERROR); +#else + term_overwrite(el, STReof, 4); + /* then do a EOF */ + term__flush(); + return (CC_EOF); +#endif + } else { +#ifdef KSHVI + el->el_line.cursor--; +#else + return (CC_ERROR); +#endif + } + } else { + if (el->el_line.cursor != el->el_line.buffer) + el->el_line.cursor--; + else + return (CC_ERROR); + } + } + c_delafter(el, el->el_state.argument); /* delete after dot */ + if (el->el_line.cursor >= el->el_line.lastchar && + el->el_line.cursor > el->el_line.buffer) + /* bounds check */ + el->el_line.cursor = el->el_line.lastchar - 1; + return (CC_REFRESH); +} + + +/* ed_kill_line(): + * Cut to the end of line + * [^K] [^K] + */ +protected el_action_t +/*ARGSUSED*/ +ed_kill_line(EditLine *el, int c __attribute__((unused))) +{ + char *kp, *cp; + + cp = el->el_line.cursor; + kp = el->el_chared.c_kill.buf; + while (cp < el->el_line.lastchar) + *kp++ = *cp++; /* copy it */ + el->el_chared.c_kill.last = kp; + /* zap! -- delete to end */ + el->el_line.lastchar = el->el_line.cursor; + return (CC_REFRESH); +} + + +/* ed_move_to_end(): + * Move cursor to the end of line + * [^E] [^E] + */ +protected el_action_t +/*ARGSUSED*/ +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 & DELETE) { + cv_delfini(el); + return (CC_REFRESH); + } + } + return (CC_CURSOR); +} + + +/* ed_move_to_beg(): + * Move cursor to the beginning of line + * [^A] [^A] + */ +protected el_action_t +/*ARGSUSED*/ +ed_move_to_beg(EditLine *el, int c __attribute__((unused))) +{ + + el->el_line.cursor = el->el_line.buffer; + + if (el->el_map.type == MAP_VI) { + /* We want FIRST non space character */ + while (isspace((unsigned char) *el->el_line.cursor)) + el->el_line.cursor++; + if (el->el_chared.c_vcmd.action & DELETE) { + cv_delfini(el); + return (CC_REFRESH); + } + } + return (CC_CURSOR); +} + + +/* ed_transpose_chars(): + * Exchange the character to the left of the cursor with the one under it + * [^T] [^T] + */ +protected el_action_t +ed_transpose_chars(EditLine *el, int c) +{ + + if (el->el_line.cursor < el->el_line.lastchar) { + if (el->el_line.lastchar <= &el->el_line.buffer[1]) + return (CC_ERROR); + else + el->el_line.cursor++; + } + if (el->el_line.cursor > &el->el_line.buffer[1]) { + /* must have at least two chars entered */ + c = el->el_line.cursor[-2]; + el->el_line.cursor[-2] = el->el_line.cursor[-1]; + el->el_line.cursor[-1] = c; + return (CC_REFRESH); + } else + return (CC_ERROR); +} + + +/* ed_next_char(): + * Move to the right one character + * [^F] [^F] + */ +protected el_action_t +/*ARGSUSED*/ +ed_next_char(EditLine *el, int c __attribute__((unused))) +{ + + if (el->el_line.cursor >= el->el_line.lastchar) + return (CC_ERROR); + + el->el_line.cursor += el->el_state.argument; + if (el->el_line.cursor > el->el_line.lastchar) + el->el_line.cursor = el->el_line.lastchar; + + if (el->el_map.type == MAP_VI) + if (el->el_chared.c_vcmd.action & DELETE) { + cv_delfini(el); + return (CC_REFRESH); + } + return (CC_CURSOR); +} + + +/* ed_prev_word(): + * Move to the beginning of the current word + * [M-b] [b] + */ +protected el_action_t +/*ARGSUSED*/ +ed_prev_word(EditLine *el, int c __attribute__((unused))) +{ + + if (el->el_line.cursor == el->el_line.buffer) + return (CC_ERROR); + + el->el_line.cursor = c__prev_word(el->el_line.cursor, + el->el_line.buffer, + el->el_state.argument, + ce__isword); + + if (el->el_map.type == MAP_VI) + if (el->el_chared.c_vcmd.action & DELETE) { + cv_delfini(el); + return (CC_REFRESH); + } + return (CC_CURSOR); +} + + +/* ed_prev_char(): + * Move to the left one character + * [^B] [^B] + */ +protected el_action_t +/*ARGSUSED*/ +ed_prev_char(EditLine *el, int c __attribute__((unused))) +{ + + if (el->el_line.cursor > el->el_line.buffer) { + el->el_line.cursor -= el->el_state.argument; + if (el->el_line.cursor < el->el_line.buffer) + el->el_line.cursor = el->el_line.buffer; + + if (el->el_map.type == MAP_VI) + if (el->el_chared.c_vcmd.action & DELETE) { + cv_delfini(el); + return (CC_REFRESH); + } + return (CC_CURSOR); + } else + return (CC_ERROR); +} + + +/* ed_quoted_insert(): + * Add the next character typed verbatim + * [^V] [^V] + */ +protected el_action_t +ed_quoted_insert(EditLine *el, int c) +{ + int num; + char tc; + + tty_quotemode(el); + num = el_getc(el, &tc); + c = (unsigned char) tc; + tty_noquotemode(el); + if (num == 1) + return (ed_insert(el, c)); + else + return (ed_end_of_file(el, 0)); +} + + +/* ed_digit(): + * Adds to argument or enters a digit + */ +protected el_action_t +ed_digit(EditLine *el, int c) +{ + + if (!isdigit(c)) + return (CC_ERROR); + + if (el->el_state.doingarg) { + /* if doing an arg, add this in... */ + if (el->el_state.lastcmd == EM_UNIVERSAL_ARGUMENT) + el->el_state.argument = c - '0'; + else { + if (el->el_state.argument > 1000000) + return (CC_ERROR); + el->el_state.argument = + (el->el_state.argument * 10) + (c - '0'); + } + return (CC_ARGHACK); + } else { + if (el->el_line.lastchar + 1 >= el->el_line.limit) { + if (!ch_enlargebufs(el, 1)) + return (CC_ERROR); + } + + if (el->el_state.inputmode != MODE_INSERT) { + el->el_chared.c_undo.buf[el->el_chared.c_undo.isize++] = + *el->el_line.cursor; + el->el_chared.c_undo.buf[el->el_chared.c_undo.isize] = + '\0'; + c_delafter(el, 1); + } + c_insert(el, 1); + *el->el_line.cursor++ = c; + el->el_state.doingarg = 0; + re_fastaddc(el); + } + return (CC_NORM); +} + + +/* ed_argument_digit(): + * Digit that starts argument + * For ESC-n + */ +protected el_action_t +ed_argument_digit(EditLine *el, int c) +{ + + if (!isdigit(c)) + return (CC_ERROR); + + if (el->el_state.doingarg) { + if (el->el_state.argument > 1000000) + return (CC_ERROR); + el->el_state.argument = (el->el_state.argument * 10) + + (c - '0'); + } else { /* else starting an argument */ + el->el_state.argument = c - '0'; + el->el_state.doingarg = 1; + } + return (CC_ARGHACK); +} + + +/* ed_unassigned(): + * Indicates unbound character + * Bound to keys that are not assigned + */ +protected el_action_t +/*ARGSUSED*/ +ed_unassigned(EditLine *el, int c __attribute__((unused))) +{ + + term_beep(el); + term__flush(); + return (CC_NORM); +} + + +/** + ** TTY key handling. + **/ + +/* ed_tty_sigint(): + * Tty interrupt character + * [^C] + */ +protected el_action_t +/*ARGSUSED*/ +ed_tty_sigint(EditLine *el __attribute__((unused)), + int c __attribute__((unused))) +{ + + return (CC_NORM); +} + + +/* ed_tty_dsusp(): + * Tty delayed suspend character + * [^Y] + */ +protected el_action_t +/*ARGSUSED*/ +ed_tty_dsusp(EditLine *el __attribute__((unused)), + int c __attribute__((unused))) +{ + + return (CC_NORM); +} + + +/* ed_tty_flush_output(): + * Tty flush output characters + * [^O] + */ +protected el_action_t +/*ARGSUSED*/ +ed_tty_flush_output(EditLine *el __attribute__((unused)), + int c __attribute__((unused))) +{ + + return (CC_NORM); +} + + +/* ed_tty_sigquit(): + * Tty quit character + * [^\] + */ +protected el_action_t +/*ARGSUSED*/ +ed_tty_sigquit(EditLine *el __attribute__((unused)), + int c __attribute__((unused))) +{ + + return (CC_NORM); +} + + +/* ed_tty_sigtstp(): + * Tty suspend character + * [^Z] + */ +protected el_action_t +/*ARGSUSED*/ +ed_tty_sigtstp(EditLine *el __attribute__((unused)), + int c __attribute__((unused))) +{ + + return (CC_NORM); +} + + +/* ed_tty_stop_output(): + * Tty disallow output characters + * [^S] + */ +protected el_action_t +/*ARGSUSED*/ +ed_tty_stop_output(EditLine *el __attribute__((unused)), + int c __attribute__((unused))) +{ + + return (CC_NORM); +} + + +/* ed_tty_start_output(): + * Tty allow output characters + * [^Q] + */ +protected el_action_t +/*ARGSUSED*/ +ed_tty_start_output(EditLine *el __attribute__((unused)), + int c __attribute__((unused))) +{ + + return (CC_NORM); +} + + +/* ed_newline(): + * Execute command + * [^J] + */ +protected el_action_t +/*ARGSUSED*/ +ed_newline(EditLine *el, int c __attribute__((unused))) +{ + + re_goto_bottom(el); + *el->el_line.lastchar++ = '\n'; + *el->el_line.lastchar = '\0'; + if (el->el_map.type == MAP_VI) + el->el_chared.c_vcmd.ins = el->el_line.buffer; + return (CC_NEWLINE); +} + + +/* ed_delete_prev_char(): + * Delete the character to the left of the cursor + * [^?] + */ +protected el_action_t +/*ARGSUSED*/ +ed_delete_prev_char(EditLine *el, int c __attribute__((unused))) +{ + + if (el->el_line.cursor <= el->el_line.buffer) + return (CC_ERROR); + + c_delbefore(el, el->el_state.argument); + el->el_line.cursor -= el->el_state.argument; + if (el->el_line.cursor < el->el_line.buffer) + el->el_line.cursor = el->el_line.buffer; + return (CC_REFRESH); +} + + +/* ed_clear_screen(): + * Clear screen leaving current line at the top + * [^L] + */ +protected el_action_t +/*ARGSUSED*/ +ed_clear_screen(EditLine *el, int c __attribute__((unused))) +{ + + term_clear_screen(el); /* clear the whole real screen */ + re_clear_display(el); /* reset everything */ + return (CC_REFRESH); +} + + +/* ed_redisplay(): + * Redisplay everything + * ^R + */ +protected el_action_t +/*ARGSUSED*/ +ed_redisplay(EditLine *el __attribute__((unused)), + int c __attribute__((unused))) +{ + + return (CC_REDISPLAY); +} + + +/* ed_start_over(): + * Erase current line and start from scratch + * [^G] + */ +protected el_action_t +/*ARGSUSED*/ +ed_start_over(EditLine *el, int c __attribute__((unused))) +{ + + ch_reset(el); + return (CC_REFRESH); +} + + +/* ed_sequence_lead_in(): + * First character in a bound sequence + * Placeholder for external keys + */ +protected el_action_t +/*ARGSUSED*/ +ed_sequence_lead_in(EditLine *el __attribute__((unused)), + int c __attribute__((unused))) +{ + + return (CC_NORM); +} + + +/* ed_prev_history(): + * Move to the previous history line + * [^P] [k] + */ +protected el_action_t +/*ARGSUSED*/ +ed_prev_history(EditLine *el, int c __attribute__((unused))) +{ + char beep = 0; + + el->el_chared.c_undo.action = NOP; + *el->el_line.lastchar = '\0'; /* just in case */ + + if (el->el_history.eventno == 0) { /* save the current buffer + * away */ + (void) strncpy(el->el_history.buf, el->el_line.buffer, + EL_BUFSIZ); + el->el_history.last = el->el_history.buf + + (el->el_line.lastchar - el->el_line.buffer); + } + el->el_history.eventno += el->el_state.argument; + + if (hist_get(el) == CC_ERROR) { + beep = 1; + /* el->el_history.eventno was fixed by first call */ + (void) hist_get(el); + } + re_refresh(el); + if (beep) + return (CC_ERROR); + else + return (CC_NORM); /* was CC_UP_HIST */ +} + + +/* ed_next_history(): + * Move to the next history line + * [^N] [j] + */ +protected el_action_t +/*ARGSUSED*/ +ed_next_history(EditLine *el, int c __attribute__((unused))) +{ + + el->el_chared.c_undo.action = NOP; + *el->el_line.lastchar = '\0'; /* just in case */ + + el->el_history.eventno -= el->el_state.argument; + + if (el->el_history.eventno < 0) { + el->el_history.eventno = 0; + return (CC_ERROR);/* make it beep */ + } + return (hist_get(el)); +} + + +/* ed_search_prev_history(): + * Search previous in history for a line matching the current + * next search history [M-P] [K] + */ +protected el_action_t +/*ARGSUSED*/ +ed_search_prev_history(EditLine *el, int c __attribute__((unused))) +{ + const char *hp; + int h; + bool_t found = 0; + + el->el_chared.c_vcmd.action = NOP; + el->el_chared.c_undo.action = NOP; + *el->el_line.lastchar = '\0'; /* just in case */ + if (el->el_history.eventno < 0) { +#ifdef DEBUG_EDIT + (void) fprintf(el->el_errfile, + "e_prev_search_hist(): eventno < 0;\n"); +#endif + el->el_history.eventno = 0; + return (CC_ERROR); + } + if (el->el_history.eventno == 0) { + (void) strncpy(el->el_history.buf, el->el_line.buffer, + EL_BUFSIZ); + el->el_history.last = el->el_history.buf + + (el->el_line.lastchar - el->el_line.buffer); + } + if (el->el_history.ref == NULL) + return (CC_ERROR); + + hp = HIST_FIRST(el); + if (hp == NULL) + return (CC_ERROR); + + c_setpat(el); /* Set search pattern !! */ + + for (h = 1; h <= el->el_history.eventno; h++) + hp = HIST_NEXT(el); + + while (hp != NULL) { +#ifdef SDEBUG + (void) fprintf(el->el_errfile, "Comparing with \"%s\"\n", hp); +#endif + if ((strncmp(hp, el->el_line.buffer, (size_t) + (el->el_line.lastchar - el->el_line.buffer)) || + hp[el->el_line.lastchar - el->el_line.buffer]) && + c_hmatch(el, hp)) { + found++; + break; + } + h++; + hp = HIST_NEXT(el); + } + + if (!found) { +#ifdef SDEBUG + (void) fprintf(el->el_errfile, "not found\n"); +#endif + return (CC_ERROR); + } + el->el_history.eventno = h; + + return (hist_get(el)); +} + + +/* ed_search_next_history(): + * Search next in history for a line matching the current + * [M-N] [J] + */ +protected el_action_t +/*ARGSUSED*/ +ed_search_next_history(EditLine *el, int c __attribute__((unused))) +{ + const char *hp; + int h; + bool_t found = 0; + + el->el_chared.c_vcmd.action = NOP; + el->el_chared.c_undo.action = NOP; + *el->el_line.lastchar = '\0'; /* just in case */ + + if (el->el_history.eventno == 0) + return (CC_ERROR); + + if (el->el_history.ref == NULL) + return (CC_ERROR); + + hp = HIST_FIRST(el); + if (hp == NULL) + return (CC_ERROR); + + c_setpat(el); /* Set search pattern !! */ + + for (h = 1; h < el->el_history.eventno && hp; h++) { +#ifdef SDEBUG + (void) fprintf(el->el_errfile, "Comparing with \"%s\"\n", hp); +#endif + if ((strncmp(hp, el->el_line.buffer, (size_t) + (el->el_line.lastchar - el->el_line.buffer)) || + hp[el->el_line.lastchar - el->el_line.buffer]) && + c_hmatch(el, hp)) + found = h; + hp = HIST_NEXT(el); + } + + if (!found) { /* is it the current history number? */ + if (!c_hmatch(el, el->el_history.buf)) { +#ifdef SDEBUG + (void) fprintf(el->el_errfile, "not found\n"); +#endif + return (CC_ERROR); + } + } + el->el_history.eventno = found; + + return (hist_get(el)); +} + + +/* ed_prev_line(): + * Move up one line + * Could be [k] [^p] + */ +protected el_action_t +/*ARGSUSED*/ +ed_prev_line(EditLine *el, int c __attribute__((unused))) +{ + char *ptr; + int nchars = c_hpos(el); + + /* + * Move to the line requested + */ + if (*(ptr = el->el_line.cursor) == '\n') + ptr--; + + for (; ptr >= el->el_line.buffer; ptr--) + if (*ptr == '\n' && --el->el_state.argument <= 0) + break; + + if (el->el_state.argument > 0) + return (CC_ERROR); + + /* + * Move to the beginning of the line + */ + for (ptr--; ptr >= el->el_line.buffer && *ptr != '\n'; ptr--) + continue; + + /* + * Move to the character requested + */ + for (ptr++; + nchars-- > 0 && ptr < el->el_line.lastchar && *ptr != '\n'; + ptr++) + continue; + + el->el_line.cursor = ptr; + return (CC_CURSOR); +} + + +/* ed_next_line(): + * Move down one line + * Could be [j] [^n] + */ +protected el_action_t +/*ARGSUSED*/ +ed_next_line(EditLine *el, int c __attribute__((unused))) +{ + char *ptr; + int nchars = c_hpos(el); + + /* + * Move to the line requested + */ + for (ptr = el->el_line.cursor; ptr < el->el_line.lastchar; ptr++) + if (*ptr == '\n' && --el->el_state.argument <= 0) + break; + + if (el->el_state.argument > 0) + return (CC_ERROR); + + /* + * Move to the character requested + */ + for (ptr++; + nchars-- > 0 && ptr < el->el_line.lastchar && *ptr != '\n'; + ptr++) + continue; + + el->el_line.cursor = ptr; + return (CC_CURSOR); +} + + +/* ed_command(): + * Editline extended command + * [M-X] [:] + */ +protected el_action_t +/*ARGSUSED*/ +ed_command(EditLine *el, int c __attribute__((unused))) +{ + char tmpbuf[EL_BUFSIZ]; + int tmplen; + + el->el_line.buffer[0] = '\0'; + el->el_line.lastchar = el->el_line.buffer; + el->el_line.cursor = el->el_line.buffer; + + c_insert(el, 3); /* prompt + ": " */ + *el->el_line.cursor++ = '\n'; + *el->el_line.cursor++ = ':'; + *el->el_line.cursor++ = ' '; + re_refresh(el); + + tmplen = c_gets(el, tmpbuf); + tmpbuf[tmplen] = '\0'; + + el->el_line.buffer[0] = '\0'; + el->el_line.lastchar = el->el_line.buffer; + el->el_line.cursor = el->el_line.buffer; + + if (parse_line(el, tmpbuf) == -1) + return (CC_ERROR); + else + return (CC_REFRESH); +} diff --git a/cmd-line-utils/libedit/compat.h b/cmd-line-utils/libedit/compat.h new file mode 100644 index 00000000000..f432ac45dfc --- /dev/null +++ b/cmd-line-utils/libedit/compat.h @@ -0,0 +1,39 @@ +#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 + +#endif diff --git a/cmd-line-utils/libedit/compat_conf.h b/cmd-line-utils/libedit/compat_conf.h new file mode 100644 index 00000000000..e2b9557f5b1 --- /dev/null +++ b/cmd-line-utils/libedit/compat_conf.h @@ -0,0 +1,2 @@ + +#include "my_config.h" diff --git a/cmd-line-utils/libedit/editline.3 b/cmd-line-utils/libedit/editline.3 new file mode 100644 index 00000000000..1b812ebcc79 --- /dev/null +++ b/cmd-line-utils/libedit/editline.3 @@ -0,0 +1,619 @@ +.\" $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 new file mode 100644 index 00000000000..b1122618939 --- /dev/null +++ b/cmd-line-utils/libedit/editrc.5 @@ -0,0 +1,491 @@ +.\" $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 new file mode 100644 index 00000000000..d436d113419 --- /dev/null +++ b/cmd-line-utils/libedit/el.c @@ -0,0 +1,468 @@ +/* $NetBSD: el.c,v 1.21 2001/01/05 22:45:30 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. + */ + +#include "compat.h" + +/* + * el.c: EditLine interface functions + */ +#include "sys.h" + +#include +#include +#include +#include +#include +#include "el.h" + +/* el_init(): + * Initialize editline and set default parameters. + */ +public EditLine * +el_init(const char *prog, FILE *fin, FILE *fout, FILE *ferr) +{ + + EditLine *el = (EditLine *) el_malloc(sizeof(EditLine)); +#ifdef DEBUG + char *tty; +#endif + + if (el == NULL) + return (NULL); + + memset(el, 0, sizeof(EditLine)); + + el->el_infd = fileno(fin); + el->el_outfile = fout; + el->el_errfile = ferr; + el->el_prog = strdup(prog); + + /* + * Initialize all the modules. Order is important!!! + */ + el->el_flags = 0; + + (void) term_init(el); + (void) key_init(el); + (void) map_init(el); + if (tty_init(el) == -1) + el->el_flags |= NO_TTY; + (void) ch_init(el); + (void) search_init(el); + (void) hist_init(el); + (void) prompt_init(el); + (void) sig_init(el); + + return (el); +} + + +/* el_end(): + * Clean up. + */ +public void +el_end(EditLine *el) +{ + + if (el == NULL) + return; + + el_reset(el); + + term_end(el); + key_end(el); + map_end(el); + tty_end(el); + ch_end(el); + search_end(el); + hist_end(el); + prompt_end(el); + sig_end(el); + + el_free((ptr_t) el->el_prog); + el_free((ptr_t) el); +} + + +/* el_reset(): + * Reset the tty and the parser + */ +public void +el_reset(EditLine *el) +{ + + tty_cookedmode(el); + ch_reset(el); /* XXX: Do we want that? */ +} + + +/* el_set(): + * set the editline parameters + */ +public int +el_set(EditLine *el, int op, ...) +{ + va_list va; + int rv; + va_start(va, op); + + if (el == NULL) + return (-1); + switch (op) { + case EL_PROMPT: + case EL_RPROMPT: + rv = prompt_set(el, va_arg(va, el_pfunc_t), op); + break; + + case EL_TERMINAL: + rv = term_set(el, va_arg(va, char *)); + break; + + case EL_EDITOR: + rv = map_set_editor(el, va_arg(va, char *)); + break; + + case EL_SIGNAL: + if (va_arg(va, int)) + el->el_flags |= HANDLE_SIGNALS; + else + el->el_flags &= ~HANDLE_SIGNALS; + rv = 0; + break; + + case EL_BIND: + case EL_TELLTC: + case EL_SETTC: + case EL_ECHOTC: + case EL_SETTY: + { + const char *argv[20]; + int i; + + for (i = 1; i < 20; i++) + if ((argv[i] = va_arg(va, 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); + break; + + default: + rv = -1; + EL_ABORT((el->el_errfile, "Bad op %d\n", op)); + break; + } + break; + } + + 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); + + rv = map_addfunc(el, name, help, func); + break; + } + + case EL_HIST: + { + hist_fun_t func = va_arg(va, hist_fun_t); + ptr_t ptr = va_arg(va, char *); + + rv = hist_set(el, func, ptr); + break; + } + + case EL_EDITMODE: + if (va_arg(va, int)) + el->el_flags &= ~EDIT_DISABLED; + else + el->el_flags |= EDIT_DISABLED; + rv = 0; + break; + + default: + rv = -1; + } + + va_end(va); + return (rv); +} + + +/* el_get(): + * retrieve the editline parameters + */ +public int +el_get(EditLine *el, int op, void *ret) +{ + int rv; + + if (el == NULL || ret == NULL) + return (-1); + switch (op) { + case EL_PROMPT: + case EL_RPROMPT: + rv = prompt_get(el, (el_pfunc_t *) & ret, op); + break; + + case EL_EDITOR: + rv = map_get_editor(el, (const char **) &ret); + break; + + case EL_SIGNAL: + *((int *) ret) = (el->el_flags & HANDLE_SIGNALS); + rv = 0; + break; + + case EL_EDITMODE: + *((int *) ret) = (!(el->el_flags & EDIT_DISABLED)); + rv = 0; + break; + +#if 0 /* XXX */ + case EL_TERMINAL: + rv = term_get(el, (const char *) &ret); + break; + + case EL_BIND: + case EL_TELLTC: + case EL_SETTC: + case EL_ECHOTC: + case EL_SETTY: + { + char *argv[20]; + int i; + + for (i = 1; i < 20; i++) + if ((argv[i] = va_arg(va, 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); + break; + + default: + rv = -1; + EL_ABORT((el->errfile, "Bad op %d\n", op)); + break; + } + break; + } + + 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); + + rv = map_addfunc(el, name, help, func); + break; + } + + case EL_HIST: + { + hist_fun_t func = va_arg(va, hist_fun_t); + ptr_t ptr = va_arg(va, char *); + rv = hist_set(el, func, ptr); + } + break; +#endif /* XXX */ + + default: + rv = -1; + } + + return (rv); +} + + +/* el_line(): + * Return editing info + */ +public const LineInfo * +el_line(EditLine *el) +{ + + return (const LineInfo *) (void *) &el->el_line; +} + +static const char elpath[] = "/.editrc"; + +/* el_source(): + * Source a file + */ +public int +el_source(EditLine *el, const char *fname) +{ + FILE *fp; + size_t len; + char *ptr, path[MAXPATHLEN]; + + fp = NULL; + if (fname == NULL) { + if (issetugid()) + return (-1); + if ((ptr = getenv("HOME")) == NULL) + return (-1); + if (strlcpy(path, ptr, sizeof(path)) >= sizeof(path)) + return (-1); + if (strlcat(path, elpath, sizeof(path)) >= sizeof(path)) + return (-1); + fname = path; + } + if (fp == NULL) + fp = fopen(fname, "r"); + if (fp == NULL) + return (-1); + + while ((ptr = fgetln(fp, &len)) != NULL) { + if (len > 0 && ptr[len - 1] == '\n') + --len; + ptr[len] = '\0'; + if (parse_line(el, ptr) == -1) { + (void) fclose(fp); + return (-1); + } + } + + (void) fclose(fp); + return (0); +} + + +/* el_resize(): + * Called from program when terminal is resized + */ +public void +el_resize(EditLine *el) +{ + int lins, cols; + sigset_t oset, nset; + + (void) sigemptyset(&nset); + (void) sigaddset(&nset, SIGWINCH); + (void) sigprocmask(SIG_BLOCK, &nset, &oset); + + /* get the correct window size */ + if (term_get_size(el, &lins, &cols)) + term_change_size(el, lins, cols); + + (void) sigprocmask(SIG_SETMASK, &oset, NULL); +} + + +/* el_beep(): + * Called from the program to beep + */ +public void +el_beep(EditLine *el) +{ + + term_beep(el); +} + + +/* el_editmode() + * Set the state of EDIT_DISABLED from the `edit' command. + */ +protected int +/*ARGSUSED*/ +el_editmode(EditLine *el, int argc, const char **argv) +{ + const char *how; + + if (argv == NULL || argc != 2 || argv[1] == NULL) + return (-1); + + how = argv[1]; + if (strcmp(how, "on") == 0) + el->el_flags &= ~EDIT_DISABLED; + else if (strcmp(how, "off") == 0) + el->el_flags |= EDIT_DISABLED; + else { + (void) fprintf(el->el_errfile, "edit: Bad value `%s'.\n", how); + return (-1); + } + return (0); +} diff --git a/cmd-line-utils/libedit/el.h b/cmd-line-utils/libedit/el.h new file mode 100644 index 00000000000..484cbab6122 --- /dev/null +++ b/cmd-line-utils/libedit/el.h @@ -0,0 +1,142 @@ +/* $NetBSD: el.h,v 1.8 2001/01/06 14:44:50 jdolecek 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. + * + * @(#)el.h 8.1 (Berkeley) 6/4/93 + */ + +/* + * el.h: Internal structures. + */ +#ifndef _h_el +#define _h_el +/* + * Local defaults + */ +#define KSHVI +#define VIDEFAULT +#define ANCHOR + +#include +#include + +#define EL_BUFSIZ 1024 /* Maximum line size */ + +#define HANDLE_SIGNALS 1<<0 +#define NO_TTY 1<<1 +#define EDIT_DISABLED 1<<2 + +typedef int bool_t; /* True or not */ + +typedef unsigned char el_action_t; /* Index to command array */ + +typedef struct coord_t { /* Position on the screen */ + int h; + int v; +} coord_t; + +typedef struct el_line_t { + char *buffer; /* Input line */ + char *cursor; /* Cursor position */ + char *lastchar; /* Last character */ + const char *limit; /* Max position */ +} el_line_t; + +/* + * Editor state + */ +typedef struct el_state_t { + int inputmode; /* What mode are we in? */ + int doingarg; /* Are we getting an argument? */ + int argument; /* Numeric argument */ + int metanext; /* Is the next char a meta char */ + el_action_t lastcmd; /* Previous command */ +} el_state_t; + +/* + * Until we come up with something better... + */ +#define el_malloc(a) malloc(a) +#define el_realloc(a,b) realloc(a, b) +#define el_free(a) free(a) + +#include "tty.h" +#include "prompt.h" +#include "key.h" +#include "term.h" +#include "refresh.h" +#include "chared.h" +#include "common.h" +#include "search.h" +#include "hist.h" +#include "map.h" +#include "parse.h" +#include "sig.h" +#include "help.h" + +struct editline { + char *el_prog; /* the program name */ + FILE *el_outfile; /* Stdio stuff */ + FILE *el_errfile; /* Stdio stuff */ + int el_infd; /* Input file descriptor */ + int el_flags; /* Various flags. */ + coord_t el_cursor; /* Cursor location */ + char **el_display; /* Real screen image = what is there */ + char **el_vdisplay; /* Virtual screen image = what we see */ + el_line_t el_line; /* The current line information */ + el_state_t el_state; /* Current editor state */ + el_term_t el_term; /* Terminal dependent stuff */ + el_tty_t el_tty; /* Tty dependent stuff */ + el_refresh_t el_refresh; /* Refresh stuff */ + el_prompt_t el_prompt; /* Prompt stuff */ + el_prompt_t el_rprompt; /* Prompt stuff */ + el_chared_t el_chared; /* Characted editor stuff */ + el_map_t el_map; /* Key mapping stuff */ + el_key_t el_key; /* Key binding stuff */ + el_history_t el_history; /* History stuff */ + el_search_t el_search; /* Search stuff */ + el_signal_t el_signal; /* Signal handling stuff */ +}; + +protected int el_editmode(EditLine *, int, const char **); + +#ifdef DEBUG +#define EL_ABORT(a) (void) (fprintf(el->el_errfile, "%s, %d: ", \ + __FILE__, __LINE__), fprintf a, abort()) +#else +#define EL_ABORT(a) abort() +#endif +#endif /* _h_el */ diff --git a/cmd-line-utils/libedit/emacs.c b/cmd-line-utils/libedit/emacs.c new file mode 100644 index 00000000000..bb5ffb2a9f6 --- /dev/null +++ b/cmd-line-utils/libedit/emacs.c @@ -0,0 +1,482 @@ +/* $NetBSD: emacs.c,v 1.9 2001/01/10 07:45:41 jdolecek 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 "compat.h" + +/* + * emacs.c: Emacs functions + */ +#include "sys.h" +#include "el.h" + +/* em_delete_or_list(): + * Delete character under cursor or list completions if at end of line + * [^D] + */ +protected el_action_t +/*ARGSUSED*/ +em_delete_or_list(EditLine *el, int c __attribute__((unused))) +{ + + 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(); + return (CC_EOF); + } else { + /* + * Here we could list completions, but it is an + * error right now + */ + term_beep(el); + return (CC_ERROR); + } + } else { + c_delafter(el, el->el_state.argument); /* delete after dot */ + if (el->el_line.cursor > el->el_line.lastchar) + el->el_line.cursor = el->el_line.lastchar; + /* bounds check */ + return (CC_REFRESH); + } +} + + +/* em_delete_next_word(): + * Cut from cursor to end of current word + * [M-d] + */ +protected el_action_t +/*ARGSUSED*/ +em_delete_next_word(EditLine *el, int c __attribute__((unused))) +{ + char *cp, *p, *kp; + + if (el->el_line.cursor == el->el_line.lastchar) + return (CC_ERROR); + + cp = c__next_word(el->el_line.cursor, el->el_line.lastchar, + el->el_state.argument, ce__isword); + + for (p = el->el_line.cursor, kp = el->el_chared.c_kill.buf; p < cp; p++) + /* save the text */ + *kp++ = *p; + el->el_chared.c_kill.last = kp; + + c_delafter(el, cp - el->el_line.cursor); /* delete after dot */ + if (el->el_line.cursor > el->el_line.lastchar) + el->el_line.cursor = el->el_line.lastchar; + /* bounds check */ + return (CC_REFRESH); +} + + +/* em_yank(): + * Paste cut buffer at cursor position + * [^Y] + */ +protected el_action_t +/*ARGSUSED*/ +em_yank(EditLine *el, int c __attribute__((unused))) +{ + char *kp, *cp; + + if (el->el_chared.c_kill.last == el->el_chared.c_kill.buf) { + if (!ch_enlargebufs(el, 1)) + return (CC_ERROR); + } + + if (el->el_line.lastchar + + (el->el_chared.c_kill.last - el->el_chared.c_kill.buf) >= + el->el_line.limit) + return (CC_ERROR); + + el->el_chared.c_kill.mark = el->el_line.cursor; + cp = el->el_line.cursor; + + /* open the space, */ + c_insert(el, el->el_chared.c_kill.last - el->el_chared.c_kill.buf); + /* copy the chars */ + for (kp = el->el_chared.c_kill.buf; kp < el->el_chared.c_kill.last; kp++) + *cp++ = *kp; + + /* if an arg, cursor at beginning else cursor at end */ + if (el->el_state.argument == 1) + el->el_line.cursor = cp; + + return (CC_REFRESH); +} + + +/* em_kill_line(): + * Cut the entire line and save in cut buffer + * [^U] + */ +protected el_action_t +/*ARGSUSED*/ +em_kill_line(EditLine *el, int c __attribute__((unused))) +{ + char *kp, *cp; + + cp = el->el_line.buffer; + kp = el->el_chared.c_kill.buf; + while (cp < el->el_line.lastchar) + *kp++ = *cp++; /* copy it */ + el->el_chared.c_kill.last = kp; + /* zap! -- delete all of it */ + el->el_line.lastchar = el->el_line.buffer; + el->el_line.cursor = el->el_line.buffer; + return (CC_REFRESH); +} + + +/* em_kill_region(): + * Cut area between mark and cursor and save in cut buffer + * [^W] + */ +protected el_action_t +/*ARGSUSED*/ +em_kill_region(EditLine *el, int c __attribute__((unused))) +{ + char *kp, *cp; + + if (!el->el_chared.c_kill.mark) + return (CC_ERROR); + + if (el->el_chared.c_kill.mark > el->el_line.cursor) { + cp = el->el_line.cursor; + kp = el->el_chared.c_kill.buf; + while (cp < el->el_chared.c_kill.mark) + *kp++ = *cp++; /* copy it */ + el->el_chared.c_kill.last = kp; + c_delafter(el, cp - el->el_line.cursor); + } else { /* mark is before cursor */ + cp = el->el_chared.c_kill.mark; + kp = el->el_chared.c_kill.buf; + while (cp < el->el_line.cursor) + *kp++ = *cp++; /* copy it */ + el->el_chared.c_kill.last = kp; + c_delbefore(el, cp - el->el_chared.c_kill.mark); + el->el_line.cursor = el->el_chared.c_kill.mark; + } + return (CC_REFRESH); +} + + +/* em_copy_region(): + * Copy area between mark and cursor to cut buffer + * [M-W] + */ +protected el_action_t +/*ARGSUSED*/ +em_copy_region(EditLine *el, int c __attribute__((unused))) +{ + char *kp, *cp; + + if (el->el_chared.c_kill.mark) + return (CC_ERROR); + + if (el->el_chared.c_kill.mark > el->el_line.cursor) { + cp = el->el_line.cursor; + kp = el->el_chared.c_kill.buf; + while (cp < el->el_chared.c_kill.mark) + *kp++ = *cp++; /* copy it */ + el->el_chared.c_kill.last = kp; + } else { + cp = el->el_chared.c_kill.mark; + kp = el->el_chared.c_kill.buf; + while (cp < el->el_line.cursor) + *kp++ = *cp++; /* copy it */ + el->el_chared.c_kill.last = kp; + } + return (CC_NORM); +} + + +/* em_gosmacs_traspose(): + * Exchange the two characters before the cursor + * Gosling emacs transpose chars [^T] + */ +protected el_action_t +em_gosmacs_traspose(EditLine *el, int c) +{ + + if (el->el_line.cursor > &el->el_line.buffer[1]) { + /* must have at least two chars entered */ + c = el->el_line.cursor[-2]; + el->el_line.cursor[-2] = el->el_line.cursor[-1]; + el->el_line.cursor[-1] = c; + return (CC_REFRESH); + } else + return (CC_ERROR); +} + + +/* em_next_word(): + * Move next to end of current word + * [M-f] + */ +protected el_action_t +/*ARGSUSED*/ +em_next_word(EditLine *el, int c __attribute__((unused))) +{ + if (el->el_line.cursor == el->el_line.lastchar) + return (CC_ERROR); + + el->el_line.cursor = c__next_word(el->el_line.cursor, + el->el_line.lastchar, + el->el_state.argument, + ce__isword); + + if (el->el_map.type == MAP_VI) + if (el->el_chared.c_vcmd.action & DELETE) { + cv_delfini(el); + return (CC_REFRESH); + } + return (CC_CURSOR); +} + + +/* em_upper_case(): + * Uppercase the characters from cursor to end of current word + * [M-u] + */ +protected el_action_t +/*ARGSUSED*/ +em_upper_case(EditLine *el, int c __attribute__((unused))) +{ + char *cp, *ep; + + ep = c__next_word(el->el_line.cursor, el->el_line.lastchar, + el->el_state.argument, ce__isword); + + for (cp = el->el_line.cursor; cp < ep; cp++) + if (islower((unsigned char) *cp)) + *cp = toupper(*cp); + + el->el_line.cursor = ep; + if (el->el_line.cursor > el->el_line.lastchar) + el->el_line.cursor = el->el_line.lastchar; + return (CC_REFRESH); +} + + +/* em_capitol_case(): + * Capitalize the characters from cursor to end of current word + * [M-c] + */ +protected el_action_t +/*ARGSUSED*/ +em_capitol_case(EditLine *el, int c __attribute__((unused))) +{ + char *cp, *ep; + + ep = c__next_word(el->el_line.cursor, el->el_line.lastchar, + el->el_state.argument, ce__isword); + + for (cp = el->el_line.cursor; cp < ep; cp++) { + if (isalpha((unsigned char) *cp)) { + if (islower((unsigned char) *cp)) + *cp = toupper(*cp); + cp++; + break; + } + } + for (; cp < ep; cp++) + if (isupper((unsigned char) *cp)) + *cp = tolower(*cp); + + el->el_line.cursor = ep; + if (el->el_line.cursor > el->el_line.lastchar) + el->el_line.cursor = el->el_line.lastchar; + return (CC_REFRESH); +} + + +/* em_lower_case(): + * Lowercase the characters from cursor to end of current word + * [M-l] + */ +protected el_action_t +/*ARGSUSED*/ +em_lower_case(EditLine *el, int c __attribute__((unused))) +{ + char *cp, *ep; + + ep = c__next_word(el->el_line.cursor, el->el_line.lastchar, + el->el_state.argument, ce__isword); + + for (cp = el->el_line.cursor; cp < ep; cp++) + if (isupper((unsigned char) *cp)) + *cp = tolower(*cp); + + el->el_line.cursor = ep; + if (el->el_line.cursor > el->el_line.lastchar) + el->el_line.cursor = el->el_line.lastchar; + return (CC_REFRESH); +} + + +/* em_set_mark(): + * Set the mark at cursor + * [^@] + */ +protected el_action_t +/*ARGSUSED*/ +em_set_mark(EditLine *el, int c __attribute__((unused))) +{ + + el->el_chared.c_kill.mark = el->el_line.cursor; + return (CC_NORM); +} + + +/* em_exchange_mark(): + * Exchange the cursor and mark + * [^X^X] + */ +protected el_action_t +/*ARGSUSED*/ +em_exchange_mark(EditLine *el, int c __attribute__((unused))) +{ + char *cp; + + cp = el->el_line.cursor; + el->el_line.cursor = el->el_chared.c_kill.mark; + el->el_chared.c_kill.mark = cp; + return (CC_CURSOR); +} + + +/* em_universal_argument(): + * Universal argument (argument times 4) + * [^U] + */ +protected el_action_t +/*ARGSUSED*/ +em_universal_argument(EditLine *el, int c __attribute__((unused))) +{ /* multiply current argument by 4 */ + + if (el->el_state.argument > 1000000) + return (CC_ERROR); + el->el_state.doingarg = 1; + el->el_state.argument *= 4; + return (CC_ARGHACK); +} + + +/* em_meta_next(): + * Add 8th bit to next character typed + * [] + */ +protected el_action_t +/*ARGSUSED*/ +em_meta_next(EditLine *el, int c __attribute__((unused))) +{ + + el->el_state.metanext = 1; + return (CC_ARGHACK); +} + + +/* em_toggle_overwrite(): + * Switch from insert to overwrite mode or vice versa + */ +protected el_action_t +/*ARGSUSED*/ +em_toggle_overwrite(EditLine *el, int c __attribute__((unused))) +{ + + el->el_state.inputmode = (el->el_state.inputmode == MODE_INSERT) ? + MODE_REPLACE : MODE_INSERT; + return (CC_NORM); +} + + +/* em_copy_prev_word(): + * Copy current word to cursor + */ +protected el_action_t +/*ARGSUSED*/ +em_copy_prev_word(EditLine *el, int c __attribute__((unused))) +{ + char *cp, *oldc, *dp; + + if (el->el_line.cursor == el->el_line.buffer) + return (CC_ERROR); + + oldc = el->el_line.cursor; + /* does a bounds check */ + cp = c__prev_word(el->el_line.cursor, el->el_line.buffer, + el->el_state.argument, ce__isword); + + c_insert(el, oldc - cp); + for (dp = oldc; cp < oldc && dp < el->el_line.lastchar; cp++) + *dp++ = *cp; + + el->el_line.cursor = dp;/* put cursor at end */ + + return (CC_REFRESH); +} + + +/* em_inc_search_next(): + * Emacs incremental next search + */ +protected el_action_t +/*ARGSUSED*/ +em_inc_search_next(EditLine *el, int c __attribute__((unused))) +{ + + el->el_search.patlen = 0; + return (ce_inc_search(el, ED_SEARCH_NEXT_HISTORY)); +} + + +/* em_inc_search_prev(): + * Emacs incremental reverse search + */ +protected el_action_t +/*ARGSUSED*/ +em_inc_search_prev(EditLine *el, int c __attribute__((unused))) +{ + + el->el_search.patlen = 0; + return (ce_inc_search(el, ED_SEARCH_PREV_HISTORY)); +} diff --git a/cmd-line-utils/libedit/fgetln.c b/cmd-line-utils/libedit/fgetln.c new file mode 100644 index 00000000000..9804866147f --- /dev/null +++ b/cmd-line-utils/libedit/fgetln.c @@ -0,0 +1,108 @@ +#include +#include "compat.h" + +#ifndef HAVE_FGETLN + +#ifdef HAVE_GETLINE + +extern int getline (char **lineptr, size_t *n, FILE *stream); + +#else + +/* The interface here is that of GNU libc's getline */ +static int +getline (char **lineptr, size_t *n, FILE *stream) +{ +#define EXPAND_CHUNK 16 + + int n_read = 0; + char *line = *lineptr; + + if (lineptr == NULL) return -1; + if (n == NULL) return -1; + if (stream == NULL) return -1; + if (*lineptr == NULL || *n == 0) return -1; + +#ifdef HAVE_FLOCKFILE + flockfile (stream); +#endif + + while (1) + { + int c; + +#ifdef HAVE_FLOCKFILE + c = getc_unlocked (stream); +#else + c = getc (stream); +#endif + + if (c == EOF) + { + if (n_read > 0) + line[n_read] = '\0'; + break; + } + + if (n_read + 2 >= *n) + { + size_t new_size; + + if (*n == 0) + new_size = 16; + else + new_size = *n * 2; + + if (*n >= new_size) /* Overflowed size_t */ + line = NULL; + else + line = (char *)*lineptr ? realloc (*lineptr, new_size) : malloc (new_size); + + if (line) + { + *lineptr = line; + *n = new_size; + } + else + { + if (*n > 0) + { + (*lineptr)[*n - 1] = '\0'; + n_read = *n - 2; + } + break; + } + } + + line[n_read] = c; + n_read++; + + if (c == '\n') + { + line[n_read] = '\0'; + break; + } + } + +#ifdef HAVE_FLOCKFILE + funlockfile (stream); +#endif + + return n_read - 1; +} +#endif /* ! HAVE_GETLINE */ + +char *fgetln(FILE *stream, size_t *len) +{ + char *ptr = NULL; + int sz; + size_t length= 0; + + sz = getline(&ptr, &length, stream); + if(len) { + *len = sz; + } + + return sz >= 0 ? ptr : NULL; +} +#endif diff --git a/cmd-line-utils/libedit/fgetln.h b/cmd-line-utils/libedit/fgetln.h new file mode 100644 index 00000000000..b2ddce01da9 --- /dev/null +++ b/cmd-line-utils/libedit/fgetln.h @@ -0,0 +1,3 @@ +#include + +char *fgetln(FILE *stream, size_t *len); diff --git a/cmd-line-utils/libedit/hist.c b/cmd-line-utils/libedit/hist.c new file mode 100644 index 00000000000..2b20c7d14dc --- /dev/null +++ b/cmd-line-utils/libedit/hist.c @@ -0,0 +1,192 @@ +/* $NetBSD: hist.c,v 1.9 2001/05/17 01:02:17 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. + */ + +#include "compat.h" + +/* + * hist.c: History access functions + */ +#include "sys.h" +#include +#include "el.h" + +/* hist_init(): + * Initialization function. + */ +protected int +hist_init(EditLine *el) +{ + + el->el_history.fun = NULL; + el->el_history.ref = NULL; + el->el_history.buf = (char *) el_malloc(EL_BUFSIZ); + el->el_history.sz = EL_BUFSIZ; + if (el->el_history.buf == NULL) + return (-1); + el->el_history.last = el->el_history.buf; + return (0); +} + + +/* hist_end(): + * clean up history; + */ +protected void +hist_end(EditLine *el) +{ + + el_free((ptr_t) el->el_history.buf); + el->el_history.buf = NULL; +} + + +/* hist_set(): + * Set new history interface + */ +protected int +hist_set(EditLine *el, hist_fun_t fun, ptr_t ptr) +{ + + el->el_history.ref = ptr; + el->el_history.fun = fun; + return (0); +} + + +/* hist_get(): + * Get a history line and update it in the buffer. + * eventno tells us the event to get. + */ +protected el_action_t +hist_get(EditLine *el) +{ + const char *hp; + int h; + + if (el->el_history.eventno == 0) { /* if really the current line */ + (void) strncpy(el->el_line.buffer, el->el_history.buf, + el->el_history.sz); + el->el_line.lastchar = el->el_line.buffer + + (el->el_history.last - el->el_history.buf); + +#ifdef KSHVI + if (el->el_map.type == MAP_VI) + el->el_line.cursor = el->el_line.buffer; + else +#endif /* KSHVI */ + el->el_line.cursor = el->el_line.lastchar; + + return (CC_REFRESH); + } + if (el->el_history.ref == NULL) + return (CC_ERROR); + + hp = HIST_FIRST(el); + + if (hp == NULL) + return (CC_ERROR); + + for (h = 1; h < el->el_history.eventno; h++) + if ((hp = HIST_NEXT(el)) == NULL) { + el->el_history.eventno = h; + return (CC_ERROR); + } + (void) strncpy(el->el_line.buffer, hp, + (size_t)(el->el_line.limit - el->el_line.buffer)); + el->el_line.lastchar = el->el_line.buffer + strlen(el->el_line.buffer); + + if (el->el_line.lastchar > el->el_line.buffer) { + if (el->el_line.lastchar[-1] == '\n') + el->el_line.lastchar--; + if (el->el_line.lastchar[-1] == ' ') + el->el_line.lastchar--; + if (el->el_line.lastchar < el->el_line.buffer) + el->el_line.lastchar = el->el_line.buffer; + } +#ifdef KSHVI + if (el->el_map.type == MAP_VI) + el->el_line.cursor = el->el_line.buffer; + else +#endif /* KSHVI */ + el->el_line.cursor = el->el_line.lastchar; + + return (CC_REFRESH); +} + + +/* hist_list() + * List history entries + */ +protected int +/*ARGSUSED*/ +hist_list(EditLine *el, int argc __attribute__((unused)), + const char **argv __attribute__((unused))) +{ + const char *str; + + if (el->el_history.ref == NULL) + return (-1); + for (str = HIST_LAST(el); str != NULL; str = HIST_PREV(el)) + (void) fprintf(el->el_outfile, "%d %s", + el->el_history.ev.num, str); + return (0); +} + +/* hist_enlargebuf() + * Enlarge history buffer to specified value. Called from el_enlargebufs(). + * Return 0 for failure, 1 for success. + */ +protected int +/*ARGSUSED*/ +hist_enlargebuf(EditLine *el, size_t oldsz, size_t newsz) +{ + char *newbuf; + + newbuf = realloc(el->el_history.buf, newsz); + if (!newbuf) + return 0; + + (void) memset(&newbuf[oldsz], '\0', newsz - oldsz); + + el->el_history.last = newbuf + + (el->el_history.last - el->el_history.buf); + el->el_history.buf = newbuf; + el->el_history.sz = newsz; + + return 1; +} diff --git a/cmd-line-utils/libedit/hist.h b/cmd-line-utils/libedit/hist.h new file mode 100644 index 00000000000..e650b6a55a9 --- /dev/null +++ b/cmd-line-utils/libedit/hist.h @@ -0,0 +1,80 @@ +/* $NetBSD: hist.h,v 1.6 2001/01/10 07:45:41 jdolecek 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. + * + * @(#)hist.h 8.1 (Berkeley) 6/4/93 + */ + +/* + * el.hist.c: History functions + */ +#ifndef _h_el_hist +#define _h_el_hist + +#include "histedit.h" + +typedef int (*hist_fun_t)(ptr_t, HistEvent *, int, ...); + +typedef struct el_history_t { + char *buf; /* The history buffer */ + size_t sz; /* Size of history buffer */ + char *last; /* The last character */ + int eventno; /* Event we are looking for */ + ptr_t ref; /* Argument for history fcns */ + hist_fun_t fun; /* Event access */ + HistEvent ev; /* Event cookie */ +} el_history_t; + +#define HIST_FUN(el, fn, arg) \ + ((((*(el)->el_history.fun) ((el)->el_history.ref, &(el)->el_history.ev, \ + fn, arg)) == -1) ? NULL : (el)->el_history.ev.str) + +#define HIST_NEXT(el) HIST_FUN(el, H_NEXT, NULL) +#define HIST_FIRST(el) HIST_FUN(el, H_FIRST, NULL) +#define HIST_LAST(el) HIST_FUN(el, H_LAST, NULL) +#define HIST_PREV(el) HIST_FUN(el, H_PREV, NULL) +#define HIST_EVENT(el, num) HIST_FUN(el, H_EVENT, num) +#define HIST_LOAD(el, fname) HIST_FUN(el, H_LOAD fname) +#define HIST_SAVE(el, fname) HIST_FUN(el, H_SAVE fname) + +protected int hist_init(EditLine *); +protected void hist_end(EditLine *); +protected el_action_t hist_get(EditLine *); +protected int hist_set(EditLine *, hist_fun_t, ptr_t); +protected int hist_list(EditLine *, int, const char **); +protected int hist_enlargebuf(EditLine *, size_t, size_t); + +#endif /* _h_el_hist */ diff --git a/cmd-line-utils/libedit/histedit.h b/cmd-line-utils/libedit/histedit.h new file mode 100644 index 00000000000..0b8d175cef1 --- /dev/null +++ b/cmd-line-utils/libedit/histedit.h @@ -0,0 +1,190 @@ +/* $NetBSD: histedit.h,v 1.16 2000/09/04 22:06:30 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. + * + * @(#)histedit.h 8.2 (Berkeley) 1/3/94 + */ + +/* + * histedit.h: Line editor and history interface. + */ +#ifndef _HISTEDIT_H_ +#define _HISTEDIT_H_ + +#include +#include + +/* + * ==== Editing ==== + */ +typedef struct editline EditLine; + +/* + * For user-defined function interface + */ +typedef struct lineinfo { + const char *buffer; + const char *cursor; + const char *lastchar; +} LineInfo; + + +/* + * EditLine editor function return codes. + * For user-defined function interface + */ +#define CC_NORM 0 +#define CC_NEWLINE 1 +#define CC_EOF 2 +#define CC_ARGHACK 3 +#define CC_REFRESH 4 +#define CC_CURSOR 5 +#define CC_ERROR 6 +#define CC_FATAL 7 +#define CC_REDISPLAY 8 +#define CC_REFRESH_BEEP 9 + +/* + * Initialization, cleanup, and resetting + */ +EditLine *el_init(const char *, FILE *, FILE *, FILE *); +void el_reset(EditLine *); +void el_end(EditLine *); + + +/* + * Get a line, a character or push a string back in the input queue + */ +const char *el_gets(EditLine *, int *); +int el_getc(EditLine *, char *); +void el_push(EditLine *, const char *); + +/* + * Beep! + */ +void el_beep(EditLine *); + +/* + * High level function internals control + * Parses argc, argv array and executes builtin editline commands + */ +int el_parse(EditLine *, int, const char **); + +/* + * Low level editline access functions + */ +int el_set(EditLine *, int, ...); +int el_get(EditLine *, int, void *); + +/* + * el_set/el_get parameters + */ +#define EL_PROMPT 0 /* , el_pfunc_t); */ +#define EL_TERMINAL 1 /* , const char *); */ +#define EL_EDITOR 2 /* , const char *); */ +#define EL_SIGNAL 3 /* , int); */ +#define EL_BIND 4 /* , const char *, ..., NULL); */ +#define EL_TELLTC 5 /* , const char *, ..., NULL); */ +#define EL_SETTC 6 /* , const char *, ..., NULL); */ +#define EL_ECHOTC 7 /* , const char *, ..., NULL); */ +#define EL_SETTY 8 /* , const char *, ..., NULL); */ +#define EL_ADDFN 9 /* , const char *, const char * */ + /* , el_func_t); */ +#define EL_HIST 10 /* , hist_fun_t, const char *); */ +#define EL_EDITMODE 11 /* , int); */ +#define EL_RPROMPT 12 /* , el_pfunc_t); */ + +/* + * Source named file or $PWD/.editrc or $HOME/.editrc + */ +int el_source(EditLine *, const char *); + +/* + * Must be called when the terminal changes size; If EL_SIGNAL + * is set this is done automatically otherwise it is the responsibility + * of the application + */ +void el_resize(EditLine *); + + +/* + * User-defined function interface. + */ +const LineInfo *el_line(EditLine *); +int el_insertstr(EditLine *, const char *); +void el_deletestr(EditLine *, int); + +/* + * ==== History ==== + */ + +typedef struct history History; + +typedef struct HistEvent { + int num; + const char *str; +} HistEvent; + +/* + * History access functions. + */ +History * history_init(void); +void history_end(History *); + +int history(History *, HistEvent *, int, ...); + +#define H_FUNC 0 /* , UTSL */ +#define H_SETSIZE 1 /* , const int); */ +#define H_GETSIZE 2 /* , void); */ +#define H_FIRST 3 /* , void); */ +#define H_LAST 4 /* , void); */ +#define H_PREV 5 /* , void); */ +#define H_NEXT 6 /* , void); */ +#define H_CURR 8 /* , const int); */ +#define H_SET 7 /* , void); */ +#define H_ADD 9 /* , const char *); */ +#define H_ENTER 10 /* , const char *); */ +#define H_APPEND 11 /* , const char *); */ +#define H_END 12 /* , void); */ +#define H_NEXT_STR 13 /* , const char *); */ +#define H_PREV_STR 14 /* , const char *); */ +#define H_NEXT_EVENT 15 /* , const int); */ +#define H_PREV_EVENT 16 /* , const int); */ +#define H_LOAD 17 /* , const char *); */ +#define H_SAVE 18 /* , const char *); */ +#define H_CLEAR 19 /* , void); */ + +#endif /* _HISTEDIT_H_ */ diff --git a/cmd-line-utils/libedit/history.c b/cmd-line-utils/libedit/history.c new file mode 100644 index 00000000000..90d94e7fc18 --- /dev/null +++ b/cmd-line-utils/libedit/history.c @@ -0,0 +1,864 @@ +/* $NetBSD: history.c,v 1.17 2001/03/20 00:08:31 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. + */ + +#include "compat.h" + +/* + * hist.c: History access functions + */ +#include "sys.h" + +#include +#include +#include +#ifdef HAVE_VIS_H +#include +#endif +#include + +static const char hist_cookie[] = "_HiStOrY_V2_\n"; + +#include "histedit.h" + +typedef int (*history_gfun_t)(ptr_t, HistEvent *); +typedef int (*history_efun_t)(ptr_t, HistEvent *, const char *); +typedef void (*history_vfun_t)(ptr_t, HistEvent *); +typedef int (*history_sfun_t)(ptr_t, HistEvent *, const int); + +struct history { + ptr_t h_ref; /* Argument for history fcns */ + int h_ent; /* Last entry point for history */ + history_gfun_t h_first; /* Get the first element */ + history_gfun_t h_next; /* Get the next element */ + history_gfun_t h_last; /* Get the last element */ + 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_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 */ +}; +#define HNEXT(h, ev) (*(h)->h_next)((h)->h_ref, ev) +#define HFIRST(h, ev) (*(h)->h_first)((h)->h_ref, ev) +#define HPREV(h, ev) (*(h)->h_prev)((h)->h_ref, ev) +#define HLAST(h, ev) (*(h)->h_last)((h)->h_ref, ev) +#define HCURR(h, ev) (*(h)->h_curr)((h)->h_ref, ev) +#define HSET(h, ev, n) (*(h)->h_set)((h)->h_ref, ev, n) +#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 h_malloc(a) malloc(a) +#define h_realloc(a, b) realloc((a), (b)) +#define h_free(a) free(a) + + +private int history_setsize(History *, HistEvent *, int); +private int history_getsize(History *, HistEvent *); +private int history_set_fun(History *, History *); +private int history_load(History *, const char *); +private int history_save(History *, const char *); +private int history_prev_event(History *, HistEvent *, int); +private int history_next_event(History *, HistEvent *, int); +private int history_next_string(History *, HistEvent *, const char *); +private int history_prev_string(History *, HistEvent *, const char *); + + +/***********************************************************************/ + +/* + * Builtin- history implementation + */ +typedef struct hentry_t { + HistEvent ev; /* What we return */ + struct hentry_t *next; /* Next entry */ + struct hentry_t *prev; /* Previous entry */ +} hentry_t; + +typedef struct history_t { + hentry_t list; /* Fake list header element */ + hentry_t *cursor; /* Current element in the list */ + int max; /* Maximum number of events */ + int cur; /* Current number of events */ + int eventid; /* For generation of unique event id */ +} 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_prev(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_enter(ptr_t, HistEvent *, const char *); +private int history_def_add(ptr_t, HistEvent *, const char *); +private void 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 *); + +#define history_def_setsize(p, num)(void) (((history_t *) p)->max = (num)) +#define history_def_getsize(p) (((history_t *) p)->cur) + +#define he_strerror(code) he_errlist[code] +#define he_seterrev(evp, code) {\ + evp->num = code;\ + evp->str = he_strerror(code);\ + } + +/* error messages */ +static const char *const he_errlist[] = { + "OK", + "unknown error", + "malloc() failed", + "first event not found", + "last event not found", + "empty list", + "no next event", + "no previous event", + "current event is invalid", + "event not found", + "can't read history from file", + "can't write history", + "required parameter(s) not supplied", + "history size negative", + "function not allowed with other history-functions-set the default", + "bad parameters" +}; +/* error codes */ +#define _HE_OK 0 +#define _HE_UNKNOWN 1 +#define _HE_MALLOC_FAILED 2 +#define _HE_FIRST_NOTFOUND 3 +#define _HE_LAST_NOTFOUND 4 +#define _HE_EMPTY_LIST 5 +#define _HE_END_REACHED 6 +#define _HE_START_REACHED 7 +#define _HE_CURR_INVALID 8 +#define _HE_NOT_FOUND 9 +#define _HE_HIST_READ 10 +#define _HE_HIST_WRITE 11 +#define _HE_PARAM_MISSING 12 +#define _HE_SIZE_NEGATIVE 13 +#define _HE_NOT_ALLOWED 14 +#define _HE_BAD_PARAM 15 + +/* history_def_first(): + * Default function to return the first event in the history. + */ +private int +history_def_first(ptr_t p, HistEvent *ev) +{ + history_t *h = (history_t *) p; + + h->cursor = h->list.next; + if (h->cursor != &h->list) + *ev = h->cursor->ev; + else { + he_seterrev(ev, _HE_FIRST_NOTFOUND); + return (-1); + } + + return (0); +} + + +/* history_def_last(): + * Default function to return the last event in the history. + */ +private int +history_def_last(ptr_t p, HistEvent *ev) +{ + history_t *h = (history_t *) p; + + h->cursor = h->list.prev; + if (h->cursor != &h->list) + *ev = h->cursor->ev; + else { + he_seterrev(ev, _HE_LAST_NOTFOUND); + return (-1); + } + + return (0); +} + + +/* history_def_next(): + * Default function to return the next event in the history. + */ +private int +history_def_next(ptr_t p, HistEvent *ev) +{ + history_t *h = (history_t *) p; + + if (h->cursor != &h->list) + h->cursor = h->cursor->next; + else { + he_seterrev(ev, _HE_EMPTY_LIST); + return (-1); + } + + if (h->cursor != &h->list) + *ev = h->cursor->ev; + else { + he_seterrev(ev, _HE_END_REACHED); + return (-1); + } + + return (0); +} + + +/* history_def_prev(): + * Default function to return the previous event in the history. + */ +private int +history_def_prev(ptr_t p, HistEvent *ev) +{ + history_t *h = (history_t *) p; + + if (h->cursor != &h->list) + h->cursor = h->cursor->prev; + else { + he_seterrev(ev, + (h->cur > 0) ? _HE_END_REACHED : _HE_EMPTY_LIST); + return (-1); + } + + if (h->cursor != &h->list) + *ev = h->cursor->ev; + else { + he_seterrev(ev, _HE_START_REACHED); + return (-1); + } + + return (0); +} + + +/* history_def_curr(): + * Default function to return the current event in the history. + */ +private int +history_def_curr(ptr_t p, HistEvent *ev) +{ + history_t *h = (history_t *) p; + + if (h->cursor != &h->list) + *ev = h->cursor->ev; + else { + he_seterrev(ev, + (h->cur > 0) ? _HE_CURR_INVALID : _HE_EMPTY_LIST); + return (-1); + } + + return (0); +} + + +/* history_def_set(): + * Default function to set the current event in the history to the + * given one. + */ +private int +history_def_set(ptr_t p, HistEvent *ev, const int n) +{ + history_t *h = (history_t *) p; + + if (h->cur == 0) { + he_seterrev(ev, _HE_EMPTY_LIST); + return (-1); + } + if (h->cursor == &h->list || h->cursor->ev.num != n) { + for (h->cursor = h->list.next; h->cursor != &h->list; + h->cursor = h->cursor->next) + if (h->cursor->ev.num == n) + break; + } + if (h->cursor == &h->list) { + he_seterrev(ev, _HE_NOT_FOUND); + return (-1); + } + return (0); +} + + +/* history_def_add(): + * Append string to element + */ +private int +history_def_add(ptr_t p, HistEvent *ev, const char *str) +{ + history_t *h = (history_t *) p; + size_t len; + char *s; + + if (h->cursor == &h->list) + return (history_def_enter(p, ev, str)); + len = strlen(h->cursor->ev.str) + strlen(str) + 1; + s = (char *) h_malloc(len); + if (!s) { + he_seterrev(ev, _HE_MALLOC_FAILED); + return (-1); + } + (void) strlcpy(s, h->cursor->ev.str, len); + (void) strlcat(s, str, len); + /* LINTED const cast */ + h_free((ptr_t) h->cursor->ev.str); + h->cursor->ev.str = s; + *ev = h->cursor->ev; + return (0); +} + + +/* history_def_delete(): + * Delete element hp of the h list + */ +/* ARGSUSED */ +private void +history_def_delete(history_t *h, + HistEvent *ev __attribute__((unused)), hentry_t *hp) +{ + + if (hp == &h->list) + abort(); + hp->prev->next = hp->next; + hp->next->prev = hp->prev; + /* LINTED const cast */ + h_free((ptr_t) hp->ev.str); + h_free(hp); + h->cur--; +} + + +/* history_def_insert(): + * Insert element with string str in the h list + */ +private int +history_def_insert(history_t *h, HistEvent *ev, const char *str) +{ + + h->cursor = (hentry_t *) h_malloc(sizeof(hentry_t)); + if (h->cursor) + h->cursor->ev.str = strdup(str); + if (!h->cursor || !h->cursor->ev.str) { + he_seterrev(ev, _HE_MALLOC_FAILED); + return (-1); + } + h->cursor->ev.num = ++h->eventid; + h->cursor->next = h->list.next; + h->cursor->prev = &h->list; + h->list.next->prev = h->cursor; + h->list.next = h->cursor; + h->cur++; + + *ev = h->cursor->ev; + return (0); +} + + +/* history_def_enter(): + * Default function to enter an item in the history + */ +private int +history_def_enter(ptr_t p, HistEvent *ev, const char *str) +{ + history_t *h = (history_t *) p; + + if (history_def_insert(h, ev, str) == -1) + return (-1); /* error, keep error message */ + + /* + * Always keep at least one entry. + * This way we don't have to check for the empty list. + */ + while (h->cur - 1 > h->max) + history_def_delete(h, ev, h->list.prev); + + return (0); +} + + +/* history_def_init(): + * Default history initialization function + */ +/* ARGSUSED */ +private void +history_def_init(ptr_t *p, HistEvent *ev __attribute__((unused)), int n) +{ + history_t *h = (history_t *) h_malloc(sizeof(history_t)); + + if (n <= 0) + n = 0; + h->eventid = 0; + h->cur = 0; + h->max = n; + h->list.next = h->list.prev = &h->list; + h->list.ev.str = NULL; + h->list.ev.num = 0; + h->cursor = &h->list; + *p = (ptr_t) h; +} + + +/* history_def_clear(): + * Default history cleanup function + */ +private void +history_def_clear(ptr_t p, HistEvent *ev) +{ + history_t *h = (history_t *) p; + + while (h->list.prev != &h->list) + history_def_delete(h, ev, h->list.prev); + h->eventid = 0; + h->cur = 0; +} + + + + +/************************************************************************/ + +/* history_init(): + * Initialization function. + */ +public History * +history_init(void) +{ + History *h = (History *) h_malloc(sizeof(History)); + HistEvent ev; + + history_def_init(&h->h_ref, &ev, 0); + h->h_ent = -1; + h->h_next = history_def_next; + h->h_first = history_def_first; + h->h_last = history_def_last; + h->h_prev = history_def_prev; + h->h_curr = history_def_curr; + h->h_set = history_def_set; + h->h_clear = history_def_clear; + h->h_enter = history_def_enter; + h->h_add = history_def_add; + + return (h); +} + + +/* history_end(): + * clean up history; + */ +public void +history_end(History *h) +{ + HistEvent ev; + + if (h->h_next == history_def_next) + history_def_clear(h->h_ref, &ev); +} + + + +/* history_setsize(): + * Set history number of events + */ +private int +history_setsize(History *h, HistEvent *ev, int num) +{ + + if (h->h_next != history_def_next) { + he_seterrev(ev, _HE_NOT_ALLOWED); + return (-1); + } + if (num < 0) { + he_seterrev(ev, _HE_BAD_PARAM); + return (-1); + } + history_def_setsize(h->h_ref, num); + return (0); +} + + +/* history_getsize(): + * Get number of events currently in history + */ +private int +history_getsize(History *h, HistEvent *ev) +{ + int retval = 0; + + if (h->h_next != history_def_next) { + he_seterrev(ev, _HE_NOT_ALLOWED); + return (-1); + } + retval = history_def_getsize(h->h_ref); + if (retval < -1) { + he_seterrev(ev, _HE_SIZE_NEGATIVE); + return (-1); + } + ev->num = retval; + return (0); +} + + +/* history_set_fun(): + * Set history functions + */ +private int +history_set_fun(History *h, History *nh) +{ + HistEvent ev; + + 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) { + if (h->h_next != history_def_next) { + history_def_init(&h->h_ref, &ev, 0); + h->h_first = history_def_first; + h->h_next = history_def_next; + h->h_last = history_def_last; + h->h_prev = history_def_prev; + h->h_curr = history_def_curr; + h->h_set = history_def_set; + h->h_clear = history_def_clear; + h->h_enter = history_def_enter; + h->h_add = history_def_add; + } + return (-1); + } + if (h->h_next == history_def_next) + history_def_clear(h->h_ref, &ev); + + h->h_ent = -1; + h->h_first = nh->h_first; + h->h_next = nh->h_next; + h->h_last = nh->h_last; + h->h_prev = nh->h_prev; + h->h_curr = nh->h_curr; + h->h_set = nh->h_set; + h->h_clear = nh->h_clear; + h->h_enter = nh->h_enter; + h->h_add = nh->h_add; + + return (0); +} + + +/* history_load(): + * History load function + */ +private int +history_load(History *h, const char *fname) +{ + FILE *fp; + char *line; + size_t sz, max_size; + char *ptr; + int i = -1; + HistEvent ev; + + if ((fp = fopen(fname, "r")) == NULL) + return (i); + + if ((line = fgetln(fp, &sz)) == NULL) + goto done; + + if (strncmp(line, hist_cookie, sz) != 0) + goto done; + + ptr = h_malloc(max_size = 1024); + for (i = 0; (line = fgetln(fp, &sz)) != NULL; i++) { + char c = line[sz]; + + if (sz != 0 && line[sz - 1] == '\n') + line[--sz] = '\0'; + else + line[sz] = '\0'; + + if (max_size < sz) { + max_size = (sz + 1023) & ~1023; + ptr = h_realloc(ptr, max_size); + } + (void) strunvis(ptr, line); + line[sz] = c; + HENTER(h, &ev, ptr); + } + h_free(ptr); + +done: + (void) fclose(fp); + return (i); +} + + +/* history_save(): + * History save function + */ +private int +history_save(History *h, const char *fname) +{ + FILE *fp; + HistEvent ev; + int i = 0, retval; + size_t len, max_size; + char *ptr; + + if ((fp = fopen(fname, "w")) == NULL) + return (-1); + + (void) fchmod(fileno(fp), S_IRUSR|S_IWUSR); + (void) fputs(hist_cookie, fp); + ptr = h_malloc(max_size = 1024); + for (retval = HLAST(h, &ev); + retval != -1; + retval = HPREV(h, &ev), i++) { + len = strlen(ev.str) * 4; + if (len >= max_size) { + max_size = (len + 1023) & 1023; + ptr = h_realloc(ptr, max_size); + } + (void) strvis(ptr, ev.str, VIS_WHITE); + (void) fprintf(fp, "%s\n", ev.str); + } + h_free(ptr); + (void) fclose(fp); + return (i); +} + + +/* history_prev_event(): + * Find the previous event, with number given + */ +private int +history_prev_event(History *h, HistEvent *ev, int num) +{ + int retval; + + for (retval = HCURR(h, ev); retval != -1; retval = HPREV(h, ev)) + if (ev->num == num) + return (0); + + he_seterrev(ev, _HE_NOT_FOUND); + return (-1); +} + + +/* history_next_event(): + * Find the next event, with number given + */ +private int +history_next_event(History *h, HistEvent *ev, int num) +{ + int retval; + + for (retval = HCURR(h, ev); retval != -1; retval = HNEXT(h, ev)) + if (ev->num == num) + return (0); + + he_seterrev(ev, _HE_NOT_FOUND); + return (-1); +} + + +/* history_prev_string(): + * Find the previous event beginning with string + */ +private int +history_prev_string(History *h, HistEvent *ev, const char *str) +{ + size_t len = strlen(str); + int retval; + + for (retval = HCURR(h, ev); retval != -1; retval = HNEXT(h, ev)) + if (strncmp(str, ev->str, len) == 0) + return (0); + + he_seterrev(ev, _HE_NOT_FOUND); + return (-1); +} + + +/* history_next_string(): + * Find the next event beginning with string + */ +private int +history_next_string(History *h, HistEvent *ev, const char *str) +{ + size_t len = strlen(str); + int retval; + + for (retval = HCURR(h, ev); retval != -1; retval = HPREV(h, ev)) + if (strncmp(str, ev->str, len) == 0) + return (0); + + he_seterrev(ev, _HE_NOT_FOUND); + return (-1); +} + + +/* history(): + * User interface to history functions. + */ +int +history(History *h, HistEvent *ev, int fun, ...) +{ + va_list va; + const char *str; + int retval; + + va_start(va, fun); + + he_seterrev(ev, _HE_OK); + + switch (fun) { + case H_GETSIZE: + retval = history_getsize(h, ev); + break; + + case H_SETSIZE: + retval = history_setsize(h, ev, va_arg(va, int)); + break; + + case H_ADD: + str = va_arg(va, const char *); + retval = HADD(h, ev, str); + break; + + case H_ENTER: + str = va_arg(va, const char *); + if ((retval = HENTER(h, ev, str)) != -1) + h->h_ent = ev->num; + break; + + case H_APPEND: + str = va_arg(va, const char *); + if ((retval = HSET(h, ev, h->h_ent)) != -1) + retval = HADD(h, ev, str); + break; + + case H_FIRST: + retval = HFIRST(h, ev); + break; + + case H_NEXT: + retval = HNEXT(h, ev); + break; + + case H_LAST: + retval = HLAST(h, ev); + break; + + case H_PREV: + retval = HPREV(h, ev); + break; + + case H_CURR: + retval = HCURR(h, ev); + break; + + case H_SET: + retval = HSET(h, ev, va_arg(va, const int)); + break; + + case H_CLEAR: + HCLEAR(h, ev); + retval = 0; + break; + + case H_LOAD: + retval = history_load(h, va_arg(va, const char *)); + if (retval == -1) + he_seterrev(ev, _HE_HIST_READ); + break; + + case H_SAVE: + retval = history_save(h, va_arg(va, const char *)); + if (retval == -1) + he_seterrev(ev, _HE_HIST_WRITE); + break; + + case H_PREV_EVENT: + retval = history_prev_event(h, ev, va_arg(va, int)); + break; + + case H_NEXT_EVENT: + retval = history_next_event(h, ev, va_arg(va, int)); + break; + + case H_PREV_STR: + retval = history_prev_string(h, ev, va_arg(va, const char *)); + break; + + case H_NEXT_STR: + retval = history_next_string(h, ev, va_arg(va, const char *)); + break; + + case H_FUNC: + { + History hf; + + hf.h_ref = va_arg(va, ptr_t); + h->h_ent = -1; + hf.h_first = va_arg(va, history_gfun_t); + hf.h_next = va_arg(va, history_gfun_t); + hf.h_last = va_arg(va, history_gfun_t); + hf.h_prev = va_arg(va, history_gfun_t); + hf.h_curr = va_arg(va, history_gfun_t); + hf.h_set = va_arg(va, history_sfun_t); + 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); + + if ((retval = history_set_fun(h, &hf)) == -1) + he_seterrev(ev, _HE_PARAM_MISSING); + break; + } + + case H_END: + history_end(h); + retval = 0; + break; + + default: + retval = -1; + he_seterrev(ev, _HE_UNKNOWN); + break; + } + va_end(va); + return (retval); +} diff --git a/cmd-line-utils/libedit/key.c b/cmd-line-utils/libedit/key.c new file mode 100644 index 00000000000..629c6aeeb9c --- /dev/null +++ b/cmd-line-utils/libedit/key.c @@ -0,0 +1,681 @@ +/* $NetBSD: key.c,v 1.12 2001/05/17 01:02:17 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. + */ + +#include "compat.h" + +/* + * 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 + * 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). + * + * Warning: + * If key is a substr of some other keys, then the longer + * keys are lost!! That is, if the keys "abcd" and "abcef" + * are in el->el_key.map, adding the key "abc" will cause the first two + * definitions to be lost. + * + * Restrictions: + * ------------- + * 1) It is not possible to have one key that is a + * substr of another. + */ +#include "sys.h" +#include +#include + +#include "el.h" + +/* + * The Nodes of the el->el_key.map. The el->el_key.map is a linked list + * of these node elements + */ +struct key_node_t { + char ch; /* single character of key */ + int type; /* node type */ + key_value_t val; /* command code or pointer to str, */ + /* if this is a leaf */ + struct key_node_t *next; /* ptr to next char of this key */ + struct key_node_t *sibling; /* ptr to another key with same prefix*/ +}; + +private int node_trav(EditLine *, key_node_t *, char *, + key_value_t *); +private int node__try(EditLine *, key_node_t *, const char *, + key_value_t *, int); +private key_node_t *node__get(int); +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 + + +/* key_init(): + * Initialize the key maps + */ +protected int +key_init(EditLine *el) +{ + + el->el_key.buf = (char *) el_malloc(KEY_BUFSIZ); + if (el->el_key.buf == NULL) + return (-1); + el->el_key.map = NULL; + key_reset(el); + return (0); +} + + +/* key_end(): + * Free the key maps + */ +protected void +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; +} + + +/* key_map_cmd(): + * Associate cmd with a key value + */ +protected key_value_t * +key_map_cmd(EditLine *el, int cmd) +{ + + el->el_key.val.cmd = (el_action_t) cmd; + return (&el->el_key.val); +} + + +/* key_map_str(): + * Associate str with a key value + */ +protected key_value_t * +key_map_str(EditLine *el, char *str) +{ + + el->el_key.val.str = str; + return (&el->el_key.val); +} + + +/* key_reset(): + * Takes all nodes on el->el_key.map and puts them on free list. Then + * initializes el->el_key.map with arrow keys + * [Always bind the ansi arrow keys?] + */ +protected void +key_reset(EditLine *el) +{ + + node__put(el, el->el_key.map); + el->el_key.map = NULL; + return; +} + + +/* key_get(): + * Calls the recursive function with entry point el->el_key.map + * Looks up *ch in map and then reads characters until a + * complete match is found or a mismatch occurs. Returns the + * type of the match found (XK_STR, XK_CMD, or XK_EXE). + * Returns NULL in val.str and XK_STR for no match. + * The last character read is returned in *ch. + */ +protected int +key_get(EditLine *el, char *ch, key_value_t *val) +{ + + return (node_trav(el, el->el_key.map, ch, val)); +} + + +/* key_add(): + * Adds key to the el->el_key.map and associates the value in val with it. + * If key is already is in el->el_key.map, the new code is applied to the + * existing key. Ntype specifies if code is a command, an + * out str or a unix command. + */ +protected void +key_add(EditLine *el, const char *key, key_value_t *val, int ntype) +{ + + if (key[0] == '\0') { + (void) fprintf(el->el_errfile, + "key_add: Null extended-key not allowed.\n"); + return; + } + if (ntype == XK_CMD && val->cmd == ED_SEQUENCE_LEAD_IN) { + (void) fprintf(el->el_errfile, + "key_add: sequence-lead-in command not allowed\n"); + return; + } + if (el->el_key.map == NULL) + /* tree is initially empty. Set up new node to match key[0] */ + el->el_key.map = node__get(key[0]); + /* it is properly initialized */ + + /* Now recurse through el->el_key.map */ + (void) node__try(el, el->el_key.map, key, val, ntype); + return; +} + + +/* key_clear(): + * + */ +protected void +key_clear(EditLine *el, el_action_t *map, const char *in) +{ + + if ((map[(unsigned char)*in] == ED_SEQUENCE_LEAD_IN) && + ((map == el->el_map.key && + el->el_map.alt[(unsigned char)*in] != ED_SEQUENCE_LEAD_IN) || + (map == el->el_map.alt && + el->el_map.key[(unsigned char)*in] != ED_SEQUENCE_LEAD_IN))) + (void) key_delete(el, in); +} + + +/* key_delete(): + * Delete the key and all longer keys staring with key, if + * they exists. + */ +protected int +key_delete(EditLine *el, const char *key) +{ + + if (key[0] == '\0') { + (void) fprintf(el->el_errfile, + "key_delete: Null extended-key not allowed.\n"); + return (-1); + } + if (el->el_key.map == NULL) + return (0); + + (void) node__delete(el, &el->el_key.map, key); + return (0); +} + + +/* key_print(): + * Print the binding associated with key key. + * Print entire el->el_key.map if null + */ +protected void +key_print(EditLine *el, const char *key) +{ + + /* do nothing if el->el_key.map is empty and null key specified */ + if (el->el_key.map == NULL && *key == 0) + return; + + el->el_key.buf[0] = '"'; + if (node_lookup(el, key, el->el_key.map, 1) <= -1) + /* key is not bound */ + (void) fprintf(el->el_errfile, "Unbound extended key \"%s\"\n", + key); + return; +} + + +/* node_trav(): + * recursively traverses node in tree until match or mismatch is + * found. May read in more characters. + */ +private int +node_trav(EditLine *el, key_node_t *ptr, char *ch, key_value_t *val) +{ + + if (ptr->ch == *ch) { + /* match found */ + if (ptr->next) { + /* key not complete so get next char */ + if (el_getc(el, ch) != 1) { /* if EOF or error */ + val->cmd = ED_END_OF_FILE; + return (XK_CMD); + /* PWP: Pretend we just read an end-of-file */ + } + return (node_trav(el, ptr->next, ch, val)); + } else { + *val = ptr->val; + if (ptr->type != XK_CMD) + *ch = '\0'; + return (ptr->type); + } + } else { + /* no match found here */ + if (ptr->sibling) { + /* try next sibling */ + return (node_trav(el, ptr->sibling, ch, val)); + } else { + /* no next sibling -- mismatch */ + val->str = NULL; + return (XK_STR); + } + } +} + + +/* node__try(): + * Find a node that matches *str or allocate a new one + */ +private int +node__try(EditLine *el, key_node_t *ptr, const char *str, key_value_t *val, int ntype) +{ + + if (ptr->ch != *str) { + key_node_t *xm; + + for (xm = ptr; xm->sibling != NULL; xm = xm->sibling) + if (xm->sibling->ch == *str) + break; + if (xm->sibling == NULL) + xm->sibling = node__get(*str); /* setup new node */ + ptr = xm->sibling; + } + if (*++str == '\0') { + /* we're there */ + if (ptr->next != NULL) { + node__put(el, ptr->next); + /* lose longer keys with this prefix */ + ptr->next = NULL; + } + switch (ptr->type) { + case XK_CMD: + case XK_NOD: + break; + case XK_STR: + case XK_EXE: + if (ptr->val.str) + el_free((ptr_t) ptr->val.str); + break; + default: + EL_ABORT((el->el_errfile, "Bad XK_ type %d\n", + ptr->type)); + break; + } + + switch (ptr->type = ntype) { + case XK_CMD: + ptr->val = *val; + break; + case XK_STR: + case XK_EXE: + ptr->val.str = strdup(val->str); + break; + default: + EL_ABORT((el->el_errfile, "Bad XK_ type %d\n", ntype)); + break; + } + } else { + /* still more chars to go */ + if (ptr->next == NULL) + ptr->next = node__get(*str); /* setup new node */ + (void) node__try(el, ptr->next, str, val, ntype); + } + return (0); +} + + +/* node__delete(): + * Delete node that matches str + */ +private int +node__delete(EditLine *el, key_node_t **inptr, const char *str) +{ + key_node_t *ptr; + key_node_t *prev_ptr = NULL; + + ptr = *inptr; + + if (ptr->ch != *str) { + key_node_t *xm; + + for (xm = ptr; xm->sibling != NULL; xm = xm->sibling) + if (xm->sibling->ch == *str) + break; + if (xm->sibling == NULL) + return (0); + prev_ptr = xm; + ptr = xm->sibling; + } + if (*++str == '\0') { + /* we're there */ + if (prev_ptr == NULL) + *inptr = ptr->sibling; + else + prev_ptr->sibling = ptr->sibling; + ptr->sibling = NULL; + node__put(el, ptr); + return (1); + } else if (ptr->next != NULL && + node__delete(el, &ptr->next, str) == 1) { + if (ptr->next != NULL) + return (0); + if (prev_ptr == NULL) + *inptr = ptr->sibling; + else + prev_ptr->sibling = ptr->sibling; + ptr->sibling = NULL; + node__put(el, ptr); + return (1); + } else { + return (0); + } +} + + +/* node__put(): + * Puts a tree of nodes onto free list using free(3). + */ +private void +node__put(EditLine *el, key_node_t *ptr) +{ + if (ptr == NULL) + return; + + if (ptr->next != NULL) { + node__put(el, ptr->next); + ptr->next = NULL; + } + node__put(el, ptr->sibling); + + switch (ptr->type) { + case XK_CMD: + case XK_NOD: + break; + case XK_EXE: + case XK_STR: + if (ptr->val.str != NULL) + el_free((ptr_t) ptr->val.str); + break; + default: + EL_ABORT((el->el_errfile, "Bad XK_ type %d\n", ptr->type)); + break; + } + el_free((ptr_t) ptr); +} + + +/* node__get(): + * Returns pointer to an key_node_t for ch. + */ +private key_node_t * +node__get(int ch) +{ + key_node_t *ptr; + + ptr = (key_node_t *) el_malloc((size_t) sizeof(key_node_t)); + if (ptr == NULL) + return NULL; + ptr->ch = ch; + ptr->type = XK_NOD; + ptr->val.str = NULL; + ptr->next = NULL; + ptr->sibling = NULL; + return (ptr); +} + + + +/* node_lookup(): + * look for the str starting at node ptr. + * Print if last node + */ +private int +node_lookup(EditLine *el, const char *str, key_node_t *ptr, int cnt) +{ + int ncnt; + + if (ptr == NULL) + return (-1); /* cannot have null ptr */ + + if (*str == 0) { + /* no more chars in str. node_enum from here. */ + (void) node_enum(el, ptr, cnt); + return (0); + } else { + /* 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, + (unsigned char) ptr->ch); + if (ptr->next != NULL) + /* not yet at leaf */ + return (node_lookup(el, str + 1, ptr->next, + ncnt + 1)); + else { + /* next node is null so key should be complete */ + if (str[1] == 0) { + el->el_key.buf[ncnt + 1] = '"'; + el->el_key.buf[ncnt + 2] = '\0'; + key_kprint(el, el->el_key.buf, + &ptr->val, ptr->type); + return (0); + } else + return (-1); + /* mismatch -- str still has chars */ + } + } else { + /* no match found try sibling */ + if (ptr->sibling) + return (node_lookup(el, str, ptr->sibling, + cnt)); + else + return (-1); + } + } +} + + +/* node_enum(): + * Traverse the node printing the characters it is bound in buffer + */ +private int +node_enum(EditLine *el, key_node_t *ptr, int cnt) +{ + int ncnt; + + if (cnt >= KEY_BUFSIZ - 5) { /* buffer too small */ + el->el_key.buf[++cnt] = '"'; + el->el_key.buf[++cnt] = '\0'; + (void) fprintf(el->el_errfile, + "Some extended keys too long for internal print buffer"); + (void) fprintf(el->el_errfile, " \"%s...\"\n", el->el_key.buf); + return (0); + } + if (ptr == NULL) { +#ifdef DEBUG_EDIT + (void) fprintf(el->el_errfile, + "node_enum: BUG!! Null ptr passed\n!"); +#endif + return (-1); + } + /* put this char at end of str */ + ncnt = key__decode_char(el->el_key.buf, cnt, (unsigned char) ptr->ch); + if (ptr->next == NULL) { + /* print this key and function */ + el->el_key.buf[ncnt + 1] = '"'; + el->el_key.buf[ncnt + 2] = '\0'; + key_kprint(el, el->el_key.buf, &ptr->val, ptr->type); + } else + (void) node_enum(el, ptr->next, ncnt + 1); + + /* go to sibling if there is one */ + if (ptr->sibling) + (void) node_enum(el, ptr->sibling, cnt); + return (0); +} + + +/* key_kprint(): + * Print the specified key and its associated + * function specified by val + */ +protected void +key_kprint(EditLine *el, const char *key, key_value_t *val, int ntype) +{ + el_bindings_t *fp; + char unparsbuf[EL_BUFSIZ]; + static const char fmt[] = "%-15s-> %s\n"; + + if (val != NULL) + switch (ntype) { + case XK_STR: + case XK_EXE: + (void) fprintf(el->el_outfile, fmt, key, + key__decode_str(val->str, unparsbuf, + ntype == XK_STR ? "\"\"" : "[]")); + break; + case XK_CMD: + for (fp = el->el_map.help; fp->name; fp++) + if (val->cmd == fp->func) { + (void) fprintf(el->el_outfile, fmt, + key, fp->name); + break; + } +#ifdef DEBUG_KEY + if (fp->name == NULL) + (void) fprintf(el->el_outfile, + "BUG! Command not found.\n"); +#endif + + break; + default: + EL_ABORT((el->el_errfile, "Bad XK_ type %d\n", ntype)); + break; + } + else + (void) fprintf(el->el_outfile, fmt, key, "no input"); +} + + +/* key__decode_char(): + * Put a printable form of char in buf. + */ +private int +key__decode_char(char *buf, int cnt, int ch) +{ + if (ch == 0) { + buf[cnt++] = '^'; + buf[cnt] = '@'; + return (cnt); + } + if (iscntrl(ch)) { + buf[cnt++] = '^'; + if (ch == '\177') + buf[cnt] = '?'; + else + buf[cnt] = ch | 0100; + } else if (ch == '^') { + buf[cnt++] = '\\'; + buf[cnt] = '^'; + } else if (ch == '\\') { + buf[cnt++] = '\\'; + buf[cnt] = '\\'; + } else if (ch == ' ' || (isprint(ch) && !isspace(ch))) { + buf[cnt] = 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'; + } + return (cnt); +} + + +/* key__decode_str(): + * Make a printable version of the ey + */ +protected char * +key__decode_str(const char *str, char *buf, const char *sep) +{ + char *b; + 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); + } + for (p = str; *p != 0; p++) { + if (iscntrl((unsigned char) *p)) { + *b++ = '^'; + if (*p == '\177') + *b++ = '?'; + else + *b++ = *p | 0100; + } else if (*p == '^' || *p == '\\') { + *b++ = '\\'; + *b++ = *p; + } else if (*p == ' ' || (isprint((unsigned char) *p) && + !isspace((unsigned char) *p))) { + *b++ = *p; + } else { + *b++ = '\\'; + *b++ = (((unsigned int) *p >> 6) & 7) + '0'; + *b++ = (((unsigned int) *p >> 3) & 7) + '0'; + *b++ = (*p & 7) + '0'; + } + } + if (sep[0] != '\0' && sep[1] != '\0') + *b++ = sep[1]; + *b++ = 0; + return (buf); /* should check for overflow */ +} diff --git a/cmd-line-utils/libedit/key.h b/cmd-line-utils/libedit/key.h new file mode 100644 index 00000000000..c3a0889f901 --- /dev/null +++ b/cmd-line-utils/libedit/key.h @@ -0,0 +1,79 @@ +/* $NetBSD: key.h,v 1.5 2001/01/23 15:55:30 jdolecek 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. + * + * @(#)key.h 8.1 (Berkeley) 6/4/93 + */ + +/* + * el.key.h: Key macro header + */ +#ifndef _h_el_key +#define _h_el_key + +typedef union key_value_t { + el_action_t cmd; /* If it is a command the # */ + char *str; /* If it is a string... */ +} key_value_t; + +typedef struct key_node_t key_node_t; + +typedef struct el_key_t { + char *buf; /* Key print buffer */ + key_node_t *map; /* Key map */ + key_value_t val; /* Local conversion buffer */ +} el_key_t; + +#define XK_CMD 0 +#define XK_STR 1 +#define XK_NOD 2 +#define XK_EXE 3 + +protected int key_init(EditLine *); +protected void key_end(EditLine *); +protected key_value_t *key_map_cmd(EditLine *, int); +protected key_value_t *key_map_str(EditLine *, char *); +protected void key_reset(EditLine *); +protected int key_get(EditLine *, char *, key_value_t *); +protected void key_add(EditLine *, const char *, key_value_t *, int); +protected void key_clear(EditLine *, el_action_t *, const char *); +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 *); + +#endif /* _h_el_key */ diff --git a/cmd-line-utils/libedit/makelist b/cmd-line-utils/libedit/makelist new file mode 100644 index 00000000000..8e9835309d6 --- /dev/null +++ b/cmd-line-utils/libedit/makelist @@ -0,0 +1,254 @@ +#!/bin/sh - +# $NetBSD: makelist,v 1.7 2001/01/09 19:22:31 jdolecek 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. +# +# @(#)makelist 5.3 (Berkeley) 6/4/93 + +# makelist.sh: Automatically generate header files... + +AWK=/usr/bin/awk +USAGE="Usage: $0 -h|-e|-fc|-fh|-bc|-bh|-m " + +if [ "x$1" = "x" ] +then + echo $USAGE 1>&2 + exit 1 +fi + +FLAG="$1" +shift + +FILES="$@" + +case $FLAG in + +# generate foo.h file from foo.c +# +-h) + set - `echo $FILES | sed -e 's/\\./_/g'` + hdr="_h_`basename $1`" + cat $FILES | $AWK ' + BEGIN { + printf("/* Automatically generated file, do not edit */\n"); + printf("#ifndef %s\n#define %s\n", "'$hdr'", "'$hdr'"); + } + /\(\):/ { + pr = substr($2, 1, 2); + if (pr == "vi" || pr == "em" || pr == "ed") { + name = substr($2, 1, length($2) - 3); +# +# XXX: need a space between name and prototype so that -fc and -fh +# parsing is much easier +# + printf("protected el_action_t\t%s (EditLine *, int);\n", name); + } + } + END { + printf("#endif /* %s */\n", "'$hdr'"); + }' + ;; + +# generate help.c from various .c files +# +-bc) + cat $FILES | $AWK ' + BEGIN { + printf("/* Automatically generated file, do not edit */\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_"; + for (i = 1; i <= length(low); i++) + tr[substr(low, i, 1)] = substr(high, i, 1); + } + /\(\):/ { + pr = substr($2, 1, 2); + if (pr == "vi" || pr == "em" || pr == "ed") { + name = substr($2, 1, length($2) - 3); + uname = ""; + fname = ""; + for (i = 1; i <= length(name); i++) { + s = substr(name, i, 1); + uname = uname tr[s]; + if (s == "_") + s = "-"; + fname = fname s; + } + + printf(" { %-30.30s %-30.30s\n","\"" fname "\",", uname ","); + ok = 1; + } + } + /^ \*/ { + if (ok) { + printf(" \""); + for (i = 2; i < NF; i++) + printf("%s ", $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"); + }' + ;; + +# generate help.h from various .c files +# +-bh) + $AWK ' + BEGIN { + printf("/* Automatically generated file, do not edit */\n"); + printf("#ifndef _h_help_c\n#define _h_help_c\n"); + printf("protected const el_bindings_t *help__get(void);\n"); + printf("#endif /* _h_help_c */\n"); + }' /dev/null + ;; + +# generate fcns.h from various .h files +# +-fh) + cat $FILES | $AWK '/el_action_t/ { print $3 }' | \ + sort | tr '[a-z]' '[A-Z]' | $AWK ' + BEGIN { + printf("/* Automatically generated file, do not edit */\n"); + printf("#ifndef _h_fcns_c\n#define _h_fcns_c\n"); + count = 0; + } + { + printf("#define\t%-30.30s\t%3d\n", $1, count++); + } + END { + printf("#define\t%-30.30s\t%3d\n", "EL_NUM_FCNS", count); + + printf("typedef el_action_t (*el_func_t)(EditLine *, int);"); + printf("\nprotected const el_func_t* func__get(void);\n"); + printf("#endif /* _h_fcns_c */\n"); + }' + ;; + +# generate fcns.c from various .h files +# +-fc) + cat $FILES | $AWK '/el_action_t/ { print $3 }' | sort | $AWK ' + BEGIN { + printf("/* Automatically generated file, do not edit */\n"); + printf("#include \"sys.h\"\n#include \"el.h\"\n"); + printf("private const el_func_t el_func[] = {"); + maxlen = 80; + needn = 1; + len = 0; + } + { + clen = 25 + 2; + len += clen; + if (len >= maxlen) + needn = 1; + if (needn) { + printf("\n "); + needn = 0; + len = 4 + clen; + } + s = $1 ","; + printf("%-26.26s ", s); + } + END { + printf("\n};\n"); + printf("\nprotected const el_func_t* func__get() { return el_func; }\n"); + }' + ;; + +# generate editline.c from various .c files +# +-e) + echo "$FILES" | tr ' ' '\012' | $AWK ' + BEGIN { + printf("/* Automatically generated file, do not edit */\n"); + printf("#define protected static\n"); + printf("#define SCCSID\n"); + } + { + printf("#include \"%s\"\n", $1); + }' + ;; + +# generate man page fragment from various .c files +# +-m) + cat $FILES | $AWK ' + BEGIN { + printf(".\\\" Section automatically generated with makelist\n"); + printf(".Bl -tag -width 4n\n"); + } + /\(\):/ { + pr = substr($2, 1, 2); + if (pr == "vi" || pr == "em" || pr == "ed") { + name = substr($2, 1, length($2) - 3); + fname = ""; + for (i = 1; i <= length(name); i++) { + s = substr(name, i, 1); + if (s == "_") + s = "-"; + fname = fname s; + } + + printf(".It Ic %s\n", fname); + ok = 1; + } + } + /^ \*/ { + if (ok) { + for (i = 2; i < NF; i++) + printf("%s ", $i); + printf("%s.\n", $i); + ok = 0; + } + } + END { + printf(".El\n"); + printf(".\\\" End of section automatically generated with makelist\n"); + }' + ;; + +*) + echo $USAGE 1>&2 + exit 1 + ;; + +esac diff --git a/cmd-line-utils/libedit/map.c b/cmd-line-utils/libedit/map.c new file mode 100644 index 00000000000..144ccf1ebe0 --- /dev/null +++ b/cmd-line-utils/libedit/map.c @@ -0,0 +1,1412 @@ +/* $NetBSD: map.c,v 1.14 2001/01/09 17:22:09 jdolecek 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 "compat.h" + +/* + * map.c: Editor function definitions + */ +#include "sys.h" +#include +#include "el.h" + +#define N_KEYS 256 + +private void map_print_key(EditLine *, el_action_t *, const char *); +private void map_print_some_keys(EditLine *, el_action_t *, int, int); +private void map_print_all_keys(EditLine *); +private void map_init_nls(EditLine *); +private void map_init_meta(EditLine *); + +/* keymap tables ; should be N_KEYS*sizeof(KEYCMD) bytes long */ + + +private const el_action_t el_map_emacs[] = { + /* 0 */ EM_SET_MARK, /* ^@ */ + /* 1 */ ED_MOVE_TO_BEG, /* ^A */ + /* 2 */ ED_PREV_CHAR, /* ^B */ + /* 3 */ ED_TTY_SIGINT, /* ^C */ + /* 4 */ EM_DELETE_OR_LIST, /* ^D */ + /* 5 */ ED_MOVE_TO_END, /* ^E */ + /* 6 */ ED_NEXT_CHAR, /* ^F */ + /* 7 */ ED_UNASSIGNED, /* ^G */ + /* 8 */ ED_DELETE_PREV_CHAR, /* ^H */ + /* 9 */ ED_UNASSIGNED, /* ^I */ + /* 10 */ ED_NEWLINE, /* ^J */ + /* 11 */ ED_KILL_LINE, /* ^K */ + /* 12 */ ED_CLEAR_SCREEN, /* ^L */ + /* 13 */ ED_NEWLINE, /* ^M */ + /* 14 */ ED_NEXT_HISTORY, /* ^N */ + /* 15 */ ED_TTY_FLUSH_OUTPUT, /* ^O */ + /* 16 */ ED_PREV_HISTORY, /* ^P */ + /* 17 */ ED_TTY_START_OUTPUT, /* ^Q */ + /* 18 */ ED_REDISPLAY, /* ^R */ + /* 19 */ ED_TTY_STOP_OUTPUT, /* ^S */ + /* 20 */ ED_TRANSPOSE_CHARS, /* ^T */ + /* 21 */ EM_KILL_LINE, /* ^U */ + /* 22 */ ED_QUOTED_INSERT, /* ^V */ + /* 23 */ EM_KILL_REGION, /* ^W */ + /* 24 */ ED_SEQUENCE_LEAD_IN, /* ^X */ + /* 25 */ EM_YANK, /* ^Y */ + /* 26 */ ED_TTY_SIGTSTP, /* ^Z */ + /* 27 */ EM_META_NEXT, /* ^[ */ + /* 28 */ ED_TTY_SIGQUIT, /* ^\ */ + /* 29 */ ED_TTY_DSUSP, /* ^] */ + /* 30 */ ED_UNASSIGNED, /* ^^ */ + /* 31 */ ED_UNASSIGNED, /* ^_ */ + /* 32 */ ED_INSERT, /* SPACE */ + /* 33 */ ED_INSERT, /* ! */ + /* 34 */ ED_INSERT, /* " */ + /* 35 */ ED_INSERT, /* # */ + /* 36 */ ED_INSERT, /* $ */ + /* 37 */ ED_INSERT, /* % */ + /* 38 */ ED_INSERT, /* & */ + /* 39 */ ED_INSERT, /* ' */ + /* 40 */ ED_INSERT, /* ( */ + /* 41 */ ED_INSERT, /* ) */ + /* 42 */ ED_INSERT, /* * */ + /* 43 */ ED_INSERT, /* + */ + /* 44 */ ED_INSERT, /* , */ + /* 45 */ ED_INSERT, /* - */ + /* 46 */ ED_INSERT, /* . */ + /* 47 */ ED_INSERT, /* / */ + /* 48 */ ED_DIGIT, /* 0 */ + /* 49 */ ED_DIGIT, /* 1 */ + /* 50 */ ED_DIGIT, /* 2 */ + /* 51 */ ED_DIGIT, /* 3 */ + /* 52 */ ED_DIGIT, /* 4 */ + /* 53 */ ED_DIGIT, /* 5 */ + /* 54 */ ED_DIGIT, /* 6 */ + /* 55 */ ED_DIGIT, /* 7 */ + /* 56 */ ED_DIGIT, /* 8 */ + /* 57 */ ED_DIGIT, /* 9 */ + /* 58 */ ED_INSERT, /* : */ + /* 59 */ ED_INSERT, /* ; */ + /* 60 */ ED_INSERT, /* < */ + /* 61 */ ED_INSERT, /* = */ + /* 62 */ ED_INSERT, /* > */ + /* 63 */ ED_INSERT, /* ? */ + /* 64 */ ED_INSERT, /* @ */ + /* 65 */ ED_INSERT, /* A */ + /* 66 */ ED_INSERT, /* B */ + /* 67 */ ED_INSERT, /* C */ + /* 68 */ ED_INSERT, /* D */ + /* 69 */ ED_INSERT, /* E */ + /* 70 */ ED_INSERT, /* F */ + /* 71 */ ED_INSERT, /* G */ + /* 72 */ ED_INSERT, /* H */ + /* 73 */ ED_INSERT, /* I */ + /* 74 */ ED_INSERT, /* J */ + /* 75 */ ED_INSERT, /* K */ + /* 76 */ ED_INSERT, /* L */ + /* 77 */ ED_INSERT, /* M */ + /* 78 */ ED_INSERT, /* N */ + /* 79 */ ED_INSERT, /* O */ + /* 80 */ ED_INSERT, /* P */ + /* 81 */ ED_INSERT, /* Q */ + /* 82 */ ED_INSERT, /* R */ + /* 83 */ ED_INSERT, /* S */ + /* 84 */ ED_INSERT, /* T */ + /* 85 */ ED_INSERT, /* U */ + /* 86 */ ED_INSERT, /* V */ + /* 87 */ ED_INSERT, /* W */ + /* 88 */ ED_INSERT, /* X */ + /* 89 */ ED_INSERT, /* Y */ + /* 90 */ ED_INSERT, /* Z */ + /* 91 */ ED_INSERT, /* [ */ + /* 92 */ ED_INSERT, /* \ */ + /* 93 */ ED_INSERT, /* ] */ + /* 94 */ ED_INSERT, /* ^ */ + /* 95 */ ED_INSERT, /* _ */ + /* 96 */ ED_INSERT, /* ` */ + /* 97 */ ED_INSERT, /* a */ + /* 98 */ ED_INSERT, /* b */ + /* 99 */ ED_INSERT, /* c */ + /* 100 */ ED_INSERT, /* d */ + /* 101 */ ED_INSERT, /* e */ + /* 102 */ ED_INSERT, /* f */ + /* 103 */ ED_INSERT, /* g */ + /* 104 */ ED_INSERT, /* h */ + /* 105 */ ED_INSERT, /* i */ + /* 106 */ ED_INSERT, /* j */ + /* 107 */ ED_INSERT, /* k */ + /* 108 */ ED_INSERT, /* l */ + /* 109 */ ED_INSERT, /* m */ + /* 110 */ ED_INSERT, /* n */ + /* 111 */ ED_INSERT, /* o */ + /* 112 */ ED_INSERT, /* p */ + /* 113 */ ED_INSERT, /* q */ + /* 114 */ ED_INSERT, /* r */ + /* 115 */ ED_INSERT, /* s */ + /* 116 */ ED_INSERT, /* t */ + /* 117 */ ED_INSERT, /* u */ + /* 118 */ ED_INSERT, /* v */ + /* 119 */ ED_INSERT, /* w */ + /* 120 */ ED_INSERT, /* x */ + /* 121 */ ED_INSERT, /* y */ + /* 122 */ ED_INSERT, /* z */ + /* 123 */ ED_INSERT, /* { */ + /* 124 */ ED_INSERT, /* | */ + /* 125 */ ED_INSERT, /* } */ + /* 126 */ ED_INSERT, /* ~ */ + /* 127 */ ED_DELETE_PREV_CHAR, /* ^? */ + /* 128 */ ED_UNASSIGNED, /* M-^@ */ + /* 129 */ ED_UNASSIGNED, /* M-^A */ + /* 130 */ ED_UNASSIGNED, /* M-^B */ + /* 131 */ ED_UNASSIGNED, /* M-^C */ + /* 132 */ ED_UNASSIGNED, /* M-^D */ + /* 133 */ ED_UNASSIGNED, /* M-^E */ + /* 134 */ ED_UNASSIGNED, /* M-^F */ + /* 135 */ ED_UNASSIGNED, /* M-^G */ + /* 136 */ ED_DELETE_PREV_WORD, /* M-^H */ + /* 137 */ ED_UNASSIGNED, /* M-^I */ + /* 138 */ ED_UNASSIGNED, /* M-^J */ + /* 139 */ ED_UNASSIGNED, /* M-^K */ + /* 140 */ ED_CLEAR_SCREEN, /* M-^L */ + /* 141 */ ED_UNASSIGNED, /* M-^M */ + /* 142 */ ED_UNASSIGNED, /* M-^N */ + /* 143 */ ED_UNASSIGNED, /* M-^O */ + /* 144 */ ED_UNASSIGNED, /* M-^P */ + /* 145 */ ED_UNASSIGNED, /* M-^Q */ + /* 146 */ ED_UNASSIGNED, /* M-^R */ + /* 147 */ ED_UNASSIGNED, /* M-^S */ + /* 148 */ ED_UNASSIGNED, /* M-^T */ + /* 149 */ ED_UNASSIGNED, /* M-^U */ + /* 150 */ ED_UNASSIGNED, /* M-^V */ + /* 151 */ ED_UNASSIGNED, /* M-^W */ + /* 152 */ ED_UNASSIGNED, /* M-^X */ + /* 153 */ ED_UNASSIGNED, /* M-^Y */ + /* 154 */ ED_UNASSIGNED, /* M-^Z */ + /* 155 */ ED_UNASSIGNED, /* M-^[ */ + /* 156 */ ED_UNASSIGNED, /* M-^\ */ + /* 157 */ ED_UNASSIGNED, /* M-^] */ + /* 158 */ ED_UNASSIGNED, /* M-^^ */ + /* 159 */ EM_COPY_PREV_WORD, /* M-^_ */ + /* 160 */ ED_UNASSIGNED, /* M-SPACE */ + /* 161 */ ED_UNASSIGNED, /* M-! */ + /* 162 */ ED_UNASSIGNED, /* M-" */ + /* 163 */ ED_UNASSIGNED, /* M-# */ + /* 164 */ ED_UNASSIGNED, /* M-$ */ + /* 165 */ ED_UNASSIGNED, /* M-% */ + /* 166 */ ED_UNASSIGNED, /* M-& */ + /* 167 */ ED_UNASSIGNED, /* M-' */ + /* 168 */ ED_UNASSIGNED, /* M-( */ + /* 169 */ ED_UNASSIGNED, /* M-) */ + /* 170 */ ED_UNASSIGNED, /* M-* */ + /* 171 */ ED_UNASSIGNED, /* M-+ */ + /* 172 */ ED_UNASSIGNED, /* M-, */ + /* 173 */ ED_UNASSIGNED, /* M-- */ + /* 174 */ ED_UNASSIGNED, /* M-. */ + /* 175 */ ED_UNASSIGNED, /* M-/ */ + /* 176 */ ED_ARGUMENT_DIGIT, /* M-0 */ + /* 177 */ ED_ARGUMENT_DIGIT, /* M-1 */ + /* 178 */ ED_ARGUMENT_DIGIT, /* M-2 */ + /* 179 */ ED_ARGUMENT_DIGIT, /* M-3 */ + /* 180 */ ED_ARGUMENT_DIGIT, /* M-4 */ + /* 181 */ ED_ARGUMENT_DIGIT, /* M-5 */ + /* 182 */ ED_ARGUMENT_DIGIT, /* M-6 */ + /* 183 */ ED_ARGUMENT_DIGIT, /* M-7 */ + /* 184 */ ED_ARGUMENT_DIGIT, /* M-8 */ + /* 185 */ ED_ARGUMENT_DIGIT, /* M-9 */ + /* 186 */ ED_UNASSIGNED, /* M-: */ + /* 187 */ ED_UNASSIGNED, /* M-; */ + /* 188 */ ED_UNASSIGNED, /* M-< */ + /* 189 */ ED_UNASSIGNED, /* M-= */ + /* 190 */ ED_UNASSIGNED, /* M-> */ + /* 191 */ ED_UNASSIGNED, /* M-? */ + /* 192 */ ED_UNASSIGNED, /* M-@ */ + /* 193 */ ED_UNASSIGNED, /* M-A */ + /* 194 */ ED_PREV_WORD, /* M-B */ + /* 195 */ EM_CAPITOL_CASE, /* M-C */ + /* 196 */ EM_DELETE_NEXT_WORD, /* M-D */ + /* 197 */ ED_UNASSIGNED, /* M-E */ + /* 198 */ EM_NEXT_WORD, /* M-F */ + /* 199 */ ED_UNASSIGNED, /* M-G */ + /* 200 */ ED_UNASSIGNED, /* M-H */ + /* 201 */ ED_UNASSIGNED, /* M-I */ + /* 202 */ ED_UNASSIGNED, /* M-J */ + /* 203 */ ED_UNASSIGNED, /* M-K */ + /* 204 */ EM_LOWER_CASE, /* M-L */ + /* 205 */ ED_UNASSIGNED, /* M-M */ + /* 206 */ ED_SEARCH_NEXT_HISTORY, /* M-N */ + /* 207 */ ED_SEQUENCE_LEAD_IN, /* M-O */ + /* 208 */ ED_SEARCH_PREV_HISTORY, /* M-P */ + /* 209 */ ED_UNASSIGNED, /* M-Q */ + /* 210 */ ED_UNASSIGNED, /* M-R */ + /* 211 */ ED_UNASSIGNED, /* M-S */ + /* 212 */ ED_UNASSIGNED, /* M-T */ + /* 213 */ EM_UPPER_CASE, /* M-U */ + /* 214 */ ED_UNASSIGNED, /* M-V */ + /* 215 */ EM_COPY_REGION, /* M-W */ + /* 216 */ ED_COMMAND, /* M-X */ + /* 217 */ ED_UNASSIGNED, /* M-Y */ + /* 218 */ ED_UNASSIGNED, /* M-Z */ + /* 219 */ ED_SEQUENCE_LEAD_IN, /* M-[ */ + /* 220 */ ED_UNASSIGNED, /* M-\ */ + /* 221 */ ED_UNASSIGNED, /* M-] */ + /* 222 */ ED_UNASSIGNED, /* M-^ */ + /* 223 */ ED_UNASSIGNED, /* M-_ */ + /* 223 */ ED_UNASSIGNED, /* M-` */ + /* 224 */ ED_UNASSIGNED, /* M-a */ + /* 225 */ ED_PREV_WORD, /* M-b */ + /* 226 */ EM_CAPITOL_CASE, /* M-c */ + /* 227 */ EM_DELETE_NEXT_WORD, /* M-d */ + /* 228 */ ED_UNASSIGNED, /* M-e */ + /* 229 */ EM_NEXT_WORD, /* M-f */ + /* 230 */ ED_UNASSIGNED, /* M-g */ + /* 231 */ ED_UNASSIGNED, /* M-h */ + /* 232 */ ED_UNASSIGNED, /* M-i */ + /* 233 */ ED_UNASSIGNED, /* M-j */ + /* 234 */ ED_UNASSIGNED, /* M-k */ + /* 235 */ EM_LOWER_CASE, /* M-l */ + /* 236 */ ED_UNASSIGNED, /* M-m */ + /* 237 */ ED_SEARCH_NEXT_HISTORY, /* M-n */ + /* 238 */ ED_UNASSIGNED, /* M-o */ + /* 239 */ ED_SEARCH_PREV_HISTORY, /* M-p */ + /* 240 */ ED_UNASSIGNED, /* M-q */ + /* 241 */ ED_UNASSIGNED, /* M-r */ + /* 242 */ ED_UNASSIGNED, /* M-s */ + /* 243 */ ED_UNASSIGNED, /* M-t */ + /* 244 */ EM_UPPER_CASE, /* M-u */ + /* 245 */ ED_UNASSIGNED, /* M-v */ + /* 246 */ EM_COPY_REGION, /* M-w */ + /* 247 */ ED_COMMAND, /* M-x */ + /* 248 */ ED_UNASSIGNED, /* M-y */ + /* 249 */ ED_UNASSIGNED, /* M-z */ + /* 250 */ ED_UNASSIGNED, /* M-{ */ + /* 251 */ ED_UNASSIGNED, /* M-| */ + /* 252 */ ED_UNASSIGNED, /* M-} */ + /* 253 */ ED_UNASSIGNED, /* M-~ */ + /* 254 */ ED_DELETE_PREV_WORD /* M-^? */ + /* 255 */ +}; + + +/* + * keymap table for vi. Each index into above tbl; should be + * N_KEYS entries long. Vi mode uses a sticky-extend to do command mode: + * insert mode characters are in the normal keymap, and command mode + * in the extended keymap. + */ +private const el_action_t el_map_vi_insert[] = { +#ifdef KSHVI + /* 0 */ ED_UNASSIGNED, /* ^@ */ + /* 1 */ ED_INSERT, /* ^A */ + /* 2 */ ED_INSERT, /* ^B */ + /* 3 */ ED_INSERT, /* ^C */ + /* 4 */ VI_LIST_OR_EOF, /* ^D */ + /* 5 */ ED_INSERT, /* ^E */ + /* 6 */ ED_INSERT, /* ^F */ + /* 7 */ ED_INSERT, /* ^G */ + /* 8 */ VI_DELETE_PREV_CHAR, /* ^H */ /* BackSpace key */ + /* 9 */ ED_INSERT, /* ^I */ /* Tab Key */ + /* 10 */ ED_NEWLINE, /* ^J */ + /* 11 */ ED_INSERT, /* ^K */ + /* 12 */ ED_INSERT, /* ^L */ + /* 13 */ ED_NEWLINE, /* ^M */ + /* 14 */ ED_INSERT, /* ^N */ + /* 15 */ ED_INSERT, /* ^O */ + /* 16 */ ED_INSERT, /* ^P */ + /* 17 */ ED_TTY_START_OUTPUT, /* ^Q */ + /* 18 */ ED_INSERT, /* ^R */ + /* 19 */ ED_TTY_STOP_OUTPUT, /* ^S */ + /* 20 */ ED_INSERT, /* ^T */ + /* 21 */ VI_KILL_LINE_PREV, /* ^U */ + /* 22 */ ED_QUOTED_INSERT, /* ^V */ + /* 23 */ ED_DELETE_PREV_WORD, /* ^W */ + /* ED_DELETE_PREV_WORD: Only until strt edit pos */ + /* 24 */ ED_INSERT, /* ^X */ + /* 25 */ ED_INSERT, /* ^Y */ + /* 26 */ ED_INSERT, /* ^Z */ + /* 27 */ VI_COMMAND_MODE, /* ^[ */ /* [ Esc ] key */ + /* 28 */ ED_TTY_SIGQUIT, /* ^\ */ + /* 29 */ ED_INSERT, /* ^] */ + /* 30 */ ED_INSERT, /* ^^ */ + /* 31 */ ED_INSERT, /* ^_ */ +#else /* !KSHVI */ + /* + * NOTE: These mappings do NOT Correspond well + * to the KSH VI editing assignments. + * On the other and they are convenient and + * many people have have gotten used to them. + */ + /* 0 */ ED_UNASSIGNED, /* ^@ */ + /* 1 */ ED_MOVE_TO_BEG, /* ^A */ + /* 2 */ ED_PREV_CHAR, /* ^B */ + /* 3 */ ED_TTY_SIGINT, /* ^C */ + /* 4 */ VI_LIST_OR_EOF, /* ^D */ + /* 5 */ ED_MOVE_TO_END, /* ^E */ + /* 6 */ ED_NEXT_CHAR, /* ^F */ + /* 7 */ ED_UNASSIGNED, /* ^G */ + /* 8 */ ED_DELETE_PREV_CHAR, /* ^H */ /* BackSpace key */ + /* 9 */ ED_UNASSIGNED, /* ^I */ /* Tab Key */ + /* 10 */ ED_NEWLINE, /* ^J */ + /* 11 */ ED_KILL_LINE, /* ^K */ + /* 12 */ ED_CLEAR_SCREEN, /* ^L */ + /* 13 */ ED_NEWLINE, /* ^M */ + /* 14 */ ED_NEXT_HISTORY, /* ^N */ + /* 15 */ ED_TTY_FLUSH_OUTPUT, /* ^O */ + /* 16 */ ED_PREV_HISTORY, /* ^P */ + /* 17 */ ED_TTY_START_OUTPUT, /* ^Q */ + /* 18 */ ED_REDISPLAY, /* ^R */ + /* 19 */ ED_TTY_STOP_OUTPUT, /* ^S */ + /* 20 */ ED_TRANSPOSE_CHARS, /* ^T */ + /* 21 */ VI_KILL_LINE_PREV, /* ^U */ + /* 22 */ ED_QUOTED_INSERT, /* ^V */ + /* 23 */ ED_DELETE_PREV_WORD, /* ^W */ + /* 24 */ ED_UNASSIGNED, /* ^X */ + /* 25 */ ED_TTY_DSUSP, /* ^Y */ + /* 26 */ ED_TTY_SIGTSTP, /* ^Z */ + /* 27 */ VI_COMMAND_MODE, /* ^[ */ + /* 28 */ ED_TTY_SIGQUIT, /* ^\ */ + /* 29 */ ED_UNASSIGNED, /* ^] */ + /* 30 */ ED_UNASSIGNED, /* ^^ */ + /* 31 */ ED_UNASSIGNED, /* ^_ */ +#endif /* KSHVI */ + /* 32 */ ED_INSERT, /* SPACE */ + /* 33 */ ED_INSERT, /* ! */ + /* 34 */ ED_INSERT, /* " */ + /* 35 */ ED_INSERT, /* # */ + /* 36 */ ED_INSERT, /* $ */ + /* 37 */ ED_INSERT, /* % */ + /* 38 */ ED_INSERT, /* & */ + /* 39 */ ED_INSERT, /* ' */ + /* 40 */ ED_INSERT, /* ( */ + /* 41 */ ED_INSERT, /* ) */ + /* 42 */ ED_INSERT, /* * */ + /* 43 */ ED_INSERT, /* + */ + /* 44 */ ED_INSERT, /* , */ + /* 45 */ ED_INSERT, /* - */ + /* 46 */ ED_INSERT, /* . */ + /* 47 */ ED_INSERT, /* / */ + /* 48 */ ED_INSERT, /* 0 */ + /* 49 */ ED_INSERT, /* 1 */ + /* 50 */ ED_INSERT, /* 2 */ + /* 51 */ ED_INSERT, /* 3 */ + /* 52 */ ED_INSERT, /* 4 */ + /* 53 */ ED_INSERT, /* 5 */ + /* 54 */ ED_INSERT, /* 6 */ + /* 55 */ ED_INSERT, /* 7 */ + /* 56 */ ED_INSERT, /* 8 */ + /* 57 */ ED_INSERT, /* 9 */ + /* 58 */ ED_INSERT, /* : */ + /* 59 */ ED_INSERT, /* ; */ + /* 60 */ ED_INSERT, /* < */ + /* 61 */ ED_INSERT, /* = */ + /* 62 */ ED_INSERT, /* > */ + /* 63 */ ED_INSERT, /* ? */ + /* 64 */ ED_INSERT, /* @ */ + /* 65 */ ED_INSERT, /* A */ + /* 66 */ ED_INSERT, /* B */ + /* 67 */ ED_INSERT, /* C */ + /* 68 */ ED_INSERT, /* D */ + /* 69 */ ED_INSERT, /* E */ + /* 70 */ ED_INSERT, /* F */ + /* 71 */ ED_INSERT, /* G */ + /* 72 */ ED_INSERT, /* H */ + /* 73 */ ED_INSERT, /* I */ + /* 74 */ ED_INSERT, /* J */ + /* 75 */ ED_INSERT, /* K */ + /* 76 */ ED_INSERT, /* L */ + /* 77 */ ED_INSERT, /* M */ + /* 78 */ ED_INSERT, /* N */ + /* 79 */ ED_INSERT, /* O */ + /* 80 */ ED_INSERT, /* P */ + /* 81 */ ED_INSERT, /* Q */ + /* 82 */ ED_INSERT, /* R */ + /* 83 */ ED_INSERT, /* S */ + /* 84 */ ED_INSERT, /* T */ + /* 85 */ ED_INSERT, /* U */ + /* 86 */ ED_INSERT, /* V */ + /* 87 */ ED_INSERT, /* W */ + /* 88 */ ED_INSERT, /* X */ + /* 89 */ ED_INSERT, /* Y */ + /* 90 */ ED_INSERT, /* Z */ + /* 91 */ ED_INSERT, /* [ */ + /* 92 */ ED_INSERT, /* \ */ + /* 93 */ ED_INSERT, /* ] */ + /* 94 */ ED_INSERT, /* ^ */ + /* 95 */ ED_INSERT, /* _ */ + /* 96 */ ED_INSERT, /* ` */ + /* 97 */ ED_INSERT, /* a */ + /* 98 */ ED_INSERT, /* b */ + /* 99 */ ED_INSERT, /* c */ + /* 100 */ ED_INSERT, /* d */ + /* 101 */ ED_INSERT, /* e */ + /* 102 */ ED_INSERT, /* f */ + /* 103 */ ED_INSERT, /* g */ + /* 104 */ ED_INSERT, /* h */ + /* 105 */ ED_INSERT, /* i */ + /* 106 */ ED_INSERT, /* j */ + /* 107 */ ED_INSERT, /* k */ + /* 108 */ ED_INSERT, /* l */ + /* 109 */ ED_INSERT, /* m */ + /* 110 */ ED_INSERT, /* n */ + /* 111 */ ED_INSERT, /* o */ + /* 112 */ ED_INSERT, /* p */ + /* 113 */ ED_INSERT, /* q */ + /* 114 */ ED_INSERT, /* r */ + /* 115 */ ED_INSERT, /* s */ + /* 116 */ ED_INSERT, /* t */ + /* 117 */ ED_INSERT, /* u */ + /* 118 */ ED_INSERT, /* v */ + /* 119 */ ED_INSERT, /* w */ + /* 120 */ ED_INSERT, /* x */ + /* 121 */ ED_INSERT, /* y */ + /* 122 */ ED_INSERT, /* z */ + /* 123 */ ED_INSERT, /* { */ + /* 124 */ ED_INSERT, /* | */ + /* 125 */ ED_INSERT, /* } */ + /* 126 */ ED_INSERT, /* ~ */ + /* 127 */ ED_DELETE_PREV_CHAR, /* ^? */ + /* 128 */ ED_UNASSIGNED, /* M-^@ */ + /* 129 */ ED_UNASSIGNED, /* M-^A */ + /* 130 */ ED_UNASSIGNED, /* M-^B */ + /* 131 */ ED_UNASSIGNED, /* M-^C */ + /* 132 */ ED_UNASSIGNED, /* M-^D */ + /* 133 */ ED_UNASSIGNED, /* M-^E */ + /* 134 */ ED_UNASSIGNED, /* M-^F */ + /* 135 */ ED_UNASSIGNED, /* M-^G */ + /* 136 */ ED_UNASSIGNED, /* M-^H */ + /* 137 */ ED_UNASSIGNED, /* M-^I */ + /* 138 */ ED_UNASSIGNED, /* M-^J */ + /* 139 */ ED_UNASSIGNED, /* M-^K */ + /* 140 */ ED_UNASSIGNED, /* M-^L */ + /* 141 */ ED_UNASSIGNED, /* M-^M */ + /* 142 */ ED_UNASSIGNED, /* M-^N */ + /* 143 */ ED_UNASSIGNED, /* M-^O */ + /* 144 */ ED_UNASSIGNED, /* M-^P */ + /* 145 */ ED_UNASSIGNED, /* M-^Q */ + /* 146 */ ED_UNASSIGNED, /* M-^R */ + /* 147 */ ED_UNASSIGNED, /* M-^S */ + /* 148 */ ED_UNASSIGNED, /* M-^T */ + /* 149 */ ED_UNASSIGNED, /* M-^U */ + /* 150 */ ED_UNASSIGNED, /* M-^V */ + /* 151 */ ED_UNASSIGNED, /* M-^W */ + /* 152 */ ED_UNASSIGNED, /* M-^X */ + /* 153 */ ED_UNASSIGNED, /* M-^Y */ + /* 154 */ ED_UNASSIGNED, /* M-^Z */ + /* 155 */ ED_UNASSIGNED, /* M-^[ */ + /* 156 */ ED_UNASSIGNED, /* M-^\ */ + /* 157 */ ED_UNASSIGNED, /* M-^] */ + /* 158 */ ED_UNASSIGNED, /* M-^^ */ + /* 159 */ ED_UNASSIGNED, /* M-^_ */ + /* 160 */ ED_UNASSIGNED, /* M-SPACE */ + /* 161 */ ED_UNASSIGNED, /* M-! */ + /* 162 */ ED_UNASSIGNED, /* M-" */ + /* 163 */ ED_UNASSIGNED, /* M-# */ + /* 164 */ ED_UNASSIGNED, /* M-$ */ + /* 165 */ ED_UNASSIGNED, /* M-% */ + /* 166 */ ED_UNASSIGNED, /* M-& */ + /* 167 */ ED_UNASSIGNED, /* M-' */ + /* 168 */ ED_UNASSIGNED, /* M-( */ + /* 169 */ ED_UNASSIGNED, /* M-) */ + /* 170 */ ED_UNASSIGNED, /* M-* */ + /* 171 */ ED_UNASSIGNED, /* M-+ */ + /* 172 */ ED_UNASSIGNED, /* M-, */ + /* 173 */ ED_UNASSIGNED, /* M-- */ + /* 174 */ ED_UNASSIGNED, /* M-. */ + /* 175 */ ED_UNASSIGNED, /* M-/ */ + /* 176 */ ED_UNASSIGNED, /* M-0 */ + /* 177 */ ED_UNASSIGNED, /* M-1 */ + /* 178 */ ED_UNASSIGNED, /* M-2 */ + /* 179 */ ED_UNASSIGNED, /* M-3 */ + /* 180 */ ED_UNASSIGNED, /* M-4 */ + /* 181 */ ED_UNASSIGNED, /* M-5 */ + /* 182 */ ED_UNASSIGNED, /* M-6 */ + /* 183 */ ED_UNASSIGNED, /* M-7 */ + /* 184 */ ED_UNASSIGNED, /* M-8 */ + /* 185 */ ED_UNASSIGNED, /* M-9 */ + /* 186 */ ED_UNASSIGNED, /* M-: */ + /* 187 */ ED_UNASSIGNED, /* M-; */ + /* 188 */ ED_UNASSIGNED, /* M-< */ + /* 189 */ ED_UNASSIGNED, /* M-= */ + /* 190 */ ED_UNASSIGNED, /* M-> */ + /* 191 */ ED_UNASSIGNED, /* M-? */ + /* 192 */ ED_UNASSIGNED, /* M-@ */ + /* 193 */ ED_UNASSIGNED, /* M-A */ + /* 194 */ ED_UNASSIGNED, /* M-B */ + /* 195 */ ED_UNASSIGNED, /* M-C */ + /* 196 */ ED_UNASSIGNED, /* M-D */ + /* 197 */ ED_UNASSIGNED, /* M-E */ + /* 198 */ ED_UNASSIGNED, /* M-F */ + /* 199 */ ED_UNASSIGNED, /* M-G */ + /* 200 */ ED_UNASSIGNED, /* M-H */ + /* 201 */ ED_UNASSIGNED, /* M-I */ + /* 202 */ ED_UNASSIGNED, /* M-J */ + /* 203 */ ED_UNASSIGNED, /* M-K */ + /* 204 */ ED_UNASSIGNED, /* M-L */ + /* 205 */ ED_UNASSIGNED, /* M-M */ + /* 206 */ ED_UNASSIGNED, /* M-N */ + /* 207 */ ED_UNASSIGNED, /* M-O */ + /* 208 */ ED_UNASSIGNED, /* M-P */ + /* 209 */ ED_UNASSIGNED, /* M-Q */ + /* 210 */ ED_UNASSIGNED, /* M-R */ + /* 211 */ ED_UNASSIGNED, /* M-S */ + /* 212 */ ED_UNASSIGNED, /* M-T */ + /* 213 */ ED_UNASSIGNED, /* M-U */ + /* 214 */ ED_UNASSIGNED, /* M-V */ + /* 215 */ ED_UNASSIGNED, /* M-W */ + /* 216 */ ED_UNASSIGNED, /* M-X */ + /* 217 */ ED_UNASSIGNED, /* M-Y */ + /* 218 */ ED_UNASSIGNED, /* M-Z */ + /* 219 */ ED_UNASSIGNED, /* M-[ */ + /* 220 */ ED_UNASSIGNED, /* M-\ */ + /* 221 */ ED_UNASSIGNED, /* M-] */ + /* 222 */ ED_UNASSIGNED, /* M-^ */ + /* 223 */ ED_UNASSIGNED, /* M-_ */ + /* 224 */ ED_UNASSIGNED, /* M-` */ + /* 225 */ ED_UNASSIGNED, /* M-a */ + /* 226 */ ED_UNASSIGNED, /* M-b */ + /* 227 */ ED_UNASSIGNED, /* M-c */ + /* 228 */ ED_UNASSIGNED, /* M-d */ + /* 229 */ ED_UNASSIGNED, /* M-e */ + /* 230 */ ED_UNASSIGNED, /* M-f */ + /* 231 */ ED_UNASSIGNED, /* M-g */ + /* 232 */ ED_UNASSIGNED, /* M-h */ + /* 233 */ ED_UNASSIGNED, /* M-i */ + /* 234 */ ED_UNASSIGNED, /* M-j */ + /* 235 */ ED_UNASSIGNED, /* M-k */ + /* 236 */ ED_UNASSIGNED, /* M-l */ + /* 237 */ ED_UNASSIGNED, /* M-m */ + /* 238 */ ED_UNASSIGNED, /* M-n */ + /* 239 */ ED_UNASSIGNED, /* M-o */ + /* 240 */ ED_UNASSIGNED, /* M-p */ + /* 241 */ ED_UNASSIGNED, /* M-q */ + /* 242 */ ED_UNASSIGNED, /* M-r */ + /* 243 */ ED_UNASSIGNED, /* M-s */ + /* 244 */ ED_UNASSIGNED, /* M-t */ + /* 245 */ ED_UNASSIGNED, /* M-u */ + /* 246 */ ED_UNASSIGNED, /* M-v */ + /* 247 */ ED_UNASSIGNED, /* M-w */ + /* 248 */ ED_UNASSIGNED, /* M-x */ + /* 249 */ ED_UNASSIGNED, /* M-y */ + /* 250 */ ED_UNASSIGNED, /* M-z */ + /* 251 */ ED_UNASSIGNED, /* M-{ */ + /* 252 */ ED_UNASSIGNED, /* M-| */ + /* 253 */ ED_UNASSIGNED, /* M-} */ + /* 254 */ ED_UNASSIGNED, /* M-~ */ + /* 255 */ ED_UNASSIGNED /* M-^? */ +}; + +private const el_action_t el_map_vi_command[] = { + /* 0 */ ED_UNASSIGNED, /* ^@ */ + /* 1 */ ED_MOVE_TO_BEG, /* ^A */ + /* 2 */ ED_UNASSIGNED, /* ^B */ + /* 3 */ ED_TTY_SIGINT, /* ^C */ + /* 4 */ ED_UNASSIGNED, /* ^D */ + /* 5 */ ED_MOVE_TO_END, /* ^E */ + /* 6 */ ED_UNASSIGNED, /* ^F */ + /* 7 */ ED_UNASSIGNED, /* ^G */ + /* 8 */ ED_PREV_CHAR, /* ^H */ + /* 9 */ ED_UNASSIGNED, /* ^I */ + /* 10 */ ED_NEWLINE, /* ^J */ + /* 11 */ ED_KILL_LINE, /* ^K */ + /* 12 */ ED_CLEAR_SCREEN, /* ^L */ + /* 13 */ ED_NEWLINE, /* ^M */ + /* 14 */ ED_NEXT_HISTORY, /* ^N */ + /* 15 */ ED_TTY_FLUSH_OUTPUT, /* ^O */ + /* 16 */ ED_PREV_HISTORY, /* ^P */ + /* 17 */ ED_TTY_START_OUTPUT, /* ^Q */ + /* 18 */ ED_REDISPLAY, /* ^R */ + /* 19 */ ED_TTY_STOP_OUTPUT, /* ^S */ + /* 20 */ ED_UNASSIGNED, /* ^T */ + /* 21 */ VI_KILL_LINE_PREV, /* ^U */ + /* 22 */ ED_UNASSIGNED, /* ^V */ + /* 23 */ ED_DELETE_PREV_WORD, /* ^W */ + /* 24 */ ED_UNASSIGNED, /* ^X */ + /* 25 */ ED_UNASSIGNED, /* ^Y */ + /* 26 */ ED_UNASSIGNED, /* ^Z */ + /* 27 */ EM_META_NEXT, /* ^[ */ + /* 28 */ ED_TTY_SIGQUIT, /* ^\ */ + /* 29 */ ED_UNASSIGNED, /* ^] */ + /* 30 */ ED_UNASSIGNED, /* ^^ */ + /* 31 */ ED_UNASSIGNED, /* ^_ */ + /* 32 */ ED_NEXT_CHAR, /* SPACE */ + /* 33 */ ED_UNASSIGNED, /* ! */ + /* 34 */ ED_UNASSIGNED, /* " */ + /* 35 */ ED_UNASSIGNED, /* # */ + /* 36 */ ED_MOVE_TO_END, /* $ */ + /* 37 */ ED_UNASSIGNED, /* % */ + /* 38 */ ED_UNASSIGNED, /* & */ + /* 39 */ ED_UNASSIGNED, /* ' */ + /* 40 */ ED_UNASSIGNED, /* ( */ + /* 41 */ ED_UNASSIGNED, /* ) */ + /* 42 */ ED_UNASSIGNED, /* * */ + /* 43 */ ED_NEXT_HISTORY, /* + */ + /* 44 */ VI_REPEAT_PREV_CHAR, /* , */ + /* 45 */ ED_PREV_HISTORY, /* - */ + /* 46 */ ED_UNASSIGNED, /* . */ + /* 47 */ VI_SEARCH_PREV, /* / */ + /* 48 */ VI_ZERO, /* 0 */ + /* 49 */ ED_ARGUMENT_DIGIT, /* 1 */ + /* 50 */ ED_ARGUMENT_DIGIT, /* 2 */ + /* 51 */ ED_ARGUMENT_DIGIT, /* 3 */ + /* 52 */ ED_ARGUMENT_DIGIT, /* 4 */ + /* 53 */ ED_ARGUMENT_DIGIT, /* 5 */ + /* 54 */ ED_ARGUMENT_DIGIT, /* 6 */ + /* 55 */ ED_ARGUMENT_DIGIT, /* 7 */ + /* 56 */ ED_ARGUMENT_DIGIT, /* 8 */ + /* 57 */ ED_ARGUMENT_DIGIT, /* 9 */ + /* 58 */ ED_COMMAND, /* : */ + /* 59 */ VI_REPEAT_NEXT_CHAR, /* ; */ + /* 60 */ ED_UNASSIGNED, /* < */ + /* 61 */ ED_UNASSIGNED, /* = */ + /* 62 */ ED_UNASSIGNED, /* > */ + /* 63 */ VI_SEARCH_NEXT, /* ? */ + /* 64 */ ED_UNASSIGNED, /* @ */ + /* 65 */ VI_ADD_AT_EOL, /* A */ + /* 66 */ VI_PREV_SPACE_WORD, /* B */ + /* 67 */ VI_CHANGE_TO_EOL, /* C */ + /* 68 */ ED_KILL_LINE, /* D */ + /* 69 */ VI_TO_END_WORD, /* E */ + /* 70 */ VI_PREV_CHAR, /* F */ + /* 71 */ ED_UNASSIGNED, /* G */ + /* 72 */ ED_UNASSIGNED, /* H */ + /* 73 */ VI_INSERT_AT_BOL, /* I */ + /* 74 */ ED_SEARCH_NEXT_HISTORY, /* J */ + /* 75 */ ED_SEARCH_PREV_HISTORY, /* K */ + /* 76 */ ED_UNASSIGNED, /* L */ + /* 77 */ ED_UNASSIGNED, /* M */ + /* 78 */ VI_REPEAT_SEARCH_PREV, /* N */ + /* 79 */ ED_SEQUENCE_LEAD_IN, /* O */ + /* 80 */ VI_PASTE_PREV, /* P */ + /* 81 */ ED_UNASSIGNED, /* Q */ + /* 82 */ VI_REPLACE_MODE, /* R */ + /* 83 */ VI_SUBSTITUTE_LINE, /* S */ + /* 84 */ VI_TO_PREV_CHAR, /* T */ + /* 85 */ ED_UNASSIGNED, /* U */ + /* 86 */ ED_UNASSIGNED, /* V */ + /* 87 */ VI_NEXT_SPACE_WORD, /* W */ + /* 88 */ ED_DELETE_PREV_CHAR, /* X */ + /* 89 */ ED_UNASSIGNED, /* Y */ + /* 90 */ ED_UNASSIGNED, /* Z */ + /* 91 */ ED_SEQUENCE_LEAD_IN, /* [ */ + /* 92 */ ED_UNASSIGNED, /* \ */ + /* 93 */ ED_UNASSIGNED, /* ] */ + /* 94 */ ED_MOVE_TO_BEG, /* ^ */ + /* 95 */ ED_UNASSIGNED, /* _ */ + /* 96 */ ED_UNASSIGNED, /* ` */ + /* 97 */ VI_ADD, /* a */ + /* 98 */ VI_PREV_WORD, /* b */ + /* 99 */ VI_CHANGE_META, /* c */ + /* 100 */ VI_DELETE_META, /* d */ + /* 101 */ VI_END_WORD, /* e */ + /* 102 */ VI_NEXT_CHAR, /* f */ + /* 103 */ ED_UNASSIGNED, /* g */ + /* 104 */ ED_PREV_CHAR, /* h */ + /* 105 */ VI_INSERT, /* i */ + /* 106 */ ED_NEXT_HISTORY, /* j */ + /* 107 */ ED_PREV_HISTORY, /* k */ + /* 108 */ ED_NEXT_CHAR, /* l */ + /* 109 */ ED_UNASSIGNED, /* m */ + /* 110 */ VI_REPEAT_SEARCH_NEXT, /* n */ + /* 111 */ ED_UNASSIGNED, /* o */ + /* 112 */ VI_PASTE_NEXT, /* p */ + /* 113 */ ED_UNASSIGNED, /* q */ + /* 114 */ VI_REPLACE_CHAR, /* r */ + /* 115 */ VI_SUBSTITUTE_CHAR, /* s */ + /* 116 */ VI_TO_NEXT_CHAR, /* t */ + /* 117 */ VI_UNDO, /* u */ + /* 118 */ ED_UNASSIGNED, /* v */ + /* 119 */ VI_NEXT_WORD, /* w */ + /* 120 */ ED_DELETE_NEXT_CHAR, /* x */ + /* 121 */ ED_UNASSIGNED, /* y */ + /* 122 */ ED_UNASSIGNED, /* z */ + /* 123 */ ED_UNASSIGNED, /* { */ + /* 124 */ ED_UNASSIGNED, /* | */ + /* 125 */ ED_UNASSIGNED, /* } */ + /* 126 */ VI_CHANGE_CASE, /* ~ */ + /* 127 */ ED_DELETE_PREV_CHAR, /* ^? */ + /* 128 */ ED_UNASSIGNED, /* M-^@ */ + /* 129 */ ED_UNASSIGNED, /* M-^A */ + /* 130 */ ED_UNASSIGNED, /* M-^B */ + /* 131 */ ED_UNASSIGNED, /* M-^C */ + /* 132 */ ED_UNASSIGNED, /* M-^D */ + /* 133 */ ED_UNASSIGNED, /* M-^E */ + /* 134 */ ED_UNASSIGNED, /* M-^F */ + /* 135 */ ED_UNASSIGNED, /* M-^G */ + /* 136 */ ED_UNASSIGNED, /* M-^H */ + /* 137 */ ED_UNASSIGNED, /* M-^I */ + /* 138 */ ED_UNASSIGNED, /* M-^J */ + /* 139 */ ED_UNASSIGNED, /* M-^K */ + /* 140 */ ED_UNASSIGNED, /* M-^L */ + /* 141 */ ED_UNASSIGNED, /* M-^M */ + /* 142 */ ED_UNASSIGNED, /* M-^N */ + /* 143 */ ED_UNASSIGNED, /* M-^O */ + /* 144 */ ED_UNASSIGNED, /* M-^P */ + /* 145 */ ED_UNASSIGNED, /* M-^Q */ + /* 146 */ ED_UNASSIGNED, /* M-^R */ + /* 147 */ ED_UNASSIGNED, /* M-^S */ + /* 148 */ ED_UNASSIGNED, /* M-^T */ + /* 149 */ ED_UNASSIGNED, /* M-^U */ + /* 150 */ ED_UNASSIGNED, /* M-^V */ + /* 151 */ ED_UNASSIGNED, /* M-^W */ + /* 152 */ ED_UNASSIGNED, /* M-^X */ + /* 153 */ ED_UNASSIGNED, /* M-^Y */ + /* 154 */ ED_UNASSIGNED, /* M-^Z */ + /* 155 */ ED_UNASSIGNED, /* M-^[ */ + /* 156 */ ED_UNASSIGNED, /* M-^\ */ + /* 157 */ ED_UNASSIGNED, /* M-^] */ + /* 158 */ ED_UNASSIGNED, /* M-^^ */ + /* 159 */ ED_UNASSIGNED, /* M-^_ */ + /* 160 */ ED_UNASSIGNED, /* M-SPACE */ + /* 161 */ ED_UNASSIGNED, /* M-! */ + /* 162 */ ED_UNASSIGNED, /* M-" */ + /* 163 */ ED_UNASSIGNED, /* M-# */ + /* 164 */ ED_UNASSIGNED, /* M-$ */ + /* 165 */ ED_UNASSIGNED, /* M-% */ + /* 166 */ ED_UNASSIGNED, /* M-& */ + /* 167 */ ED_UNASSIGNED, /* M-' */ + /* 168 */ ED_UNASSIGNED, /* M-( */ + /* 169 */ ED_UNASSIGNED, /* M-) */ + /* 170 */ ED_UNASSIGNED, /* M-* */ + /* 171 */ ED_UNASSIGNED, /* M-+ */ + /* 172 */ ED_UNASSIGNED, /* M-, */ + /* 173 */ ED_UNASSIGNED, /* M-- */ + /* 174 */ ED_UNASSIGNED, /* M-. */ + /* 175 */ ED_UNASSIGNED, /* M-/ */ + /* 176 */ ED_UNASSIGNED, /* M-0 */ + /* 177 */ ED_UNASSIGNED, /* M-1 */ + /* 178 */ ED_UNASSIGNED, /* M-2 */ + /* 179 */ ED_UNASSIGNED, /* M-3 */ + /* 180 */ ED_UNASSIGNED, /* M-4 */ + /* 181 */ ED_UNASSIGNED, /* M-5 */ + /* 182 */ ED_UNASSIGNED, /* M-6 */ + /* 183 */ ED_UNASSIGNED, /* M-7 */ + /* 184 */ ED_UNASSIGNED, /* M-8 */ + /* 185 */ ED_UNASSIGNED, /* M-9 */ + /* 186 */ ED_UNASSIGNED, /* M-: */ + /* 187 */ ED_UNASSIGNED, /* M-; */ + /* 188 */ ED_UNASSIGNED, /* M-< */ + /* 189 */ ED_UNASSIGNED, /* M-= */ + /* 190 */ ED_UNASSIGNED, /* M-> */ + /* 191 */ ED_UNASSIGNED, /* M-? */ + /* 192 */ ED_UNASSIGNED, /* M-@ */ + /* 193 */ ED_UNASSIGNED, /* M-A */ + /* 194 */ ED_UNASSIGNED, /* M-B */ + /* 195 */ ED_UNASSIGNED, /* M-C */ + /* 196 */ ED_UNASSIGNED, /* M-D */ + /* 197 */ ED_UNASSIGNED, /* M-E */ + /* 198 */ ED_UNASSIGNED, /* M-F */ + /* 199 */ ED_UNASSIGNED, /* M-G */ + /* 200 */ ED_UNASSIGNED, /* M-H */ + /* 201 */ ED_UNASSIGNED, /* M-I */ + /* 202 */ ED_UNASSIGNED, /* M-J */ + /* 203 */ ED_UNASSIGNED, /* M-K */ + /* 204 */ ED_UNASSIGNED, /* M-L */ + /* 205 */ ED_UNASSIGNED, /* M-M */ + /* 206 */ ED_UNASSIGNED, /* M-N */ + /* 207 */ ED_SEQUENCE_LEAD_IN, /* M-O */ + /* 208 */ ED_UNASSIGNED, /* M-P */ + /* 209 */ ED_UNASSIGNED, /* M-Q */ + /* 210 */ ED_UNASSIGNED, /* M-R */ + /* 211 */ ED_UNASSIGNED, /* M-S */ + /* 212 */ ED_UNASSIGNED, /* M-T */ + /* 213 */ ED_UNASSIGNED, /* M-U */ + /* 214 */ ED_UNASSIGNED, /* M-V */ + /* 215 */ ED_UNASSIGNED, /* M-W */ + /* 216 */ ED_UNASSIGNED, /* M-X */ + /* 217 */ ED_UNASSIGNED, /* M-Y */ + /* 218 */ ED_UNASSIGNED, /* M-Z */ + /* 219 */ ED_SEQUENCE_LEAD_IN, /* M-[ */ + /* 220 */ ED_UNASSIGNED, /* M-\ */ + /* 221 */ ED_UNASSIGNED, /* M-] */ + /* 222 */ ED_UNASSIGNED, /* M-^ */ + /* 223 */ ED_UNASSIGNED, /* M-_ */ + /* 224 */ ED_UNASSIGNED, /* M-` */ + /* 225 */ ED_UNASSIGNED, /* M-a */ + /* 226 */ ED_UNASSIGNED, /* M-b */ + /* 227 */ ED_UNASSIGNED, /* M-c */ + /* 228 */ ED_UNASSIGNED, /* M-d */ + /* 229 */ ED_UNASSIGNED, /* M-e */ + /* 230 */ ED_UNASSIGNED, /* M-f */ + /* 231 */ ED_UNASSIGNED, /* M-g */ + /* 232 */ ED_UNASSIGNED, /* M-h */ + /* 233 */ ED_UNASSIGNED, /* M-i */ + /* 234 */ ED_UNASSIGNED, /* M-j */ + /* 235 */ ED_UNASSIGNED, /* M-k */ + /* 236 */ ED_UNASSIGNED, /* M-l */ + /* 237 */ ED_UNASSIGNED, /* M-m */ + /* 238 */ ED_UNASSIGNED, /* M-n */ + /* 239 */ ED_UNASSIGNED, /* M-o */ + /* 240 */ ED_UNASSIGNED, /* M-p */ + /* 241 */ ED_UNASSIGNED, /* M-q */ + /* 242 */ ED_UNASSIGNED, /* M-r */ + /* 243 */ ED_UNASSIGNED, /* M-s */ + /* 244 */ ED_UNASSIGNED, /* M-t */ + /* 245 */ ED_UNASSIGNED, /* M-u */ + /* 246 */ ED_UNASSIGNED, /* M-v */ + /* 247 */ ED_UNASSIGNED, /* M-w */ + /* 248 */ ED_UNASSIGNED, /* M-x */ + /* 249 */ ED_UNASSIGNED, /* M-y */ + /* 250 */ ED_UNASSIGNED, /* M-z */ + /* 251 */ ED_UNASSIGNED, /* M-{ */ + /* 252 */ ED_UNASSIGNED, /* M-| */ + /* 253 */ ED_UNASSIGNED, /* M-} */ + /* 254 */ ED_UNASSIGNED, /* M-~ */ + /* 255 */ ED_UNASSIGNED /* M-^? */ +}; + + +/* map_init(): + * Initialize and allocate the maps + */ +protected int +map_init(EditLine *el) +{ + + /* + * Make sure those are correct before starting. + */ +#ifdef MAP_DEBUG + if (sizeof(el_map_emacs) != N_KEYS * sizeof(el_action_t)) + EL_ABORT((el->errfile, "Emacs map incorrect\n")); + if (sizeof(el_map_vi_command) != N_KEYS * sizeof(el_action_t)) + EL_ABORT((el->errfile, "Vi command map incorrect\n")); + if (sizeof(el_map_vi_insert) != N_KEYS * sizeof(el_action_t)) + EL_ABORT((el->errfile, "Vi insert map incorrect\n")); +#endif + + el->el_map.alt = (el_action_t *)el_malloc(sizeof(el_action_t) * N_KEYS); + if (el->el_map.alt == NULL) + return (-1); + el->el_map.key = (el_action_t *)el_malloc(sizeof(el_action_t) * N_KEYS); + if (el->el_map.key == NULL) + return (-1); + el->el_map.emacs = el_map_emacs; + el->el_map.vic = el_map_vi_command; + el->el_map.vii = el_map_vi_insert; + el->el_map.help = (el_bindings_t *) el_malloc(sizeof(el_bindings_t) * + EL_NUM_FCNS); + if (el->el_map.help == NULL) + return (-1); + (void) memcpy(el->el_map.help, help__get(), + sizeof(el_bindings_t) * EL_NUM_FCNS); + el->el_map.func = (el_func_t *)el_malloc(sizeof(el_func_t) * + EL_NUM_FCNS); + if (el->el_map.func == NULL) + return (-1); + memcpy(el->el_map.func, func__get(), sizeof(el_func_t) * EL_NUM_FCNS); + el->el_map.nfunc = EL_NUM_FCNS; + +#ifdef VIDEFAULT + map_init_vi(el); +#else + map_init_emacs(el); +#endif /* VIDEFAULT */ + return (0); +} + + +/* map_end(): + * Free the space taken by the editor maps + */ +protected void +map_end(EditLine *el) +{ + + el_free((ptr_t) el->el_map.alt); + el->el_map.alt = NULL; + el_free((ptr_t) el->el_map.key); + el->el_map.key = NULL; + el->el_map.emacs = NULL; + el->el_map.vic = NULL; + el->el_map.vii = NULL; + el_free((ptr_t) el->el_map.help); + el->el_map.help = NULL; + el_free((ptr_t) el->el_map.func); + el->el_map.func = NULL; +} + + +/* map_init_nls(): + * Find all the printable keys and bind them to self insert + */ +private void +map_init_nls(EditLine *el) +{ + int i; + + el_action_t *map = el->el_map.key; + + for (i = 0200; i <= 0377; i++) + if (isprint(i)) + map[i] = ED_INSERT; +} + + +/* map_init_meta(): + * Bind all the meta keys to the appropriate ESC- sequence + */ +private void +map_init_meta(EditLine *el) +{ + char buf[3]; + int i; + el_action_t *map = el->el_map.key; + el_action_t *alt = el->el_map.alt; + + for (i = 0; i <= 0377 && map[i] != EM_META_NEXT; i++) + continue; + + if (i > 0377) { + for (i = 0; i <= 0377 && alt[i] != EM_META_NEXT; i++) + continue; + if (i > 0377) { + i = 033; + if (el->el_map.type == MAP_VI) + map = alt; + } else + map = alt; + } + buf[0] = (char) i; + buf[2] = 0; + for (i = 0200; i <= 0377; i++) + switch (map[i]) { + case ED_INSERT: + case ED_UNASSIGNED: + case ED_SEQUENCE_LEAD_IN: + break; + default: + buf[1] = i & 0177; + key_add(el, buf, key_map_cmd(el, (int) map[i]), XK_CMD); + break; + } + map[(int) buf[0]] = ED_SEQUENCE_LEAD_IN; +} + + +/* map_init_vi(): + * Initialize the vi bindings + */ +protected void +map_init_vi(EditLine *el) +{ + int i; + el_action_t *key = el->el_map.key; + el_action_t *alt = el->el_map.alt; + const el_action_t *vii = el->el_map.vii; + const el_action_t *vic = el->el_map.vic; + + el->el_map.type = MAP_VI; + el->el_map.current = el->el_map.key; + + key_reset(el); + + for (i = 0; i < N_KEYS; i++) { + key[i] = vii[i]; + alt[i] = vic[i]; + } + + map_init_meta(el); + map_init_nls(el); + + tty_bind_char(el, 1); + term_bind_arrow(el); +} + + +/* map_init_emacs(): + * Initialize the emacs bindings + */ +protected void +map_init_emacs(EditLine *el) +{ + int i; + char buf[3]; + el_action_t *key = el->el_map.key; + el_action_t *alt = el->el_map.alt; + const el_action_t *emacs = el->el_map.emacs; + + el->el_map.type = MAP_EMACS; + el->el_map.current = el->el_map.key; + key_reset(el); + + for (i = 0; i < N_KEYS; i++) { + key[i] = emacs[i]; + alt[i] = ED_UNASSIGNED; + } + + map_init_meta(el); + map_init_nls(el); + + buf[0] = CONTROL('X'); + buf[1] = CONTROL('X'); + buf[2] = 0; + key_add(el, buf, key_map_cmd(el, EM_EXCHANGE_MARK), XK_CMD); + + tty_bind_char(el, 1); + term_bind_arrow(el); +} + + +/* map_set_editor(): + * Set the editor + */ +protected int +map_set_editor(EditLine *el, char *editor) +{ + + if (strcmp(editor, "emacs") == 0) { + map_init_emacs(el); + return (0); + } + if (strcmp(editor, "vi") == 0) { + map_init_vi(el); + return (0); + } + return (-1); +} + + +/* map_get_editor(): + * Retrieve the editor + */ +protected int +map_get_editor(EditLine *el, const char **editor) +{ + + if (editor == NULL) + return (-1); + switch (el->el_map.type) { + case MAP_EMACS: + *editor = "emacs"; + return (0); + case MAP_VI: + *editor = "vi"; + return (0); + } + return (-1); +} + + +/* map_print_key(): + * Print the function description for 1 key + */ +private void +map_print_key(EditLine *el, el_action_t *map, const char *in) +{ + char outbuf[EL_BUFSIZ]; + el_bindings_t *bp; + + if (in[0] == '\0' || in[1] == '\0') { + (void) key__decode_str(in, outbuf, ""); + for (bp = el->el_map.help; bp->name != NULL; bp++) + if (bp->func == map[(unsigned char) *in]) { + (void) fprintf(el->el_outfile, + "%s\t->\t%s\n", outbuf, bp->name); + return; + } + } else + key_print(el, in); +} + + +/* map_print_some_keys(): + * Print keys from first to last + */ +private void +map_print_some_keys(EditLine *el, el_action_t *map, int first, int last) +{ + el_bindings_t *bp; + char firstbuf[2], lastbuf[2]; + char unparsbuf[EL_BUFSIZ], extrabuf[EL_BUFSIZ]; + + firstbuf[0] = first; + firstbuf[1] = 0; + lastbuf[0] = last; + lastbuf[1] = 0; + if (map[first] == ED_UNASSIGNED) { + if (first == last) + (void) fprintf(el->el_outfile, + "%-15s-> is undefined\n", + key__decode_str(firstbuf, unparsbuf, STRQQ)); + return; + } + for (bp = el->el_map.help; bp->name != NULL; bp++) { + if (bp->func == map[first]) { + if (first == last) { + (void) fprintf(el->el_outfile, "%-15s-> %s\n", + key__decode_str(firstbuf, unparsbuf, STRQQ), + bp->name); + } else { + (void) fprintf(el->el_outfile, + "%-4s to %-7s-> %s\n", + key__decode_str(firstbuf, unparsbuf, STRQQ), + key__decode_str(lastbuf, extrabuf, STRQQ), + bp->name); + } + return; + } + } +#ifdef MAP_DEBUG + if (map == el->el_map.key) { + (void) fprintf(el->el_outfile, + "BUG!!! %s isn't bound to anything.\n", + key__decode_str(firstbuf, unparsbuf, STRQQ)); + (void) fprintf(el->el_outfile, "el->el_map.key[%d] == %d\n", + first, el->el_map.key[first]); + } else { + (void) fprintf(el->el_outfile, + "BUG!!! %s isn't bound to anything.\n", + key__decode_str(firstbuf, unparsbuf, STRQQ)); + (void) fprintf(el->el_outfile, "el->el_map.alt[%d] == %d\n", + first, el->el_map.alt[first]); + } +#endif + EL_ABORT((el->el_errfile, "Error printing keys\n")); +} + + +/* map_print_all_keys(): + * Print the function description for all keys. + */ +private void +map_print_all_keys(EditLine *el) +{ + int prev, i; + + (void) fprintf(el->el_outfile, "Standard key bindings\n"); + prev = 0; + for (i = 0; i < N_KEYS; i++) { + if (el->el_map.key[prev] == el->el_map.key[i]) + continue; + map_print_some_keys(el, el->el_map.key, prev, i - 1); + prev = i; + } + map_print_some_keys(el, el->el_map.key, prev, i - 1); + + (void) fprintf(el->el_outfile, "Alternative key bindings\n"); + prev = 0; + for (i = 0; i < N_KEYS; i++) { + if (el->el_map.alt[prev] == el->el_map.alt[i]) + continue; + map_print_some_keys(el, el->el_map.alt, prev, i - 1); + prev = i; + } + map_print_some_keys(el, el->el_map.alt, prev, i - 1); + + (void) fprintf(el->el_outfile, "Multi-character bindings\n"); + key_print(el, ""); + (void) fprintf(el->el_outfile, "Arrow key bindings\n"); + term_print_arrow(el, ""); +} + + +/* map_bind(): + * Add/remove/change bindings + */ +protected int +map_bind(EditLine *el, int argc, const char **argv) +{ + el_action_t *map; + int ntype, rem; + const char *p; + char inbuf[EL_BUFSIZ]; + char outbuf[EL_BUFSIZ]; + const char *in = NULL; + char *out = NULL; + el_bindings_t *bp; + int cmd; + int key; + + if (argv == NULL) + return (-1); + + map = el->el_map.key; + ntype = XK_CMD; + key = rem = 0; + for (argc = 1; (p = argv[argc]) != NULL; argc++) + if (p[0] == '-') + switch (p[1]) { + case 'a': + map = el->el_map.alt; + break; + + case 's': + ntype = XK_STR; + break; +#ifdef notyet + case 'c': + ntype = XK_EXE; + break; +#endif + case 'k': + key = 1; + break; + + case 'r': + rem = 1; + break; + + case 'v': + map_init_vi(el); + return (0); + + case 'e': + map_init_emacs(el); + return (0); + + case 'l': + for (bp = el->el_map.help; bp->name != NULL; + bp++) + (void) fprintf(el->el_outfile, + "%s\n\t%s\n", + bp->name, bp->description); + return (0); + default: + (void) fprintf(el->el_errfile, + "%s: Invalid switch `%c'.\n", + argv[0], p[1]); + } + else + break; + + if (argv[argc] == NULL) { + map_print_all_keys(el); + return (0); + } + if (key) + in = argv[argc++]; + else if ((in = parse__string(inbuf, argv[argc++])) == NULL) { + (void) fprintf(el->el_errfile, + "%s: Invalid \\ or ^ in instring.\n", + argv[0]); + return (-1); + } + if (rem) { + if (key) { + (void) term_clear_arrow(el, in); + return (-1); + } + if (in[1]) + (void) key_delete(el, in); + else if (map[(unsigned char) *in] == ED_SEQUENCE_LEAD_IN) + (void) key_delete(el, in); + else + map[(unsigned char) *in] = ED_UNASSIGNED; + return (0); + } + if (argv[argc] == NULL) { + if (key) + term_print_arrow(el, in); + else + map_print_key(el, map, in); + return (0); + } +#ifdef notyet + if (argv[argc + 1] != NULL) { + bindkey_usage(); + return (-1); + } +#endif + + switch (ntype) { + case XK_STR: + case XK_EXE: + if ((out = parse__string(outbuf, argv[argc])) == NULL) { + (void) fprintf(el->el_errfile, + "%s: Invalid \\ or ^ in outstring.\n", argv[0]); + return (-1); + } + if (key) + term_set_arrow(el, in, key_map_str(el, out), ntype); + else + key_add(el, in, key_map_str(el, out), ntype); + map[(unsigned char) *in] = ED_SEQUENCE_LEAD_IN; + break; + + case XK_CMD: + if ((cmd = parse_cmd(el, argv[argc])) == -1) { + (void) fprintf(el->el_errfile, + "%s: Invalid command `%s'.\n", argv[0], argv[argc]); + return (-1); + } + if (key) + term_set_arrow(el, in, key_map_str(el, out), ntype); + else { + if (in[1]) { + key_add(el, in, key_map_cmd(el, cmd), ntype); + map[(unsigned char) *in] = ED_SEQUENCE_LEAD_IN; + } else { + key_clear(el, map, in); + map[(unsigned char) *in] = cmd; + } + } + break; + + default: + EL_ABORT((el->el_errfile, "Bad XK_ type\n", ntype)); + break; + } + return (0); +} + + +/* map_addfunc(): + * add a user defined function + */ +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; + + if (name == NULL || help == NULL || func == NULL) + return (-1); + + if ((p = el_realloc(el->el_map.func, nf * sizeof(el_func_t))) == NULL) + return (-1); + el->el_map.func = (el_func_t *) p; + if ((p = el_realloc(el->el_map.help, nf * sizeof(el_bindings_t))) + == NULL) + return (-1); + el->el_map.help = (el_bindings_t *) p; + + nf = el->el_map.nfunc; + el->el_map.func[nf] = 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/map.h b/cmd-line-utils/libedit/map.h new file mode 100644 index 00000000000..6033eaf1a87 --- /dev/null +++ b/cmd-line-utils/libedit/map.h @@ -0,0 +1,79 @@ +/* $NetBSD: map.h,v 1.6 2001/01/09 17:22:09 jdolecek 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. + * + * @(#)map.h 8.1 (Berkeley) 6/4/93 + */ + +/* + * el.map.h: Editor maps + */ +#ifndef _h_el_map +#define _h_el_map + +typedef struct el_bindings_t { /* for the "bind" shell command */ + const char *name; /* function name for bind command */ + int func; /* function numeric value */ + const char *description; /* description of function */ +} el_bindings_t; + + +typedef struct el_map_t { + el_action_t *alt; /* The current alternate key map */ + el_action_t *key; /* The current normal key map */ + el_action_t *current; /* The keymap we are using */ + const el_action_t *emacs; /* The default emacs key map */ + const el_action_t *vic; /* The vi command mode key map */ + const el_action_t *vii; /* The vi insert mode key map */ + int type; /* Emacs or vi */ + el_bindings_t *help; /* The help for the editor functions */ + el_func_t *func; /* List of available functions */ + int nfunc; /* The number of functions/help items */ +} el_map_t; + +#define MAP_EMACS 0 +#define MAP_VI 1 + +protected int map_bind(EditLine *, int, const char **); +protected int map_init(EditLine *); +protected void map_end(EditLine *); +protected void map_init_vi(EditLine *); +protected void map_init_emacs(EditLine *); +protected int map_set_editor(EditLine *, char *); +protected int map_get_editor(EditLine *, const char **); +protected int map_addfunc(EditLine *, const char *, const char *, el_func_t); + +#endif /* _h_el_map */ diff --git a/cmd-line-utils/libedit/parse.c b/cmd-line-utils/libedit/parse.c new file mode 100644 index 00000000000..b6d077793af --- /dev/null +++ b/cmd-line-utils/libedit/parse.c @@ -0,0 +1,253 @@ +/* $NetBSD: parse.c,v 1.14 2001/01/23 15:55:30 jdolecek 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 "compat.h" + +/* + * parse.c: parse an editline extended command + * + * commands are: + * + * bind + * echotc + * edit + * gettc + * history + * settc + * setty + */ +#include "sys.h" +#include "el.h" +#include "tokenizer.h" +#include + +private const struct { + const char *name; + int (*func)(EditLine *, int, const char **); +} cmds[] = { + { "bind", map_bind }, + { "echotc", term_echotc }, + { "edit", el_editmode }, + { "history", hist_list }, + { "telltc", term_telltc }, + { "settc", term_settc }, + { "setty", tty_stty }, + { NULL, NULL } +}; + + +/* parse_line(): + * Parse a line and dispatch it + */ +protected int +parse_line(EditLine *el, const char *line) +{ + const char **argv; + int argc; + Tokenizer *tok; + + tok = tok_init(NULL); + tok_line(tok, line, &argc, &argv); + argc = el_parse(el, argc, argv); + tok_end(tok); + return (argc); +} + + +/* el_parse(): + * Command dispatcher + */ +public int +el_parse(EditLine *el, int argc, const char *argv[]) +{ + const char *ptr; + int i; + + if (argc < 1) + return (-1); + ptr = strchr(argv[0], ':'); + if (ptr != NULL) { + char *tprog; + size_t l; + + if (ptr == argv[0]) + return (0); + l = ptr - argv[0] - 1; + tprog = (char *) el_malloc(l + 1); + if (tprog == NULL) + return (0); + (void) strncpy(tprog, argv[0], l); + tprog[l] = '\0'; + ptr++; + l = el_match(el->el_prog, tprog); + el_free(tprog); + if (!l) + return (0); + } else + ptr = argv[0]; + + for (i = 0; cmds[i].name != NULL; i++) + if (strcmp(cmds[i].name, ptr) == 0) { + i = (*cmds[i].func) (el, argc, argv); + return (-i); + } + return (-1); +} + + +/* parse__escape(): + * Parse a string of the form ^ \ \ and return + * the appropriate character or -1 if the escape is not valid + */ +protected int +parse__escape(const char **const ptr) +{ + const char *p; + int c; + + p = *ptr; + + if (p[1] == 0) + return (-1); + + if (*p == '\\') { + p++; + switch (*p) { + case 'a': + c = '\007'; /* Bell */ + break; + case 'b': + c = '\010'; /* Backspace */ + break; + case 't': + c = '\011'; /* Horizontal Tab */ + break; + case 'n': + c = '\012'; /* New Line */ + break; + case 'v': + c = '\013'; /* Vertical Tab */ + break; + case 'f': + c = '\014'; /* Form Feed */ + break; + case 'r': + c = '\015'; /* Carriage Return */ + break; + case 'e': + c = '\033'; /* Escape */ + break; + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + { + int cnt, ch; + + for (cnt = 0, c = 0; cnt < 3; cnt++) { + ch = *p++; + if (ch < '0' || ch > '7') { + p--; + break; + } + c = (c << 3) | (ch - '0'); + } + if ((c & 0xffffff00) != 0) + return (-1); + --p; + break; + } + default: + c = *p; + break; + } + } else if (*p == '^' && isalpha((unsigned char) p[1])) { + p++; + c = (*p == '?') ? '\177' : (*p & 0237); + } else + c = *p; + *ptr = ++p; + return (c); +} +/* parse__string(): + * Parse the escapes from in and put the raw string out + */ +protected char * +parse__string(char *out, const char *in) +{ + char *rv = out; + int n; + + for (;;) + switch (*in) { + case '\0': + *out = '\0'; + return (rv); + + case '\\': + case '^': + if ((n = parse__escape(&in)) == -1) + return (NULL); + *out++ = n; + break; + + default: + *out++ = *in++; + break; + } +} + + +/* parse_cmd(): + * Return the command number for the command string given + * or -1 if one is not found + */ +protected int +parse_cmd(EditLine *el, const char *cmd) +{ + el_bindings_t *b; + + for (b = el->el_map.help; b->name != NULL; b++) + if (strcmp(b->name, cmd) == 0) + return (b->func); + return (-1); +} diff --git a/cmd-line-utils/libedit/parse.h b/cmd-line-utils/libedit/parse.h new file mode 100644 index 00000000000..4aaef2f834a --- /dev/null +++ b/cmd-line-utils/libedit/parse.h @@ -0,0 +1,52 @@ +/* $NetBSD: parse.h,v 1.4 2000/09/04 22:06:31 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. + * + * @(#)parse.h 8.1 (Berkeley) 6/4/93 + */ + +/* + * el.parse.h: Parser functions + */ +#ifndef _h_el_parse +#define _h_el_parse + +protected int parse_line(EditLine *, const char *); +protected int parse__escape(const char ** const); +protected char *parse__string(char *, const char *); +protected int parse_cmd(EditLine *, const char *); + +#endif /* _h_el_parse */ diff --git a/cmd-line-utils/libedit/prompt.c b/cmd-line-utils/libedit/prompt.c new file mode 100644 index 00000000000..fb7d9d35936 --- /dev/null +++ b/cmd-line-utils/libedit/prompt.c @@ -0,0 +1,168 @@ +/* $NetBSD: prompt.c,v 1.8 2001/01/10 07:45:41 jdolecek 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 "compat.h" + +/* + * prompt.c: Prompt printing functions + */ +#include "sys.h" +#include +#include "el.h" + +private char *prompt_default(EditLine *); +private char *prompt_default_r(EditLine *); + +/* prompt_default(): + * Just a default prompt, in case the user did not provide one + */ +private char * +/*ARGSUSED*/ +prompt_default(EditLine *el __attribute__((unused))) +{ + static char a[3] = {'?', ' ', '\0'}; + + return (a); +} + + +/* prompt_default_r(): + * Just a default rprompt, in case the user did not provide one + */ +private char * +/*ARGSUSED*/ +prompt_default_r(EditLine *el __attribute__((unused))) +{ + static char a[1] = {'\0'}; + + return (a); +} + + +/* prompt_print(): + * Print the prompt and update the prompt position. + * We use an array of integers in case we want to pass + * literal escape sequences in the prompt and we want a + * bit to flag them + */ +protected void +prompt_print(EditLine *el, int op) +{ + el_prompt_t *elp; + char *p; + + if (op == EL_PROMPT) + elp = &el->el_prompt; + else + elp = &el->el_rprompt; + p = (elp->p_func) (el); + while (*p) + re_putc(el, *p++, 1); + + elp->p_pos.v = el->el_refresh.r_cursor.v; + elp->p_pos.h = el->el_refresh.r_cursor.h; +} + + +/* prompt_init(): + * Initialize the prompt stuff + */ +protected int +prompt_init(EditLine *el) +{ + + el->el_prompt.p_func = prompt_default; + el->el_prompt.p_pos.v = 0; + el->el_prompt.p_pos.h = 0; + el->el_rprompt.p_func = prompt_default_r; + el->el_rprompt.p_pos.v = 0; + el->el_rprompt.p_pos.h = 0; + return (0); +} + + +/* prompt_end(): + * Clean up the prompt stuff + */ +protected void +/*ARGSUSED*/ +prompt_end(EditLine *el __attribute__((unused))) +{ +} + + +/* prompt_set(): + * Install a prompt printing function + */ +protected int +prompt_set(EditLine *el, el_pfunc_t prf, int op) +{ + el_prompt_t *p; + + if (op == EL_PROMPT) + p = &el->el_prompt; + else + p = &el->el_rprompt; + if (prf == NULL) { + if (op == EL_PROMPT) + p->p_func = prompt_default; + else + p->p_func = prompt_default_r; + } else + p->p_func = prf; + p->p_pos.v = 0; + p->p_pos.h = 0; + return (0); +} + + +/* prompt_get(): + * Retrieve the prompt printing function + */ +protected int +prompt_get(EditLine *el, el_pfunc_t *prf, int op) +{ + + if (prf == NULL) + return (-1); + if (op == EL_PROMPT) + *prf = el->el_prompt.p_func; + else + *prf = el->el_rprompt.p_func; + return (0); +} diff --git a/cmd-line-utils/libedit/prompt.h b/cmd-line-utils/libedit/prompt.h new file mode 100644 index 00000000000..08810e22a0c --- /dev/null +++ b/cmd-line-utils/libedit/prompt.h @@ -0,0 +1,62 @@ +/* $NetBSD: prompt.h,v 1.5 2000/09/04 22:06:31 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. + * + * @(#)prompt.h 8.1 (Berkeley) 6/4/93 + */ + +/* + * el.prompt.h: Prompt printing stuff + */ +#ifndef _h_el_prompt +#define _h_el_prompt + +#include "histedit.h" + +typedef char * (*el_pfunc_t)(EditLine*); + +typedef struct el_prompt_t { + el_pfunc_t p_func; /* Function to return the prompt */ + coord_t p_pos; /* position in the line after prompt */ +} el_prompt_t; + +protected void prompt_print(EditLine *, int); +protected int prompt_set(EditLine *, el_pfunc_t, int); +protected int prompt_get(EditLine *, el_pfunc_t *, int); +protected int prompt_init(EditLine *); +protected void prompt_end(EditLine *); + +#endif /* _h_el_prompt */ diff --git a/cmd-line-utils/libedit/read.c b/cmd-line-utils/libedit/read.c new file mode 100644 index 00000000000..05f9e3da454 --- /dev/null +++ b/cmd-line-utils/libedit/read.c @@ -0,0 +1,514 @@ +/* $NetBSD: read.c,v 1.19 2001/01/10 07:45:41 jdolecek 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 "compat.h" + +/* + * read.c: Clean this junk up! This is horrible code. + * Terminal read functions + */ +#include "sys.h" +#include +#include +#include +#include "el.h" + +#define OKCMD -1 + +private int read__fixio(int, int); +private int read_preread(EditLine *); +private int read_getcmd(EditLine *, el_action_t *, char *); +private int read_char(EditLine *, char *); + +#ifdef DEBUG_EDIT +private void +read_debug(EditLine *el) +{ + + if (el->el_line.cursor > el->el_line.lastchar) + (void) fprintf(el->el_errfile, "cursor > lastchar\r\n"); + if (el->el_line.cursor < el->el_line.buffer) + (void) fprintf(el->el_errfile, "cursor < buffer\r\n"); + if (el->el_line.cursor > el->el_line.limit) + (void) fprintf(el->el_errfile, "cursor > limit\r\n"); + if (el->el_line.lastchar > el->el_line.limit) + (void) fprintf(el->el_errfile, "lastchar > limit\r\n"); + if (el->el_line.limit != &el->el_line.buffer[EL_BUFSIZ - 2]) + (void) fprintf(el->el_errfile, "limit != &buffer[EL_BUFSIZ-2]\r\n"); +} +#endif /* DEBUG_EDIT */ + + +/* read__fixio(): + * Try to recover from a read error + */ +/* ARGSUSED */ +private int +read__fixio(int fd __attribute__((unused)), int e) +{ + + switch (e) { + case -1: /* Make sure that the code is reachable */ + +#ifdef EWOULDBLOCK + case EWOULDBLOCK: +#ifndef TRY_AGAIN +#define TRY_AGAIN +#endif +#endif /* EWOULDBLOCK */ + +#if defined(POSIX) && defined(EAGAIN) +#if defined(EWOULDBLOCK) && EWOULDBLOCK != EAGAIN + case EAGAIN: +#ifndef TRY_AGAIN +#define TRY_AGAIN +#endif +#endif /* EWOULDBLOCK && EWOULDBLOCK != EAGAIN */ +#endif /* POSIX && EAGAIN */ + + e = 0; +#ifdef TRY_AGAIN +#if defined(F_SETFL) && defined(O_NDELAY) + if ((e = fcntl(fd, F_GETFL, 0)) == -1) + return (-1); + + if (fcntl(fd, F_SETFL, e & ~O_NDELAY) == -1) + return (-1); + else + e = 1; +#endif /* F_SETFL && O_NDELAY */ + +#ifdef FIONBIO + { + int zero = 0; + + if (ioctl(fd, FIONBIO, (ioctl_t) & zero) == -1) + return (-1); + else + e = 1; + } +#endif /* FIONBIO */ + +#endif /* TRY_AGAIN */ + return (e ? 0 : -1); + + case EINTR: + return (0); + + default: + return (-1); + } +} + + +/* read_preread(): + * Try to read the stuff in the input queue; + */ +private int +read_preread(EditLine *el) +{ + int chrs = 0; + + if (el->el_chared.c_macro.nline) { + el_free((ptr_t) el->el_chared.c_macro.nline); + el->el_chared.c_macro.nline = NULL; + } + if (el->el_tty.t_mode == ED_IO) + return (0); + +#ifdef FIONREAD + (void) ioctl(el->el_infd, FIONREAD, (ioctl_t) & chrs); + if (chrs > 0) { + char buf[EL_BUFSIZ]; + + chrs = read(el->el_infd, buf, + (size_t) MIN(chrs, EL_BUFSIZ - 1)); + if (chrs > 0) { + buf[chrs] = '\0'; + el->el_chared.c_macro.nline = strdup(buf); + el_push(el, el->el_chared.c_macro.nline); + } + } +#endif /* FIONREAD */ + + return (chrs > 0); +} + + +/* el_push(): + * Push a macro + */ +public void +el_push(EditLine *el, const char *str) +{ + c_macro_t *ma = &el->el_chared.c_macro; + + if (str != NULL && ma->level + 1 < EL_MAXMACRO) { + ma->level++; + /* LINTED const cast */ + ma->macro[ma->level] = (char *) str; + } else { + term_beep(el); + term__flush(); + } +} + + +/* read_getcmd(): + * Return next command from the input stream. + */ +private int +read_getcmd(EditLine *el, el_action_t *cmdnum, char *ch) +{ + el_action_t cmd = ED_UNASSIGNED; + int num; + + while (cmd == ED_UNASSIGNED || cmd == ED_SEQUENCE_LEAD_IN) { + if ((num = el_getc(el, ch)) != 1) /* if EOF or error */ + return (num); + +#ifdef KANJI + if ((*ch & 0200)) { + el->el_state.metanext = 0; + cmd = CcViMap[' ']; + break; + } else +#endif /* KANJI */ + + if (el->el_state.metanext) { + el->el_state.metanext = 0; + *ch |= 0200; + } + cmd = el->el_map.current[(unsigned char) *ch]; + if (cmd == ED_SEQUENCE_LEAD_IN) { + key_value_t val; + switch (key_get(el, ch, &val)) { + case XK_CMD: + cmd = val.cmd; + break; + case XK_STR: + el_push(el, val.str); + break; +#ifdef notyet + case XK_EXE: + /* XXX: In the future to run a user function */ + RunCommand(val.str); + break; +#endif + default: + EL_ABORT((el->el_errfile, "Bad XK_ type \n")); + break; + } + } + if (el->el_map.alt == NULL) + el->el_map.current = el->el_map.key; + } + *cmdnum = cmd; + return (OKCMD); +} + + +/* read_char(): + * Read a character from the tty. + */ +private int +read_char(EditLine *el, char *cp) +{ + int num_read; + int tried = 0; + + while ((num_read = read(el->el_infd, cp, 1)) == -1) + if (!tried && read__fixio(el->el_infd, errno) == 0) + tried = 1; + else { + *cp = '\0'; + return (-1); + } + + return (num_read); +} + + +/* el_getc(): + * Read a character + */ +public int +el_getc(EditLine *el, char *cp) +{ + int num_read; + c_macro_t *ma = &el->el_chared.c_macro; + + term__flush(); + for (;;) { + if (ma->level < 0) { + if (!read_preread(el)) + break; + } + if (ma->level < 0) + break; + + if (*ma->macro[ma->level] == 0) { + ma->level--; + continue; + } + *cp = *ma->macro[ma->level]++ & 0377; + if (*ma->macro[ma->level] == 0) { /* Needed for QuoteMode + * On */ + ma->level--; + } + return (1); + } + +#ifdef DEBUG_READ + (void) fprintf(el->el_errfile, "Turning raw mode on\n"); +#endif /* DEBUG_READ */ + if (tty_rawmode(el) < 0)/* make sure the tty is set up correctly */ + return (0); + +#ifdef DEBUG_READ + (void) fprintf(el->el_errfile, "Reading a character\n"); +#endif /* DEBUG_READ */ + num_read = read_char(el, cp); +#ifdef DEBUG_READ + (void) fprintf(el->el_errfile, "Got it %c\n", *cp); +#endif /* DEBUG_READ */ + return (num_read); +} + + +public const char * +el_gets(EditLine *el, int *nread) +{ + int retval; + el_action_t cmdnum = 0; + int num; /* how many chars we have read at NL */ + char ch; +#ifdef FIONREAD + c_macro_t *ma = &el->el_chared.c_macro; +#endif /* FIONREAD */ + + if (el->el_flags & HANDLE_SIGNALS) + sig_set(el); + + if (el->el_flags & NO_TTY) { + char *cp = el->el_line.buffer; + size_t idx; + + while (read_char(el, cp) == 1) { + /* make sure there is space for next character */ + if (cp + 1 >= el->el_line.limit) { + idx = (cp - el->el_line.buffer); + if (!ch_enlargebufs(el, 2)) + break; + cp = &el->el_line.buffer[idx]; + } + cp++; + if (cp[-1] == '\r' || cp[-1] == '\n') + break; + } + + el->el_line.cursor = el->el_line.lastchar = cp; + *cp = '\0'; + if (nread) + *nread = el->el_line.cursor - el->el_line.buffer; + return (el->el_line.buffer); + } + re_clear_display(el); /* reset the display stuff */ + ch_reset(el); + +#ifdef FIONREAD + if (el->el_tty.t_mode == EX_IO && ma->level < 0) { + long chrs = 0; + + (void) ioctl(el->el_infd, FIONREAD, (ioctl_t) & chrs); + if (chrs == 0) { + if (tty_rawmode(el) < 0) { + if (nread) + *nread = 0; + return (NULL); + } + } + } +#endif /* FIONREAD */ + + re_refresh(el); /* print the prompt */ + + if (el->el_flags & EDIT_DISABLED) { + char *cp = el->el_line.buffer; + size_t idx; + + term__flush(); + + while (read_char(el, cp) == 1) { + /* make sure there is space next character */ + if (cp + 1 >= el->el_line.limit) { + idx = (cp - el->el_line.buffer); + if (!ch_enlargebufs(el, 2)) + break; + cp = &el->el_line.buffer[idx]; + } + cp++; + if (cp[-1] == '\r' || cp[-1] == '\n') + break; + } + + el->el_line.cursor = el->el_line.lastchar = cp; + *cp = '\0'; + if (nread) + *nread = el->el_line.cursor - el->el_line.buffer; + return (el->el_line.buffer); + } + for (num = OKCMD; num == OKCMD;) { /* while still editing this + * line */ +#ifdef DEBUG_EDIT + read_debug(el); +#endif /* DEBUG_EDIT */ + /* if EOF or error */ + if ((num = read_getcmd(el, &cmdnum, &ch)) != OKCMD) { +#ifdef DEBUG_READ + (void) fprintf(el->el_errfile, + "Returning from el_gets %d\n", num); +#endif /* DEBUG_READ */ + break; + } + if ((int) cmdnum >= 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); +#endif /* DEBUG_EDIT */ + continue; /* try again */ + } + /* now do the real command */ +#ifdef DEBUG_READ + { + el_bindings_t *b; + for (b = el->el_map.help; b->name; b++) + if (b->func == cmdnum) + break; + if (b->name) + (void) fprintf(el->el_errfile, + "Executing %s\n", b->name); + else + (void) fprintf(el->el_errfile, + "Error command = %d\n", cmdnum); + } +#endif /* DEBUG_READ */ + retval = (*el->el_map.func[cmdnum]) (el, ch); + + /* save the last command here */ + el->el_state.lastcmd = cmdnum; + + /* use any return value */ + switch (retval) { + case CC_CURSOR: + el->el_state.argument = 1; + el->el_state.doingarg = 0; + re_refresh_cursor(el); + break; + + case CC_REDISPLAY: + re_clear_lines(el); + re_clear_display(el); + /* FALLTHROUGH */ + + case CC_REFRESH: + el->el_state.argument = 1; + el->el_state.doingarg = 0; + re_refresh(el); + break; + + case CC_REFRESH_BEEP: + el->el_state.argument = 1; + el->el_state.doingarg = 0; + re_refresh(el); + term_beep(el); + break; + + case CC_NORM: /* normal char */ + el->el_state.argument = 1; + el->el_state.doingarg = 0; + break; + + case CC_ARGHACK: /* Suggested by Rich Salz */ + /* */ + break; /* keep going... */ + + case CC_EOF: /* end of file typed */ + num = 0; + break; + + case CC_NEWLINE: /* normal end of line */ + num = el->el_line.lastchar - el->el_line.buffer; + break; + + case CC_FATAL: /* fatal error, reset to known state */ +#ifdef DEBUG_READ + (void) fprintf(el->el_errfile, + "*** editor fatal ERROR ***\r\n\n"); +#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 */ + re_refresh(el); /* print the prompt again */ + el->el_state.argument = 1; + el->el_state.doingarg = 0; + break; + + case CC_ERROR: + default: /* functions we don't know about */ +#ifdef DEBUG_READ + (void) fprintf(el->el_errfile, + "*** editor ERROR ***\r\n\n"); +#endif /* DEBUG_READ */ + el->el_state.argument = 1; + el->el_state.doingarg = 0; + term_beep(el); + term__flush(); + break; + } + } + + /* make sure the tty is set up correctly */ + (void) tty_cookedmode(el); + term__flush(); /* flush any buffered output */ + if (el->el_flags & HANDLE_SIGNALS) + sig_clr(el); + if (nread) + *nread = num; + return (num ? el->el_line.buffer : NULL); +} diff --git a/cmd-line-utils/libedit/readline.c b/cmd-line-utils/libedit/readline.c new file mode 100644 index 00000000000..e9c8409102c --- /dev/null +++ b/cmd-line-utils/libedit/readline.c @@ -0,0 +1,1661 @@ +/* $NetBSD: readline.c,v 1.19 2001/01/10 08:10:45 jdolecek 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. + * 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. + */ + +#include "compat.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "histedit.h" +#include "readline/readline.h" +#include "sys.h" +#include "el.h" +#include "fcns.h" /* for EL_NUM_FCNS */ + +/* for rl_complete() */ +#define TAB '\r' + +/* see comment at the #ifdef for sense of this */ +#define GDB_411_HACK + +/* readline compatibility stuff - look at readline sources/documentation */ +/* to see what these variables mean */ +const char *rl_library_version = "EditLine wrapper"; +const char *rl_readline_name = ""; +FILE *rl_instream = NULL; +FILE *rl_outstream = NULL; +int rl_point = 0; +int rl_end = 0; +char *rl_line_buffer = NULL; + +int history_base = 1; /* probably never subject to change */ +int history_length = 0; +int max_input_history = 0; +char history_expansion_char = '!'; +char history_subst_char = '^'; +const char *history_no_expand_chars = " \t\n=("; +Function *history_inhibit_expansion_function = NULL; + +int rl_inhibit_completion = 0; +int rl_attempted_completion_over = 0; +const char *rl_basic_word_break_characters = " \t\n\"\\'`@$><=;|&{("; +char *rl_completer_word_break_characters = NULL; +char *rl_completer_quote_characters = NULL; +CPFunction *rl_completion_entry_function = NULL; +CPPFunction *rl_attempted_completion_function = NULL; + +/* + * This is set to character indicating type of completion being done by + * rl_complete_internal(); this is available for application completion + * functions. + */ +int rl_completion_type = 0; + +/* + * If more than this number of items results from query for possible + * completions, we ask user if they are sure to really display the list. + */ +int rl_completion_query_items = 100; + +/* + * List of characters which are word break characters, but should be left + * 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; + +/* + * This is the character appended to the completed words if at the end of + * the line. Default is ' ' (a space). + */ +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 int el_rl_complete_cmdnum = 0; + +/* internal functions */ +static unsigned char _el_rl_complete(EditLine *, int); +static char *_get_prompt(EditLine *); +static HIST_ENTRY *_move_history(int); +static int _history_search_gen(const char *, int, int); +static int _history_expand_command(const char *, 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 *); + +/* + * needed for prompt switching in readline() + */ +static char *el_rl_prompt = NULL; + + +/* ARGSUSED */ +static char * +_get_prompt(EditLine *el __attribute__((unused))) +{ + return (el_rl_prompt); +} + + +/* + * generic function for moving around history + */ +static HIST_ENTRY * +_move_history(int op) +{ + HistEvent ev; + static HIST_ENTRY rl_he; + + if (history(h, &ev, op) != 0) + return (HIST_ENTRY *) NULL; + + rl_he.line = ev.str; + rl_he.data = ""; + + return (&rl_he); +} + + +/* + * READLINE compatibility stuff + */ + +/* + * initialize rl compat stuff + */ +int +rl_initialize(void) +{ + HistEvent ev; + const LineInfo *li; + int i; + int editmode = 1; + struct termios t; + + if (e != NULL) + el_end(e); + if (h != NULL) + history_end(h); + + if (!rl_instream) + rl_instream = stdin; + if (!rl_outstream) + rl_outstream = stdout; + + /* + * See if we don't really want to run the editor + */ + if (tcgetattr(fileno(rl_instream), &t) != -1 && (t.c_lflag & ECHO) == 0) + editmode = 0; + + e = el_init(rl_readline_name, rl_instream, rl_outstream, stderr); + + if (!editmode) + el_set(e, EL_EDITMODE, 0); + + h = history_init(); + if (!e || !h) + return (-1); + + history(h, &ev, H_SETSIZE, INT_MAX); /* unlimited */ + history_length = 0; + max_input_history = INT_MAX; + el_set(e, EL_HIST, history, h); + + /* for proper prompt printing in readline() */ + el_rl_prompt = strdup(""); + el_set(e, EL_PROMPT, _get_prompt); + el_set(e, EL_SIGNAL, 1); + + /* set default mode to "emacs"-style and read setting afterwards */ + /* so this can be overriden */ + el_set(e, EL_EDITOR, "emacs"); + + /* + * Word completition - this has to go AFTER rebinding keys + * to emacs-style. + */ + el_set(e, EL_ADDFN, "rl_complete", + "ReadLine compatible completition function", + _el_rl_complete); + el_set(e, EL_BIND, "^I", "rl_complete", 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); + + /* + * Unfortunately, some applications really do use rl_point + * and rl_line_buffer directly. + */ + li = el_line(e); + /* LINTED const cast */ + rl_line_buffer = (char *) li->buffer; + rl_point = rl_end = 0; + + return (0); +} + + +/* + * read one line from input stream and return it, chomping + * trailing newline (if there is any) + */ +char * +readline(const char *prompt) +{ + HistEvent ev; + int count; + const char *ret; + + if (e == NULL || h == NULL) + rl_initialize(); + + /* update prompt accordingly to what has been passed */ + if (!prompt) + prompt = ""; + if (strcmp(el_rl_prompt, prompt) != 0) { + free(el_rl_prompt); + el_rl_prompt = strdup(prompt); + } + /* get one line from input stream */ + ret = el_gets(e, &count); + + if (ret && count > 0) { + char *foo; + int lastidx; + + foo = strdup(ret); + lastidx = count - 1; + if (foo[lastidx] == '\n') + foo[lastidx] = '\0'; + + ret = foo; + } else + ret = NULL; + + history(h, &ev, H_GETSIZE); + history_length = ev.num; + + /* LINTED const cast */ + return (char *) ret; +} + +/* + * history functions + */ + +/* + * is normally called before application starts to use + * history expansion functions + */ +void +using_history(void) +{ + if (h == NULL || e == NULL) + rl_initialize(); +} + + +/* + * substitute ``what'' with ``with'', returning resulting string; if + * globally == 1, substitutes all occurences of what, otherwise only the + * first one + */ +static char * +_rl_compat_sub(const char *str, const char *what, const char *with, + int globally) +{ + char *result; + const char *temp, *new; + unsigned int len, with_len, what_len, add; + size_t size, i; + + result = malloc((size = 16)); + temp = str; + with_len = strlen(with); + what_len = strlen(what); + len = 0; + do { + new = strstr(temp, what); + if (new) { + i = new - temp; + add = i + with_len; + if (i + add + 1 >= size) { + size += add + 1; + result = realloc(result, size); + } + (void) strncpy(&result[len], temp, i); + len += i; + (void) strcpy(&result[len], with); /* safe */ + len += with_len; + temp = new + what_len; + } else { + add = strlen(temp); + if (len + add + 1 >= size) { + size += add + 1; + result = realloc(result, size); + } + (void) strcpy(&result[len], temp); /* safe */ + len += add; + temp = NULL; + } + } while (temp && globally); + result[len] = '\0'; + + return (result); +} + + +/* + * the real function doing history expansion - takes as argument command + * to do and data upon which the command should be executed + * does expansion the way I've understood readline documentation + * word designator ``%'' isn't supported (yet ?) + * + * returns 0 if data was not modified, 1 if it was and 2 if the string + * should be only printed and not executed; in case of error, + * returns -1 and *result points to NULL + * it's callers responsibility to free() string returned in *result + */ +static int +_history_expand_command(const char *command, size_t cmdlen, char **result) +{ + char **arr, *tempcmd, *line, *search = NULL, *cmd; + const char *event_data = NULL; + static char *from = NULL, *to = NULL; + int start = -1, end = -1, max, i, idx; + int h_on = 0, t_on = 0, r_on = 0, e_on = 0, p_on = 0, g_on = 0; + int event_num = 0, retval; + size_t cmdsize; + + *result = NULL; + + cmd = alloca(cmdlen + 1); + (void) strncpy(cmd, command, cmdlen); + cmd[cmdlen] = 0; + + idx = 1; + /* find out which event to take */ + if (cmd[idx] == history_expansion_char) { + event_num = history_length; + idx++; + } else { + int off, num; + size_t len; + off = idx; + while (cmd[off] && !strchr(":^$*-%", cmd[off])) + off++; + num = atoi(&cmd[idx]); + if (num != 0) { + event_num = num; + if (num < 0) + event_num += history_length + 1; + } else { + int prefix = 1, curr_num; + HistEvent ev; + + len = off - idx; + if (cmd[idx] == '?') { + idx++, len--; + if (cmd[off - 1] == '?') + len--; + else if (cmd[off] != '\n' && cmd[off] != '\0') + return (-1); + prefix = 0; + } + search = alloca(len + 1); + (void) strncpy(search, &cmd[idx], len); + search[len] = '\0'; + + if (history(h, &ev, H_CURR) != 0) + return (-1); + curr_num = ev.num; + + if (prefix) + retval = history_search_prefix(search, -1); + else + retval = history_search(search, -1); + + if (retval == -1) { + fprintf(rl_outstream, "%s: Event not found\n", + search); + return (-1); + } + if (history(h, &ev, H_CURR) != 0) + return (-1); + event_data = ev.str; + + /* roll back to original position */ + history(h, &ev, H_NEXT_EVENT, curr_num); + } + idx = off; + } + + if (!event_data && event_num >= 0) { + HIST_ENTRY *rl_he; + rl_he = history_get(event_num); + if (!rl_he) + return (0); + event_data = rl_he->line; + } else + return (-1); + + if (cmd[idx] != ':') + return (-1); + cmd += idx + 1; + + /* recognize cmd */ + if (*cmd == '^') + start = end = 1, cmd++; + else if (*cmd == '$') + start = end = -1, cmd++; + else if (*cmd == '*') + start = 1, end = -1, cmd++; + else if (isdigit((unsigned char) *cmd)) { + const char *temp; + int shifted = 0; + + start = atoi(cmd); + temp = cmd; + for (; isdigit((unsigned char) *cmd); cmd++); + if (temp != cmd) + shifted = 1; + if (shifted && *cmd == '-') { + if (!isdigit((unsigned char) *(cmd + 1))) + end = -2; + else { + end = atoi(cmd + 1); + for (; isdigit((unsigned char) *cmd); cmd++); + } + } else if (shifted && *cmd == '*') + end = -1, cmd++; + else if (shifted) + end = start; + } + if (*cmd == ':') + cmd++; + + line = strdup(event_data); + for (; *cmd; cmd++) { + if (*cmd == ':') + continue; + else if (*cmd == 'h') + h_on = 1 | g_on, g_on = 0; + else if (*cmd == 't') + t_on = 1 | g_on, g_on = 0; + else if (*cmd == 'r') + r_on = 1 | g_on, g_on = 0; + else if (*cmd == 'e') + e_on = 1 | g_on, g_on = 0; + else if (*cmd == 'p') + p_on = 1 | g_on, g_on = 0; + else if (*cmd == 'g') + g_on = 2; + else if (*cmd == 's' || *cmd == '&') { + char *what, *with, delim; + size_t len, from_len; + size_t size; + + if (*cmd == '&' && (from == NULL || to == NULL)) + continue; + else if (*cmd == 's') { + delim = *(++cmd), cmd++; + size = 16; + what = realloc(from, size); + len = 0; + for (; *cmd && *cmd != delim; cmd++) { + if (*cmd == '\\' + && *(cmd + 1) == delim) + cmd++; + if (len >= size) + what = realloc(what, + (size <<= 1)); + what[len++] = *cmd; + } + what[len] = '\0'; + from = what; + if (*what == '\0') { + free(what); + if (search) + from = strdup(search); + else { + from = NULL; + return (-1); + } + } + cmd++; /* shift after delim */ + if (!*cmd) + continue; + + size = 16; + with = realloc(to, size); + len = 0; + from_len = strlen(from); + for (; *cmd && *cmd != delim; cmd++) { + if (len + from_len + 1 >= size) { + size += from_len + 1; + with = realloc(with, size); + } + if (*cmd == '&') { + /* safe */ + (void) strcpy(&with[len], from); + len += from_len; + continue; + } + if (*cmd == '\\' + && (*(cmd + 1) == delim + || *(cmd + 1) == '&')) + cmd++; + with[len++] = *cmd; + } + with[len] = '\0'; + to = with; + + tempcmd = _rl_compat_sub(line, from, to, + (g_on) ? 1 : 0); + free(line); + line = tempcmd; + g_on = 0; + } + } + } + + arr = history_tokenize(line); + free(line); /* no more needed */ + if (arr && *arr == NULL) + free(arr), arr = NULL; + if (!arr) + return (-1); + + /* find out max valid idx to array of array */ + max = 0; + for (i = 0; arr[i]; i++) + max++; + max--; + + /* set boundaries to something relevant */ + if (start < 0) + start = 1; + if (end < 0) + end = max - ((end < -1) ? 1 : 0); + + /* check boundaries ... */ + if (start > max || end > max || start > end) + return (-1); + + for (i = 0; i <= max; i++) { + char *temp; + if (h_on && (i == 1 || h_on > 1) && + (temp = strrchr(arr[i], '/'))) + *(temp + 1) = '\0'; + if (t_on && (i == 1 || t_on > 1) && + (temp = strrchr(arr[i], '/'))) + (void) strcpy(arr[i], temp + 1); + if (r_on && (i == 1 || r_on > 1) && + (temp = strrchr(arr[i], '.'))) + *temp = '\0'; + if (e_on && (i == 1 || e_on > 1) && + (temp = strrchr(arr[i], '.'))) + (void) strcpy(arr[i], temp); + } + + cmdsize = 1, cmdlen = 0; + tempcmd = malloc(cmdsize); + for (i = start; start <= i && i <= end; i++) { + int arr_len; + + arr_len = strlen(arr[i]); + if (cmdlen + arr_len + 1 >= cmdsize) { + cmdsize += arr_len + 1; + tempcmd = realloc(tempcmd, cmdsize); + } + (void) strcpy(&tempcmd[cmdlen], arr[i]); /* safe */ + cmdlen += arr_len; + tempcmd[cmdlen++] = ' '; /* add a space */ + } + while (cmdlen > 0 && isspace((unsigned char) tempcmd[cmdlen - 1])) + cmdlen--; + tempcmd[cmdlen] = '\0'; + + *result = tempcmd; + + for (i = 0; i <= max; i++) + free(arr[i]); + free(arr), arr = (char **) NULL; + return (p_on) ? 2 : 1; +} + + +/* + * csh-style history expansion + */ +int +history_expand(char *str, char **output) +{ + int i, retval = 0, idx; + size_t size; + char *temp, *result; + + if (h == NULL || e == NULL) + rl_initialize(); + + *output = strdup(str); /* do it early */ + + if (str[0] == history_subst_char) { + /* ^foo^foo2^ is equivalent to !!:s^foo^foo2^ */ + temp = alloca(4 + strlen(str) + 1); + temp[0] = temp[1] = history_expansion_char; + temp[2] = ':'; + temp[3] = 's'; + (void) strcpy(temp + 4, str); + str = temp; + } +#define ADD_STRING(what, len) \ + { \ + if (idx + len + 1 > size) \ + result = realloc(result, (size += len + 1)); \ + (void)strncpy(&result[idx], what, len); \ + idx += len; \ + result[idx] = '\0'; \ + } + + result = NULL; + size = idx = 0; + for (i = 0; str[i];) { + int start, j, loop_again; + size_t len; + + loop_again = 1; + start = j = i; +loop: + for (; str[j]; j++) { + if (str[j] == '\\' && + str[j + 1] == history_expansion_char) { + (void) strcpy(&str[j], &str[j + 1]); + continue; + } + if (!loop_again) { + if (str[j] == '?') { + while (str[j] && str[++j] != '?'); + if (str[j] == '?') + j++; + } else if (isspace((unsigned char) str[j])) + break; + } + if (str[j] == history_expansion_char + && !strchr(history_no_expand_chars, str[j + 1]) + && (!history_inhibit_expansion_function || + (*history_inhibit_expansion_function)(str, j) == 0)) + break; + } + + if (str[j] && str[j + 1] != '#' && loop_again) { + i = j; + j++; + if (str[j] == history_expansion_char) + j++; + loop_again = 0; + goto loop; + } + len = i - start; + temp = &str[start]; + ADD_STRING(temp, len); + + if (str[i] == '\0' || str[i] != history_expansion_char + || str[i + 1] == '#') { + len = j - i; + temp = &str[i]; + ADD_STRING(temp, len); + if (start == 0) + retval = 0; + else + retval = 1; + break; + } + retval = _history_expand_command(&str[i], (size_t) (j - i), + &temp); + if (retval != -1) { + len = strlen(temp); + ADD_STRING(temp, len); + } + i = j; + } /* for(i ...) */ + + if (retval == 2) { + add_history(temp); +#ifdef GDB_411_HACK + /* gdb 4.11 has been shipped with readline, where */ + /* history_expand() returned -1 when the line */ + /* should not be executed; in readline 2.1+ */ + /* it should return 2 in such a case */ + retval = -1; +#endif + } + free(*output); + *output = result; + + return (retval); +} + + +/* + * Parse the string into individual tokens, similarily to how shell would do it. + */ +char ** +history_tokenize(const char *str) +{ + int size = 1, result_idx = 0, i, start; + size_t len; + char **result = NULL, *temp, delim = '\0'; + + for (i = 0; str[i]; i++) { + while (isspace((unsigned char) str[i])) + i++; + start = i; + for (; str[i]; i++) { + if (str[i] == '\\') { + if (str[i+1] != '\0') + i++; + } else if (str[i] == delim) + delim = '\0'; + else if (!delim && + (isspace((unsigned char) str[i]) || + strchr("()<>;&|$", str[i]))) + break; + else if (!delim && strchr("'`\"", str[i])) + delim = str[i]; + } + + if (result_idx + 2 >= size) { + size <<= 1; + result = realloc(result, size * sizeof(char *)); + } + len = i - start; + temp = malloc(len + 1); + (void) strncpy(temp, &str[start], len); + temp[len] = '\0'; + result[result_idx++] = temp; + result[result_idx] = NULL; + } + + return (result); +} + + +/* + * limit size of history record to ``max'' events + */ +void +stifle_history(int max) +{ + HistEvent ev; + + if (h == NULL || e == NULL) + rl_initialize(); + + if (history(h, &ev, H_SETSIZE, max) == 0) + max_input_history = max; +} + + +/* + * "unlimit" size of history - set the limit to maximum allowed int value + */ +int +unstifle_history(void) +{ + HistEvent ev; + int omax; + + history(h, &ev, H_SETSIZE, INT_MAX); + omax = max_input_history; + max_input_history = INT_MAX; + return (omax); /* some value _must_ be returned */ +} + + +int +history_is_stifled(void) +{ + + /* cannot return true answer */ + return (max_input_history != INT_MAX); +} + + +/* + * read history from a file given + */ +int +read_history(const char *filename) +{ + HistEvent ev; + + if (h == NULL || e == NULL) + rl_initialize(); + return (history(h, &ev, H_LOAD, filename)); +} + + +/* + * write history to a file given + */ +int +write_history(const char *filename) +{ + HistEvent ev; + + if (h == NULL || e == NULL) + rl_initialize(); + return (history(h, &ev, H_SAVE, filename)); +} + + +/* + * returns history ``num''th event + * + * returned pointer points to static variable + */ +HIST_ENTRY * +history_get(int num) +{ + static HIST_ENTRY she; + HistEvent ev; + int i = 1, curr_num; + + if (h == NULL || e == NULL) + rl_initialize(); + + /* rewind to beginning */ + if (history(h, &ev, H_CURR) != 0) + return (NULL); + curr_num = ev.num; + if (history(h, &ev, H_LAST) != 0) + return (NULL); /* error */ + while (i < num && history(h, &ev, H_PREV) == 0) + i++; + if (i != num) + return (NULL); /* not so many entries */ + + she.line = ev.str; + she.data = NULL; + + /* rewind history to the same event it was before */ + (void) history(h, &ev, H_FIRST); + (void) history(h, &ev, H_NEXT_EVENT, curr_num); + + return (&she); +} + + +/* + * add the line to history table + */ +int +add_history(const char *line) +{ + HistEvent ev; + + if (h == NULL || e == NULL) + rl_initialize(); + + (void) history(h, &ev, H_ENTER, line); + if (history(h, &ev, H_GETSIZE) == 0) + history_length = ev.num; + + return (!(history_length > 0)); /* return 0 if all is okay */ +} + + +/* + * clear the history list - delete all entries + */ +void +clear_history(void) +{ + HistEvent ev; + + history(h, &ev, H_CLEAR); +} + + +/* + * returns offset of the current history event + */ +int +where_history(void) +{ + HistEvent ev; + int curr_num, off; + + if (history(h, &ev, H_CURR) != 0) + return (0); + curr_num = ev.num; + + history(h, &ev, H_FIRST); + off = 1; + while (ev.num != curr_num && history(h, &ev, H_NEXT) == 0) + off++; + + return (off); +} + + +/* + * returns current history event or NULL if there is no such event + */ +HIST_ENTRY * +current_history(void) +{ + + return (_move_history(H_CURR)); +} + + +/* + * returns total number of bytes history events' data are using + */ +int +history_total_bytes(void) +{ + HistEvent ev; + int curr_num, size; + + if (history(h, &ev, H_CURR) != 0) + return (-1); + curr_num = ev.num; + + history(h, &ev, H_FIRST); + size = 0; + do + size += strlen(ev.str); + while (history(h, &ev, H_NEXT) == 0); + + /* get to the same position as before */ + history(h, &ev, H_PREV_EVENT, curr_num); + + return (size); +} + + +/* + * sets the position in the history list to ``pos'' + */ +int +history_set_pos(int pos) +{ + HistEvent ev; + int off, curr_num; + + if (pos > history_length || pos < 0) + return (-1); + + history(h, &ev, H_CURR); + curr_num = ev.num; + history(h, &ev, H_FIRST); + off = 0; + while (off < pos && history(h, &ev, H_NEXT) == 0) + off++; + + if (off != pos) { /* do a rollback in case of error */ + history(h, &ev, H_FIRST); + history(h, &ev, H_NEXT_EVENT, curr_num); + return (-1); + } + return (0); +} + + +/* + * returns previous event in history and shifts pointer accordingly + */ +HIST_ENTRY * +previous_history(void) +{ + + return (_move_history(H_PREV)); +} + + +/* + * returns next event in history and shifts pointer accordingly + */ +HIST_ENTRY * +next_history(void) +{ + + return (_move_history(H_NEXT)); +} + + +/* + * generic history search function + */ +static int +_history_search_gen(const char *str, int direction, int pos) +{ + HistEvent ev; + const char *strp; + int curr_num; + + if (history(h, &ev, H_CURR) != 0) + return (-1); + curr_num = ev.num; + + for (;;) { + strp = strstr(ev.str, str); + if (strp && (pos < 0 || &ev.str[pos] == strp)) + return (int) (strp - ev.str); + if (history(h, &ev, direction < 0 ? H_PREV : H_NEXT) != 0) + break; + } + + history(h, &ev, direction < 0 ? H_NEXT_EVENT : H_PREV_EVENT, curr_num); + + return (-1); +} + + +/* + * searches for first history event containing the str + */ +int +history_search(const char *str, int direction) +{ + + return (_history_search_gen(str, direction, -1)); +} + + +/* + * searches for first history event beginning with str + */ +int +history_search_prefix(const char *str, int direction) +{ + + return (_history_search_gen(str, direction, 0)); +} + + +/* + * search for event in history containing str, starting at offset + * abs(pos); continue backward, if pos<0, forward otherwise + */ +/* ARGSUSED */ +int +history_search_pos(const char *str, + int direction __attribute__((unused)), int pos) +{ + HistEvent ev; + int curr_num, off; + + off = (pos > 0) ? pos : -pos; + pos = (pos > 0) ? 1 : -1; + + if (history(h, &ev, H_CURR) != 0) + return (-1); + curr_num = ev.num; + + if (history_set_pos(off) != 0 || history(h, &ev, H_CURR) != 0) + return (-1); + + + for (;;) { + if (strstr(ev.str, str)) + return (off); + if (history(h, &ev, (pos < 0) ? H_PREV : H_NEXT) != 0) + break; + } + + /* set "current" pointer back to previous state */ + history(h, &ev, (pos < 0) ? H_NEXT_EVENT : H_PREV_EVENT, curr_num); + + return (-1); +} + + +/********************************/ +/* completition 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) +{ + 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); + else { + len = temp - txt + 1; /* text until string after slash */ + temp = malloc(len); + (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); + (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 * +filename_completion_function(const char *text, 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) { + if (dir != NULL) { + closedir(dir); + dir = NULL; + } + temp = strrchr(text, '/'); + if (temp) { + temp++; + filename = realloc(filename, strlen(temp) + 1); + (void) strcpy(filename, temp); + len = temp - text; /* including last slash */ + dirname = realloc(dirname, len + 1); + (void) strncpy(dirname, text, len); + dirname[len] = '\0'; + } else { + filename = strdup(text); + dirname = NULL; + } + + /* support for ``~user'' syntax */ + if (dirname && *dirname == '~') { + temp = tilde_expand(dirname); + dirname = realloc(dirname, strlen(temp) + 1); + (void) strcpy(dirname, temp); /* safe */ + free(temp); /* no longer needed */ + } + /* will be used in cycle */ + filename_len = strlen(filename); + if (filename_len == 0) + return (NULL); /* no expansion possible */ + + dir = opendir(dirname ? dirname : "."); + if (!dir) + return (NULL); /* cannot open the directory */ + } + /* find the match */ + while ((entry = readdir(dir)) != NULL) { + /* otherwise, get first entry where first */ + /* filename_len characters are equal */ + if (entry->d_name[0] == filename[0] +#if defined(__SVR4) || defined(__linux__) + && strlen(entry->d_name) >= filename_len +#else + && entry->d_namlen >= filename_len +#endif + && strncmp(entry->d_name, filename, + filename_len) == 0) + break; + } + + if (entry) { /* match found */ + + struct stat stbuf; +#if defined(__SVR4) || defined(__linux__) + len = strlen(entry->d_name) + +#else + len = entry->d_namlen + +#endif + ((dirname) ? strlen(dirname) : 0) + 1 + 1; + temp = malloc(len); + (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 + temp = NULL; + + return (temp); +} + + +/* + * a completion generator for usernames; returns _first_ username + * which starts with supplied text + * text contains a partial username preceded by random character + * (usually '~'); state is ignored + * it's callers responsibility to free returned value + */ +char * +username_completion_function(const char *text, int state) +{ + struct passwd *pwd; + + if (text[0] == '\0') + return (NULL); + + if (*text == '~') + text++; + + if (state == 0) + setpwent(); + + while ((pwd = getpwent()) && text[0] == pwd->pw_name[0] + && strcmp(text, pwd->pw_name) == 0); + + if (pwd == NULL) { + endpwent(); + return (NULL); + } + return (strdup(pwd->pw_name)); +} + + +/* + * 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); +} + + +/* + * returns list of completitions 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; + unsigned int matches; + + if (h == NULL || e == NULL) + rl_initialize(); + + matches = 0; + match_list_len = 1; + while ((retstr = (*genfunc) (text, matches)) != NULL) { + if (matches + 1 >= match_list_len) { + match_list_len <<= 1; + match_list = realloc(match_list, + match_list_len * sizeof(char *)); + } + match_list[++matches] = retstr; + } + + if (!match_list) + return (char **) 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); + (void) strncpy(retstr, match_list[1], max_equal); + retstr[max_equal] = '\0'; + match_list[0] = retstr; + + /* add NULL as last pointer to the array */ + if (matches + 1 >= match_list_len) + match_list = realloc(match_list, + (match_list_len + 1) * sizeof(char *)); + 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; +{ + /*LINTED const castaway*/ + const char *s1 = ((const char **)i1)[0]; + /*LINTED const castaway*/ + const char *s2 = ((const char **)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; +{ + 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++) + fprintf(e->el_outfile, "%-*s ", max, matches[idx]); + fprintf(e->el_outfile, "\n"); + } +} + +/* + * 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) +{ + 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_point = li->cursor - li->buffer; + rl_end = li->lastchar - li->buffer; + + if (!rl_attempted_completion_function) + matches = completion_matches(temp, complet_func); + else { + int end = li->cursor - li->buffer; + matches = (*rl_attempted_completion_function) (temp, (int) + (end - len), end); + } + + 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 completition 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 */ + fprintf(e->el_outfile, "\n"); + + /* + * If there are too many items, ask user for display + * confirmation. + */ + if (matches_num > rl_completion_query_items) { + fprintf(e->el_outfile, + "Display all %d possibilities? (y or n) ", + matches_num); + fflush(e->el_outfile); + if (getc(stdin) != 'y') + match_display = 0; + 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); +} + + +/* + * complete word at current point + */ +int +rl_complete(int ignore, int invoking_key) +{ + if (h == NULL || e == NULL) + rl_initialize(); + + if (rl_inhibit_completion) { + rl_insert(ignore, invoking_key); + 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)); +} + + +/* + * misc other functions + */ + +/* + * bind key c to readline-type function func + */ +int +rl_bind_key(int c, int func(int, int)) +{ + int retval = -1; + + if (h == NULL || e == NULL) + rl_initialize(); + + if (func == rl_insert) { + /* XXX notice there is no range checking of ``c'' */ + e->el_map.key[c] = ED_INSERT; + retval = 0; + } + return (retval); +} + + +/* + * read one key from input - handles chars pushed back + * to input stream also + */ +int +rl_read_key(void) +{ + char fooarr[2 * sizeof(int)]; + + if (e == NULL || h == NULL) + rl_initialize(); + + return (el_getc(e, fooarr)); +} + + +/* + * reset the terminal + */ +/* ARGSUSED */ +void +rl_reset_terminal(const char *p __attribute__((unused))) +{ + + if (h == NULL || e == NULL) + rl_initialize(); + el_reset(e); +} + + +/* + * insert character ``c'' back into input stream, ``count'' times + */ +int +rl_insert(int count, int c) +{ + char arr[2]; + + if (h == NULL || e == NULL) + rl_initialize(); + + /* XXX - int -> char conversion can lose on multichars */ + arr[0] = c; + arr[1] = '\0'; + + for (; count > 0; count--) + el_push(e, arr); + + return (0); +} diff --git a/cmd-line-utils/libedit/readline/readline.h b/cmd-line-utils/libedit/readline/readline.h new file mode 100644 index 00000000000..851ade02b53 --- /dev/null +++ b/cmd-line-utils/libedit/readline/readline.h @@ -0,0 +1,127 @@ +/* $NetBSD: readline.h,v 1.1 2001/01/05 21:15:50 jdolecek 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. + * 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. + */ +#ifndef _READLINE_H_ +#define _READLINE_H_ + +#include +#if HAVE_SYS_CDEFS_H +#include +#else +#ifndef __BEGIN_DECLS +#if defined(__cplusplus) +#define __BEGIN_DECLS extern "C" { +#define __END_DECLS } +#else +#define __BEGIN_DECLS +#define __END_DECLS +#endif +#endif +#endif + +/* list of readline stuff supported by editline library's readline wrapper */ + +/* typedefs */ +typedef int Function(const char *, int); +typedef void VFunction(void); +typedef char *CPFunction(const char *, int); +typedef char **CPPFunction(const char *, int, int); + +typedef struct _hist_entry { + const char *line; + const char *data; +} HIST_ENTRY; + +/* global variables used by readline enabled applications */ +__BEGIN_DECLS +extern const char *rl_library_version; +extern const char *rl_readline_name; +extern FILE *rl_instream; +extern FILE *rl_outstream; +extern char *rl_line_buffer; +extern int rl_point, rl_end; +extern int history_base, history_length; +extern int max_input_history; +extern const 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 CPPFunction *rl_attempted_completion_function; +extern int rl_completion_type; +extern int rl_completion_query_items; +extern char *rl_special_prefixes; +extern int rl_completion_append_character; + +/* supported functions */ +char *readline(const char *); +int rl_initialize(void); + +void using_history(void); +int add_history(const char *); +void clear_history(void); +void stifle_history(int); +int unstifle_history(void); +int history_is_stifled(void); +int where_history(void); +HIST_ENTRY *current_history(void); +HIST_ENTRY *history_get(int); +int history_total_bytes(void); +int history_set_pos(int); +HIST_ENTRY *previous_history(void); +HIST_ENTRY *next_history(void); +int history_search(const char *, int); +int history_search_prefix(const char *, int); +int history_search_pos(const char *, int, int); +int read_history(const char *); +int write_history(const char *); +int history_expand(char *, char **); +char **history_tokenize(const char *); + +char *tilde_expand(char *); +char *filename_completion_function(const char *, int); +char *username_completion_function(const char *, int); +int rl_complete(int, int); +int rl_read_key(void); +char **completion_matches(const char *, CPFunction *); +void rl_display_match_list(char **, int, int); + +int rl_insert(int, int); +void rl_reset_terminal(const char *); +int rl_bind_key(int, int (*)(int, int)); +__END_DECLS + +#endif /* _READLINE_H_ */ diff --git a/cmd-line-utils/libedit/refresh.c b/cmd-line-utils/libedit/refresh.c new file mode 100644 index 00000000000..534e7e12304 --- /dev/null +++ b/cmd-line-utils/libedit/refresh.c @@ -0,0 +1,1100 @@ +/* $NetBSD: refresh.c,v 1.17 2001/04/13 00:53:11 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 "compat.h" + +/* + * refresh.c: Lower level screen refreshing functions + */ +#include "sys.h" +#include +#include +#include +#include + +#include "el.h" + +private void re_addc(EditLine *, int); +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__strncopy(char *, char *, size_t); +private void re__copy_and_pad(char *, const char *, size_t); + +#ifdef DEBUG_REFRESH +private void re_printstr(EditLine *, char *, char *, char *); +#define __F el->el_errfile +#define ELRE_ASSERT(a, b, c) do \ + if (a) { \ + (void) fprintf b; \ + c; \ + } \ + while (0) +#define ELRE_DEBUG(a, b) ELRE_ASSERT(a,b,;) + +/* re_printstr(): + * Print a string on the debugging pty + */ +private void +re_printstr(EditLine *el, char *str, char *f, char *t) +{ + + ELRE_DEBUG(1, (__F, "%s:\"", str)); + while (f < t) + ELRE_DEBUG(1, (__F, "%c", *f++ & 0177)); + ELRE_DEBUG(1, (__F, "\"\r\n")); +} +#else +#define ELRE_ASSERT(a, b, c) +#define ELRE_DEBUG(a, b) +#endif + + +/* re_addc(): + * Draw c, expanding tabs, control chars etc. + */ +private void +re_addc(EditLine *el, int c) +{ + + if (isprint(c)) { + re_putc(el, c, 1); + return; + } + if (c == '\n') { /* expand the newline */ + int oldv = el->el_refresh.r_cursor.v; + re_putc(el, '\0', 0); /* assure end of line */ + if (oldv == el->el_refresh.r_cursor.v) { /* XXX */ + el->el_refresh.r_cursor.h = 0; /* reset cursor pos */ + el->el_refresh.r_cursor.v++; + } + return; + } + if (c == '\t') { /* expand the tab */ + for (;;) { + re_putc(el, ' ', 1); + if ((el->el_refresh.r_cursor.h & 07) == 0) + break; /* go until tab stop */ + } + } else if (iscntrl(c)) { + re_putc(el, '^', 1); + if (c == '\177') + re_putc(el, '?', 1); + else + /* uncontrolify it; works only for iso8859-1 like sets */ + re_putc(el, (c | 0100), 1); + } else { + re_putc(el, '\\', 1); + re_putc(el, (int) ((((unsigned int) c >> 6) & 07) + '0'), 1); + re_putc(el, (int) ((((unsigned int) c >> 3) & 07) + '0'), 1); + re_putc(el, (c & 07) + '0', 1); + } +} + + +/* re_putc(): + * Draw the character given + */ +protected void +re_putc(EditLine *el, int c, int shift) +{ + + ELRE_DEBUG(1, (__F, "printing %3.3o '%c'\r\n", c, c)); + + el->el_vdisplay[el->el_refresh.r_cursor.v][el->el_refresh.r_cursor.h] = c; + if (!shift) + return; + + el->el_refresh.r_cursor.h++; /* advance to next place */ + if (el->el_refresh.r_cursor.h >= el->el_term.t_size.h) { + el->el_vdisplay[el->el_refresh.r_cursor.v][el->el_term.t_size.h] = '\0'; + /* assure end of line */ + el->el_refresh.r_cursor.h = 0; /* reset it. */ + + /* + * If we would overflow (input is longer than terminal size), + * emulate scroll by dropping first line and shuffling the rest. + * We do this via pointer shuffling - it's safe in this case + * and we avoid memcpy(). + */ + if (el->el_refresh.r_cursor.v + 1 >= el->el_term.t_size.v) { + int i, lins = el->el_term.t_size.v; + char *firstline = el->el_vdisplay[0]; + + for(i=1; i < lins; i++) + el->el_vdisplay[i-1] = el->el_vdisplay[i]; + + firstline[0] = '\0'; /* empty the string */ + el->el_vdisplay[i-1] = firstline; + } else + el->el_refresh.r_cursor.v++; + + ELRE_ASSERT(el->el_refresh.r_cursor.v >= el->el_term.t_size.v, + (__F, "\r\nre_putc: overflow! r_cursor.v == %d > %d\r\n", + el->el_refresh.r_cursor.v, el->el_term.t_size.v), + abort()); + } +} + + +/* re_refresh(): + * draws the new virtual screen image from the current input + * line, then goes line-by-line changing the real image to the new + * virtual image. The routine to re-draw a line can be replaced + * easily in hopes of a smarter one being placed there. + */ +protected void +re_refresh(EditLine *el) +{ + int i, rhdiff; + char *cp, *st; + coord_t cur; +#ifdef notyet + size_t termsz; +#endif + + ELRE_DEBUG(1, (__F, "el->el_line.buffer = :%s:\r\n", + el->el_line.buffer)); + + /* reset the Drawing cursor */ + el->el_refresh.r_cursor.h = 0; + el->el_refresh.r_cursor.v = 0; + + /* temporarily draw rprompt to calculate its size */ + prompt_print(el, EL_RPROMPT); + + /* reset the Drawing cursor */ + el->el_refresh.r_cursor.h = 0; + el->el_refresh.r_cursor.v = 0; + + cur.h = -1; /* set flag in case I'm not set */ + cur.v = 0; + + prompt_print(el, EL_PROMPT); + + /* draw the current input buffer */ +#if notyet + termsz = el->el_term.t_size.h * el->el_term.t_size.v; + if (el->el_line.lastchar - el->el_line.buffer > termsz) { + /* + * If line is longer than terminal, process only part + * of line which would influence display. + */ + size_t rem = (el->el_line.lastchar-el->el_line.buffer)%termsz; + + st = el->el_line.lastchar - rem + - (termsz - (((rem / el->el_term.t_size.v) - 1) + * el->el_term.t_size.v)); + } else +#endif + st = el->el_line.buffer; + + for (cp = st; cp < el->el_line.lastchar; cp++) { + if (cp == el->el_line.cursor) { + /* save for later */ + cur.h = el->el_refresh.r_cursor.h; + cur.v = el->el_refresh.r_cursor.v; + } + re_addc(el, (unsigned char) *cp); + } + + if (cur.h == -1) { /* if I haven't been set yet, I'm at the end */ + cur.h = el->el_refresh.r_cursor.h; + cur.v = el->el_refresh.r_cursor.v; + } + rhdiff = el->el_term.t_size.h - el->el_refresh.r_cursor.h - + el->el_rprompt.p_pos.h; + if (el->el_rprompt.p_pos.h && !el->el_rprompt.p_pos.v && + !el->el_refresh.r_cursor.v && rhdiff > 1) { + /* + * have a right-hand side prompt that will fit + * on the end of the first line with at least + * one character gap to the input buffer. + */ + while (--rhdiff > 0) /* pad out with spaces */ + re_putc(el, ' ', 1); + prompt_print(el, EL_RPROMPT); + } else { + el->el_rprompt.p_pos.h = 0; /* flag "not using rprompt" */ + el->el_rprompt.p_pos.v = 0; + } + + re_putc(el, '\0', 0); /* make line ended with NUL, no cursor shift */ + + el->el_refresh.r_newcv = el->el_refresh.r_cursor.v; + + ELRE_DEBUG(1, (__F, + "term.h=%d vcur.h=%d vcur.v=%d vdisplay[0]=\r\n:%80.80s:\r\n", + el->el_term.t_size.h, el->el_refresh.r_cursor.h, + el->el_refresh.r_cursor.v, el->el_vdisplay[0])); + + ELRE_DEBUG(1, (__F, "updating %d lines.\r\n", el->el_refresh.r_newcv)); + for (i = 0; i <= el->el_refresh.r_newcv; i++) { + /* NOTE THAT re_update_line MAY CHANGE el_display[i] */ + re_update_line(el, el->el_display[i], el->el_vdisplay[i], i); + + /* + * Copy the new line to be the current one, and pad out with + * spaces to the full width of the terminal so that if we try + * moving the cursor by writing the character that is at the + * end of the screen line, it won't be a NUL or some old + * leftover stuff. + */ + re__copy_and_pad(el->el_display[i], el->el_vdisplay[i], + (size_t) el->el_term.t_size.h); + } + ELRE_DEBUG(1, (__F, + "\r\nel->el_refresh.r_cursor.v=%d,el->el_refresh.r_oldcv=%d i=%d\r\n", + el->el_refresh.r_cursor.v, el->el_refresh.r_oldcv, i)); + + if (el->el_refresh.r_oldcv > el->el_refresh.r_newcv) + for (; i <= el->el_refresh.r_oldcv; i++) { + term_move_to_line(el, i); + term_move_to_char(el, 0); + term_clear_EOL(el, (int) strlen(el->el_display[i])); +#ifdef DEBUG_REFRESH + term_overwrite(el, "C\b", 2); +#endif /* DEBUG_REFRESH */ + el->el_display[i][0] = '\0'; + } + + el->el_refresh.r_oldcv = el->el_refresh.r_newcv; /* set for next time */ + ELRE_DEBUG(1, (__F, + "\r\ncursor.h = %d, cursor.v = %d, cur.h = %d, cur.v = %d\r\n", + el->el_refresh.r_cursor.h, el->el_refresh.r_cursor.v, + cur.h, cur.v)); + term_move_to_line(el, cur.v); /* go to where the cursor is */ + term_move_to_char(el, cur.h); +} + + +/* re_goto_bottom(): + * used to go to last used screen line + */ +protected void +re_goto_bottom(EditLine *el) +{ + + term_move_to_line(el, el->el_refresh.r_oldcv); + term__putc('\r'); + term__putc('\n'); + re_clear_display(el); + term__flush(); +} + + +/* re_insert(): + * insert num characters of s into d (in front of the character) + * at dat, maximum length of d is dlen + */ +private void +/*ARGSUSED*/ +re_insert(EditLine *el __attribute__((unused)), + char *d, int dat, int dlen, char *s, int num) +{ + char *a, *b; + + if (num <= 0) + return; + if (num > dlen - dat) + num = dlen - dat; + + 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)); + + /* open up the space for num chars */ + if (num > 0) { + b = d + dlen - 1; + a = b - num; + while (a >= &d[dat]) + *b-- = *a--; + d[dlen] = '\0'; /* just in case */ + } + 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)); + + /* copy the characters */ + for (a = d + dat; (a < d + dlen) && (num > 0); num--) + *a++ = *s++; + + 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)); +} + + +/* re_delete(): + * delete num characters d at dat, maximum length of d is dlen + */ +private void +/*ARGSUSED*/ +re_delete(EditLine *el __attribute__((unused)), + char *d, int dat, int dlen, int num) +{ + char *a, *b; + + if (num <= 0) + return; + if (dat + num >= dlen) { + d[dat] = '\0'; + return; + } + ELRE_DEBUG(1, + (__F, "re_delete() starting: %d at %d max %d, d == \"%s\"\n", + num, dat, dlen, d)); + + /* open up the space for num chars */ + if (num > 0) { + b = d + dat; + a = b + num; + while (a < &d[dlen]) + *b++ = *a++; + d[dlen] = '\0'; /* just in case */ + } + ELRE_DEBUG(1, + (__F, "re_delete() after delete: %d at %d max %d, d == \"%s\"\n", + num, dat, dlen, d)); +} + + +/* re__strncopy(): + * Like strncpy without padding. + */ +private void +re__strncopy(char *a, char *b, size_t n) +{ + + while (n-- && *b) + *a++ = *b++; +} + + +/***************************************************************** + re_update_line() is based on finding the middle difference of each line + on the screen; vis: + + /old first difference + /beginning of line | /old last same /old EOL + v v v v +old: eddie> Oh, my little gruntle-buggy is to me, as lurgid as +new: eddie> Oh, my little buggy says to me, as lurgid as + ^ ^ ^ ^ + \beginning of line | \new last same \new end of line + \new first difference + + all are character pointers for the sake of speed. Special cases for + no differences, as well as for end of line additions must be handled. +**************************************************************** */ + +/* Minimum at which doing an insert it "worth it". This should be about + * half the "cost" of going into insert mode, inserting a character, and + * going back out. This should really be calculated from the termcap + * data... For the moment, a good number for ANSI terminals. + */ +#define MIN_END_KEEP 4 + +private void +re_update_line(EditLine *el, char *old, char *new, int i) +{ + char *o, *n, *p, c; + char *ofd, *ols, *oe, *nfd, *nls, *ne; + char *osb, *ose, *nsb, *nse; + int fx, sx; + + /* + * find first diff + */ + for (o = old, n = new; *o && (*o == *n); o++, n++) + continue; + ofd = o; + nfd = n; + + /* + * Find the end of both old and new + */ + while (*o) + o++; + /* + * Remove any trailing blanks off of the end, being careful not to + * back up past the beginning. + */ + while (ofd < o) { + if (o[-1] != ' ') + break; + o--; + } + oe = o; + *oe = '\0'; + + while (*n) + n++; + + /* remove blanks from end of new */ + while (nfd < n) { + if (n[-1] != ' ') + break; + n--; + } + ne = n; + *ne = '\0'; + + /* + * if no diff, continue to next line of redraw + */ + if (*ofd == '\0' && *nfd == '\0') { + ELRE_DEBUG(1, (__F, "no difference.\r\n")); + return; + } + /* + * find last same pointer + */ + while ((o > ofd) && (n > nfd) && (*--o == *--n)) + continue; + ols = ++o; + nls = ++n; + + /* + * find same begining and same end + */ + osb = ols; + nsb = nls; + ose = ols; + nse = nls; + + /* + * case 1: insert: scan from nfd to nls looking for *ofd + */ + if (*ofd) { + for (c = *ofd, n = nfd; n < nls; n++) { + if (c == *n) { + for (o = ofd, p = n; + p < nls && o < ols && *o == *p; + o++, p++) + continue; + /* + * if the new match is longer and it's worth + * keeping, then we take it + */ + if (((nse - nsb) < (p - n)) && + (2 * (p - n) > n - nfd)) { + nsb = n; + nse = p; + osb = ofd; + ose = o; + } + } + } + } + /* + * case 2: delete: scan from ofd to ols looking for *nfd + */ + if (*nfd) { + for (c = *nfd, o = ofd; o < ols; o++) { + if (c == *o) { + for (n = nfd, p = o; + p < ols && n < nls && *p == *n; + p++, n++) + continue; + /* + * if the new match is longer and it's worth + * keeping, then we take it + */ + if (((ose - osb) < (p - o)) && + (2 * (p - o) > o - ofd)) { + nsb = nfd; + nse = n; + osb = o; + ose = p; + } + } + } + } + /* + * Pragmatics I: If old trailing whitespace or not enough characters to + * save to be worth it, then don't save the last same info. + */ + if ((oe - ols) < MIN_END_KEEP) { + ols = oe; + nls = ne; + } + /* + * Pragmatics II: if the terminal isn't smart enough, make the data + * dumber so the smart update doesn't try anything fancy + */ + + /* + * fx is the number of characters we need to insert/delete: in the + * beginning to bring the two same begins together + */ + fx = (nsb - nfd) - (osb - ofd); + /* + * sx is the number of characters we need to insert/delete: in the + * end to bring the two same last parts together + */ + sx = (nls - nse) - (ols - ose); + + if (!EL_CAN_INSERT) { + if (fx > 0) { + osb = ols; + ose = ols; + nsb = nls; + nse = nls; + } + if (sx > 0) { + ols = oe; + nls = ne; + } + if ((ols - ofd) < (nls - nfd)) { + ols = oe; + nls = ne; + } + } + if (!EL_CAN_DELETE) { + if (fx < 0) { + osb = ols; + ose = ols; + nsb = nls; + nse = nls; + } + if (sx < 0) { + ols = oe; + nls = ne; + } + if ((ols - ofd) > (nls - nfd)) { + ols = oe; + nls = ne; + } + } + /* + * Pragmatics III: make sure the middle shifted pointers are correct if + * they don't point to anything (we may have moved ols or nls). + */ + /* if the change isn't worth it, don't bother */ + /* was: if (osb == ose) */ + if ((ose - osb) < MIN_END_KEEP) { + osb = ols; + ose = ols; + nsb = nls; + nse = nls; + } + /* + * Now that we are done with pragmatics we recompute fx, sx + */ + fx = (nsb - nfd) - (osb - ofd); + sx = (nls - nse) - (ols - ose); + + ELRE_DEBUG(1, (__F, "\n")); + 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", + nfd - new, nsb - new, nse - new, nls - new, ne - new)); + ELRE_DEBUG(1, (__F, + "xxx-xxx:\"00000000001111111111222222222233333333334\"\r\n")); + ELRE_DEBUG(1, (__F, + "xxx-xxx:\"01234567890123456789012345678901234567890\"\r\n")); +#ifdef DEBUG_REFRESH + re_printstr(el, "old- oe", old, oe); + re_printstr(el, "new- ne", new, ne); + re_printstr(el, "old-ofd", old, ofd); + re_printstr(el, "new-nfd", new, nfd); + re_printstr(el, "ofd-osb", ofd, osb); + re_printstr(el, "nfd-nsb", nfd, nsb); + re_printstr(el, "osb-ose", osb, ose); + re_printstr(el, "nsb-nse", nsb, nse); + re_printstr(el, "ose-ols", ose, ols); + re_printstr(el, "nse-nls", nse, nls); + re_printstr(el, "ols- oe", ols, oe); + re_printstr(el, "nls- ne", nls, ne); +#endif /* DEBUG_REFRESH */ + + /* + * el_cursor.v to this line i MUST be in this routine so that if we + * don't have to change the line, we don't move to it. el_cursor.h to + * first diff char + */ + term_move_to_line(el, i); + + /* + * at this point we have something like this: + * + * /old /ofd /osb /ose /ols /oe + * v.....................v v..................v v........v + * eddie> Oh, my fredded gruntle-buggy is to me, as foo var lurgid as + * eddie> Oh, my fredded quiux buggy is to me, as gruntle-lurgid as + * ^.....................^ ^..................^ ^........^ + * \new \nfd \nsb \nse \nls \ne + * + * fx is the difference in length between the chars between nfd and + * nsb, and the chars between ofd and osb, and is thus the number of + * characters to delete if < 0 (new is shorter than old, as above), + * or insert (new is longer than short). + * + * sx is the same for the second differences. + */ + + /* + * if we have a net insert on the first difference, AND inserting the + * net amount ((nsb-nfd) - (osb-ofd)) won't push the last useful + * character (which is ne if nls != ne, otherwise is nse) off the edge + * of the screen (el->el_term.t_size.h) else we do the deletes first + * so that we keep everything we need to. + */ + + /* + * if the last same is the same like the end, there is no last same + * part, otherwise we want to keep the last same part set p to the + * last useful old character + */ + p = (ols != oe) ? oe : ose; + + /* + * if (There is a diffence in the beginning) && (we need to insert + * characters) && (the number of characters to insert is less than + * the term width) + * We need to do an insert! + * else if (we need to delete characters) + * We need to delete characters! + * else + * No insert or delete + */ + if ((nsb != nfd) && fx > 0 && + ((p - old) + fx <= el->el_term.t_size.h)) { + ELRE_DEBUG(1, + (__F, "first diff insert at %d...\r\n", nfd - new)); + /* + * Move to the first char to insert, where the first diff is. + */ + term_move_to_char(el, nfd - new); + /* + * Check if we have stuff to keep at end + */ + if (nsb != ne) { + ELRE_DEBUG(1, (__F, "with stuff to keep at end\r\n")); + /* + * insert fx chars of new starting at nfd + */ + if (fx > 0) { + ELRE_DEBUG(!EL_CAN_INSERT, (__F, + "ERROR: cannot insert in early first diff\n")); + term_insertwrite(el, nfd, fx); + re_insert(el, old, ofd - old, + el->el_term.t_size.h, nfd, fx); + } + /* + * write (nsb-nfd) - fx chars of new starting at + * (nfd + fx) + */ + term_overwrite(el, nfd + fx, (nsb - nfd) - fx); + re__strncopy(ofd + fx, nfd + fx, + (size_t) ((nsb - nfd) - fx)); + } else { + ELRE_DEBUG(1, (__F, "without anything to save\r\n")); + term_overwrite(el, nfd, (nsb - nfd)); + re__strncopy(ofd, nfd, (size_t) (nsb - nfd)); + /* + * Done + */ + return; + } + } else if (fx < 0) { + ELRE_DEBUG(1, + (__F, "first diff delete at %d...\r\n", ofd - old)); + /* + * move to the first char to delete where the first diff is + */ + term_move_to_char(el, ofd - old); + /* + * Check if we have stuff to save + */ + if (osb != oe) { + ELRE_DEBUG(1, (__F, "with stuff to save at end\r\n")); + /* + * fx is less than zero *always* here but we check + * for code symmetry + */ + if (fx < 0) { + ELRE_DEBUG(!EL_CAN_DELETE, (__F, + "ERROR: cannot delete in first diff\n")); + term_deletechars(el, -fx); + re_delete(el, old, ofd - old, + el->el_term.t_size.h, -fx); + } + /* + * write (nsb-nfd) chars of new starting at nfd + */ + term_overwrite(el, nfd, (nsb - nfd)); + re__strncopy(ofd, nfd, (size_t) (nsb - nfd)); + + } else { + ELRE_DEBUG(1, (__F, + "but with nothing left to save\r\n")); + /* + * 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)); + /* + * Done + */ + return; + } + } else + fx = 0; + + if (sx < 0 && (ose - old) + fx < el->el_term.t_size.h) { + ELRE_DEBUG(1, (__F, + "second diff delete at %d...\r\n", (ose - old) + fx)); + /* + * Check if we have stuff to delete + */ + /* + * fx is the number of characters inserted (+) or deleted (-) + */ + + term_move_to_char(el, (ose - old) + fx); + /* + * Check if we have stuff to save + */ + if (ols != oe) { + ELRE_DEBUG(1, (__F, "with stuff to save at end\r\n")); + /* + * Again a duplicate test. + */ + if (sx < 0) { + ELRE_DEBUG(!EL_CAN_DELETE, (__F, + "ERROR: cannot delete in second diff\n")); + term_deletechars(el, -sx); + } + /* + * write (nls-nse) chars of new starting at nse + */ + term_overwrite(el, nse, (nls - nse)); + } else { + 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)); + } + } + /* + * if we have a first insert AND WE HAVEN'T ALREADY DONE IT... + */ + if ((nsb != nfd) && (osb - ofd) <= (nsb - nfd) && (fx == 0)) { + ELRE_DEBUG(1, (__F, "late first diff insert at %d...\r\n", + nfd - new)); + + term_move_to_char(el, nfd - new); + /* + * Check if we have stuff to keep at the end + */ + if (nsb != ne) { + ELRE_DEBUG(1, (__F, "with stuff to keep at end\r\n")); + /* + * We have to recalculate fx here because we set it + * to zero above as a flag saying that we hadn't done + * an early first insert. + */ + fx = (nsb - nfd) - (osb - ofd); + if (fx > 0) { + /* + * insert fx chars of new starting at nfd + */ + ELRE_DEBUG(!EL_CAN_INSERT, (__F, + "ERROR: cannot insert in late first diff\n")); + term_insertwrite(el, nfd, fx); + re_insert(el, old, ofd - old, + el->el_term.t_size.h, nfd, fx); + } + /* + * write (nsb-nfd) - fx chars of new starting at + * (nfd + fx) + */ + term_overwrite(el, nfd + fx, (nsb - nfd) - fx); + re__strncopy(ofd + fx, nfd + fx, + (size_t) ((nsb - nfd) - fx)); + } else { + ELRE_DEBUG(1, (__F, "without anything to save\r\n")); + term_overwrite(el, nfd, (nsb - nfd)); + re__strncopy(ofd, nfd, (size_t) (nsb - nfd)); + } + } + /* + * line is now NEW up to nse + */ + if (sx >= 0) { + ELRE_DEBUG(1, (__F, + "second diff insert at %d...\r\n", nse - new)); + term_move_to_char(el, nse - new); + if (ols != oe) { + ELRE_DEBUG(1, (__F, "with stuff to keep at end\r\n")); + if (sx > 0) { + /* insert sx chars of new starting at nse */ + ELRE_DEBUG(!EL_CAN_INSERT, (__F, + "ERROR: cannot insert in second diff\n")); + term_insertwrite(el, nse, sx); + } + /* + * write (nls-nse) - sx chars of new starting at + * (nse + sx) + */ + term_overwrite(el, nse + sx, (nls - nse) - sx); + } else { + ELRE_DEBUG(1, (__F, "without anything to save\r\n")); + term_overwrite(el, nse, (nls - nse)); + + /* + * No need to do a clear-to-end here because we were + * doing a second insert, so we will have over + * written all of the old string. + */ + } + } + ELRE_DEBUG(1, (__F, "done.\r\n")); +} + + +/* re__copy_and_pad(): + * Copy string and pad with spaces + */ +private void +re__copy_and_pad(char *dst, const char *src, size_t width) +{ + unsigned int i; + + for (i = 0; i < width; i++) { + if (*src == '\0') + break; + *dst++ = *src++; + } + + for (; i < width; i++) + *dst++ = ' '; + + *dst = '\0'; +} + + +/* re_refresh_cursor(): + * Move to the new cursor position + */ +protected void +re_refresh_cursor(EditLine *el) +{ + char *cp, c; + int h, v, th; + + /* first we must find where the cursor is... */ + h = el->el_prompt.p_pos.h; + v = el->el_prompt.p_pos.v; + th = el->el_term.t_size.h; /* optimize for speed */ + + /* do input buffer to el->el_line.cursor */ + for (cp = el->el_line.buffer; cp < el->el_line.cursor; cp++) { + c = *cp; + h++; /* all chars at least this long */ + + if (c == '\n') {/* handle newline in data part too */ + h = 0; + v++; + } else { + if (c == '\t') { /* if a tab, to next tab stop */ + while (h & 07) { + h++; + } + } else if (iscntrl((unsigned char) c)) { + /* if control char */ + h++; + if (h > th) { /* if overflow, compensate */ + h = 1; + v++; + } + } else if (!isprint((unsigned char) c)) { + h += 3; + if (h > th) { /* if overflow, compensate */ + h = h - th; + v++; + } + } + } + + if (h >= th) { /* check, extra long tabs picked up here also */ + h = 0; + v++; + } + } + + /* now go there */ + term_move_to_line(el, v); + term_move_to_char(el, h); + term__flush(); +} + + +/* re_fastputc(): + * Add a character fast. + */ +private void +re_fastputc(EditLine *el, int c) +{ + + term__putc(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 */ + el->el_cursor.h = 0; + + /* + * If we would overflow (input is longer than terminal size), + * emulate scroll by dropping first line and shuffling the rest. + * We do this via pointer shuffling - it's safe in this case + * and we avoid memcpy(). + */ + if (el->el_cursor.v + 1 >= el->el_term.t_size.v) { + int i, lins = el->el_term.t_size.v; + char *firstline = el->el_display[0]; + + for(i=1; i < lins; i++) + el->el_display[i-1] = el->el_display[i]; + + re__copy_and_pad(firstline, "", 0); + el->el_display[i-1] = firstline; + } else { + el->el_cursor.v++; + el->el_refresh.r_oldcv++; + } + if (EL_HAS_AUTO_MARGINS) { + if (EL_HAS_MAGIC_MARGINS) { + term__putc(' '); + term__putc('\b'); + } + } else { + term__putc('\r'); + term__putc('\n'); + } + } +} + + +/* re_fastaddc(): + * we added just one char, handle it fast. + * Assumes that screen cursor == real cursor + */ +protected void +re_fastaddc(EditLine *el) +{ + char c; + int rhdiff; + + c = el->el_line.cursor[-1]; + + if (c == '\t' || el->el_line.cursor != el->el_line.lastchar) { + re_refresh(el); /* too hard to handle */ + return; + } + rhdiff = el->el_term.t_size.h - el->el_cursor.h - + el->el_rprompt.p_pos.h; + if (el->el_rprompt.p_pos.h && rhdiff < 3) { + re_refresh(el); /* clear out rprompt if less than 1 char gap */ + return; + } /* else (only do at end of line, no TAB) */ + if (iscntrl((unsigned char) c)) { /* if control char, do caret */ + char mc = (c == '\177') ? '?' : (c | 0100); + re_fastputc(el, '^'); + re_fastputc(el, mc); + } else if (isprint((unsigned char) c)) { /* normal char */ + re_fastputc(el, c); + } else { + re_fastputc(el, '\\'); + re_fastputc(el, (int) ((((unsigned int) c >> 6) & 7) + '0')); + re_fastputc(el, (int) ((((unsigned int) c >> 3) & 7) + '0')); + re_fastputc(el, (c & 7) + '0'); + } + term__flush(); +} + + +/* re_clear_display(): + * clear the screen buffers so that new new prompt starts fresh. + */ +protected void +re_clear_display(EditLine *el) +{ + int i; + + el->el_cursor.v = 0; + el->el_cursor.h = 0; + for (i = 0; i < el->el_term.t_size.v; i++) + el->el_display[i][0] = '\0'; + el->el_refresh.r_oldcv = 0; +} + + +/* re_clear_lines(): + * Make sure all lines are *really* blank + */ +protected void +re_clear_lines(EditLine *el) +{ + + if (EL_CAN_CEOL) { + int i; + term_move_to_char(el, 0); + for (i = 0; i <= el->el_refresh.r_oldcv; i++) { + /* for each line on the screen */ + term_move_to_line(el, i); + term_clear_EOL(el, el->el_term.t_size.h); + } + term_move_to_line(el, 0); + } 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 */ + } +} diff --git a/cmd-line-utils/libedit/refresh.h b/cmd-line-utils/libedit/refresh.h new file mode 100644 index 00000000000..33c0887c1b3 --- /dev/null +++ b/cmd-line-utils/libedit/refresh.h @@ -0,0 +1,63 @@ +/* $NetBSD: refresh.h,v 1.4 2001/01/10 07:45:42 jdolecek 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. + * + * @(#)refresh.h 8.1 (Berkeley) 6/4/93 + */ + +/* + * el.refresh.h: Screen refresh functions + */ +#ifndef _h_el_refresh +#define _h_el_refresh + +#include "histedit.h" + +typedef struct { + coord_t r_cursor; /* Refresh cursor position */ + int r_oldcv; /* Vertical locations */ + int r_newcv; +} el_refresh_t; + +protected void re_putc(EditLine *, int, int); +protected void re_clear_lines(EditLine *); +protected void re_clear_display(EditLine *); +protected void re_refresh(EditLine *); +protected void re_refresh_cursor(EditLine *); +protected void re_fastaddc(EditLine *); +protected void re_goto_bottom(EditLine *); + +#endif /* _h_el_refresh */ diff --git a/cmd-line-utils/libedit/search.c b/cmd-line-utils/libedit/search.c new file mode 100644 index 00000000000..bdc3a1e8bb9 --- /dev/null +++ b/cmd-line-utils/libedit/search.c @@ -0,0 +1,646 @@ +/* $NetBSD: search.c,v 1.11 2001/01/23 15:55:31 jdolecek 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 "compat.h" + +/* + * search.c: History and character search functions + */ +#include "sys.h" +#include +#if HAVE_SYS_TYPES_H +#include +#endif +#if defined(REGEX) +#include +#elif defined(REGEXP) +#include +#endif +#include "el.h" + +/* + * Adjust cursor in vi mode to include the character under it + */ +#define EL_CURSOR(el) \ + ((el)->el_line.cursor + (((el)->el_map.type == MAP_VI) && \ + ((el)->el_map.current == (el)->el_map.alt))) + +/* search_init(): + * Initialize the search stuff + */ +protected int +search_init(EditLine *el) +{ + + el->el_search.patbuf = (char *) el_malloc(EL_BUFSIZ); + if (el->el_search.patbuf == NULL) + return (-1); + el->el_search.patlen = 0; + el->el_search.patdir = -1; + el->el_search.chacha = '\0'; + el->el_search.chadir = -1; + return (0); +} + + +/* search_end(): + * Initialize the search stuff + */ +protected void +search_end(EditLine *el) +{ + + el_free((ptr_t) el->el_search.patbuf); + el->el_search.patbuf = NULL; +} + + +#ifdef REGEXP +/* regerror(): + * Handle regular expression errors + */ +public void +/*ARGSUSED*/ +regerror(const char *msg) +{ +} +#endif + + +/* el_match(): + * Return if string matches pattern + */ +protected int +el_match(const char *str, const char *pat) +{ +#if defined (REGEX) + regex_t re; + int rv; +#elif defined (REGEXP) + regexp *rp; + int rv; +#else + extern char *re_comp(const char *); + extern int re_exec(const char *); +#endif + + if (strstr(str, pat) != NULL) + return (1); + +#if defined(REGEX) + if (regcomp(&re, pat, 0) == 0) { + rv = regexec(&re, str, 0, NULL, 0) == 0; + regfree(&re); + } else { + rv = 0; + } + return (rv); +#elif defined(REGEXP) + if ((re = regcomp(pat)) != NULL) { + rv = regexec(re, str); + free((ptr_t) re); + } else { + rv = 0; + } + return (rv); +#else + if (re_comp(pat) != NULL) + return (0); + else + return (re_exec(str) == 1); +#endif +} + + +/* c_hmatch(): + * return True if the pattern matches the prefix + */ +protected int +c_hmatch(EditLine *el, const char *str) +{ +#ifdef SDEBUG + (void) fprintf(el->el_errfile, "match `%s' with `%s'\n", + el->el_search.patbuf, str); +#endif /* SDEBUG */ + + return (el_match(str, el->el_search.patbuf)); +} + + +/* c_setpat(): + * Set the history seatch pattern + */ +protected void +c_setpat(EditLine *el) +{ + if (el->el_state.lastcmd != ED_SEARCH_PREV_HISTORY && + el->el_state.lastcmd != ED_SEARCH_NEXT_HISTORY) { + el->el_search.patlen = EL_CURSOR(el) - el->el_line.buffer; + if (el->el_search.patlen >= EL_BUFSIZ) + el->el_search.patlen = EL_BUFSIZ - 1; + if (el->el_search.patlen != 0) { + (void) strncpy(el->el_search.patbuf, el->el_line.buffer, + el->el_search.patlen); + el->el_search.patbuf[el->el_search.patlen] = '\0'; + } else + el->el_search.patlen = strlen(el->el_search.patbuf); + } +#ifdef SDEBUG + (void) fprintf(el->el_errfile, "\neventno = %d\n", + el->el_history.eventno); + (void) fprintf(el->el_errfile, "patlen = %d\n", el->el_search.patlen); + (void) fprintf(el->el_errfile, "patbuf = \"%s\"\n", + el->el_search.patbuf); + (void) fprintf(el->el_errfile, "cursor %d lastchar %d\n", + EL_CURSOR(el) - el->el_line.buffer, + el->el_line.lastchar - el->el_line.buffer); +#endif +} + + +/* ce_inc_search(): + * Emacs incremental search + */ +protected el_action_t +ce_inc_search(EditLine *el, int dir) +{ + static const char STRfwd[] = {'f', 'w', 'd', '\0'}, + STRbck[] = {'b', 'c', 'k', '\0'}; + static char pchar = ':';/* ':' = normal, '?' = failed */ + static char endcmd[2] = {'\0', '\0'}; + char ch, *ocursor = el->el_line.cursor, oldpchar = pchar; + const char *cp; + + el_action_t ret = CC_NORM; + + int ohisteventno = el->el_history.eventno; + int oldpatlen = el->el_search.patlen; + int newdir = dir; + int done, redo; + + if (el->el_line.lastchar + sizeof(STRfwd) / sizeof(char) + 2 + + el->el_search.patlen >= el->el_line.limit) + return (CC_ERROR); + + for (;;) { + + if (el->el_search.patlen == 0) { /* first round */ + pchar = ':'; +#ifdef ANCHOR + el->el_search.patbuf[el->el_search.patlen++] = '.'; + el->el_search.patbuf[el->el_search.patlen++] = '*'; +#endif + } + done = redo = 0; + *el->el_line.lastchar++ = '\n'; + for (cp = (newdir == ED_SEARCH_PREV_HISTORY) ? STRbck : STRfwd; + *cp; *el->el_line.lastchar++ = *cp++) + continue; + *el->el_line.lastchar++ = pchar; + for (cp = &el->el_search.patbuf[1]; + cp < &el->el_search.patbuf[el->el_search.patlen]; + *el->el_line.lastchar++ = *cp++) + continue; + *el->el_line.lastchar = '\0'; + re_refresh(el); + + if (el_getc(el, &ch) != 1) + return (ed_end_of_file(el, 0)); + + switch (el->el_map.current[(unsigned char) ch]) { + case ED_INSERT: + case ED_DIGIT: + if (el->el_search.patlen > EL_BUFSIZ - 3) + term_beep(el); + else { + el->el_search.patbuf[el->el_search.patlen++] = + ch; + *el->el_line.lastchar++ = ch; + *el->el_line.lastchar = '\0'; + re_refresh(el); + } + break; + + case EM_INC_SEARCH_NEXT: + newdir = ED_SEARCH_NEXT_HISTORY; + redo++; + break; + + case EM_INC_SEARCH_PREV: + newdir = ED_SEARCH_PREV_HISTORY; + redo++; + break; + + case ED_DELETE_PREV_CHAR: + if (el->el_search.patlen > 1) + done++; + else + term_beep(el); + break; + + default: + switch (ch) { + case 0007: /* ^G: Abort */ + ret = CC_ERROR; + done++; + break; + + case 0027: /* ^W: Append word */ + /* No can do if globbing characters in pattern */ + for (cp = &el->el_search.patbuf[1];; cp++) + if (cp >= &el->el_search.patbuf[el->el_search.patlen]) { + el->el_line.cursor += + el->el_search.patlen - 1; + cp = c__next_word(el->el_line.cursor, + el->el_line.lastchar, 1, + ce__isword); + while (el->el_line.cursor < cp && + *el->el_line.cursor != '\n') { + if (el->el_search.patlen > + EL_BUFSIZ - 3) { + term_beep(el); + break; + } + el->el_search.patbuf[el->el_search.patlen++] = + *el->el_line.cursor; + *el->el_line.lastchar++ = + *el->el_line.cursor++; + } + el->el_line.cursor = ocursor; + *el->el_line.lastchar = '\0'; + re_refresh(el); + break; + } else if (isglob(*cp)) { + term_beep(el); + break; + } + break; + + default: /* Terminate and execute cmd */ + endcmd[0] = ch; + el_push(el, endcmd); + /* FALLTHROUGH */ + + case 0033: /* ESC: Terminate */ + ret = CC_REFRESH; + done++; + break; + } + break; + } + + while (el->el_line.lastchar > el->el_line.buffer && + *el->el_line.lastchar != '\n') + *el->el_line.lastchar-- = '\0'; + *el->el_line.lastchar = '\0'; + + if (!done) { + + /* Can't search if unmatched '[' */ + for (cp = &el->el_search.patbuf[el->el_search.patlen-1], + ch = ']'; + cp > el->el_search.patbuf; + cp--) + if (*cp == '[' || *cp == ']') { + ch = *cp; + break; + } + if (el->el_search.patlen > 1 && ch != '[') { + if (redo && newdir == dir) { + if (pchar == '?') { /* wrap around */ + el->el_history.eventno = + newdir == ED_SEARCH_PREV_HISTORY ? 0 : 0x7fffffff; + if (hist_get(el) == CC_ERROR) + /* el->el_history.event + * no was fixed by + * first call */ + (void) hist_get(el); + el->el_line.cursor = newdir == + ED_SEARCH_PREV_HISTORY ? + el->el_line.lastchar : + el->el_line.buffer; + } else + el->el_line.cursor += + newdir == + ED_SEARCH_PREV_HISTORY ? + -1 : 1; + } +#ifdef ANCHOR + el->el_search.patbuf[el->el_search.patlen++] = + '.'; + el->el_search.patbuf[el->el_search.patlen++] = + '*'; +#endif + el->el_search.patbuf[el->el_search.patlen] = + '\0'; + if (el->el_line.cursor < el->el_line.buffer || + el->el_line.cursor > el->el_line.lastchar || + (ret = ce_search_line(el, + &el->el_search.patbuf[1], + newdir)) == CC_ERROR) { + /* avoid c_setpat */ + el->el_state.lastcmd = + (el_action_t) newdir; + ret = newdir == ED_SEARCH_PREV_HISTORY ? + ed_search_prev_history(el, 0) : + ed_search_next_history(el, 0); + if (ret != CC_ERROR) { + el->el_line.cursor = newdir == + ED_SEARCH_PREV_HISTORY ? + el->el_line.lastchar : + el->el_line.buffer; + (void) ce_search_line(el, + &el->el_search.patbuf[1], + newdir); + } + } + el->el_search.patbuf[--el->el_search.patlen] = + '\0'; + if (ret == CC_ERROR) { + term_beep(el); + if (el->el_history.eventno != + ohisteventno) { + el->el_history.eventno = + ohisteventno; + if (hist_get(el) == CC_ERROR) + return (CC_ERROR); + } + el->el_line.cursor = ocursor; + pchar = '?'; + } else { + pchar = ':'; + } + } + ret = ce_inc_search(el, newdir); + + if (ret == CC_ERROR && pchar == '?' && oldpchar == ':') + /* + * break abort of failed search at last + * non-failed + */ + ret = CC_NORM; + + } + if (ret == CC_NORM || (ret == CC_ERROR && oldpatlen == 0)) { + /* restore on normal return or error exit */ + pchar = oldpchar; + el->el_search.patlen = oldpatlen; + if (el->el_history.eventno != ohisteventno) { + el->el_history.eventno = ohisteventno; + if (hist_get(el) == CC_ERROR) + return (CC_ERROR); + } + el->el_line.cursor = ocursor; + if (ret == CC_ERROR) + re_refresh(el); + } + if (done || ret != CC_NORM) + return (ret); + } +} + + +/* cv_search(): + * Vi search. + */ +protected el_action_t +cv_search(EditLine *el, int dir) +{ + char ch; + char tmpbuf[EL_BUFSIZ]; + int tmplen; + + tmplen = 0; +#ifdef ANCHOR + tmpbuf[tmplen++] = '.'; + tmpbuf[tmplen++] = '*'; +#endif + + el->el_line.buffer[0] = '\0'; + el->el_line.lastchar = el->el_line.buffer; + el->el_line.cursor = el->el_line.buffer; + el->el_search.patdir = dir; + + c_insert(el, 2); /* prompt + '\n' */ + *el->el_line.cursor++ = '\n'; + *el->el_line.cursor++ = dir == ED_SEARCH_PREV_HISTORY ? '/' : '?'; + re_refresh(el); + +#ifdef ANCHOR +#define LEN 2 +#else +#define LEN 0 +#endif + + tmplen = c_gets(el, &tmpbuf[LEN]) + LEN; + ch = tmpbuf[tmplen]; + tmpbuf[tmplen] = '\0'; + + if (tmplen == LEN) { + /* + * Use the old pattern, but wild-card it. + */ + if (el->el_search.patlen == 0) { + el->el_line.buffer[0] = '\0'; + el->el_line.lastchar = el->el_line.buffer; + el->el_line.cursor = el->el_line.buffer; + re_refresh(el); + return (CC_ERROR); + } +#ifdef ANCHOR + if (el->el_search.patbuf[0] != '.' && + el->el_search.patbuf[0] != '*') { + (void) strncpy(tmpbuf, el->el_search.patbuf, + sizeof(tmpbuf) - 1); + el->el_search.patbuf[0] = '.'; + el->el_search.patbuf[1] = '*'; + (void) strncpy(&el->el_search.patbuf[2], tmpbuf, + EL_BUFSIZ - 3); + el->el_search.patlen++; + el->el_search.patbuf[el->el_search.patlen++] = '.'; + el->el_search.patbuf[el->el_search.patlen++] = '*'; + el->el_search.patbuf[el->el_search.patlen] = '\0'; + } +#endif + } else { +#ifdef ANCHOR + tmpbuf[tmplen++] = '.'; + tmpbuf[tmplen++] = '*'; +#endif + tmpbuf[tmplen] = '\0'; + (void) strncpy(el->el_search.patbuf, tmpbuf, EL_BUFSIZ - 1); + el->el_search.patlen = tmplen; + } + el->el_state.lastcmd = (el_action_t) dir; /* avoid c_setpat */ + el->el_line.cursor = el->el_line.lastchar = el->el_line.buffer; + if ((dir == ED_SEARCH_PREV_HISTORY ? ed_search_prev_history(el, 0) : + ed_search_next_history(el, 0)) == CC_ERROR) { + re_refresh(el); + return (CC_ERROR); + } else { + if (ch == 0033) { + re_refresh(el); + *el->el_line.lastchar++ = '\n'; + *el->el_line.lastchar = '\0'; + re_goto_bottom(el); + return (CC_NEWLINE); + } else + return (CC_REFRESH); + } +} + + +/* ce_search_line(): + * Look for a pattern inside a line + */ +protected el_action_t +ce_search_line(EditLine *el, char *pattern, int dir) +{ + char *cp; + + if (dir == ED_SEARCH_PREV_HISTORY) { + for (cp = el->el_line.cursor; cp >= el->el_line.buffer; cp--) + if (el_match(cp, pattern)) { + el->el_line.cursor = cp; + return (CC_NORM); + } + return (CC_ERROR); + } else { + for (cp = el->el_line.cursor; *cp != '\0' && + cp < el->el_line.limit; cp++) + if (el_match(cp, pattern)) { + el->el_line.cursor = cp; + return (CC_NORM); + } + return (CC_ERROR); + } +} + + +/* cv_repeat_srch(): + * Vi repeat search + */ +protected el_action_t +cv_repeat_srch(EditLine *el, int c) +{ + +#ifdef SDEBUG + (void) fprintf(el->el_errfile, "dir %d patlen %d patbuf %s\n", + c, el->el_search.patlen, el->el_search.patbuf); +#endif + + el->el_state.lastcmd = (el_action_t) c; /* Hack to stop c_setpat */ + el->el_line.lastchar = el->el_line.buffer; + + switch (c) { + case ED_SEARCH_NEXT_HISTORY: + return (ed_search_next_history(el, 0)); + case ED_SEARCH_PREV_HISTORY: + return (ed_search_prev_history(el, 0)); + default: + return (CC_ERROR); + } +} + + +/* cv_csearch_back(): + * Vi character search reverse + */ +protected el_action_t +cv_csearch_back(EditLine *el, int ch, int count, int tflag) +{ + char *cp; + + cp = el->el_line.cursor; + while (count--) { + if (*cp == ch) + cp--; + while (cp > el->el_line.buffer && *cp != ch) + cp--; + } + + if (cp < el->el_line.buffer || (cp == el->el_line.buffer && *cp != ch)) + return (CC_ERROR); + + if (*cp == ch && tflag) + cp++; + + el->el_line.cursor = cp; + + if (el->el_chared.c_vcmd.action & DELETE) { + el->el_line.cursor++; + cv_delfini(el); + return (CC_REFRESH); + } + re_refresh_cursor(el); + return (CC_NORM); +} + + +/* cv_csearch_fwd(): + * Vi character search forward + */ +protected el_action_t +cv_csearch_fwd(EditLine *el, int ch, int count, int tflag) +{ + char *cp; + + cp = el->el_line.cursor; + while (count--) { + if (*cp == ch) + cp++; + while (cp < el->el_line.lastchar && *cp != ch) + cp++; + } + + if (cp >= el->el_line.lastchar) + return (CC_ERROR); + + if (*cp == ch && tflag) + cp--; + + el->el_line.cursor = cp; + + if (el->el_chared.c_vcmd.action & DELETE) { + el->el_line.cursor++; + cv_delfini(el); + return (CC_REFRESH); + } + re_refresh_cursor(el); + return (CC_NORM); +} diff --git a/cmd-line-utils/libedit/search.h b/cmd-line-utils/libedit/search.h new file mode 100644 index 00000000000..676bbe2e35b --- /dev/null +++ b/cmd-line-utils/libedit/search.h @@ -0,0 +1,70 @@ +/* $NetBSD: search.h,v 1.5 2000/09/04 22:06:32 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. + * + * @(#)search.h 8.1 (Berkeley) 6/4/93 + */ + +/* + * el.search.h: Line and history searching utilities + */ +#ifndef _h_el_search +#define _h_el_search + +#include "histedit.h" + +typedef struct el_search_t { + char *patbuf; /* The pattern buffer */ + size_t patlen; /* Length of the pattern buffer */ + int patdir; /* Direction of the last search */ + int chadir; /* Character search direction */ + char chacha; /* Character we are looking for */ +} el_search_t; + + +protected int el_match(const char *, const char *); +protected int search_init(EditLine *); +protected void search_end(EditLine *); +protected int c_hmatch(EditLine *, const char *); +protected void c_setpat(EditLine *); +protected el_action_t ce_inc_search(EditLine *, int); +protected el_action_t cv_search(EditLine *, int); +protected el_action_t ce_search_line(EditLine *, char *, int); +protected el_action_t cv_repeat_srch(EditLine *, int); +protected el_action_t cv_csearch_back(EditLine *, int, int, int); +protected el_action_t cv_csearch_fwd(EditLine *, int, int, int); + +#endif /* _h_el_search */ diff --git a/cmd-line-utils/libedit/sig.c b/cmd-line-utils/libedit/sig.c new file mode 100644 index 00000000000..19408a0a8f2 --- /dev/null +++ b/cmd-line-utils/libedit/sig.c @@ -0,0 +1,192 @@ +/* $NetBSD: sig.c,v 1.8 2001/01/09 17:31:04 jdolecek 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 "compat.h" + +/* + * sig.c: Signal handling stuff. + * our policy is to trap all signals, set a good state + * and pass the ball to our caller. + */ +#include "sys.h" +#include "el.h" +#include + +private EditLine *sel = NULL; + +private const int sighdl[] = { +#define _DO(a) (a), + ALLSIGS +#undef _DO + - 1 +}; + +private void sig_handler(int); + +/* 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) +{ + int i; + sigset_t nset, oset; + + (void) sigemptyset(&nset); + (void) sigaddset(&nset, signo); + (void) sigprocmask(SIG_BLOCK, &nset, &oset); + + switch (signo) { + case SIGCONT: + tty_rawmode(sel); + if (ed_redisplay(sel, 0) == CC_REFRESH) + re_refresh(sel); + term__flush(); + break; + + case SIGWINCH: + el_resize(sel); + break; + + default: + tty_cookedmode(sel); + break; + } + + for (i = 0; sighdl[i] != -1; i++) + if (signo == sighdl[i]) + break; + + (void) signal(signo, sel->el_signal[i]); + (void) sigprocmask(SIG_SETMASK, &oset, NULL); + (void) kill(0, signo); +} + + +/* sig_init(): + * Initialize all signal stuff + */ +protected int +sig_init(EditLine *el) +{ + int i; + sigset_t nset, oset; + + (void) sigemptyset(&nset); +#define _DO(a) (void) sigaddset(&nset, a); + ALLSIGS +#undef _DO + (void) sigprocmask(SIG_BLOCK, &nset, &oset); + +#define SIGSIZE (sizeof(sighdl) / sizeof(sighdl[0]) * sizeof(sig_t)) + + el->el_signal = (sig_t *) el_malloc(SIGSIZE); + if (el->el_signal == NULL) + return (-1); + for (i = 0; sighdl[i] != -1; i++) + el->el_signal[i] = SIG_ERR; + + (void) sigprocmask(SIG_SETMASK, &oset, NULL); + + return (0); +} + + +/* sig_end(): + * Clear all signal stuff + */ +protected void +sig_end(EditLine *el) +{ + + el_free((ptr_t) el->el_signal); + el->el_signal = NULL; +} + + +/* sig_set(): + * set all the signal handlers + */ +protected void +sig_set(EditLine *el) +{ + int i; + sigset_t nset, oset; + + (void) sigemptyset(&nset); +#define _DO(a) (void) sigaddset(&nset, a); + ALLSIGS +#undef _DO + (void) sigprocmask(SIG_BLOCK, &nset, &oset); + + for (i = 0; sighdl[i] != -1; i++) { + sig_t s; + /* This could happen if we get interrupted */ + if ((s = signal(sighdl[i], sig_handler)) != sig_handler) + el->el_signal[i] = s; + } + sel = el; + (void) sigprocmask(SIG_SETMASK, &oset, NULL); +} + + +/* sig_clr(): + * clear all the signal handlers + */ +protected void +sig_clr(EditLine *el) +{ + int i; + sigset_t nset, oset; + + (void) sigemptyset(&nset); +#define _DO(a) (void) sigaddset(&nset, a); + ALLSIGS +#undef _DO + (void) sigprocmask(SIG_BLOCK, &nset, &oset); + + for (i = 0; sighdl[i] != -1; i++) + if (el->el_signal[i] != SIG_ERR) + (void) signal(sighdl[i], el->el_signal[i]); + + sel = NULL; /* we are going to die if the handler is + * called */ + (void) sigprocmask(SIG_SETMASK, &oset, NULL); +} diff --git a/cmd-line-utils/libedit/sig.h b/cmd-line-utils/libedit/sig.h new file mode 100644 index 00000000000..e7231b65cf4 --- /dev/null +++ b/cmd-line-utils/libedit/sig.h @@ -0,0 +1,72 @@ +/* $NetBSD: sig.h,v 1.3 2000/09/04 22:06:32 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. + * + * @(#)sig.h 8.1 (Berkeley) 6/4/93 + */ + +/* + * el.sig.h: Signal handling functions + */ +#ifndef _h_el_sig +#define _h_el_sig + +#include + +#include "histedit.h" + +/* + * Define here all the signals we are going to handle + * The _DO macro is used to iterate in the source code + */ +#define ALLSIGS \ + _DO(SIGINT) \ + _DO(SIGTSTP) \ + _DO(SIGSTOP) \ + _DO(SIGQUIT) \ + _DO(SIGHUP) \ + _DO(SIGTERM) \ + _DO(SIGCONT) \ + _DO(SIGWINCH) + +typedef sig_t *el_signal_t; + +protected void sig_end(EditLine*); +protected int sig_init(EditLine*); +protected void sig_set(EditLine*); +protected void sig_clr(EditLine*); + +#endif /* _h_el_sig */ diff --git a/cmd-line-utils/libedit/strlcpy.c b/cmd-line-utils/libedit/strlcpy.c new file mode 100644 index 00000000000..74317d99cd2 --- /dev/null +++ b/cmd-line-utils/libedit/strlcpy.c @@ -0,0 +1,28 @@ +#include + +#ifndef HAVE_STRLCPY +size_t strlcpy(char *dst, const char *src, size_t size) +{ + if(size) { + strncpy(dst, src, size-1); + dst[size-1] = '\0'; + } else { + dst[0] = '\0'; + } + return strlen(src); +} + +size_t strlcat(char *dst, const char *src, size_t size) +{ + int dl = strlen(dst); + int sz = size-dl-1; + + if(sz >= 0) { + strncat(dst, src, sz); + dst[sz] = '\0'; + } + + return dl+strlen(src); +} + +#endif diff --git a/cmd-line-utils/libedit/strlcpy.h b/cmd-line-utils/libedit/strlcpy.h new file mode 100644 index 00000000000..e4d3a7ffa3f --- /dev/null +++ b/cmd-line-utils/libedit/strlcpy.h @@ -0,0 +1,2 @@ +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 new file mode 100644 index 00000000000..d9007243456 --- /dev/null +++ b/cmd-line-utils/libedit/sys.h @@ -0,0 +1,94 @@ +/* $NetBSD: sys.h,v 1.4 2000/09/04 22:06:32 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. + * + * @(#)sys.h 8.1 (Berkeley) 6/4/93 + */ + +/* + * sys.h: Put all the stupid compiler and system dependencies here... + */ +#ifndef _h_sys +#define _h_sys + +#ifndef public +# define public /* Externally visible functions/variables */ +#endif + +#ifndef private +# define private static /* Always hidden internals */ +#endif + +#ifndef protected +# define protected /* Redefined from elsewhere to "static" */ + /* When we want to hide everything */ +#endif + +#if HAVE_SYS_CDEFS_H +#include +#endif + +#ifndef _PTR_T +# define _PTR_T +typedef void *ptr_t; +#endif + +#ifndef _IOCTL_T +# define _IOCTL_T +typedef void *ioctl_t; +#endif + +#include + +#define REGEX /* Use POSIX.2 regular expression functions */ +#undef REGEXP /* Use UNIX V8 regular expression functions */ + +#if defined(__sun__) && defined(__SVR4) +# undef REGEX +# undef REGEXP +# include +typedef void (*sig_t)(int); +#endif + +#ifndef __P +#ifdef __STDC__ +#define __P(x) x +#else +#define __P(x) () +#endif +#endif + +#endif /* _h_sys */ diff --git a/cmd-line-utils/libedit/term.c b/cmd-line-utils/libedit/term.c new file mode 100644 index 00000000000..df8d1ea67cc --- /dev/null +++ b/cmd-line-utils/libedit/term.c @@ -0,0 +1,1564 @@ +/* $NetBSD: term.c,v 1.32 2001/01/23 15:55:31 jdolecek 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 "compat.h" + +/* + * term.c: Editor/termcap-curses interface + * We have to declare a static variable here, since the + * termcap putchar routine does not take an argument! + */ +#include "sys.h" +#include +#include +#include +#include +#include +#include +#include +#include + +#include "el.h" + +/* + * IMPORTANT NOTE: these routines are allowed to look at the current screen + * and the current possition assuming that it is correct. If this is not + * true, then the update will be WRONG! This is (should be) a valid + * assumption... + */ + +#define TC_BUFSIZE 2048 + +#define GoodStr(a) (el->el_term.t_str[a] != NULL && \ + el->el_term.t_str[a][0] != '\0') +#define Str(a) el->el_term.t_str[a] +#define Val(a) el->el_term.t_val[a] + +#ifdef notdef +private const struct { + const char *b_name; + int b_rate; +} baud_rate[] = { +#ifdef B0 + { "0", B0 }, +#endif +#ifdef B50 + { "50", B50 }, +#endif +#ifdef B75 + { "75", B75 }, +#endif +#ifdef B110 + { "110", B110 }, +#endif +#ifdef B134 + { "134", B134 }, +#endif +#ifdef B150 + { "150", B150 }, +#endif +#ifdef B200 + { "200", B200 }, +#endif +#ifdef B300 + { "300", B300 }, +#endif +#ifdef B600 + { "600", B600 }, +#endif +#ifdef B900 + { "900", B900 }, +#endif +#ifdef B1200 + { "1200", B1200 }, +#endif +#ifdef B1800 + { "1800", B1800 }, +#endif +#ifdef B2400 + { "2400", B2400 }, +#endif +#ifdef B3600 + { "3600", B3600 }, +#endif +#ifdef B4800 + { "4800", B4800 }, +#endif +#ifdef B7200 + { "7200", B7200 }, +#endif +#ifdef B9600 + { "9600", B9600 }, +#endif +#ifdef EXTA + { "19200", EXTA }, +#endif +#ifdef B19200 + { "19200", B19200 }, +#endif +#ifdef EXTB + { "38400", EXTB }, +#endif +#ifdef B38400 + { "38400", B38400 }, +#endif + { NULL, 0 } +}; +#endif + +private const struct termcapstr { + const char *name; + const char *long_name; +} tstr[] = { +#define T_al 0 + { "al", "add new blank line" }, +#define T_bl 1 + { "bl", "audible bell" }, +#define T_cd 2 + { "cd", "clear to bottom" }, +#define T_ce 3 + { "ce", "clear to end of line" }, +#define T_ch 4 + { "ch", "cursor to horiz pos" }, +#define T_cl 5 + { "cl", "clear screen" }, +#define T_dc 6 + { "dc", "delete a character" }, +#define T_dl 7 + { "dl", "delete a line" }, +#define T_dm 8 + { "dm", "start delete mode" }, +#define T_ed 9 + { "ed", "end delete mode" }, +#define T_ei 10 + { "ei", "end insert mode" }, +#define T_fs 11 + { "fs", "cursor from status line" }, +#define T_ho 12 + { "ho", "home cursor" }, +#define T_ic 13 + { "ic", "insert character" }, +#define T_im 14 + { "im", "start insert mode" }, +#define T_ip 15 + { "ip", "insert padding" }, +#define T_kd 16 + { "kd", "sends cursor down" }, +#define T_kl 17 + { "kl", "sends cursor left" }, +#define T_kr 18 + { "kr", "sends cursor right" }, +#define T_ku 19 + { "ku", "sends cursor up" }, +#define T_md 20 + { "md", "begin bold" }, +#define T_me 21 + { "me", "end attributes" }, +#define T_nd 22 + { "nd", "non destructive space" }, +#define T_se 23 + { "se", "end standout" }, +#define T_so 24 + { "so", "begin standout" }, +#define T_ts 25 + { "ts", "cursor to status line" }, +#define T_up 26 + { "up", "cursor up one" }, +#define T_us 27 + { "us", "begin underline" }, +#define T_ue 28 + { "ue", "end underline" }, +#define T_vb 29 + { "vb", "visible bell" }, +#define T_DC 30 + { "DC", "delete multiple chars" }, +#define T_DO 31 + { "DO", "cursor down multiple" }, +#define T_IC 32 + { "IC", "insert multiple chars" }, +#define T_LE 33 + { "LE", "cursor left multiple" }, +#define T_RI 34 + { "RI", "cursor right multiple" }, +#define T_UP 35 + { "UP", "cursor up multiple" }, +#define T_kh 36 + { "kh", "send cursor home" }, +#define T_at7 37 + { "@7", "send cursor end" }, +#define T_str 38 + { NULL, NULL } +}; + +private const struct termcapval { + const char *name; + const char *long_name; +} tval[] = { +#define T_am 0 + { "am", "has automatic margins" }, +#define T_pt 1 + { "pt", "has physical tabs" }, +#define T_li 2 + { "li", "Number of lines" }, +#define T_co 3 + { "co", "Number of columns" }, +#define T_km 4 + { "km", "Has meta key" }, +#define T_xt 5 + { "xt", "Tab chars destructive" }, +#define T_xn 6 + { "xn", "newline ignored at right margin" }, +#define T_MT 7 + { "MT", "Has meta key" }, /* XXX? */ +#define T_val 8 + { NULL, NULL, } +}; +/* do two or more of the attributes use me */ + +private void term_setflags(EditLine *); +private int term_rebuffer_display(EditLine *); +private void term_free_display(EditLine *); +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 FILE *term_outfile = NULL; /* XXX: How do we fix that? */ + + +/* term_setflags(): + * Set the terminal capability flags + */ +private void +term_setflags(EditLine *el) +{ + EL_FLAGS = 0; + if (el->el_tty.t_tabs) + EL_FLAGS |= (Val(T_pt) && !Val(T_xt)) ? TERM_CAN_TAB : 0; + + EL_FLAGS |= (Val(T_km) || Val(T_MT)) ? TERM_HAS_META : 0; + EL_FLAGS |= GoodStr(T_ce) ? TERM_CAN_CEOL : 0; + EL_FLAGS |= (GoodStr(T_dc) || GoodStr(T_DC)) ? TERM_CAN_DELETE : 0; + EL_FLAGS |= (GoodStr(T_im) || GoodStr(T_ic) || GoodStr(T_IC)) ? + TERM_CAN_INSERT : 0; + EL_FLAGS |= (GoodStr(T_up) || GoodStr(T_UP)) ? TERM_CAN_UP : 0; + EL_FLAGS |= Val(T_am) ? TERM_HAS_AUTO_MARGINS : 0; + EL_FLAGS |= Val(T_xn) ? TERM_HAS_MAGIC_MARGINS : 0; + + if (GoodStr(T_me) && GoodStr(T_ue)) + EL_FLAGS |= (strcmp(Str(T_me), Str(T_ue)) == 0) ? + TERM_CAN_ME : 0; + else + EL_FLAGS &= ~TERM_CAN_ME; + if (GoodStr(T_me) && GoodStr(T_se)) + EL_FLAGS |= (strcmp(Str(T_me), Str(T_se)) == 0) ? + TERM_CAN_ME : 0; + + +#ifdef DEBUG_SCREEN + if (!EL_CAN_UP) { + (void) fprintf(el->el_errfile, + "WARNING: Your terminal cannot move up.\n"); + (void) fprintf(el->el_errfile, + "Editing may be odd for long lines.\n"); + } + if (!EL_CAN_CEOL) + (void) fprintf(el->el_errfile, "no clear EOL capability.\n"); + if (!EL_CAN_DELETE) + (void) fprintf(el->el_errfile, "no delete char capability.\n"); + if (!EL_CAN_INSERT) + (void) fprintf(el->el_errfile, "no insert char capability.\n"); +#endif /* DEBUG_SCREEN */ +} + + +/* term_init(): + * Initialize the terminal stuff + */ +protected int +term_init(EditLine *el) +{ + + el->el_term.t_buf = (char *) el_malloc(TC_BUFSIZE); + if (el->el_term.t_buf == NULL) + return (-1); + el->el_term.t_cap = (char *) el_malloc(TC_BUFSIZE); + if (el->el_term.t_cap == NULL) + return (-1); + el->el_term.t_fkey = (fkey_t *) el_malloc(A_K_NKEYS * sizeof(fkey_t)); + if (el->el_term.t_fkey == NULL) + return (-1); + el->el_term.t_loc = 0; + el->el_term.t_str = (char **) el_malloc(T_str * sizeof(char *)); + if (el->el_term.t_str == NULL) + return (-1); + (void) memset(el->el_term.t_str, 0, T_str * sizeof(char *)); + el->el_term.t_val = (int *) el_malloc(T_val * sizeof(int)); + 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; + if (term_set(el, NULL) == -1) + return (-1); + term_init_arrow(el); + return (0); +} +/* term_end(): + * Clean up the terminal stuff + */ +protected void +term_end(EditLine *el) +{ + + el_free((ptr_t) el->el_term.t_buf); + el->el_term.t_buf = NULL; + el_free((ptr_t) el->el_term.t_cap); + el->el_term.t_cap = NULL; + el->el_term.t_loc = 0; + el_free((ptr_t) el->el_term.t_str); + el->el_term.t_str = NULL; + el_free((ptr_t) el->el_term.t_val); + el->el_term.t_val = NULL; + term_free_display(el); +} + + +/* term_alloc(): + * Maintain a string pool for termcap strings + */ +private void +term_alloc(EditLine *el, const struct termcapstr *t, const char *cap) +{ + char termbuf[TC_BUFSIZE]; + int tlen, clen; + char **tlist = el->el_term.t_str; + char **tmp, **str = &tlist[t - tstr]; + + if (cap == NULL || *cap == '\0') { + *str = NULL; + return; + } else + clen = strlen(cap); + + tlen = *str == NULL ? 0 : strlen(*str); + + /* + * New string is shorter; no need to allocate space + */ + if (clen <= tlen) { + (void) strcpy(*str, cap); /* XXX strcpy is safe */ + return; + } + /* + * New string is longer; see if we have enough space to append + */ + if (el->el_term.t_loc + 3 < TC_BUFSIZE) { + /* XXX strcpy is safe */ + (void) strcpy(*str = &el->el_term.t_buf[el->el_term.t_loc], + cap); + el->el_term.t_loc += clen + 1; /* one for \0 */ + return; + } + /* + * Compact our buffer; no need to check compaction, cause we know it + * fits... + */ + tlen = 0; + for (tmp = tlist; tmp < &tlist[T_str]; tmp++) + if (*tmp != NULL && *tmp != '\0' && *tmp != *str) { + char *ptr; + + for (ptr = *tmp; *ptr != '\0'; termbuf[tlen++] = *ptr++) + continue; + termbuf[tlen++] = '\0'; + } + memcpy(el->el_term.t_buf, termbuf, TC_BUFSIZE); + el->el_term.t_loc = tlen; + if (el->el_term.t_loc + 3 >= TC_BUFSIZE) { + (void) fprintf(el->el_errfile, + "Out of termcap string space.\n"); + return; + } + /* XXX strcpy is safe */ + (void) strcpy(*str = &el->el_term.t_buf[el->el_term.t_loc], cap); + el->el_term.t_loc += clen + 1; /* one for \0 */ + return; +} + + +/* term_rebuffer_display(): + * Rebuffer the display after the screen changed size + */ +private int +term_rebuffer_display(EditLine *el) +{ + coord_t *c = &el->el_term.t_size; + + term_free_display(el); + + c->h = Val(T_co); + c->v = Val(T_li); + + if (term_alloc_display(el) == -1) + return (-1); + return (0); +} + + +/* term_alloc_display(): + * Allocate a new display. + */ +private int +term_alloc_display(EditLine *el) +{ + int i; + char **b; + coord_t *c = &el->el_term.t_size; + + b = (char **) el_malloc((size_t) (sizeof(char *) * (c->v + 1))); + if (b == NULL) + 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) + return (-1); + } + b[c->v] = NULL; + el->el_display = b; + + b = (char **) el_malloc((size_t) (sizeof(char *) * (c->v + 1))); + if (b == NULL) + 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) + return (-1); + } + b[c->v] = NULL; + el->el_vdisplay = b; + return (0); +} + + +/* term_free_display(): + * Free the display buffers + */ +private void +term_free_display(EditLine *el) +{ + char **b; + char **bufp; + + b = el->el_display; + el->el_display = NULL; + if (b != NULL) { + for (bufp = b; *bufp != NULL; bufp++) + el_free((ptr_t) * bufp); + el_free((ptr_t) b); + } + b = el->el_vdisplay; + el->el_vdisplay = NULL; + if (b != NULL) { + for (bufp = b; *bufp != NULL; bufp++) + el_free((ptr_t) * bufp); + el_free((ptr_t) b); + } +} + + +/* term_move_to_line(): + * move to line (first line == 0) + * as efficiently as possible + */ +protected void +term_move_to_line(EditLine *el, int where) +{ + int del; + + if (where == el->el_cursor.v) + return; + + if (where > el->el_term.t_size.v) { +#ifdef DEBUG_SCREEN + (void) fprintf(el->el_errfile, + "term_move_to_line: where is ridiculous: %d\r\n", where); +#endif /* DEBUG_SCREEN */ + return; + } + if ((del = where - el->el_cursor.v) > 0) { + while (del > 0) { + if (EL_HAS_AUTO_MARGINS && + el->el_display[el->el_cursor.v][0] != '\0') { + /* move without newline */ + term_move_to_char(el, el->el_term.t_size.h - 1); + term_overwrite(el, + &el->el_display[el->el_cursor.v][el->el_cursor.h], + 1); + /* updates Cursor */ + del--; + } else { + if ((del > 1) && GoodStr(T_DO)) { + (void) tputs(tgoto(Str(T_DO), del, del), + del, term__putc); + del = 0; + } else { + for (; del > 0; del--) + term__putc('\n'); + /* because the \n will become \r\n */ + el->el_cursor.h = 0; + } + } + } + } else { /* del < 0 */ + if (GoodStr(T_UP) && (-del > 1 || !GoodStr(T_up))) + (void) tputs(tgoto(Str(T_UP), -del, -del), -del, + term__putc); + else { + if (GoodStr(T_up)) + for (; del < 0; del++) + (void) tputs(Str(T_up), 1, term__putc); + } + } + el->el_cursor.v = where;/* now where is here */ +} + + +/* term_move_to_char(): + * Move to the character position specified + */ +protected void +term_move_to_char(EditLine *el, int where) +{ + int del, i; + +mc_again: + if (where == el->el_cursor.h) + return; + + if (where > el->el_term.t_size.h) { +#ifdef DEBUG_SCREEN + (void) fprintf(el->el_errfile, + "term_move_to_char: where is riduculous: %d\r\n", where); +#endif /* DEBUG_SCREEN */ + return; + } + if (!where) { /* if where is first column */ + term__putc('\r'); /* do a CR */ + el->el_cursor.h = 0; + return; + } + del = where - el->el_cursor.h; + + if ((del < -4 || del > 4) && GoodStr(T_ch)) + /* go there directly */ + (void) tputs(tgoto(Str(T_ch), where, where), where, term__putc); + else { + if (del > 0) { /* moving forward */ + if ((del > 4) && GoodStr(T_RI)) + (void) tputs(tgoto(Str(T_RI), del, del), + del, term__putc); + else { + /* if I can do tabs, use them */ + if (EL_CAN_TAB) { + if ((el->el_cursor.h & 0370) != + (where & 0370)) { + /* if not within tab stop */ + for (i = + (el->el_cursor.h & 0370); + i < (where & 0370); + i += 8) + term__putc('\t'); + /* then tab over */ + el->el_cursor.h = where & 0370; + } + } + /* + * it's usually cheaper to just write the + * chars, so we do. + */ + /* + * NOTE THAT term_overwrite() WILL CHANGE + * el->el_cursor.h!!! + */ + term_overwrite(el, + &el->el_display[el->el_cursor.v][el->el_cursor.h], + where - el->el_cursor.h); + + } + } else { /* del < 0 := moving backward */ + if ((-del > 4) && GoodStr(T_LE)) + (void) tputs(tgoto(Str(T_LE), -del, -del), + -del, term__putc); + else { /* can't go directly there */ + /* + * if the "cost" is greater than the "cost" + * from col 0 + */ + if (EL_CAN_TAB ? + ((unsigned int)-del > (((unsigned int) where >> 3) + + (where & 07))) + : (-del > where)) { + term__putc('\r'); /* do a CR */ + el->el_cursor.h = 0; + goto mc_again; /* and try again */ + } + for (i = 0; i < -del; i++) + term__putc('\b'); + } + } + } + el->el_cursor.h = where; /* now where is here */ +} + + +/* term_overwrite(): + * Overstrike num characters + */ +protected void +term_overwrite(EditLine *el, const char *cp, int n) +{ + if (n <= 0) + return; /* catch bugs */ + + if (n > el->el_term.t_size.h) { +#ifdef DEBUG_SCREEN + (void) fprintf(el->el_errfile, + "term_overwrite: n is riduculous: %d\r\n", n); +#endif /* DEBUG_SCREEN */ + return; + } + do { + term__putc(*cp++); + el->el_cursor.h++; + } while (--n); + + if (el->el_cursor.h >= el->el_term.t_size.h) { /* wrap? */ + if (EL_HAS_AUTO_MARGINS) { /* yes */ + el->el_cursor.h = 0; + el->el_cursor.v++; + if (EL_HAS_MAGIC_MARGINS) { + /* force the wrap to avoid the "magic" + * situation */ + char c; + if ((c = el->el_display[el->el_cursor.v][el->el_cursor.h]) + != '\0') + term_overwrite(el, &c, 1); + else + term__putc(' '); + el->el_cursor.h = 1; + } + } else /* no wrap, but cursor stays on screen */ + el->el_cursor.h = el->el_term.t_size.h; + } +} + + +/* term_deletechars(): + * Delete num characters + */ +protected void +term_deletechars(EditLine *el, int num) +{ + if (num <= 0) + return; + + if (!EL_CAN_DELETE) { +#ifdef DEBUG_EDIT + (void) fprintf(el->el_errfile, " ERROR: cannot delete \n"); +#endif /* DEBUG_EDIT */ + return; + } + if (num > el->el_term.t_size.h) { +#ifdef DEBUG_SCREEN + (void) fprintf(el->el_errfile, + "term_deletechars: num is riduculous: %d\r\n", num); +#endif /* DEBUG_SCREEN */ + return; + } + 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); + return; + } + if (GoodStr(T_dm)) /* if I have delete mode */ + (void) tputs(Str(T_dm), 1, term__putc); + + if (GoodStr(T_dc)) /* else do one at a time */ + while (num--) + (void) tputs(Str(T_dc), 1, term__putc); + + if (GoodStr(T_ed)) /* if I have delete mode */ + (void) tputs(Str(T_ed), 1, term__putc); +} + + +/* term_insertwrite(): + * Puts terminal in insert character mode or inserts num + * characters in the line + */ +protected void +term_insertwrite(EditLine *el, char *cp, int num) +{ + if (num <= 0) + return; + if (!EL_CAN_INSERT) { +#ifdef DEBUG_EDIT + (void) fprintf(el->el_errfile, " ERROR: cannot insert \n"); +#endif /* DEBUG_EDIT */ + return; + } + if (num > el->el_term.t_size.h) { +#ifdef DEBUG_SCREEN + (void) fprintf(el->el_errfile, + "StartInsert: num is riduculous: %d\r\n", num); +#endif /* DEBUG_SCREEN */ + return; + } + 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_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); + + el->el_cursor.h += num; + do + term__putc(*cp++); + while (--num); + + if (GoodStr(T_ip)) /* have to make num chars insert */ + (void) tputs(Str(T_ip), 1, term__putc); + + (void) tputs(Str(T_ei), 1, term__putc); + return; + } + do { + if (GoodStr(T_ic)) /* have to make num chars insert */ + (void) tputs(Str(T_ic), 1, term__putc); + /* insert a char */ + + term__putc(*cp++); + + el->el_cursor.h++; + + if (GoodStr(T_ip)) /* have to make num chars insert */ + (void) tputs(Str(T_ip), 1, term__putc); + /* pad the inserted char */ + + } while (--num); +} + + +/* term_clear_EOL(): + * clear to end of line. There are num characters to clear + */ +protected void +term_clear_EOL(EditLine *el, int num) +{ + int i; + + if (EL_CAN_CEOL && GoodStr(T_ce)) + (void) tputs(Str(T_ce), 1, term__putc); + else { + for (i = 0; i < num; i++) + term__putc(' '); + el->el_cursor.h += num; /* have written num spaces */ + } +} + + +/* term_clear_screen(): + * Clear the screen + */ +protected void +term_clear_screen(EditLine *el) +{ /* clear the whole screen and home */ + + if (GoodStr(T_cl)) + /* send the clear screen code */ + (void) tputs(Str(T_cl), Val(T_li), term__putc); + else if (GoodStr(T_ho) && GoodStr(T_cd)) { + (void) tputs(Str(T_ho), Val(T_li), term__putc); /* home */ + /* clear to bottom of screen */ + (void) tputs(Str(T_cd), Val(T_li), term__putc); + } else { + term__putc('\r'); + term__putc('\n'); + } +} + + +/* term_beep(): + * Beep the way the terminal wants us + */ +protected void +term_beep(EditLine *el) +{ + if (GoodStr(T_bl)) + /* what termcap says we should use */ + (void) tputs(Str(T_bl), 1, term__putc); + else + term__putc('\007'); /* an ASCII bell; ^G */ +} + + +#ifdef notdef +/* term_clear_to_bottom(): + * Clear to the bottom of the screen + */ +protected void +term_clear_to_bottom(EditLine *el) +{ + if (GoodStr(T_cd)) + (void) tputs(Str(T_cd), Val(T_li), term__putc); + else if (GoodStr(T_ce)) + (void) tputs(Str(T_ce), Val(T_li), term__putc); +} +#endif + + +/* term_set(): + * Read in the terminal capabilities from the requested terminal + */ +protected int +term_set(EditLine *el, const char *term) +{ + int i; + char buf[TC_BUFSIZE]; + char *area; + const struct termcapstr *t; + sigset_t oset, nset; + int lins, cols; + + (void) sigemptyset(&nset); + (void) sigaddset(&nset, SIGWINCH); + (void) sigprocmask(SIG_BLOCK, &nset, &oset); + + area = buf; + + + if (term == NULL) + term = getenv("TERM"); + + if (!term || !term[0]) + term = "dumb"; + + if (strcmp(term, "emacs") == 0) + el->el_flags |= EDIT_DISABLED; + + memset(el->el_term.t_cap, 0, TC_BUFSIZE); + + i = tgetent(el->el_term.t_cap, term); + + if (i <= 0) { + if (i == -1) + (void) fprintf(el->el_errfile, + "Cannot read termcap database;\n"); + else if (i == 0) + (void) fprintf(el->el_errfile, + "No entry for terminal type \"%s\";\n", term); + (void) fprintf(el->el_errfile, + "using dumb terminal settings.\n"); + Val(T_co) = 80; /* do a dumb terminal */ + Val(T_pt) = Val(T_km) = Val(T_li) = 0; + Val(T_xt) = Val(T_MT); + for (t = tstr; t->name != NULL; t++) + term_alloc(el, t, NULL); + } else { + /* auto/magic margins */ + Val(T_am) = tgetflag("am"); + Val(T_xn) = tgetflag("xn"); + /* Can we tab */ + Val(T_pt) = tgetflag("pt"); + Val(T_xt) = tgetflag("xt"); + /* do we have a meta? */ + Val(T_km) = tgetflag("km"); + Val(T_MT) = tgetflag("MT"); + /* Get the size */ + Val(T_co) = tgetnum("co"); + Val(T_li) = tgetnum("li"); + for (t = tstr; t->name != NULL; t++) + term_alloc(el, t, tgetstr(t->name, &area)); + } + + if (Val(T_co) < 2) + Val(T_co) = 80; /* just in case */ + if (Val(T_li) < 1) + Val(T_li) = 24; + + el->el_term.t_size.v = Val(T_co); + el->el_term.t_size.h = Val(T_li); + + term_setflags(el); + + /* get the correct window size */ + (void) term_get_size(el, &lins, &cols); + if (term_change_size(el, lins, cols) == -1) + return (-1); + (void) sigprocmask(SIG_SETMASK, &oset, NULL); + term_bind_arrow(el); + return (i <= 0 ? -1 : 0); +} + + +/* term_get_size(): + * Return the new window size in lines and cols, and + * true if the size was changed. + */ +protected int +term_get_size(EditLine *el, int *lins, int *cols) +{ + + *cols = Val(T_co); + *lins = Val(T_li); + +#ifdef TIOCGWINSZ + { + struct winsize ws; + if (ioctl(el->el_infd, TIOCGWINSZ, (ioctl_t) & ws) != -1) { + if (ws.ws_col) + *cols = ws.ws_col; + if (ws.ws_row) + *lins = ws.ws_row; + } + } +#endif +#ifdef TIOCGSIZE + { + struct ttysize ts; + if (ioctl(el->el_infd, TIOCGSIZE, (ioctl_t) & ts) != -1) { + if (ts.ts_cols) + *cols = ts.ts_cols; + if (ts.ts_lines) + *lins = ts.ts_lines; + } + } +#endif + return (Val(T_co) != *cols || Val(T_li) != *lins); +} + + +/* term_change_size(): + * Change the size of the terminal + */ +protected int +term_change_size(EditLine *el, int lins, int cols) +{ + /* + * Just in case + */ + Val(T_co) = (cols < 2) ? 80 : cols; + Val(T_li) = (lins < 1) ? 24 : lins; + + /* re-make display buffers */ + if (term_rebuffer_display(el) == -1) + return (-1); + re_clear_display(el); + return (0); +} + + +/* term_init_arrow(): + * Initialize the arrow key bindings from termcap + */ +private void +term_init_arrow(EditLine *el) +{ + fkey_t *arrow = el->el_term.t_fkey; + + arrow[A_K_DN].name = "down"; + arrow[A_K_DN].key = T_kd; + arrow[A_K_DN].fun.cmd = ED_NEXT_HISTORY; + arrow[A_K_DN].type = XK_CMD; + + arrow[A_K_UP].name = "up"; + arrow[A_K_UP].key = T_ku; + arrow[A_K_UP].fun.cmd = ED_PREV_HISTORY; + arrow[A_K_UP].type = XK_CMD; + + arrow[A_K_LT].name = "left"; + arrow[A_K_LT].key = T_kl; + arrow[A_K_LT].fun.cmd = ED_PREV_CHAR; + arrow[A_K_LT].type = XK_CMD; + + arrow[A_K_RT].name = "right"; + arrow[A_K_RT].key = T_kr; + arrow[A_K_RT].fun.cmd = ED_NEXT_CHAR; + arrow[A_K_RT].type = XK_CMD; + + arrow[A_K_HO].name = "home"; + arrow[A_K_HO].key = T_kh; + arrow[A_K_HO].fun.cmd = ED_MOVE_TO_BEG; + arrow[A_K_HO].type = XK_CMD; + + arrow[A_K_EN].name = "end"; + arrow[A_K_EN].key = T_at7; + arrow[A_K_EN].fun.cmd = ED_MOVE_TO_END; + arrow[A_K_EN].type = XK_CMD; +} + + +/* term_reset_arrow(): + * Reset arrow key bindings + */ +private void +term_reset_arrow(EditLine *el) +{ + fkey_t *arrow = el->el_term.t_fkey; + static const char strA[] = {033, '[', 'A', '\0'}; + static const char strB[] = {033, '[', 'B', '\0'}; + static const char strC[] = {033, '[', 'C', '\0'}; + static const char strD[] = {033, '[', 'D', '\0'}; + static const char strH[] = {033, '[', 'H', '\0'}; + static const char strF[] = {033, '[', 'F', '\0'}; + static const char stOA[] = {033, 'O', 'A', '\0'}; + static const char stOB[] = {033, 'O', 'B', '\0'}; + static const char stOC[] = {033, 'O', 'C', '\0'}; + static const char stOD[] = {033, 'O', 'D', '\0'}; + static const char stOH[] = {033, 'O', 'H', '\0'}; + static const char stOF[] = {033, 'O', 'F', '\0'}; + + key_add(el, strA, &arrow[A_K_UP].fun, arrow[A_K_UP].type); + key_add(el, strB, &arrow[A_K_DN].fun, arrow[A_K_DN].type); + key_add(el, strC, &arrow[A_K_RT].fun, arrow[A_K_RT].type); + key_add(el, strD, &arrow[A_K_LT].fun, arrow[A_K_LT].type); + key_add(el, strH, &arrow[A_K_HO].fun, arrow[A_K_HO].type); + key_add(el, strF, &arrow[A_K_EN].fun, arrow[A_K_EN].type); + key_add(el, stOA, &arrow[A_K_UP].fun, arrow[A_K_UP].type); + key_add(el, stOB, &arrow[A_K_DN].fun, arrow[A_K_DN].type); + key_add(el, stOC, &arrow[A_K_RT].fun, arrow[A_K_RT].type); + key_add(el, stOD, &arrow[A_K_LT].fun, arrow[A_K_LT].type); + key_add(el, stOH, &arrow[A_K_HO].fun, arrow[A_K_HO].type); + key_add(el, stOF, &arrow[A_K_EN].fun, arrow[A_K_EN].type); + + if (el->el_map.type == MAP_VI) { + key_add(el, &strA[1], &arrow[A_K_UP].fun, arrow[A_K_UP].type); + key_add(el, &strB[1], &arrow[A_K_DN].fun, arrow[A_K_DN].type); + key_add(el, &strC[1], &arrow[A_K_RT].fun, arrow[A_K_RT].type); + key_add(el, &strD[1], &arrow[A_K_LT].fun, arrow[A_K_LT].type); + key_add(el, &strH[1], &arrow[A_K_HO].fun, arrow[A_K_HO].type); + key_add(el, &strF[1], &arrow[A_K_EN].fun, arrow[A_K_EN].type); + key_add(el, &stOA[1], &arrow[A_K_UP].fun, arrow[A_K_UP].type); + key_add(el, &stOB[1], &arrow[A_K_DN].fun, arrow[A_K_DN].type); + key_add(el, &stOC[1], &arrow[A_K_RT].fun, arrow[A_K_RT].type); + key_add(el, &stOD[1], &arrow[A_K_LT].fun, arrow[A_K_LT].type); + key_add(el, &stOH[1], &arrow[A_K_HO].fun, arrow[A_K_HO].type); + key_add(el, &stOF[1], &arrow[A_K_EN].fun, arrow[A_K_EN].type); + } +} + + +/* term_set_arrow(): + * Set an arrow key binding + */ +protected int +term_set_arrow(EditLine *el, const char *name, key_value_t *fun, int type) +{ + fkey_t *arrow = el->el_term.t_fkey; + int i; + + for (i = 0; i < A_K_NKEYS; i++) + if (strcmp(name, arrow[i].name) == 0) { + arrow[i].fun = *fun; + arrow[i].type = type; + return (0); + } + return (-1); +} + + +/* term_clear_arrow(): + * Clear an arrow key binding + */ +protected int +term_clear_arrow(EditLine *el, const char *name) +{ + fkey_t *arrow = el->el_term.t_fkey; + int i; + + for (i = 0; i < A_K_NKEYS; i++) + if (strcmp(name, arrow[i].name) == 0) { + arrow[i].type = XK_NOD; + return (0); + } + return (-1); +} + + +/* term_print_arrow(): + * Print the arrow key bindings + */ +protected void +term_print_arrow(EditLine *el, const char *name) +{ + int i; + fkey_t *arrow = el->el_term.t_fkey; + + for (i = 0; i < A_K_NKEYS; i++) + if (*name == '\0' || strcmp(name, arrow[i].name) == 0) + if (arrow[i].type != XK_NOD) + key_kprint(el, arrow[i].name, &arrow[i].fun, + arrow[i].type); +} + + +/* term_bind_arrow(): + * Bind the arrow keys + */ +protected void +term_bind_arrow(EditLine *el) +{ + el_action_t *map; + const el_action_t *dmap; + int i, j; + char *p; + fkey_t *arrow = el->el_term.t_fkey; + + /* Check if the components needed are initialized */ + if (el->el_term.t_buf == NULL || el->el_map.key == NULL) + return; + + map = el->el_map.type == MAP_VI ? el->el_map.alt : el->el_map.key; + dmap = el->el_map.type == MAP_VI ? el->el_map.vic : el->el_map.emacs; + + term_reset_arrow(el); + + for (i = 0; i < A_K_NKEYS; i++) { + p = el->el_term.t_str[arrow[i].key]; + if (p && *p) { + j = (unsigned char) *p; + /* + * Assign the arrow keys only if: + * + * 1. They are multi-character arrow keys and the user + * has not re-assigned the leading character, or + * has re-assigned the leading character to be + * ED_SEQUENCE_LEAD_IN + * 2. They are single arrow keys pointing to an + * unassigned key. + */ + if (arrow[i].type == XK_NOD) + key_clear(el, map, p); + else { + if (p[1] && (dmap[j] == map[j] || + map[j] == ED_SEQUENCE_LEAD_IN)) { + key_add(el, p, &arrow[i].fun, + arrow[i].type); + map[j] = ED_SEQUENCE_LEAD_IN; + } else if (map[j] == ED_UNASSIGNED) { + key_clear(el, map, p); + if (arrow[i].type == XK_CMD) + map[j] = arrow[i].fun.cmd; + else + key_add(el, p, &arrow[i].fun, + arrow[i].type); + } + } + } + } +} + + +/* term__putc(): + * Add a character + */ +protected int +term__putc(int c) +{ + + return (fputc(c, term_outfile)); +} + + +/* term__flush(): + * Flush output + */ +protected void +term__flush(void) +{ + + (void) fflush(term_outfile); +} + + +/* term_telltc(): + * Print the current termcap characteristics + */ +protected int +/*ARGSUSED*/ +term_telltc(EditLine *el, int + argc __attribute__((unused)), + const char **argv __attribute__((unused))) +{ + const struct termcapstr *t; + char **ts; + char upbuf[EL_BUFSIZ]; + + (void) fprintf(el->el_outfile, "\n\tYour terminal has the\n"); + (void) fprintf(el->el_outfile, "\tfollowing characteristics:\n\n"); + (void) fprintf(el->el_outfile, "\tIt has %d columns and %d lines\n", + Val(T_co), Val(T_li)); + (void) fprintf(el->el_outfile, + "\tIt has %s meta key\n", EL_HAS_META ? "a" : "no"); + (void) fprintf(el->el_outfile, + "\tIt can%suse tabs\n", EL_CAN_TAB ? " " : "not "); + (void) fprintf(el->el_outfile, "\tIt %s automatic margins\n", + EL_HAS_AUTO_MARGINS ? "has" : "does not have"); + if (EL_HAS_AUTO_MARGINS) + (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++) + (void) fprintf(el->el_outfile, "\t%25s (%s) == %s\n", + t->long_name, + t->name, *ts && **ts ? + key__decode_str(*ts, upbuf, "") : "(empty)"); + (void) fputc('\n', el->el_outfile); + return (0); +} + + +/* term_settc(): + * Change the current terminal characteristics + */ +protected int +/*ARGSUSED*/ +term_settc(EditLine *el, int argc __attribute__((unused)), const char **argv) +{ + const struct termcapstr *ts; + const struct termcapval *tv; + const char *what, *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) { + term_alloc(el, ts, how); + term_setflags(el); + 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) { + 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; + + 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); + } + } + return (-1); +} + + +/* term_echotc(): + * Print the termcap string out with variable substitution + */ +protected int +/*ARGSUSED*/ +term_echotc(EditLine *el, int argc __attribute__((unused)), const char **argv) +{ + char *cap, *scap, *ep; + int arg_need, arg_cols, arg_rows; + int verbose = 0, silent = 0; + char *area; + static const char fmts[] = "%s\n", fmtd[] = "%d\n"; + const struct termcapstr *t; + char buf[TC_BUFSIZE]; + long i; + + area = buf; + + if (argv == NULL || argv[1] == NULL) + return (-1); + argv++; + + if (argv[0][0] == '-') { + switch (argv[0][1]) { + case 'v': + verbose = 1; + break; + case 's': + silent = 1; + break; + default: + /* stderror(ERR_NAME | ERR_TCUSAGE); */ + break; + } + argv++; + } + if (!*argv || *argv[0] == '\0') + return (0); + if (strcmp(*argv, "tabs") == 0) { + (void) fprintf(el->el_outfile, fmts, EL_CAN_TAB ? "yes" : "no"); + return (0); + } else if (strcmp(*argv, "meta") == 0) { + (void) fprintf(el->el_outfile, fmts, Val(T_km) ? "yes" : "no"); + return (0); + } else if (strcmp(*argv, "xn") == 0) { + (void) fprintf(el->el_outfile, fmts, EL_HAS_MAGIC_MARGINS ? + "yes" : "no"); + return (0); + } else if (strcmp(*argv, "am") == 0) { + (void) fprintf(el->el_outfile, fmts, EL_HAS_AUTO_MARGINS ? + "yes" : "no"); + return (0); + } else if (strcmp(*argv, "baud") == 0) { +#ifdef notdef + int i; + + for (i = 0; baud_rate[i].b_name != NULL; i++) + if (el->el_tty.t_speed == baud_rate[i].b_rate) { + (void) fprintf(el->el_outfile, fmts, + baud_rate[i].b_name); + return (0); + } + (void) fprintf(el->el_outfile, fmtd, 0); +#else + (void) fprintf(el->el_outfile, fmtd, el->el_tty.t_speed); +#endif + return (0); + } else if (strcmp(*argv, "rows") == 0 || strcmp(*argv, "lines") == 0) { + (void) fprintf(el->el_outfile, fmtd, Val(T_li)); + return (0); + } else if (strcmp(*argv, "cols") == 0) { + (void) fprintf(el->el_outfile, fmtd, Val(T_co)); + return (0); + } + /* + * Try to use our local definition first + */ + scap = NULL; + for (t = tstr; t->name != NULL; t++) + if (strcmp(t->name, *argv) == 0) { + scap = el->el_term.t_str[t - tstr]; + break; + } + if (t->name == NULL) + scap = tgetstr(*argv, &area); + if (!scap || scap[0] == '\0') { + if (!silent) + (void) fprintf(el->el_errfile, + "echotc: Termcap parameter `%s' not found.\n", + *argv); + return (-1); + } + /* + * Count home many values we need for this capability. + */ + for (cap = scap, arg_need = 0; *cap; cap++) + if (*cap == '%') + switch (*++cap) { + case 'd': + case '2': + case '3': + case '.': + case '+': + arg_need++; + break; + case '%': + case '>': + case 'i': + case 'r': + case 'n': + case 'B': + case 'D': + break; + default: + /* + * hpux has lot's of them... + */ + if (verbose) + (void) fprintf(el->el_errfile, + "echotc: Warning: unknown termcap %% `%c'.\n", + *cap); + /* This is bad, but I won't complain */ + break; + } + + switch (arg_need) { + case 0: + argv++; + if (*argv && *argv[0]) { + if (!silent) + (void) fprintf(el->el_errfile, + "echotc: Warning: Extra argument `%s'.\n", + *argv); + return (-1); + } + (void) tputs(scap, 1, term__putc); + break; + case 1: + argv++; + if (!*argv || *argv[0] == '\0') { + if (!silent) + (void) fprintf(el->el_errfile, + "echotc: Warning: Missing argument.\n"); + return (-1); + } + arg_cols = 0; + i = strtol(*argv, &ep, 10); + if (*ep != '\0' || i < 0) { + if (!silent) + (void) fprintf(el->el_errfile, + "echotc: Bad value `%s' for rows.\n", + *argv); + return (-1); + } + arg_rows = (int) i; + argv++; + if (*argv && *argv[0]) { + if (!silent) + (void) fprintf(el->el_errfile, + "echotc: Warning: Extra argument `%s'.\n", + *argv); + return (-1); + } + (void) tputs(tgoto(scap, arg_cols, arg_rows), 1, term__putc); + break; + default: + /* This is wrong, but I will ignore it... */ + if (verbose) + (void) fprintf(el->el_errfile, + "echotc: Warning: Too many required arguments (%d).\n", + arg_need); + /* FALLTHROUGH */ + case 2: + argv++; + if (!*argv || *argv[0] == '\0') { + if (!silent) + (void) fprintf(el->el_errfile, + "echotc: Warning: Missing argument.\n"); + return (-1); + } + i = strtol(*argv, &ep, 10); + if (*ep != '\0' || i < 0) { + if (!silent) + (void) fprintf(el->el_errfile, + "echotc: Bad value `%s' for cols.\n", + *argv); + return (-1); + } + arg_cols = (int) i; + argv++; + if (!*argv || *argv[0] == '\0') { + if (!silent) + (void) fprintf(el->el_errfile, + "echotc: Warning: Missing argument.\n"); + return (-1); + } + i = strtol(*argv, &ep, 10); + if (*ep != '\0' || i < 0) { + if (!silent) + (void) fprintf(el->el_errfile, + "echotc: Bad value `%s' for rows.\n", + *argv); + return (-1); + } + arg_rows = (int) i; + if (*ep != '\0') { + if (!silent) + (void) fprintf(el->el_errfile, + "echotc: Bad value `%s'.\n", *argv); + return (-1); + } + argv++; + if (*argv && *argv[0]) { + if (!silent) + (void) fprintf(el->el_errfile, + "echotc: Warning: Extra argument `%s'.\n", + *argv); + return (-1); + } + (void) tputs(tgoto(scap, arg_cols, arg_rows), arg_rows, + term__putc); + break; + } + return (0); +} diff --git a/cmd-line-utils/libedit/term.h b/cmd-line-utils/libedit/term.h new file mode 100644 index 00000000000..9f03c549515 --- /dev/null +++ b/cmd-line-utils/libedit/term.h @@ -0,0 +1,124 @@ +/* $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/tokenizer.c b/cmd-line-utils/libedit/tokenizer.c new file mode 100644 index 00000000000..7a7e5b5ed75 --- /dev/null +++ b/cmd-line-utils/libedit/tokenizer.c @@ -0,0 +1,391 @@ +/* $NetBSD: tokenizer.c,v 1.7 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. + */ + +#include "compat.h" + +/* + * tokenize.c: Bourne shell like tokenizer + */ +#include "sys.h" +#include +#include +#include "tokenizer.h" + +typedef enum { + Q_none, Q_single, Q_double, Q_one, Q_doubleone +} quote_t; + +#define IFS "\t \n" + +#define TOK_KEEP 1 +#define TOK_EAT 2 + +#define WINCR 20 +#define AINCR 10 + +#define tok_malloc(a) malloc(a) +#define tok_free(a) free(a) +#define tok_realloc(a, b) realloc(a, b) + + +struct tokenizer { + char *ifs; /* In field separator */ + int argc, amax; /* Current and maximum number of args */ + const char **argv; /* Argument list */ + char *wptr, *wmax; /* Space and limit on the word buffer */ + char *wstart; /* Beginning of next word */ + char *wspace; /* Space of word buffer */ + quote_t quote; /* Quoting state */ + int flags; /* flags; */ +}; + + +private void tok_finish(Tokenizer *); + + +/* tok_finish(): + * Finish a word in the tokenizer. + */ +private void +tok_finish(Tokenizer *tok) +{ + + *tok->wptr = '\0'; + if ((tok->flags & TOK_KEEP) || tok->wptr != tok->wstart) { + tok->argv[tok->argc++] = tok->wstart; + tok->argv[tok->argc] = NULL; + tok->wstart = ++tok->wptr; + } + tok->flags &= ~TOK_KEEP; +} + + +/* tok_init(): + * Initialize the tokenizer + */ +public Tokenizer * +tok_init(const char *ifs) +{ + Tokenizer *tok = (Tokenizer *) tok_malloc(sizeof(Tokenizer)); + + tok->ifs = strdup(ifs ? ifs : IFS); + tok->argc = 0; + tok->amax = AINCR; + tok->argv = (const char **) tok_malloc(sizeof(char *) * tok->amax); + if (tok->argv == NULL) + return (NULL); + tok->argv[0] = NULL; + tok->wspace = (char *) tok_malloc(WINCR); + if (tok->wspace == NULL) + return (NULL); + tok->wmax = tok->wspace + WINCR; + tok->wstart = tok->wspace; + tok->wptr = tok->wspace; + tok->flags = 0; + tok->quote = Q_none; + + return (tok); +} + + +/* tok_reset(): + * Reset the tokenizer + */ +public void +tok_reset(Tokenizer *tok) +{ + + tok->argc = 0; + tok->wstart = tok->wspace; + tok->wptr = tok->wspace; + tok->flags = 0; + tok->quote = Q_none; +} + + +/* tok_end(): + * Clean up + */ +public void +tok_end(Tokenizer *tok) +{ + + tok_free((ptr_t) tok->ifs); + tok_free((ptr_t) tok->wspace); + tok_free((ptr_t) tok->argv); + tok_free((ptr_t) tok); +} + + + +/* tok_line(): + * Bourne shell like tokenizing + * Return: + * -1: Internal error + * 3: Quoted return + * 2: Unmatched double quote + * 1: Unmatched single quote + * 0: Ok + */ +public int +tok_line(Tokenizer *tok, const char *line, int *argc, const char ***argv) +{ + const char *ptr; + + for (;;) { + switch (*(ptr = line++)) { + case '\'': + tok->flags |= TOK_KEEP; + tok->flags &= ~TOK_EAT; + switch (tok->quote) { + case Q_none: + tok->quote = Q_single; /* Enter single quote + * mode */ + break; + + case Q_single: /* Exit single quote mode */ + tok->quote = Q_none; + break; + + case Q_one: /* Quote this ' */ + tok->quote = Q_none; + *tok->wptr++ = *ptr; + break; + + case Q_double: /* Stay in double quote mode */ + *tok->wptr++ = *ptr; + break; + + case Q_doubleone: /* Quote this ' */ + tok->quote = Q_double; + *tok->wptr++ = *ptr; + break; + + default: + return (-1); + } + break; + + case '"': + tok->flags &= ~TOK_EAT; + tok->flags |= TOK_KEEP; + switch (tok->quote) { + case Q_none: /* Enter double quote mode */ + tok->quote = Q_double; + break; + + case Q_double: /* Exit double quote mode */ + tok->quote = Q_none; + break; + + case Q_one: /* Quote this " */ + tok->quote = Q_none; + *tok->wptr++ = *ptr; + break; + + case Q_single: /* Stay in single quote mode */ + *tok->wptr++ = *ptr; + break; + + case Q_doubleone: /* Quote this " */ + tok->quote = Q_double; + *tok->wptr++ = *ptr; + break; + + default: + return (-1); + } + break; + + case '\\': + tok->flags |= TOK_KEEP; + tok->flags &= ~TOK_EAT; + switch (tok->quote) { + case Q_none: /* Quote next character */ + tok->quote = Q_one; + break; + + case Q_double: /* Quote next character */ + tok->quote = Q_doubleone; + break; + + case Q_one: /* Quote this, restore state */ + *tok->wptr++ = *ptr; + tok->quote = Q_none; + break; + + case Q_single: /* Stay in single quote mode */ + *tok->wptr++ = *ptr; + break; + + case Q_doubleone: /* Quote this \ */ + tok->quote = Q_double; + *tok->wptr++ = *ptr; + break; + + default: + return (-1); + } + break; + + case '\n': + tok->flags &= ~TOK_EAT; + switch (tok->quote) { + case Q_none: + tok_finish(tok); + *argv = tok->argv; + *argc = tok->argc; + return (0); + + case Q_single: + case Q_double: + *tok->wptr++ = *ptr; /* Add the return */ + break; + + case Q_doubleone: /* Back to double, eat the '\n' */ + tok->flags |= TOK_EAT; + tok->quote = Q_double; + break; + + case Q_one: /* No quote, more eat the '\n' */ + tok->flags |= TOK_EAT; + tok->quote = Q_none; + break; + + default: + return (0); + } + break; + + case '\0': + switch (tok->quote) { + case Q_none: + /* Finish word and return */ + if (tok->flags & TOK_EAT) { + tok->flags &= ~TOK_EAT; + return (3); + } + tok_finish(tok); + *argv = tok->argv; + *argc = tok->argc; + return (0); + + case Q_single: + return (1); + + case Q_double: + return (2); + + case Q_doubleone: + tok->quote = Q_double; + *tok->wptr++ = *ptr; + break; + + case Q_one: + tok->quote = Q_none; + *tok->wptr++ = *ptr; + break; + + default: + return (-1); + } + break; + + default: + tok->flags &= ~TOK_EAT; + switch (tok->quote) { + case Q_none: + if (strchr(tok->ifs, *ptr) != NULL) + tok_finish(tok); + else + *tok->wptr++ = *ptr; + break; + + case Q_single: + case Q_double: + *tok->wptr++ = *ptr; + break; + + + case Q_doubleone: + *tok->wptr++ = '\\'; + tok->quote = Q_double; + *tok->wptr++ = *ptr; + break; + + case Q_one: + tok->quote = Q_none; + *tok->wptr++ = *ptr; + break; + + default: + return (-1); + + } + break; + } + + if (tok->wptr >= tok->wmax - 4) { + size_t size = tok->wmax - tok->wspace + WINCR; + char *s = (char *) tok_realloc(tok->wspace, size); + /* SUPPRESS 22 */ + int offs = s - tok->wspace; + if (s == NULL) + return (-1); + + if (offs != 0) { + int i; + for (i = 0; i < tok->argc; i++) + tok->argv[i] = tok->argv[i] + offs; + tok->wptr = tok->wptr + offs; + tok->wstart = tok->wstart + offs; + tok->wmax = s + size; + tok->wspace = s; + } + } + if (tok->argc >= tok->amax - 4) { + const char **p; + tok->amax += AINCR; + p = (const char **) tok_realloc(tok->argv, + tok->amax * sizeof(char *)); + if (p == NULL) + return (-1); + tok->argv = p; + } + } +} diff --git a/cmd-line-utils/libedit/tokenizer.h b/cmd-line-utils/libedit/tokenizer.h new file mode 100644 index 00000000000..14919fd3f84 --- /dev/null +++ b/cmd-line-utils/libedit/tokenizer.h @@ -0,0 +1,54 @@ +/* $NetBSD: tokenizer.h,v 1.4 2000/09/04 22:06:33 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. + * + * @(#)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 new file mode 100644 index 00000000000..2c7b502136d --- /dev/null +++ b/cmd-line-utils/libedit/tty.c @@ -0,0 +1,1177 @@ +/* $NetBSD: tty.c,v 1.15 2001/05/17 01:02:17 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. + */ + +#include "compat.h" + +/* + * tty.c: tty interface stuff + */ +#include "sys.h" +#include "tty.h" +#include "el.h" + +typedef struct ttymodes_t { + const char *m_name; + u_int m_value; + int m_type; +} ttymodes_t; + +typedef struct ttymap_t { + int nch, och; /* Internal and termio rep of chars */ + el_action_t bind[3]; /* emacs, vi, and vi-cmd */ +} ttymap_t; + + +private const ttyperm_t ttyperm = { + { + {"iflag:", ICRNL, (INLCR | IGNCR)}, + {"oflag:", (OPOST | ONLCR), ONLRET}, + {"cflag:", 0, 0}, + {"lflag:", (ISIG | ICANON | ECHO | ECHOE | ECHOCTL | IEXTEN), + (NOFLSH | ECHONL | EXTPROC | FLUSHO)}, + {"chars:", 0, 0}, + }, + { + {"iflag:", (INLCR | ICRNL), IGNCR}, + {"oflag:", (OPOST | ONLCR), ONLRET}, + {"cflag:", 0, 0}, + {"lflag:", ISIG, + (NOFLSH | ICANON | ECHO | ECHOK | ECHONL | EXTPROC | IEXTEN | FLUSHO)}, + {"chars:", (C_SH(C_MIN) | C_SH(C_TIME) | C_SH(C_SWTCH) | C_SH(C_DSWTCH) | + C_SH(C_SUSP) | C_SH(C_DSUSP) | C_SH(C_EOL) | C_SH(C_DISCARD) | + C_SH(C_PGOFF) | C_SH(C_PAGE) | C_SH(C_STATUS)), 0} + }, + { + {"iflag:", 0, IXON | IXOFF | INLCR | ICRNL}, + {"oflag:", 0, 0}, + {"cflag:", 0, 0}, + {"lflag:", 0, ISIG | IEXTEN}, + {"chars:", 0, 0}, + } +}; + +private const ttychar_t ttychar = { + { + CINTR, CQUIT, CERASE, CKILL, + CEOF, CEOL, CEOL2, CSWTCH, + CDSWTCH, CERASE2, CSTART, CSTOP, + CWERASE, CSUSP, CDSUSP, CREPRINT, + CDISCARD, CLNEXT, CSTATUS, CPAGE, + CPGOFF, CKILL2, CBRK, CMIN, + CTIME + }, + { + CINTR, CQUIT, CERASE, CKILL, + _POSIX_VDISABLE, _POSIX_VDISABLE, _POSIX_VDISABLE, _POSIX_VDISABLE, + _POSIX_VDISABLE, CERASE2, CSTART, CSTOP, + _POSIX_VDISABLE, CSUSP, _POSIX_VDISABLE, _POSIX_VDISABLE, + CDISCARD, _POSIX_VDISABLE, _POSIX_VDISABLE, _POSIX_VDISABLE, + _POSIX_VDISABLE, _POSIX_VDISABLE, _POSIX_VDISABLE, 1, + 0 + }, + { + 0, 0, 0, 0, + 0, 0, 0, 0, + 0, 0, 0, 0, + 0, 0, 0, 0, + 0, 0, 0, 0, + 0, 0, 0, 0, + 0 + } +}; + +private const ttymap_t tty_map[] = { +#ifdef VERASE + {C_ERASE, VERASE, + {ED_DELETE_PREV_CHAR, VI_DELETE_PREV_CHAR, ED_PREV_CHAR}}, +#endif /* VERASE */ +#ifdef VERASE2 + {C_ERASE2, VERASE2, + {ED_DELETE_PREV_CHAR, VI_DELETE_PREV_CHAR, ED_PREV_CHAR}}, +#endif /* VERASE2 */ +#ifdef VKILL + {C_KILL, VKILL, + {EM_KILL_LINE, VI_KILL_LINE_PREV, ED_UNASSIGNED}}, +#endif /* VKILL */ +#ifdef VKILL2 + {C_KILL2, VKILL2, + {EM_KILL_LINE, VI_KILL_LINE_PREV, ED_UNASSIGNED}}, +#endif /* VKILL2 */ +#ifdef VEOF + {C_EOF, VEOF, + {EM_DELETE_OR_LIST, VI_LIST_OR_EOF, ED_UNASSIGNED}}, +#endif /* VEOF */ +#ifdef VWERASE + {C_WERASE, VWERASE, + {ED_DELETE_PREV_WORD, ED_DELETE_PREV_WORD, ED_PREV_WORD}}, +#endif /* VWERASE */ +#ifdef VREPRINT + {C_REPRINT, VREPRINT, + {ED_REDISPLAY, ED_INSERT, ED_REDISPLAY}}, +#endif /* VREPRINT */ +#ifdef VLNEXT + {C_LNEXT, VLNEXT, + {ED_QUOTED_INSERT, ED_QUOTED_INSERT, ED_UNASSIGNED}}, +#endif /* VLNEXT */ + {-1, -1, + {ED_UNASSIGNED, ED_UNASSIGNED, ED_UNASSIGNED}} +}; + +private const ttymodes_t ttymodes[] = { +#ifdef IGNBRK + {"ignbrk", IGNBRK, MD_INP}, +#endif /* IGNBRK */ +#ifdef BRKINT + {"brkint", BRKINT, MD_INP}, +#endif /* BRKINT */ +#ifdef IGNPAR + {"ignpar", IGNPAR, MD_INP}, +#endif /* IGNPAR */ +#ifdef PARMRK + {"parmrk", PARMRK, MD_INP}, +#endif /* PARMRK */ +#ifdef INPCK + {"inpck", INPCK, MD_INP}, +#endif /* INPCK */ +#ifdef ISTRIP + {"istrip", ISTRIP, MD_INP}, +#endif /* ISTRIP */ +#ifdef INLCR + {"inlcr", INLCR, MD_INP}, +#endif /* INLCR */ +#ifdef IGNCR + {"igncr", IGNCR, MD_INP}, +#endif /* IGNCR */ +#ifdef ICRNL + {"icrnl", ICRNL, MD_INP}, +#endif /* ICRNL */ +#ifdef IUCLC + {"iuclc", IUCLC, MD_INP}, +#endif /* IUCLC */ +#ifdef IXON + {"ixon", IXON, MD_INP}, +#endif /* IXON */ +#ifdef IXANY + {"ixany", IXANY, MD_INP}, +#endif /* IXANY */ +#ifdef IXOFF + {"ixoff", IXOFF, MD_INP}, +#endif /* IXOFF */ +#ifdef IMAXBEL + {"imaxbel", IMAXBEL, MD_INP}, +#endif /* IMAXBEL */ + +#ifdef OPOST + {"opost", OPOST, MD_OUT}, +#endif /* OPOST */ +#ifdef OLCUC + {"olcuc", OLCUC, MD_OUT}, +#endif /* OLCUC */ +#ifdef ONLCR + {"onlcr", ONLCR, MD_OUT}, +#endif /* ONLCR */ +#ifdef OCRNL + {"ocrnl", OCRNL, MD_OUT}, +#endif /* OCRNL */ +#ifdef ONOCR + {"onocr", ONOCR, MD_OUT}, +#endif /* ONOCR */ +#ifdef ONOEOT + {"onoeot", ONOEOT, MD_OUT}, +#endif /* ONOEOT */ +#ifdef ONLRET + {"onlret", ONLRET, MD_OUT}, +#endif /* ONLRET */ +#ifdef OFILL + {"ofill", OFILL, MD_OUT}, +#endif /* OFILL */ +#ifdef OFDEL + {"ofdel", OFDEL, MD_OUT}, +#endif /* OFDEL */ +#ifdef NLDLY + {"nldly", NLDLY, MD_OUT}, +#endif /* NLDLY */ +#ifdef CRDLY + {"crdly", CRDLY, MD_OUT}, +#endif /* CRDLY */ +#ifdef TABDLY + {"tabdly", TABDLY, MD_OUT}, +#endif /* TABDLY */ +#ifdef XTABS + {"xtabs", XTABS, MD_OUT}, +#endif /* XTABS */ +#ifdef BSDLY + {"bsdly", BSDLY, MD_OUT}, +#endif /* BSDLY */ +#ifdef VTDLY + {"vtdly", VTDLY, MD_OUT}, +#endif /* VTDLY */ +#ifdef FFDLY + {"ffdly", FFDLY, MD_OUT}, +#endif /* FFDLY */ +#ifdef PAGEOUT + {"pageout", PAGEOUT, MD_OUT}, +#endif /* PAGEOUT */ +#ifdef WRAP + {"wrap", WRAP, MD_OUT}, +#endif /* WRAP */ + +#ifdef CIGNORE + {"cignore", CIGNORE, MD_CTL}, +#endif /* CBAUD */ +#ifdef CBAUD + {"cbaud", CBAUD, MD_CTL}, +#endif /* CBAUD */ +#ifdef CSTOPB + {"cstopb", CSTOPB, MD_CTL}, +#endif /* CSTOPB */ +#ifdef CREAD + {"cread", CREAD, MD_CTL}, +#endif /* CREAD */ +#ifdef PARENB + {"parenb", PARENB, MD_CTL}, +#endif /* PARENB */ +#ifdef PARODD + {"parodd", PARODD, MD_CTL}, +#endif /* PARODD */ +#ifdef HUPCL + {"hupcl", HUPCL, MD_CTL}, +#endif /* HUPCL */ +#ifdef CLOCAL + {"clocal", CLOCAL, MD_CTL}, +#endif /* CLOCAL */ +#ifdef LOBLK + {"loblk", LOBLK, MD_CTL}, +#endif /* LOBLK */ +#ifdef CIBAUD + {"cibaud", CIBAUD, MD_CTL}, +#endif /* CIBAUD */ +#ifdef CRTSCTS +#ifdef CCTS_OFLOW + {"ccts_oflow", CCTS_OFLOW, MD_CTL}, +#else + {"crtscts", CRTSCTS, MD_CTL}, +#endif /* CCTS_OFLOW */ +#endif /* CRTSCTS */ +#ifdef CRTS_IFLOW + {"crts_iflow", CRTS_IFLOW, MD_CTL}, +#endif /* CRTS_IFLOW */ +#ifdef CDTRCTS + {"cdtrcts", CDTRCTS, MD_CTL}, +#endif /* CDTRCTS */ +#ifdef MDMBUF + {"mdmbuf", MDMBUF, MD_CTL}, +#endif /* MDMBUF */ +#ifdef RCV1EN + {"rcv1en", RCV1EN, MD_CTL}, +#endif /* RCV1EN */ +#ifdef XMT1EN + {"xmt1en", XMT1EN, MD_CTL}, +#endif /* XMT1EN */ + +#ifdef ISIG + {"isig", ISIG, MD_LIN}, +#endif /* ISIG */ +#ifdef ICANON + {"icanon", ICANON, MD_LIN}, +#endif /* ICANON */ +#ifdef XCASE + {"xcase", XCASE, MD_LIN}, +#endif /* XCASE */ +#ifdef ECHO + {"echo", ECHO, MD_LIN}, +#endif /* ECHO */ +#ifdef ECHOE + {"echoe", ECHOE, MD_LIN}, +#endif /* ECHOE */ +#ifdef ECHOK + {"echok", ECHOK, MD_LIN}, +#endif /* ECHOK */ +#ifdef ECHONL + {"echonl", ECHONL, MD_LIN}, +#endif /* ECHONL */ +#ifdef NOFLSH + {"noflsh", NOFLSH, MD_LIN}, +#endif /* NOFLSH */ +#ifdef TOSTOP + {"tostop", TOSTOP, MD_LIN}, +#endif /* TOSTOP */ +#ifdef ECHOCTL + {"echoctl", ECHOCTL, MD_LIN}, +#endif /* ECHOCTL */ +#ifdef ECHOPRT + {"echoprt", ECHOPRT, MD_LIN}, +#endif /* ECHOPRT */ +#ifdef ECHOKE + {"echoke", ECHOKE, MD_LIN}, +#endif /* ECHOKE */ +#ifdef DEFECHO + {"defecho", DEFECHO, MD_LIN}, +#endif /* DEFECHO */ +#ifdef FLUSHO + {"flusho", FLUSHO, MD_LIN}, +#endif /* FLUSHO */ +#ifdef PENDIN + {"pendin", PENDIN, MD_LIN}, +#endif /* PENDIN */ +#ifdef IEXTEN + {"iexten", IEXTEN, MD_LIN}, +#endif /* IEXTEN */ +#ifdef NOKERNINFO + {"nokerninfo", NOKERNINFO, MD_LIN}, +#endif /* NOKERNINFO */ +#ifdef ALTWERASE + {"altwerase", ALTWERASE, MD_LIN}, +#endif /* ALTWERASE */ +#ifdef EXTPROC + {"extproc", EXTPROC, MD_LIN}, +#endif /* EXTPROC */ + +#if defined(VINTR) + {"intr", C_SH(C_INTR), MD_CHAR}, +#endif /* VINTR */ +#if defined(VQUIT) + {"quit", C_SH(C_QUIT), MD_CHAR}, +#endif /* VQUIT */ +#if defined(VERASE) + {"erase", C_SH(C_ERASE), MD_CHAR}, +#endif /* VERASE */ +#if defined(VKILL) + {"kill", C_SH(C_KILL), MD_CHAR}, +#endif /* VKILL */ +#if defined(VEOF) + {"eof", C_SH(C_EOF), MD_CHAR}, +#endif /* VEOF */ +#if defined(VEOL) + {"eol", C_SH(C_EOL), MD_CHAR}, +#endif /* VEOL */ +#if defined(VEOL2) + {"eol2", C_SH(C_EOL2), MD_CHAR}, +#endif /* VEOL2 */ +#if defined(VSWTCH) + {"swtch", C_SH(C_SWTCH), MD_CHAR}, +#endif /* VSWTCH */ +#if defined(VDSWTCH) + {"dswtch", C_SH(C_DSWTCH), MD_CHAR}, +#endif /* VDSWTCH */ +#if defined(VERASE2) + {"erase2", C_SH(C_ERASE2), MD_CHAR}, +#endif /* VERASE2 */ +#if defined(VSTART) + {"start", C_SH(C_START), MD_CHAR}, +#endif /* VSTART */ +#if defined(VSTOP) + {"stop", C_SH(C_STOP), MD_CHAR}, +#endif /* VSTOP */ +#if defined(VWERASE) + {"werase", C_SH(C_WERASE), MD_CHAR}, +#endif /* VWERASE */ +#if defined(VSUSP) + {"susp", C_SH(C_SUSP), MD_CHAR}, +#endif /* VSUSP */ +#if defined(VDSUSP) + {"dsusp", C_SH(C_DSUSP), MD_CHAR}, +#endif /* VDSUSP */ +#if defined(VREPRINT) + {"reprint", C_SH(C_REPRINT), MD_CHAR}, +#endif /* VREPRINT */ +#if defined(VDISCARD) + {"discard", C_SH(C_DISCARD), MD_CHAR}, +#endif /* VDISCARD */ +#if defined(VLNEXT) + {"lnext", C_SH(C_LNEXT), MD_CHAR}, +#endif /* VLNEXT */ +#if defined(VSTATUS) + {"status", C_SH(C_STATUS), MD_CHAR}, +#endif /* VSTATUS */ +#if defined(VPAGE) + {"page", C_SH(C_PAGE), MD_CHAR}, +#endif /* VPAGE */ +#if defined(VPGOFF) + {"pgoff", C_SH(C_PGOFF), MD_CHAR}, +#endif /* VPGOFF */ +#if defined(VKILL2) + {"kill2", C_SH(C_KILL2), MD_CHAR}, +#endif /* VKILL2 */ +#if defined(VBRK) + {"brk", C_SH(C_BRK), MD_CHAR}, +#endif /* VBRK */ +#if defined(VMIN) + {"min", C_SH(C_MIN), MD_CHAR}, +#endif /* VMIN */ +#if defined(VTIME) + {"time", C_SH(C_TIME), MD_CHAR}, +#endif /* VTIME */ + {NULL, 0, -1}, +}; + + + +#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 void tty__getchar(struct termios *, unsigned char *); +private void tty__setchar(struct termios *, unsigned char *); +private speed_t tty__getspeed(struct termios *); +private int tty_setup(EditLine *); + +#define t_qu t_ts + + +/* tty_setup(): + * Get the tty parameters and initialize the editing state + */ +private int +tty_setup(EditLine *el) +{ + int rst = 1; + + if (el->el_flags & EDIT_DISABLED) + return (0); + + if (tty_getty(el, &el->el_tty.t_ed) == -1) { +#ifdef DEBUG_TTY + (void) fprintf(el->el_errfile, + "tty_setup: tty_getty: %s\n", strerror(errno)); +#endif /* DEBUG_TTY */ + return (-1); + } + el->el_tty.t_ts = el->el_tty.t_ex = el->el_tty.t_ed; + + el->el_tty.t_speed = tty__getspeed(&el->el_tty.t_ex); + el->el_tty.t_tabs = tty__gettabs(&el->el_tty.t_ex); + el->el_tty.t_eight = tty__geteightbit(&el->el_tty.t_ex); + + el->el_tty.t_ex.c_iflag &= ~el->el_tty.t_t[EX_IO][MD_INP].t_clrmask; + el->el_tty.t_ex.c_iflag |= el->el_tty.t_t[EX_IO][MD_INP].t_setmask; + + el->el_tty.t_ex.c_oflag &= ~el->el_tty.t_t[EX_IO][MD_OUT].t_clrmask; + el->el_tty.t_ex.c_oflag |= el->el_tty.t_t[EX_IO][MD_OUT].t_setmask; + + el->el_tty.t_ex.c_cflag &= ~el->el_tty.t_t[EX_IO][MD_CTL].t_clrmask; + el->el_tty.t_ex.c_cflag |= el->el_tty.t_t[EX_IO][MD_CTL].t_setmask; + + el->el_tty.t_ex.c_lflag &= ~el->el_tty.t_t[EX_IO][MD_LIN].t_clrmask; + el->el_tty.t_ex.c_lflag |= el->el_tty.t_t[EX_IO][MD_LIN].t_setmask; + + /* + * Reset the tty chars to reasonable defaults + * If they are disabled, then enable them. + */ + if (rst) { + if (tty__cooked_mode(&el->el_tty.t_ts)) { + tty__getchar(&el->el_tty.t_ts, el->el_tty.t_c[TS_IO]); + /* + * Don't affect CMIN and CTIME for the editor mode + */ + for (rst = 0; rst < C_NCC - 2; rst++) + if (el->el_tty.t_c[TS_IO][rst] != + el->el_tty.t_vdisable + && el->el_tty.t_c[ED_IO][rst] != + el->el_tty.t_vdisable) + el->el_tty.t_c[ED_IO][rst] = + el->el_tty.t_c[TS_IO][rst]; + for (rst = 0; rst < C_NCC; rst++) + if (el->el_tty.t_c[TS_IO][rst] != + el->el_tty.t_vdisable) + el->el_tty.t_c[EX_IO][rst] = + 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) { +#ifdef DEBUG_TTY + (void) fprintf(el->el_errfile, + "tty_setup: tty_setty: %s\n", + strerror(errno)); +#endif /* DEBUG_TTY */ + return (-1); + } + } else + tty__setchar(&el->el_tty.t_ex, el->el_tty.t_c[EX_IO]); + + 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; + + el->el_tty.t_ed.c_oflag &= ~el->el_tty.t_t[ED_IO][MD_OUT].t_clrmask; + el->el_tty.t_ed.c_oflag |= el->el_tty.t_t[ED_IO][MD_OUT].t_setmask; + + el->el_tty.t_ed.c_cflag &= ~el->el_tty.t_t[ED_IO][MD_CTL].t_clrmask; + el->el_tty.t_ed.c_cflag |= el->el_tty.t_t[ED_IO][MD_CTL].t_setmask; + + el->el_tty.t_ed.c_lflag &= ~el->el_tty.t_t[ED_IO][MD_LIN].t_clrmask; + el->el_tty.t_ed.c_lflag |= el->el_tty.t_t[ED_IO][MD_LIN].t_setmask; + + tty__setchar(&el->el_tty.t_ed, el->el_tty.t_c[ED_IO]); + tty_bind_char(el, 1); + return (0); +} + +protected int +tty_init(EditLine *el) +{ + + el->el_tty.t_mode = EX_IO; + el->el_tty.t_vdisable = _POSIX_VDISABLE; + (void) memcpy(el->el_tty.t_t, ttyperm, sizeof(ttyperm_t)); + (void) memcpy(el->el_tty.t_c, ttychar, sizeof(ttychar_t)); + return (tty_setup(el)); +} + + +/* tty_end(): + * Restore the tty to its original settings + */ +protected void +/*ARGSUSED*/ +tty_end(EditLine *el __attribute__((unused))) +{ + + /* XXX: Maybe reset to an initial state? */ +} + + +/* tty__getspeed(): + * Get the tty speed + */ +private speed_t +tty__getspeed(struct termios *td) +{ + speed_t spd; + + if ((spd = cfgetispeed(td)) == 0) + spd = cfgetospeed(td); + return (spd); +} + + +/* tty__getchar(): + * Get the tty characters + */ +private void +tty__getchar(struct termios *td, unsigned char *s) +{ + +#ifdef VINTR + s[C_INTR] = td->c_cc[VINTR]; +#endif /* VINTR */ +#ifdef VQUIT + s[C_QUIT] = td->c_cc[VQUIT]; +#endif /* VQUIT */ +#ifdef VERASE + s[C_ERASE] = td->c_cc[VERASE]; +#endif /* VERASE */ +#ifdef VKILL + s[C_KILL] = td->c_cc[VKILL]; +#endif /* VKILL */ +#ifdef VEOF + s[C_EOF] = td->c_cc[VEOF]; +#endif /* VEOF */ +#ifdef VEOL + s[C_EOL] = td->c_cc[VEOL]; +#endif /* VEOL */ +#ifdef VEOL2 + s[C_EOL2] = td->c_cc[VEOL2]; +#endif /* VEOL2 */ +#ifdef VSWTCH + s[C_SWTCH] = td->c_cc[VSWTCH]; +#endif /* VSWTCH */ +#ifdef VDSWTCH + s[C_DSWTCH] = td->c_cc[VDSWTCH]; +#endif /* VDSWTCH */ +#ifdef VERASE2 + s[C_ERASE2] = td->c_cc[VERASE2]; +#endif /* VERASE2 */ +#ifdef VSTART + s[C_START] = td->c_cc[VSTART]; +#endif /* VSTART */ +#ifdef VSTOP + s[C_STOP] = td->c_cc[VSTOP]; +#endif /* VSTOP */ +#ifdef VWERASE + s[C_WERASE] = td->c_cc[VWERASE]; +#endif /* VWERASE */ +#ifdef VSUSP + s[C_SUSP] = td->c_cc[VSUSP]; +#endif /* VSUSP */ +#ifdef VDSUSP + s[C_DSUSP] = td->c_cc[VDSUSP]; +#endif /* VDSUSP */ +#ifdef VREPRINT + s[C_REPRINT] = td->c_cc[VREPRINT]; +#endif /* VREPRINT */ +#ifdef VDISCARD + s[C_DISCARD] = td->c_cc[VDISCARD]; +#endif /* VDISCARD */ +#ifdef VLNEXT + s[C_LNEXT] = td->c_cc[VLNEXT]; +#endif /* VLNEXT */ +#ifdef VSTATUS + s[C_STATUS] = td->c_cc[VSTATUS]; +#endif /* VSTATUS */ +#ifdef VPAGE + s[C_PAGE] = td->c_cc[VPAGE]; +#endif /* VPAGE */ +#ifdef VPGOFF + s[C_PGOFF] = td->c_cc[VPGOFF]; +#endif /* VPGOFF */ +#ifdef VKILL2 + s[C_KILL2] = td->c_cc[VKILL2]; +#endif /* KILL2 */ +#ifdef VMIN + s[C_MIN] = td->c_cc[VMIN]; +#endif /* VMIN */ +#ifdef VTIME + s[C_TIME] = td->c_cc[VTIME]; +#endif /* VTIME */ +} /* tty__getchar */ + + +/* tty__setchar(): + * Set the tty characters + */ +private void +tty__setchar(struct termios *td, unsigned char *s) +{ + +#ifdef VINTR + td->c_cc[VINTR] = s[C_INTR]; +#endif /* VINTR */ +#ifdef VQUIT + td->c_cc[VQUIT] = s[C_QUIT]; +#endif /* VQUIT */ +#ifdef VERASE + td->c_cc[VERASE] = s[C_ERASE]; +#endif /* VERASE */ +#ifdef VKILL + td->c_cc[VKILL] = s[C_KILL]; +#endif /* VKILL */ +#ifdef VEOF + td->c_cc[VEOF] = s[C_EOF]; +#endif /* VEOF */ +#ifdef VEOL + td->c_cc[VEOL] = s[C_EOL]; +#endif /* VEOL */ +#ifdef VEOL2 + td->c_cc[VEOL2] = s[C_EOL2]; +#endif /* VEOL2 */ +#ifdef VSWTCH + td->c_cc[VSWTCH] = s[C_SWTCH]; +#endif /* VSWTCH */ +#ifdef VDSWTCH + td->c_cc[VDSWTCH] = s[C_DSWTCH]; +#endif /* VDSWTCH */ +#ifdef VERASE2 + td->c_cc[VERASE2] = s[C_ERASE2]; +#endif /* VERASE2 */ +#ifdef VSTART + td->c_cc[VSTART] = s[C_START]; +#endif /* VSTART */ +#ifdef VSTOP + td->c_cc[VSTOP] = s[C_STOP]; +#endif /* VSTOP */ +#ifdef VWERASE + td->c_cc[VWERASE] = s[C_WERASE]; +#endif /* VWERASE */ +#ifdef VSUSP + td->c_cc[VSUSP] = s[C_SUSP]; +#endif /* VSUSP */ +#ifdef VDSUSP + td->c_cc[VDSUSP] = s[C_DSUSP]; +#endif /* VDSUSP */ +#ifdef VREPRINT + td->c_cc[VREPRINT] = s[C_REPRINT]; +#endif /* VREPRINT */ +#ifdef VDISCARD + td->c_cc[VDISCARD] = s[C_DISCARD]; +#endif /* VDISCARD */ +#ifdef VLNEXT + td->c_cc[VLNEXT] = s[C_LNEXT]; +#endif /* VLNEXT */ +#ifdef VSTATUS + td->c_cc[VSTATUS] = s[C_STATUS]; +#endif /* VSTATUS */ +#ifdef VPAGE + td->c_cc[VPAGE] = s[C_PAGE]; +#endif /* VPAGE */ +#ifdef VPGOFF + td->c_cc[VPGOFF] = s[C_PGOFF]; +#endif /* VPGOFF */ +#ifdef VKILL2 + td->c_cc[VKILL2] = s[C_KILL2]; +#endif /* VKILL2 */ +#ifdef VMIN + td->c_cc[VMIN] = s[C_MIN]; +#endif /* VMIN */ +#ifdef VTIME + td->c_cc[VTIME] = s[C_TIME]; +#endif /* VTIME */ +} /* tty__setchar */ + + +/* tty_bind_char(): + * Rebind the editline functions + */ +protected void +tty_bind_char(EditLine *el, int force) +{ + + unsigned char *t_n = el->el_tty.t_c[ED_IO]; + unsigned char *t_o = el->el_tty.t_ed.c_cc; + unsigned char new[2], old[2]; + const ttymap_t *tp; + el_action_t *map, *alt; + const el_action_t *dmap, *dalt; + new[1] = old[1] = '\0'; + + map = el->el_map.key; + alt = el->el_map.alt; + if (el->el_map.type == MAP_VI) { + dmap = el->el_map.vii; + dalt = el->el_map.vic; + } else { + dmap = el->el_map.emacs; + dalt = NULL; + } + + for (tp = tty_map; tp->nch != -1; tp++) { + new[0] = t_n[tp->nch]; + old[0] = t_o[tp->och]; + if (new[0] == old[0] && !force) + continue; + /* Put the old default binding back, and set the new binding */ + key_clear(el, map, (char *)old); + map[old[0]] = dmap[old[0]]; + key_clear(el, map, (char *)new); + /* MAP_VI == 1, MAP_EMACS == 0... */ + map[new[0]] = tp->bind[el->el_map.type]; + if (dalt) { + key_clear(el, alt, (char *)old); + alt[old[0]] = dalt[old[0]]; + key_clear(el, alt, (char *)new); + alt[new[0]] = tp->bind[el->el_map.type + 1]; + } + } +} + + +/* tty_rawmode(): + * Set terminal into 1 character at a time mode. + */ +protected int +tty_rawmode(EditLine *el) +{ + + if (el->el_tty.t_mode == ED_IO || el->el_tty.t_mode == QU_IO) + return (0); + + if (el->el_flags & EDIT_DISABLED) + return (0); + + if (tty_getty(el, &el->el_tty.t_ts) == -1) { +#ifdef DEBUG_TTY + (void) fprintf(el->el_errfile, "tty_rawmode: tty_getty: %s\n", + strerror(errno)); +#endif /* DEBUG_TTY */ + return (-1); + } + /* + * We always keep up with the eight bit setting and the speed of the + * tty. But only we only believe changes that are made to cooked mode! + */ + el->el_tty.t_eight = tty__geteightbit(&el->el_tty.t_ts); + el->el_tty.t_speed = tty__getspeed(&el->el_tty.t_ts); + + if (tty__getspeed(&el->el_tty.t_ex) != el->el_tty.t_speed || + tty__getspeed(&el->el_tty.t_ed) != el->el_tty.t_speed) { + (void) cfsetispeed(&el->el_tty.t_ex, el->el_tty.t_speed); + (void) cfsetospeed(&el->el_tty.t_ex, el->el_tty.t_speed); + (void) cfsetispeed(&el->el_tty.t_ed, el->el_tty.t_speed); + (void) cfsetospeed(&el->el_tty.t_ed, el->el_tty.t_speed); + } + if (tty__cooked_mode(&el->el_tty.t_ts)) { + if (el->el_tty.t_ts.c_cflag != el->el_tty.t_ex.c_cflag) { + el->el_tty.t_ex.c_cflag = + el->el_tty.t_ts.c_cflag; + el->el_tty.t_ex.c_cflag &= + ~el->el_tty.t_t[EX_IO][MD_CTL].t_clrmask; + el->el_tty.t_ex.c_cflag |= + el->el_tty.t_t[EX_IO][MD_CTL].t_setmask; + + el->el_tty.t_ed.c_cflag = + el->el_tty.t_ts.c_cflag; + el->el_tty.t_ed.c_cflag &= + ~el->el_tty.t_t[ED_IO][MD_CTL].t_clrmask; + el->el_tty.t_ed.c_cflag |= + el->el_tty.t_t[ED_IO][MD_CTL].t_setmask; + } + if ((el->el_tty.t_ts.c_lflag != el->el_tty.t_ex.c_lflag) && + (el->el_tty.t_ts.c_lflag != el->el_tty.t_ed.c_lflag)) { + el->el_tty.t_ex.c_lflag = + el->el_tty.t_ts.c_lflag; + el->el_tty.t_ex.c_lflag &= + ~el->el_tty.t_t[EX_IO][MD_LIN].t_clrmask; + el->el_tty.t_ex.c_lflag |= + el->el_tty.t_t[EX_IO][MD_LIN].t_setmask; + + el->el_tty.t_ed.c_lflag = + el->el_tty.t_ts.c_lflag; + el->el_tty.t_ed.c_lflag &= + ~el->el_tty.t_t[ED_IO][MD_LIN].t_clrmask; + el->el_tty.t_ed.c_lflag |= + el->el_tty.t_t[ED_IO][MD_LIN].t_setmask; + } + if ((el->el_tty.t_ts.c_iflag != el->el_tty.t_ex.c_iflag) && + (el->el_tty.t_ts.c_iflag != el->el_tty.t_ed.c_iflag)) { + el->el_tty.t_ex.c_iflag = + el->el_tty.t_ts.c_iflag; + el->el_tty.t_ex.c_iflag &= + ~el->el_tty.t_t[EX_IO][MD_INP].t_clrmask; + el->el_tty.t_ex.c_iflag |= + el->el_tty.t_t[EX_IO][MD_INP].t_setmask; + + el->el_tty.t_ed.c_iflag = + el->el_tty.t_ts.c_iflag; + 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; + } + if ((el->el_tty.t_ts.c_oflag != el->el_tty.t_ex.c_oflag) && + (el->el_tty.t_ts.c_oflag != el->el_tty.t_ed.c_oflag)) { + el->el_tty.t_ex.c_oflag = + el->el_tty.t_ts.c_oflag; + el->el_tty.t_ex.c_oflag &= + ~el->el_tty.t_t[EX_IO][MD_OUT].t_clrmask; + el->el_tty.t_ex.c_oflag |= + el->el_tty.t_t[EX_IO][MD_OUT].t_setmask; + + el->el_tty.t_ed.c_oflag = + el->el_tty.t_ts.c_oflag; + el->el_tty.t_ed.c_oflag &= + ~el->el_tty.t_t[ED_IO][MD_OUT].t_clrmask; + el->el_tty.t_ed.c_oflag |= + el->el_tty.t_t[ED_IO][MD_OUT].t_setmask; + } + if (tty__gettabs(&el->el_tty.t_ex) == 0) + el->el_tty.t_tabs = 0; + else + el->el_tty.t_tabs = EL_CAN_TAB ? 1 : 0; + + { + int i; + + tty__getchar(&el->el_tty.t_ts, el->el_tty.t_c[TS_IO]); + /* + * Check if the user made any changes. + * If he did, then propagate the changes to the + * edit and execute data structures. + */ + for (i = 0; i < C_NCC; i++) + if (el->el_tty.t_c[TS_IO][i] != + el->el_tty.t_c[EX_IO][i]) + break; + + if (i != C_NCC) { + /* + * Propagate changes only to the unprotected + * chars that have been modified just now. + */ + for (i = 0; i < C_NCC; i++) { + if (!((el->el_tty.t_t[ED_IO][MD_CHAR].t_setmask & C_SH(i))) + && (el->el_tty.t_c[TS_IO][i] != el->el_tty.t_c[EX_IO][i])) + el->el_tty.t_c[ED_IO][i] = el->el_tty.t_c[TS_IO][i]; + if (el->el_tty.t_t[ED_IO][MD_CHAR].t_clrmask & C_SH(i)) + el->el_tty.t_c[ED_IO][i] = el->el_tty.t_vdisable; + } + tty_bind_char(el, 0); + tty__setchar(&el->el_tty.t_ed, el->el_tty.t_c[ED_IO]); + + for (i = 0; i < C_NCC; i++) { + if (!((el->el_tty.t_t[EX_IO][MD_CHAR].t_setmask & C_SH(i))) + && (el->el_tty.t_c[TS_IO][i] != el->el_tty.t_c[EX_IO][i])) + el->el_tty.t_c[EX_IO][i] = el->el_tty.t_c[TS_IO][i]; + if (el->el_tty.t_t[EX_IO][MD_CHAR].t_clrmask & C_SH(i)) + el->el_tty.t_c[EX_IO][i] = el->el_tty.t_vdisable; + } + tty__setchar(&el->el_tty.t_ex, el->el_tty.t_c[EX_IO]); + } + } + } + if (tty_setty(el, &el->el_tty.t_ed) == -1) { +#ifdef DEBUG_TTY + (void) fprintf(el->el_errfile, "tty_rawmode: tty_setty: %s\n", + strerror(errno)); +#endif /* DEBUG_TTY */ + return (-1); + } + el->el_tty.t_mode = ED_IO; + return (0); +} + + +/* tty_cookedmode(): + * Set the tty back to normal mode + */ +protected int +tty_cookedmode(EditLine *el) +{ /* set tty in normal setup */ + + if (el->el_tty.t_mode == EX_IO) + return (0); + + if (el->el_flags & EDIT_DISABLED) + return (0); + + if (tty_setty(el, &el->el_tty.t_ex) == -1) { +#ifdef DEBUG_TTY + (void) fprintf(el->el_errfile, + "tty_cookedmode: tty_setty: %s\n", + strerror(errno)); +#endif /* DEBUG_TTY */ + return (-1); + } + el->el_tty.t_mode = EX_IO; + return (0); +} + + +/* tty_quotemode(): + * Turn on quote mode + */ +protected int +tty_quotemode(EditLine *el) +{ + if (el->el_tty.t_mode == QU_IO) + return (0); + + el->el_tty.t_qu = el->el_tty.t_ed; + + el->el_tty.t_qu.c_iflag &= ~el->el_tty.t_t[QU_IO][MD_INP].t_clrmask; + el->el_tty.t_qu.c_iflag |= el->el_tty.t_t[QU_IO][MD_INP].t_setmask; + + el->el_tty.t_qu.c_oflag &= ~el->el_tty.t_t[QU_IO][MD_OUT].t_clrmask; + el->el_tty.t_qu.c_oflag |= el->el_tty.t_t[QU_IO][MD_OUT].t_setmask; + + el->el_tty.t_qu.c_cflag &= ~el->el_tty.t_t[QU_IO][MD_CTL].t_clrmask; + el->el_tty.t_qu.c_cflag |= el->el_tty.t_t[QU_IO][MD_CTL].t_setmask; + + 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) { +#ifdef DEBUG_TTY + (void) fprintf(el->el_errfile, "QuoteModeOn: tty_setty: %s\n", + strerror(errno)); +#endif /* DEBUG_TTY */ + return (-1); + } + el->el_tty.t_mode = QU_IO; + return (0); +} + + +/* tty_noquotemode(): + * Turn off quote mode + */ +protected int +tty_noquotemode(EditLine *el) +{ + + if (el->el_tty.t_mode != QU_IO) + return (0); + if (tty_setty(el, &el->el_tty.t_ed) == -1) { +#ifdef DEBUG_TTY + (void) fprintf(el->el_errfile, "QuoteModeOff: tty_setty: %s\n", + strerror(errno)); +#endif /* DEBUG_TTY */ + return (-1); + } + el->el_tty.t_mode = ED_IO; + return (0); +} + + +/* tty_stty(): + * Stty builtin + */ +protected int +/*ARGSUSED*/ +tty_stty(EditLine *el, int argc __attribute__((unused)), const char **argv) +{ + const ttymodes_t *m; + char x; + const char *d; + int aflag = 0; + const char *s; + const char *name; + int z = EX_IO; + + if (argv == NULL) + return (-1); + name = *argv++; + + while (argv && *argv && argv[0][0] == '-' && argv[0][2] == '\0') + switch (argv[0][1]) { + case 'a': + aflag++; + argv++; + break; + case 'd': + argv++; + z = ED_IO; + break; + case 'x': + argv++; + z = EX_IO; + break; + case 'q': + argv++; + z = QU_IO; + break; + default: + (void) fprintf(el->el_errfile, + "%s: Unknown switch `%c'.\n", + name, argv[0][1]); + return (-1); + } + + if (!argv || !*argv) { + int i = -1; + int len = 0, st = 0, cu; + for (m = ttymodes; m->m_name; m++) { + if (m->m_type != i) { + (void) fprintf(el->el_outfile, "%s%s", + i != -1 ? "\n" : "", + el->el_tty.t_t[z][m->m_type].t_name); + i = m->m_type; + 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 (x != '\0' || aflag) { + + cu = strlen(m->m_name) + (x != '\0') + 1; + + if (len + cu >= el->el_term.t_size.h) { + (void) fprintf(el->el_outfile, "\n%*s", + st, ""); + len = st + cu; + } else + len += cu; + + if (x != '\0') + (void) fprintf(el->el_outfile, "%c%s ", + x, m->m_name); + else + (void) fprintf(el->el_outfile, "%s ", + m->m_name); + } + } + (void) fprintf(el->el_outfile, "\n"); + return (0); + } + while (argv && (s = *argv++)) { + switch (*s) { + case '+': + case '-': + x = *s++; + break; + default: + x = '\0'; + break; + } + d = s; + for (m = ttymodes; m->m_name; m++) + if (strcmp(m->m_name, d) == 0) + break; + + if (!m->m_name) { + (void) fprintf(el->el_errfile, + "%s: Invalid argument `%s'.\n", name, d); + return (-1); + } + switch (x) { + case '+': + el->el_tty.t_t[z][m->m_type].t_setmask |= m->m_value; + el->el_tty.t_t[z][m->m_type].t_clrmask &= ~m->m_value; + break; + case '-': + el->el_tty.t_t[z][m->m_type].t_setmask &= ~m->m_value; + el->el_tty.t_t[z][m->m_type].t_clrmask |= m->m_value; + break; + default: + el->el_tty.t_t[z][m->m_type].t_setmask &= ~m->m_value; + el->el_tty.t_t[z][m->m_type].t_clrmask &= ~m->m_value; + break; + } + } + return (0); +} + + +#ifdef notyet +/* tty_printchar(): + * DEbugging routine to print the tty characters + */ +private void +tty_printchar(EditLine *el, unsigned char *s) +{ + ttyperm_t *m; + int i; + + for (i = 0; i < C_NCC; i++) { + for (m = el->el_tty.t_t; m->m_name; m++) + if (m->m_type == MD_CHAR && C_SH(i) == m->m_value) + break; + if (m->m_name) + (void) fprintf(el->el_errfile, "%s ^%c ", + m->m_name, s[i] + 'A' - 1); + if (i % 5 == 0) + (void) fprintf(el->el_errfile, "\n"); + } + (void) fprintf(el->el_errfile, "\n"); +} +#endif /* notyet */ diff --git a/cmd-line-utils/libedit/tty.h b/cmd-line-utils/libedit/tty.h new file mode 100644 index 00000000000..5fdcfadf0dc --- /dev/null +++ b/cmd-line-utils/libedit/tty.h @@ -0,0 +1,484 @@ +/* $NetBSD: tty.h,v 1.8 2000/09/04 22:06:33 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. + * + * @(#)tty.h 8.1 (Berkeley) 6/4/93 + */ + +/* + * el.tty.h: Local terminal header + */ +#ifndef _h_el_tty +#define _h_el_tty + +#include "histedit.h" +#include +#include + +/* Define our own since everyone gets it wrong! */ +#define CONTROL(A) ((A) & 037) + +/* + * Aix compatible names + */ +# if defined(VWERSE) && !defined(VWERASE) +# define VWERASE VWERSE +# endif /* VWERSE && !VWERASE */ + +# if defined(VDISCRD) && !defined(VDISCARD) +# define VDISCARD VDISCRD +# endif /* VDISCRD && !VDISCARD */ + +# if defined(VFLUSHO) && !defined(VDISCARD) +# define VDISCARD VFLUSHO +# endif /* VFLUSHO && VDISCARD */ + +# if defined(VSTRT) && !defined(VSTART) +# define VSTART VSTRT +# endif /* VSTRT && ! VSTART */ + +# if defined(VSTAT) && !defined(VSTATUS) +# define VSTATUS VSTAT +# endif /* VSTAT && ! VSTATUS */ + +# ifndef ONLRET +# define ONLRET 0 +# endif /* ONLRET */ + +# ifndef TAB3 +# ifdef OXTABS +# define TAB3 OXTABS +# else +# define TAB3 0 +# endif /* OXTABS */ +# endif /* !TAB3 */ + +# if defined(OXTABS) && !defined(XTABS) +# define XTABS OXTABS +# endif /* OXTABS && !XTABS */ + +# ifndef ONLCR +# define ONLCR 0 +# endif /* ONLCR */ + +# ifndef IEXTEN +# define IEXTEN 0 +# endif /* IEXTEN */ + +# ifndef ECHOCTL +# define ECHOCTL 0 +# endif /* ECHOCTL */ + +# ifndef PARENB +# define PARENB 0 +# endif /* PARENB */ + +# ifndef EXTPROC +# define EXTPROC 0 +# endif /* EXTPROC */ + +# ifndef FLUSHO +# define FLUSHO 0 +# endif /* FLUSHO */ + + +# if defined(VDISABLE) && !defined(_POSIX_VDISABLE) +# define _POSIX_VDISABLE VDISABLE +# endif /* VDISABLE && ! _POSIX_VDISABLE */ + +/* + * Work around ISC's definition of IEXTEN which is + * XCASE! + */ +# ifdef ISC +# if defined(IEXTEN) && defined(XCASE) +# if IEXTEN == XCASE +# undef IEXTEN +# define IEXTEN 0 +# endif /* IEXTEN == XCASE */ +# endif /* IEXTEN && XCASE */ +# if defined(IEXTEN) && !defined(XCASE) +# define XCASE IEXTEN +# undef IEXTEN +# define IEXTEN 0 +# endif /* IEXTEN && !XCASE */ +# endif /* ISC */ + +/* + * Work around convex weirdness where turning off IEXTEN makes us + * lose all postprocessing! + */ +#if defined(convex) || defined(__convex__) +# if defined(IEXTEN) && IEXTEN != 0 +# undef IEXTEN +# define IEXTEN 0 +# endif /* IEXTEN != 0 */ +#endif /* convex || __convex__ */ + +/* + * So that we don't lose job control. + */ +#ifdef __SVR4 +# undef CSWTCH +#endif + +#ifndef _POSIX_VDISABLE +# define _POSIX_VDISABLE ((unsigned char) -1) +#endif /* _POSIX_VDISABLE */ + +#if !defined(CREPRINT) && defined(CRPRNT) +# define CREPRINT CRPRNT +#endif /* !CREPRINT && CRPRNT */ +#if !defined(CDISCARD) && defined(CFLUSH) +# define CDISCARD CFLUSH +#endif /* !CDISCARD && CFLUSH */ + +#ifndef CINTR +# define CINTR CONTROL('c') +#endif /* CINTR */ +#ifndef CQUIT +# define CQUIT 034 /* ^\ */ +#endif /* CQUIT */ +#ifndef CERASE +# define CERASE 0177 /* ^? */ +#endif /* CERASE */ +#ifndef CKILL +# define CKILL CONTROL('u') +#endif /* CKILL */ +#ifndef CEOF +# define CEOF CONTROL('d') +#endif /* CEOF */ +#ifndef CEOL +# define CEOL _POSIX_VDISABLE +#endif /* CEOL */ +#ifndef CEOL2 +# define CEOL2 _POSIX_VDISABLE +#endif /* CEOL2 */ +#ifndef CSWTCH +# define CSWTCH _POSIX_VDISABLE +#endif /* CSWTCH */ +#ifndef CDSWTCH +# define CDSWTCH _POSIX_VDISABLE +#endif /* CDSWTCH */ +#ifndef CERASE2 +# define CERASE2 _POSIX_VDISABLE +#endif /* CERASE2 */ +#ifndef CSTART +# define CSTART CONTROL('q') +#endif /* CSTART */ +#ifndef CSTOP +# define CSTOP CONTROL('s') +#endif /* CSTOP */ +#ifndef CSUSP +# define CSUSP CONTROL('z') +#endif /* CSUSP */ +#ifndef CDSUSP +# define CDSUSP CONTROL('y') +#endif /* CDSUSP */ + +#ifdef hpux + +# ifndef CREPRINT +# define CREPRINT _POSIX_VDISABLE +# endif /* CREPRINT */ +# ifndef CDISCARD +# define CDISCARD _POSIX_VDISABLE +# endif /* CDISCARD */ +# ifndef CLNEXT +# define CLNEXT _POSIX_VDISABLE +# endif /* CLNEXT */ +# ifndef CWERASE +# define CWERASE _POSIX_VDISABLE +# endif /* CWERASE */ + +#else /* !hpux */ + +# ifndef CREPRINT +# define CREPRINT CONTROL('r') +# endif /* CREPRINT */ +# ifndef CDISCARD +# define CDISCARD CONTROL('o') +# endif /* CDISCARD */ +# ifndef CLNEXT +# define CLNEXT CONTROL('v') +# endif /* CLNEXT */ +# ifndef CWERASE +# define CWERASE CONTROL('w') +# endif /* CWERASE */ + +#endif /* hpux */ + +#ifndef CSTATUS +# define CSTATUS CONTROL('t') +#endif /* CSTATUS */ +#ifndef CPAGE +# define CPAGE ' ' +#endif /* CPAGE */ +#ifndef CPGOFF +# define CPGOFF CONTROL('m') +#endif /* CPGOFF */ +#ifndef CKILL2 +# define CKILL2 _POSIX_VDISABLE +#endif /* CKILL2 */ +#ifndef CBRK +# ifndef masscomp +# define CBRK 0377 +# else +# define CBRK '\0' +# endif /* masscomp */ +#endif /* CBRK */ +#ifndef CMIN +# define CMIN CEOF +#endif /* CMIN */ +#ifndef CTIME +# define CTIME CEOL +#endif /* CTIME */ + +/* + * Fix for sun inconsistency. On termio VSUSP and the rest of the + * ttychars > NCC are defined. So we undefine them. + */ +#if defined(TERMIO) || defined(POSIX) +# if defined(POSIX) && defined(NCCS) +# define NUMCC NCCS +# else +# ifdef NCC +# define NUMCC NCC +# endif /* NCC */ +# endif /* POSIX && NCCS */ +# ifdef NUMCC +# ifdef VINTR +# if NUMCC <= VINTR +# undef VINTR +# endif /* NUMCC <= VINTR */ +# endif /* VINTR */ +# ifdef VQUIT +# if NUMCC <= VQUIT +# undef VQUIT +# endif /* NUMCC <= VQUIT */ +# endif /* VQUIT */ +# ifdef VERASE +# if NUMCC <= VERASE +# undef VERASE +# endif /* NUMCC <= VERASE */ +# endif /* VERASE */ +# ifdef VKILL +# if NUMCC <= VKILL +# undef VKILL +# endif /* NUMCC <= VKILL */ +# endif /* VKILL */ +# ifdef VEOF +# if NUMCC <= VEOF +# undef VEOF +# endif /* NUMCC <= VEOF */ +# endif /* VEOF */ +# ifdef VEOL +# if NUMCC <= VEOL +# undef VEOL +# endif /* NUMCC <= VEOL */ +# endif /* VEOL */ +# ifdef VEOL2 +# if NUMCC <= VEOL2 +# undef VEOL2 +# endif /* NUMCC <= VEOL2 */ +# endif /* VEOL2 */ +# ifdef VSWTCH +# if NUMCC <= VSWTCH +# undef VSWTCH +# endif /* NUMCC <= VSWTCH */ +# endif /* VSWTCH */ +# ifdef VDSWTCH +# if NUMCC <= VDSWTCH +# undef VDSWTCH +# endif /* NUMCC <= VDSWTCH */ +# endif /* VDSWTCH */ +# ifdef VERASE2 +# if NUMCC <= VERASE2 +# undef VERASE2 +# endif /* NUMCC <= VERASE2 */ +# endif /* VERASE2 */ +# ifdef VSTART +# if NUMCC <= VSTART +# undef VSTART +# endif /* NUMCC <= VSTART */ +# endif /* VSTART */ +# ifdef VSTOP +# if NUMCC <= VSTOP +# undef VSTOP +# endif /* NUMCC <= VSTOP */ +# endif /* VSTOP */ +# ifdef VWERASE +# if NUMCC <= VWERASE +# undef VWERASE +# endif /* NUMCC <= VWERASE */ +# endif /* VWERASE */ +# ifdef VSUSP +# if NUMCC <= VSUSP +# undef VSUSP +# endif /* NUMCC <= VSUSP */ +# endif /* VSUSP */ +# ifdef VDSUSP +# if NUMCC <= VDSUSP +# undef VDSUSP +# endif /* NUMCC <= VDSUSP */ +# endif /* VDSUSP */ +# ifdef VREPRINT +# if NUMCC <= VREPRINT +# undef VREPRINT +# endif /* NUMCC <= VREPRINT */ +# endif /* VREPRINT */ +# ifdef VDISCARD +# if NUMCC <= VDISCARD +# undef VDISCARD +# endif /* NUMCC <= VDISCARD */ +# endif /* VDISCARD */ +# ifdef VLNEXT +# if NUMCC <= VLNEXT +# undef VLNEXT +# endif /* NUMCC <= VLNEXT */ +# endif /* VLNEXT */ +# ifdef VSTATUS +# if NUMCC <= VSTATUS +# undef VSTATUS +# endif /* NUMCC <= VSTATUS */ +# endif /* VSTATUS */ +# ifdef VPAGE +# if NUMCC <= VPAGE +# undef VPAGE +# endif /* NUMCC <= VPAGE */ +# endif /* VPAGE */ +# ifdef VPGOFF +# if NUMCC <= VPGOFF +# undef VPGOFF +# endif /* NUMCC <= VPGOFF */ +# endif /* VPGOFF */ +# ifdef VKILL2 +# if NUMCC <= VKILL2 +# undef VKILL2 +# endif /* NUMCC <= VKILL2 */ +# endif /* VKILL2 */ +# ifdef VBRK +# if NUMCC <= VBRK +# undef VBRK +# endif /* NUMCC <= VBRK */ +# endif /* VBRK */ +# ifdef VMIN +# if NUMCC <= VMIN +# undef VMIN +# endif /* NUMCC <= VMIN */ +# endif /* VMIN */ +# ifdef VTIME +# if NUMCC <= VTIME +# undef VTIME +# endif /* NUMCC <= VTIME */ +# endif /* VTIME */ +# endif /* NUMCC */ +#endif /* !POSIX */ + +#define C_INTR 0 +#define C_QUIT 1 +#define C_ERASE 2 +#define C_KILL 3 +#define C_EOF 4 +#define C_EOL 5 +#define C_EOL2 6 +#define C_SWTCH 7 +#define C_DSWTCH 8 +#define C_ERASE2 9 +#define C_START 10 +#define C_STOP 11 +#define C_WERASE 12 +#define C_SUSP 13 +#define C_DSUSP 14 +#define C_REPRINT 15 +#define C_DISCARD 16 +#define C_LNEXT 17 +#define C_STATUS 18 +#define C_PAGE 19 +#define C_PGOFF 20 +#define C_KILL2 21 +#define C_BRK 22 +#define C_MIN 23 +#define C_TIME 24 +#define C_NCC 25 +#define C_SH(A) (1 << (A)) + +/* + * Terminal dependend data structures + */ +#define EX_IO 0 /* while we are executing */ +#define ED_IO 1 /* while we are editing */ +#define TS_IO 2 /* new mode from terminal */ +#define QU_IO 2 /* used only for quoted chars */ +#define NN_IO 3 /* The number of entries */ + +#define MD_INP 0 +#define MD_OUT 1 +#define MD_CTL 2 +#define MD_LIN 3 +#define MD_CHAR 4 +#define MD_NN 5 + +typedef struct { + const char *t_name; + u_int t_setmask; + u_int t_clrmask; +} ttyperm_t[NN_IO][MD_NN]; + +typedef unsigned char ttychar_t[NN_IO][C_NCC]; + +protected int tty_init(EditLine *); +protected void tty_end(EditLine *); +protected int tty_stty(EditLine *, int, const char**); +protected int tty_rawmode(EditLine *); +protected int tty_cookedmode(EditLine *); +protected int tty_quotemode(EditLine *); +protected int tty_noquotemode(EditLine *); +protected void tty_bind_char(EditLine *, int); + +typedef struct { + ttyperm_t t_t; + ttychar_t t_c; + struct termios t_ex, t_ed, t_ts; + int t_tabs; + int t_eight; + speed_t t_speed; + int t_mode; + unsigned char t_vdisable; +} el_tty_t; + + +#endif /* _h_el_tty */ diff --git a/cmd-line-utils/libedit/vi.c b/cmd-line-utils/libedit/vi.c new file mode 100644 index 00000000000..296e11eb4d9 --- /dev/null +++ b/cmd-line-utils/libedit/vi.c @@ -0,0 +1,935 @@ +/* $NetBSD: vi.c,v 1.8 2000/09/04 22:06:33 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 "compat.h" + +/* + * vi.c: Vi mode commands. + */ +#include "sys.h" +#include "el.h" + +private el_action_t cv_action(EditLine *, int); +private el_action_t cv_paste(EditLine *, int); + +/* cv_action(): + * Handle vi actions. + */ +private el_action_t +cv_action(EditLine *el, int c) +{ + char *cp, *kp; + + if (el->el_chared.c_vcmd.action & DELETE) { + el->el_chared.c_vcmd.action = NOP; + el->el_chared.c_vcmd.pos = 0; + + el->el_chared.c_undo.isize = 0; + el->el_chared.c_undo.dsize = 0; + kp = el->el_chared.c_undo.buf; + for (cp = el->el_line.buffer; cp < el->el_line.lastchar; cp++) { + *kp++ = *cp; + el->el_chared.c_undo.dsize++; + } + + el->el_chared.c_undo.action = INSERT; + el->el_chared.c_undo.ptr = el->el_line.buffer; + 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; + + return (CC_REFRESH); + } + el->el_chared.c_vcmd.pos = el->el_line.cursor; + el->el_chared.c_vcmd.action = c; + return (CC_ARGHACK); + +#ifdef notdef + /* + * I don't think that this is needed. But we keep it for now + */ + else + if (el_chared.c_vcmd.action == NOP) { + el->el_chared.c_vcmd.pos = el->el_line.cursor; + el->el_chared.c_vcmd.action = c; + return (CC_ARGHACK); + } else { + el->el_chared.c_vcmd.action = 0; + el->el_chared.c_vcmd.pos = 0; + return (CC_ERROR); + } +#endif +} + + +/* cv_paste(): + * Paste previous deletion before or after the cursor + */ +private el_action_t +cv_paste(EditLine *el, int c) +{ + char *ptr; + c_undo_t *un = &el->el_chared.c_undo; + +#ifdef DEBUG_PASTE + (void) fprintf(el->el_errfile, "Paste: %x \"%s\" +%d -%d\n", + un->action, un->buf, un->isize, un->dsize); +#endif + if (un->isize == 0) + return (CC_ERROR); + + if (!c && el->el_line.cursor < el->el_line.lastchar) + el->el_line.cursor++; + ptr = el->el_line.cursor; + + c_insert(el, (int) un->isize); + if (el->el_line.cursor + un->isize > el->el_line.lastchar) + return (CC_ERROR); + (void) memcpy(ptr, un->buf, un->isize); + return (CC_REFRESH); +} + + +/* vi_paste_next(): + * Vi paste previous deletion to the right of the cursor + * [p] + */ +protected el_action_t +/*ARGSUSED*/ +vi_paste_next(EditLine *el, int c __attribute__((unused))) +{ + + return (cv_paste(el, 0)); +} + + +/* vi_paste_prev(): + * Vi paste previous deletion to the left of the cursor + * [P] + */ +protected el_action_t +/*ARGSUSED*/ +vi_paste_prev(EditLine *el, int c __attribute__((unused))) +{ + + return (cv_paste(el, 1)); +} + + +/* vi_prev_space_word(): + * Vi move to the previous space delimited word + * [B] + */ +protected el_action_t +/*ARGSUSED*/ +vi_prev_space_word(EditLine *el, int c __attribute__((unused))) +{ + + if (el->el_line.cursor == el->el_line.buffer) + return (CC_ERROR); + + el->el_line.cursor = cv_prev_word(el, el->el_line.cursor, + el->el_line.buffer, + el->el_state.argument, + cv__isword); + + if (el->el_chared.c_vcmd.action & DELETE) { + cv_delfini(el); + return (CC_REFRESH); + } + return (CC_CURSOR); +} + + +/* vi_prev_word(): + * Vi move to the previous word + * [B] + */ +protected el_action_t +/*ARGSUSED*/ +vi_prev_word(EditLine *el, int c __attribute__((unused))) +{ + + if (el->el_line.cursor == el->el_line.buffer) + return (CC_ERROR); + + el->el_line.cursor = cv_prev_word(el, el->el_line.cursor, + el->el_line.buffer, + el->el_state.argument, + ce__isword); + + if (el->el_chared.c_vcmd.action & DELETE) { + cv_delfini(el); + return (CC_REFRESH); + } + return (CC_CURSOR); +} + + +/* vi_next_space_word(): + * Vi move to the next space delimited word + * [W] + */ +protected el_action_t +/*ARGSUSED*/ +vi_next_space_word(EditLine *el, int c __attribute__((unused))) +{ + + if (el->el_line.cursor == el->el_line.lastchar) + return (CC_ERROR); + + el->el_line.cursor = cv_next_word(el, el->el_line.cursor, + el->el_line.lastchar, + el->el_state.argument, + cv__isword); + + if (el->el_map.type == MAP_VI) + if (el->el_chared.c_vcmd.action & DELETE) { + cv_delfini(el); + return (CC_REFRESH); + } + return (CC_CURSOR); +} + + +/* vi_next_word(): + * Vi move to the next word + * [w] + */ +protected el_action_t +/*ARGSUSED*/ +vi_next_word(EditLine *el, int c __attribute__((unused))) +{ + + if (el->el_line.cursor == el->el_line.lastchar) + return (CC_ERROR); + + el->el_line.cursor = cv_next_word(el, el->el_line.cursor, + el->el_line.lastchar, + el->el_state.argument, + ce__isword); + + if (el->el_map.type == MAP_VI) + if (el->el_chared.c_vcmd.action & DELETE) { + cv_delfini(el); + return (CC_REFRESH); + } + return (CC_CURSOR); +} + + +/* vi_change_case(): + * Vi change case of character under the cursor and advance one character + * [~] + */ +protected el_action_t +vi_change_case(EditLine *el, int c) +{ + + if (el->el_line.cursor < el->el_line.lastchar) { + c = *el->el_line.cursor; + if (isupper(c)) + *el->el_line.cursor++ = tolower(c); + else if (islower(c)) + *el->el_line.cursor++ = toupper(c); + else + el->el_line.cursor++; + re_fastaddc(el); + return (CC_NORM); + } + return (CC_ERROR); +} + + +/* vi_change_meta(): + * Vi change prefix command + * [c] + */ +protected el_action_t +/*ARGSUSED*/ +vi_change_meta(EditLine *el, int c __attribute__((unused))) +{ + + /* + * Delete with insert == change: first we delete and then we leave in + * insert mode. + */ + return (cv_action(el, DELETE | INSERT)); +} + + +/* vi_insert_at_bol(): + * Vi enter insert mode at the beginning of line + * [I] + */ +protected el_action_t +/*ARGSUSED*/ +vi_insert_at_bol(EditLine *el, int c __attribute__((unused))) +{ + + el->el_line.cursor = el->el_line.buffer; + el->el_chared.c_vcmd.ins = el->el_line.cursor; + + el->el_chared.c_undo.ptr = el->el_line.cursor; + el->el_chared.c_undo.action = DELETE; + + el->el_map.current = el->el_map.key; + return (CC_CURSOR); +} + + +/* vi_replace_char(): + * Vi replace character under the cursor with the next character typed + * [r] + */ +protected el_action_t +/*ARGSUSED*/ +vi_replace_char(EditLine *el, int c __attribute__((unused))) +{ + + el->el_map.current = el->el_map.key; + el->el_state.inputmode = MODE_REPLACE_1; + el->el_chared.c_undo.action = CHANGE; + el->el_chared.c_undo.ptr = el->el_line.cursor; + el->el_chared.c_undo.isize = 0; + el->el_chared.c_undo.dsize = 0; + return (CC_NORM); +} + + +/* vi_replace_mode(): + * Vi enter replace mode + * [R] + */ +protected el_action_t +/*ARGSUSED*/ +vi_replace_mode(EditLine *el, int c __attribute__((unused))) +{ + + el->el_map.current = el->el_map.key; + el->el_state.inputmode = MODE_REPLACE; + el->el_chared.c_undo.action = CHANGE; + el->el_chared.c_undo.ptr = el->el_line.cursor; + el->el_chared.c_undo.isize = 0; + el->el_chared.c_undo.dsize = 0; + return (CC_NORM); +} + + +/* vi_substitute_char(): + * Vi replace character under the cursor and enter insert mode + * [r] + */ +protected el_action_t +/*ARGSUSED*/ +vi_substitute_char(EditLine *el, int c __attribute__((unused))) +{ + + c_delafter(el, el->el_state.argument); + el->el_map.current = el->el_map.key; + return (CC_REFRESH); +} + + +/* vi_substitute_line(): + * Vi substitute entire line + * [S] + */ +protected el_action_t +/*ARGSUSED*/ +vi_substitute_line(EditLine *el, int c __attribute__((unused))) +{ + + (void) em_kill_line(el, 0); + el->el_map.current = el->el_map.key; + return (CC_REFRESH); +} + + +/* vi_change_to_eol(): + * Vi change to end of line + * [C] + */ +protected el_action_t +/*ARGSUSED*/ +vi_change_to_eol(EditLine *el, int c __attribute__((unused))) +{ + + (void) ed_kill_line(el, 0); + el->el_map.current = el->el_map.key; + return (CC_REFRESH); +} + + +/* vi_insert(): + * Vi enter insert mode + * [i] + */ +protected el_action_t +/*ARGSUSED*/ +vi_insert(EditLine *el, int c __attribute__((unused))) +{ + + el->el_map.current = el->el_map.key; + + el->el_chared.c_vcmd.ins = el->el_line.cursor; + el->el_chared.c_undo.ptr = el->el_line.cursor; + el->el_chared.c_undo.action = DELETE; + + return (CC_NORM); +} + + +/* vi_add(): + * Vi enter insert mode after the cursor + * [a] + */ +protected el_action_t +/*ARGSUSED*/ +vi_add(EditLine *el, int c __attribute__((unused))) +{ + int ret; + + el->el_map.current = el->el_map.key; + if (el->el_line.cursor < el->el_line.lastchar) { + el->el_line.cursor++; + if (el->el_line.cursor > el->el_line.lastchar) + el->el_line.cursor = el->el_line.lastchar; + ret = CC_CURSOR; + } else + ret = CC_NORM; + + el->el_chared.c_vcmd.ins = el->el_line.cursor; + el->el_chared.c_undo.ptr = el->el_line.cursor; + el->el_chared.c_undo.action = DELETE; + + return (ret); +} + + +/* vi_add_at_eol(): + * Vi enter insert mode at end of line + * [A] + */ +protected el_action_t +/*ARGSUSED*/ +vi_add_at_eol(EditLine *el, int c __attribute__((unused))) +{ + + el->el_map.current = el->el_map.key; + el->el_line.cursor = el->el_line.lastchar; + + /* Mark where insertion begins */ + el->el_chared.c_vcmd.ins = el->el_line.lastchar; + el->el_chared.c_undo.ptr = el->el_line.lastchar; + el->el_chared.c_undo.action = DELETE; + return (CC_CURSOR); +} + + +/* vi_delete_meta(): + * Vi delete prefix command + * [d] + */ +protected el_action_t +/*ARGSUSED*/ +vi_delete_meta(EditLine *el, int c __attribute__((unused))) +{ + + return (cv_action(el, DELETE)); +} + + +/* vi_end_word(): + * Vi move to the end of the current space delimited word + * [E] + */ +protected el_action_t +/*ARGSUSED*/ +vi_end_word(EditLine *el, int c __attribute__((unused))) +{ + + if (el->el_line.cursor == el->el_line.lastchar) + return (CC_ERROR); + + el->el_line.cursor = cv__endword(el->el_line.cursor, + el->el_line.lastchar, el->el_state.argument); + + if (el->el_chared.c_vcmd.action & DELETE) { + el->el_line.cursor++; + cv_delfini(el); + return (CC_REFRESH); + } + return (CC_CURSOR); +} + + +/* vi_to_end_word(): + * Vi move to the end of the current word + * [e] + */ +protected el_action_t +/*ARGSUSED*/ +vi_to_end_word(EditLine *el, int c __attribute__((unused))) +{ + + if (el->el_line.cursor == el->el_line.lastchar) + return (CC_ERROR); + + el->el_line.cursor = cv__endword(el->el_line.cursor, + el->el_line.lastchar, el->el_state.argument); + + if (el->el_chared.c_vcmd.action & DELETE) { + el->el_line.cursor++; + cv_delfini(el); + return (CC_REFRESH); + } + return (CC_CURSOR); +} + + +/* vi_undo(): + * Vi undo last change + * [u] + */ +protected el_action_t +/*ARGSUSED*/ +vi_undo(EditLine *el, int c __attribute__((unused))) +{ + char *cp, *kp; + char temp; + int i, size; + c_undo_t *un = &el->el_chared.c_undo; + +#ifdef DEBUG_UNDO + (void) fprintf(el->el_errfile, "Undo: %x \"%s\" +%d -%d\n", + un->action, un->buf, un->isize, un->dsize); +#endif + switch (un->action) { + case DELETE: + if (un->dsize == 0) + return (CC_NORM); + + (void) memcpy(un->buf, un->ptr, un->dsize); + for (cp = un->ptr; cp <= el->el_line.lastchar; cp++) + *cp = cp[un->dsize]; + + el->el_line.lastchar -= un->dsize; + el->el_line.cursor = un->ptr; + + un->action = INSERT; + un->isize = un->dsize; + un->dsize = 0; + break; + + case DELETE | INSERT: + size = un->isize - un->dsize; + if (size > 0) + i = un->dsize; + else + i = un->isize; + cp = un->ptr; + kp = un->buf; + while (i-- > 0) { + temp = *kp; + *kp++ = *cp; + *cp++ = temp; + } + if (size > 0) { + el->el_line.cursor = cp; + c_insert(el, size); + while (size-- > 0 && cp < el->el_line.lastchar) { + temp = *kp; + *kp++ = *cp; + *cp++ = temp; + } + } else if (size < 0) { + size = -size; + for (; cp <= el->el_line.lastchar; cp++) { + *kp++ = *cp; + *cp = cp[size]; + } + el->el_line.lastchar -= size; + } + el->el_line.cursor = un->ptr; + i = un->dsize; + un->dsize = un->isize; + un->isize = i; + break; + + case INSERT: + if (un->isize == 0) + return (CC_NORM); + + el->el_line.cursor = un->ptr; + c_insert(el, (int) un->isize); + (void) memcpy(un->ptr, un->buf, un->isize); + un->action = DELETE; + un->dsize = un->isize; + un->isize = 0; + break; + + case CHANGE: + if (un->isize == 0) + return (CC_NORM); + + el->el_line.cursor = un->ptr; + size = (int) (el->el_line.cursor - el->el_line.lastchar); + if (size < (int)un->isize) + size = un->isize; + cp = un->ptr; + kp = un->buf; + for (i = 0; i < size; i++) { + temp = *kp; + *kp++ = *cp; + *cp++ = temp; + } + un->dsize = 0; + break; + + default: + return (CC_ERROR); + } + + return (CC_REFRESH); +} + + +/* vi_command_mode(): + * Vi enter command mode (use alternative key bindings) + * [] + */ +protected el_action_t +/*ARGSUSED*/ +vi_command_mode(EditLine *el, int c __attribute__((unused))) +{ + int size; + + /* [Esc] cancels pending action */ + el->el_chared.c_vcmd.ins = 0; + el->el_chared.c_vcmd.action = NOP; + el->el_chared.c_vcmd.pos = 0; + + el->el_state.doingarg = 0; + size = el->el_chared.c_undo.ptr - el->el_line.cursor; + if (size < 0) + size = -size; + if (el->el_chared.c_undo.action == (INSERT | DELETE) || + el->el_chared.c_undo.action == DELETE) + el->el_chared.c_undo.dsize = size; + else + el->el_chared.c_undo.isize = size; + + el->el_state.inputmode = MODE_INSERT; + el->el_map.current = el->el_map.alt; +#ifdef VI_MOVE + if (el->el_line.cursor > el->el_line.buffer) + el->el_line.cursor--; +#endif + return (CC_CURSOR); +} + + +/* vi_zero(): + * Vi move to the beginning of line + * [0] + */ +protected el_action_t +vi_zero(EditLine *el, int c) +{ + + if (el->el_state.doingarg) { + if (el->el_state.argument > 1000000) + return (CC_ERROR); + el->el_state.argument = + (el->el_state.argument * 10) + (c - '0'); + return (CC_ARGHACK); + } else { + el->el_line.cursor = el->el_line.buffer; + if (el->el_chared.c_vcmd.action & DELETE) { + cv_delfini(el); + return (CC_REFRESH); + } + return (CC_CURSOR); + } +} + + +/* vi_delete_prev_char(): + * Vi move to previous character (backspace) + * [^H] + */ +protected el_action_t +/*ARGSUSED*/ +vi_delete_prev_char(EditLine *el, int c __attribute__((unused))) +{ + + if (el->el_chared.c_vcmd.ins == 0) + return (CC_ERROR); + + if (el->el_chared.c_vcmd.ins > + el->el_line.cursor - el->el_state.argument) + return (CC_ERROR); + + c_delbefore(el, el->el_state.argument); + el->el_line.cursor -= el->el_state.argument; + + return (CC_REFRESH); +} + + +/* vi_list_or_eof(): + * Vi list choices for completion or indicate end of file if empty line + * [^D] + */ +protected el_action_t +/*ARGSUSED*/ +vi_list_or_eof(EditLine *el, int c __attribute__((unused))) +{ + +#ifdef notyet + if (el->el_line.cursor == el->el_line.lastchar && + el->el_line.cursor == el->el_line.buffer) { +#endif + term_overwrite(el, STReof, 4); /* then do a EOF */ + term__flush(); + return (CC_EOF); +#ifdef notyet + } else { + re_goto_bottom(el); + *el->el_line.lastchar = '\0'; /* just in case */ + return (CC_LIST_CHOICES); + } +#endif +} + + +/* vi_kill_line_prev(): + * Vi cut from beginning of line to cursor + * [^U] + */ +protected el_action_t +/*ARGSUSED*/ +vi_kill_line_prev(EditLine *el, int c __attribute__((unused))) +{ + char *kp, *cp; + + cp = el->el_line.buffer; + kp = el->el_chared.c_kill.buf; + while (cp < el->el_line.cursor) + *kp++ = *cp++; /* copy it */ + el->el_chared.c_kill.last = kp; + c_delbefore(el, el->el_line.cursor - el->el_line.buffer); + el->el_line.cursor = el->el_line.buffer; /* zap! */ + return (CC_REFRESH); +} + + +/* vi_search_prev(): + * Vi search history previous + * [?] + */ +protected el_action_t +/*ARGSUSED*/ +vi_search_prev(EditLine *el, int c __attribute__((unused))) +{ + + return (cv_search(el, ED_SEARCH_PREV_HISTORY)); +} + + +/* vi_search_next(): + * Vi search history next + * [/] + */ +protected el_action_t +/*ARGSUSED*/ +vi_search_next(EditLine *el, int c __attribute__((unused))) +{ + + return (cv_search(el, ED_SEARCH_NEXT_HISTORY)); +} + + +/* vi_repeat_search_next(): + * Vi repeat current search in the same search direction + * [n] + */ +protected el_action_t +/*ARGSUSED*/ +vi_repeat_search_next(EditLine *el, int c __attribute__((unused))) +{ + + if (el->el_search.patlen == 0) + return (CC_ERROR); + else + return (cv_repeat_srch(el, el->el_search.patdir)); +} + + +/* vi_repeat_search_prev(): + * Vi repeat current search in the opposite search direction + * [N] + */ +/*ARGSUSED*/ +protected el_action_t +vi_repeat_search_prev(EditLine *el, int c __attribute__((unused))) +{ + + if (el->el_search.patlen == 0) + return (CC_ERROR); + else + return (cv_repeat_srch(el, + el->el_search.patdir == ED_SEARCH_PREV_HISTORY ? + ED_SEARCH_NEXT_HISTORY : ED_SEARCH_PREV_HISTORY)); +} + + +/* vi_next_char(): + * Vi move to the character specified next + * [f] + */ +protected el_action_t +/*ARGSUSED*/ +vi_next_char(EditLine *el, int c __attribute__((unused))) +{ + char ch; + + if (el_getc(el, &ch) != 1) + return (ed_end_of_file(el, 0)); + + el->el_search.chadir = CHAR_FWD; + el->el_search.chacha = ch; + + return (cv_csearch_fwd(el, ch, el->el_state.argument, 0)); + +} + + +/* vi_prev_char(): + * Vi move to the character specified previous + * [F] + */ +protected el_action_t +/*ARGSUSED*/ +vi_prev_char(EditLine *el, int c __attribute__((unused))) +{ + char ch; + + if (el_getc(el, &ch) != 1) + return (ed_end_of_file(el, 0)); + + el->el_search.chadir = CHAR_BACK; + el->el_search.chacha = ch; + + return (cv_csearch_back(el, ch, el->el_state.argument, 0)); +} + + +/* vi_to_next_char(): + * Vi move up to the character specified next + * [t] + */ +protected el_action_t +/*ARGSUSED*/ +vi_to_next_char(EditLine *el, int c __attribute__((unused))) +{ + char ch; + + if (el_getc(el, &ch) != 1) + return (ed_end_of_file(el, 0)); + + return (cv_csearch_fwd(el, ch, el->el_state.argument, 1)); + +} + + +/* vi_to_prev_char(): + * Vi move up to the character specified previous + * [T] + */ +protected el_action_t +/*ARGSUSED*/ +vi_to_prev_char(EditLine *el, int c __attribute__((unused))) +{ + char ch; + + if (el_getc(el, &ch) != 1) + return (ed_end_of_file(el, 0)); + + return (cv_csearch_back(el, ch, el->el_state.argument, 1)); +} + + +/* vi_repeat_next_char(): + * Vi repeat current character search in the same search direction + * [;] + */ +protected el_action_t +/*ARGSUSED*/ +vi_repeat_next_char(EditLine *el, int c __attribute__((unused))) +{ + + if (el->el_search.chacha == 0) + return (CC_ERROR); + + return (el->el_search.chadir == CHAR_FWD + ? cv_csearch_fwd(el, el->el_search.chacha, + el->el_state.argument, 0) + : cv_csearch_back(el, el->el_search.chacha, + el->el_state.argument, 0)); +} + + +/* vi_repeat_prev_char(): + * Vi repeat current character search in the opposite search direction + * [,] + */ +protected el_action_t +/*ARGSUSED*/ +vi_repeat_prev_char(EditLine *el, int c __attribute__((unused))) +{ + + if (el->el_search.chacha == 0) + return (CC_ERROR); + + return el->el_search.chadir == CHAR_BACK ? + cv_csearch_fwd(el, el->el_search.chacha, el->el_state.argument, 0) : + cv_csearch_back(el, el->el_search.chacha, el->el_state.argument, 0); +} diff --git a/readline/.cvsignore b/cmd-line-utils/readline/.cvsignore similarity index 100% rename from readline/.cvsignore rename to cmd-line-utils/readline/.cvsignore diff --git a/readline/COPYING b/cmd-line-utils/readline/COPYING similarity index 100% rename from readline/COPYING rename to cmd-line-utils/readline/COPYING diff --git a/readline/INSTALL b/cmd-line-utils/readline/INSTALL similarity index 100% rename from readline/INSTALL rename to cmd-line-utils/readline/INSTALL diff --git a/readline/Makefile.am b/cmd-line-utils/readline/Makefile.am similarity index 91% rename from readline/Makefile.am rename to cmd-line-utils/readline/Makefile.am index 018df11be7d..7c4fe8eeb91 100644 --- a/readline/Makefile.am +++ b/cmd-line-utils/readline/Makefile.am @@ -2,7 +2,8 @@ # Makefile for the GNU readline library. # Copyright (C) 1994,1996,1997 Free Software Foundation, Inc. -INCLUDES = -I$(top_srcdir)/include +# Last -I$(top_srcdir) needed for RedHat! +INCLUDES = -I$(top_srcdir)/include -I$(top_srcdir) noinst_LIBRARIES = libreadline.a diff --git a/readline/README b/cmd-line-utils/readline/README similarity index 100% rename from readline/README rename to cmd-line-utils/readline/README diff --git a/readline/ansi_stdlib.h b/cmd-line-utils/readline/ansi_stdlib.h similarity index 100% rename from readline/ansi_stdlib.h rename to cmd-line-utils/readline/ansi_stdlib.h diff --git a/readline/bind.c b/cmd-line-utils/readline/bind.c similarity index 100% rename from readline/bind.c rename to cmd-line-utils/readline/bind.c diff --git a/readline/callback.c b/cmd-line-utils/readline/callback.c similarity index 100% rename from readline/callback.c rename to cmd-line-utils/readline/callback.c diff --git a/readline/chardefs.h b/cmd-line-utils/readline/chardefs.h similarity index 100% rename from readline/chardefs.h rename to cmd-line-utils/readline/chardefs.h diff --git a/readline/complete.c b/cmd-line-utils/readline/complete.c similarity index 100% rename from readline/complete.c rename to cmd-line-utils/readline/complete.c diff --git a/readline/configure.in b/cmd-line-utils/readline/configure.in similarity index 100% rename from readline/configure.in rename to cmd-line-utils/readline/configure.in diff --git a/readline/display.c b/cmd-line-utils/readline/display.c similarity index 100% rename from readline/display.c rename to cmd-line-utils/readline/display.c diff --git a/readline/emacs_keymap.c b/cmd-line-utils/readline/emacs_keymap.c similarity index 100% rename from readline/emacs_keymap.c rename to cmd-line-utils/readline/emacs_keymap.c diff --git a/readline/funmap.c b/cmd-line-utils/readline/funmap.c similarity index 100% rename from readline/funmap.c rename to cmd-line-utils/readline/funmap.c diff --git a/readline/histexpand.c b/cmd-line-utils/readline/histexpand.c similarity index 100% rename from readline/histexpand.c rename to cmd-line-utils/readline/histexpand.c diff --git a/readline/histfile.c b/cmd-line-utils/readline/histfile.c similarity index 100% rename from readline/histfile.c rename to cmd-line-utils/readline/histfile.c diff --git a/readline/histlib.h b/cmd-line-utils/readline/histlib.h similarity index 100% rename from readline/histlib.h rename to cmd-line-utils/readline/histlib.h diff --git a/readline/history.c b/cmd-line-utils/readline/history.c similarity index 100% rename from readline/history.c rename to cmd-line-utils/readline/history.c diff --git a/readline/history.h b/cmd-line-utils/readline/history.h similarity index 100% rename from readline/history.h rename to cmd-line-utils/readline/history.h diff --git a/readline/histsearch.c b/cmd-line-utils/readline/histsearch.c similarity index 100% rename from readline/histsearch.c rename to cmd-line-utils/readline/histsearch.c diff --git a/readline/input.c b/cmd-line-utils/readline/input.c similarity index 100% rename from readline/input.c rename to cmd-line-utils/readline/input.c diff --git a/readline/isearch.c b/cmd-line-utils/readline/isearch.c similarity index 100% rename from readline/isearch.c rename to cmd-line-utils/readline/isearch.c diff --git a/readline/keymaps.c b/cmd-line-utils/readline/keymaps.c similarity index 100% rename from readline/keymaps.c rename to cmd-line-utils/readline/keymaps.c diff --git a/readline/keymaps.h b/cmd-line-utils/readline/keymaps.h similarity index 100% rename from readline/keymaps.h rename to cmd-line-utils/readline/keymaps.h diff --git a/readline/kill.c b/cmd-line-utils/readline/kill.c similarity index 100% rename from readline/kill.c rename to cmd-line-utils/readline/kill.c diff --git a/readline/macro.c b/cmd-line-utils/readline/macro.c similarity index 100% rename from readline/macro.c rename to cmd-line-utils/readline/macro.c diff --git a/readline/mbutil.c b/cmd-line-utils/readline/mbutil.c similarity index 100% rename from readline/mbutil.c rename to cmd-line-utils/readline/mbutil.c diff --git a/readline/misc.c b/cmd-line-utils/readline/misc.c similarity index 100% rename from readline/misc.c rename to cmd-line-utils/readline/misc.c diff --git a/readline/nls.c b/cmd-line-utils/readline/nls.c similarity index 100% rename from readline/nls.c rename to cmd-line-utils/readline/nls.c diff --git a/readline/parens.c b/cmd-line-utils/readline/parens.c similarity index 100% rename from readline/parens.c rename to cmd-line-utils/readline/parens.c diff --git a/readline/posixdir.h b/cmd-line-utils/readline/posixdir.h similarity index 100% rename from readline/posixdir.h rename to cmd-line-utils/readline/posixdir.h diff --git a/readline/posixjmp.h b/cmd-line-utils/readline/posixjmp.h similarity index 100% rename from readline/posixjmp.h rename to cmd-line-utils/readline/posixjmp.h diff --git a/readline/posixstat.h b/cmd-line-utils/readline/posixstat.h similarity index 100% rename from readline/posixstat.h rename to cmd-line-utils/readline/posixstat.h diff --git a/readline/readline.c b/cmd-line-utils/readline/readline.c similarity index 100% rename from readline/readline.c rename to cmd-line-utils/readline/readline.c diff --git a/readline/readline.h b/cmd-line-utils/readline/readline.h similarity index 100% rename from readline/readline.h rename to cmd-line-utils/readline/readline.h diff --git a/readline/rlconf.h b/cmd-line-utils/readline/rlconf.h similarity index 100% rename from readline/rlconf.h rename to cmd-line-utils/readline/rlconf.h diff --git a/readline/rldefs.h b/cmd-line-utils/readline/rldefs.h similarity index 100% rename from readline/rldefs.h rename to cmd-line-utils/readline/rldefs.h diff --git a/readline/rlmbutil.h b/cmd-line-utils/readline/rlmbutil.h similarity index 100% rename from readline/rlmbutil.h rename to cmd-line-utils/readline/rlmbutil.h diff --git a/readline/rlprivate.h b/cmd-line-utils/readline/rlprivate.h similarity index 100% rename from readline/rlprivate.h rename to cmd-line-utils/readline/rlprivate.h diff --git a/readline/rlshell.h b/cmd-line-utils/readline/rlshell.h similarity index 100% rename from readline/rlshell.h rename to cmd-line-utils/readline/rlshell.h diff --git a/readline/rlstdc.h b/cmd-line-utils/readline/rlstdc.h similarity index 100% rename from readline/rlstdc.h rename to cmd-line-utils/readline/rlstdc.h diff --git a/readline/rltty.c b/cmd-line-utils/readline/rltty.c similarity index 100% rename from readline/rltty.c rename to cmd-line-utils/readline/rltty.c diff --git a/readline/rltty.h b/cmd-line-utils/readline/rltty.h similarity index 100% rename from readline/rltty.h rename to cmd-line-utils/readline/rltty.h diff --git a/readline/rltypedefs.h b/cmd-line-utils/readline/rltypedefs.h similarity index 100% rename from readline/rltypedefs.h rename to cmd-line-utils/readline/rltypedefs.h diff --git a/readline/rlwinsize.h b/cmd-line-utils/readline/rlwinsize.h similarity index 100% rename from readline/rlwinsize.h rename to cmd-line-utils/readline/rlwinsize.h diff --git a/readline/search.c b/cmd-line-utils/readline/search.c similarity index 100% rename from readline/search.c rename to cmd-line-utils/readline/search.c diff --git a/readline/shell.c b/cmd-line-utils/readline/shell.c similarity index 100% rename from readline/shell.c rename to cmd-line-utils/readline/shell.c diff --git a/readline/signals.c b/cmd-line-utils/readline/signals.c similarity index 100% rename from readline/signals.c rename to cmd-line-utils/readline/signals.c diff --git a/readline/tcap.h b/cmd-line-utils/readline/tcap.h similarity index 100% rename from readline/tcap.h rename to cmd-line-utils/readline/tcap.h diff --git a/readline/terminal.c b/cmd-line-utils/readline/terminal.c similarity index 100% rename from readline/terminal.c rename to cmd-line-utils/readline/terminal.c diff --git a/readline/text.c b/cmd-line-utils/readline/text.c similarity index 100% rename from readline/text.c rename to cmd-line-utils/readline/text.c diff --git a/readline/tilde.c b/cmd-line-utils/readline/tilde.c similarity index 100% rename from readline/tilde.c rename to cmd-line-utils/readline/tilde.c diff --git a/readline/tilde.h b/cmd-line-utils/readline/tilde.h similarity index 100% rename from readline/tilde.h rename to cmd-line-utils/readline/tilde.h diff --git a/readline/undo.c b/cmd-line-utils/readline/undo.c similarity index 100% rename from readline/undo.c rename to cmd-line-utils/readline/undo.c diff --git a/readline/util.c b/cmd-line-utils/readline/util.c similarity index 100% rename from readline/util.c rename to cmd-line-utils/readline/util.c diff --git a/readline/vi_keymap.c b/cmd-line-utils/readline/vi_keymap.c similarity index 100% rename from readline/vi_keymap.c rename to cmd-line-utils/readline/vi_keymap.c diff --git a/readline/vi_mode.c b/cmd-line-utils/readline/vi_mode.c similarity index 100% rename from readline/vi_mode.c rename to cmd-line-utils/readline/vi_mode.c diff --git a/readline/xmalloc.c b/cmd-line-utils/readline/xmalloc.c similarity index 100% rename from readline/xmalloc.c rename to cmd-line-utils/readline/xmalloc.c diff --git a/readline/xmalloc.h b/cmd-line-utils/readline/xmalloc.h similarity index 100% rename from readline/xmalloc.h rename to cmd-line-utils/readline/xmalloc.h diff --git a/configure.in b/configure.in index f2e64ddffe4..4f2ff269381 100644 --- a/configure.in +++ b/configure.in @@ -1574,8 +1574,37 @@ fi MYSQL_PTHREAD_YIELD ###################################################################### -# For readline-4.0 (We simply move the mimimum amount of stuff from -# the readline configure.in here) +# For readline/libedit (We simply move the mimimum amount of stuff from +# the readline/libedit configure.in here) + +dnl Checks for programs. +AC_PROG_CC +AC_PROG_AWK +AC_PROG_INSTALL + +dnl Checks for header files. +AC_HEADER_DIRENT +AC_HEADER_STDC +AC_HEADER_SYS_WAIT +AC_CHECK_HEADERS(limits.h malloc.h sys/ioctl.h unistd.h sys/cdefs.h sys/types.h) + +dnl Checks for typedefs, structures, and compiler characteristics. +AC_C_CONST +AC_TYPE_SIZE_T + +dnl Checks for library functions. +AC_FUNC_ALLOCA +AC_PROG_GCC_TRADITIONAL +AC_TYPE_SIGNAL +AC_CHECK_FUNCS(re_comp regcomp strdup strerror strstr strtol) + +AC_CHECK_HEADERS(vis.h) +AC_CHECK_FUNCS(strlcat strlcpy) +AC_CHECK_FUNCS(issetugid) +AC_CHECK_FUNCS(fgetln) +AC_CHECK_FUNCS(getline flockfile) + +# from old readline settting: MAKE_SHELL=/bin/sh AC_SUBST(MAKE_SHELL) @@ -1605,7 +1634,7 @@ else fi AC_SUBST(TERMCAP_LIB) -# End of readline stuff +# End of readline/libedit stuff ######################################################################### dnl Checks for library functions. @@ -1989,20 +2018,56 @@ AC_SUBST(bench_dirs) AC_ARG_WITH(readline, [ --without-readline Use system readline instead of bundled copy.], [ with_readline=$withval ], - [ with_readline=yes ] + [ with_readline=undefined ] ) -if test "$with_readline" = "yes" + +AC_ARG_WITH(libedit, + [ --without-libedit Use system libedit instead of bundled copy.], + [ with_libedit=$withval ], + [ with_libedit=undefined ] + ) + +compile_readline= no +compile_libedit= no + +if [test "$with_libedit" = "yes"] && [test "$with_readline" = "yes"] then - readline_dir="readline" - readline_link="\$(top_builddir)/readline/libreadline.a" -else - # This requires readline to be in a standard place. Mosty for linux - # there readline may be a shared library. - readline_dir="" - readline_link="-lreadline" + AC_MSG_ERROR([You can not use --with-readline and --with-libedit at the same time, please choose one of it]) fi + +mkdir include/readline + +if [test "$with_libedit" = "yes"] || [test "$with_libedit" = "undefined"] && [test "$with_readline" = "undefined"] +then + readline_dir="cmd-line-utils/libedit" + readline_link="\$(top_builddir)/cmd-line-utils/libedit/liblibedit.a" + readline_h_ln_cmd="\$(LN) \$(top_builddir)/cmd-line-utils/libedit/readline/*.h readline/" + compile_libedit=yes + AC_DEFINE_UNQUOTED(USE_LIBEDIT_INTERFACE) +elif test "$with_readline" = "yes" +then + readline_dir="cmd-line-utils/readline" + readline_link="\$(top_builddir)/cmd-line-utils/readline/libreadline.a" + readline_h_ln_cmd="\$(LN) \$(top_builddir)/cmd-line-utils/readline/*.h readline/" + compile_readline=yes + AC_DEFINE_UNQUOTED(USE_NEW_READLINE_INTERFACE) +else + MYSQL_CHECK_LIBEDIT_INTERFACE + MYSQL_CHECK_NEW_RL_INTERFACE + if ["$mysql_cv_new_rl_interface"="yes"] || [test "$mysql_cv_libedit_interface"="no"] + then + readline_dir="" + readline_link="-lreadline" + else + readline_dir="" + readline_link="-ledit" + fi + readline_h_ln_cmd="" +fi + AC_SUBST(readline_dir) AC_SUBST(readline_link) +AC_SUBST(readline_h_ln_cmd) dnl In order to add new charset, you must add charset name to @@ -2311,6 +2376,7 @@ EOF if test X"$have_innodb" = Xyes then + innodb_conf_flags="" sql_server_dirs="$sql_server_dirs innobase" echo "CONFIGURING FOR INNODB" if test ! -d "innobase"; then @@ -2323,7 +2389,11 @@ EOF /* ) rel_srcdir="$srcdir" ;; * ) rel_srcdir="../$srcdir" ;; esac - (cd innobase && sh $rel_srcdir/innobase/configure) \ + if test "x$enable_dependency_tracking" == xno + then + innodb_conf_flags=--disable-dependency-tracking + fi + (cd innobase && sh $rel_srcdir/innobase/configure $innodb_conf_flags) \ || AC_MSG_ERROR([could not configure INNODB]) echo "END OF INNODB CONFIGURATION" @@ -2381,12 +2451,23 @@ AC_SUBST(GXX) #AC_SUBST(TOOLS_LIBS) # Output results + +if test "$compile_readline" = "yes" +then + AC_OUTPUT(cmd-line-utils/readline/Makefile) +fi + +if test "$compile_libedit" = "yes" +then + AC_OUTPUT(cmd-line-utils/libedit/Makefile) +fi + AC_OUTPUT(Makefile extra/Makefile mysys/Makefile isam/Makefile \ strings/Makefile regex/Makefile heap/Makefile \ bdb/Makefile \ myisam/Makefile myisammrg/Makefile \ os2/Makefile os2/include/Makefile os2/include/sys/Makefile \ - man/Makefile BUILD/Makefile readline/Makefile vio/Makefile \ + man/Makefile BUILD/Makefile vio/Makefile \ libmysql_r/Makefile libmysqld/Makefile libmysqld/examples/Makefile \ libmysql/Makefile client/Makefile \ pstack/Makefile pstack/aout/Makefile sql/Makefile sql/share/Makefile \ diff --git a/extra/mysql_waitpid.c b/extra/mysql_waitpid.c index 14d3f893c60..007db959111 100644 --- a/extra/mysql_waitpid.c +++ b/extra/mysql_waitpid.c @@ -1,9 +1,26 @@ -#include +/* Copyright (C) 2003 MySQL AB + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ + +/* Wait until a program dies */ + +#include +#include +#include #include #include -#include -#include -#include static const char *VER= "1.1"; static char *progname; diff --git a/include/Makefile.am b/include/Makefile.am index 652278e8a80..e197cee4cde 100644 --- a/include/Makefile.am +++ b/include/Makefile.am @@ -37,16 +37,25 @@ SUPERCLEANFILES = mysql_version.h my_config.h # Some include files that may be moved and patched by configure DISTCLEANFILES = sched.h +clean: + $(RM) -f readline/* +distclean: + $(RM) -f readline/* + all-local: my_config.h # Since we include my_config.h it better exist from the beginning link_sources: $(CP) ../config.h my_config.h + $(RM) -f readline/* + @readline_h_ln_cmd@ # Keep automake happy my_config.h: ../config.h $(CP) ../config.h my_config.h + $(RM) -f readline/* + @readline_h_ln_cmd@ # These files should not be included in distributions since they are # generated by configure from the .h.in files diff --git a/include/config-win.h b/include/config-win.h index 9c1c1ae4830..dce543cd2b0 100644 --- a/include/config-win.h +++ b/include/config-win.h @@ -152,7 +152,9 @@ typedef uint rf_SetTimer; #define access(A,B) _access(A,B) #endif -#if defined(__cplusplus) +#if !defined(__cplusplus) +#define inline __inline +#endif /* __cplusplus */ inline double rint(double nr) { @@ -175,9 +177,6 @@ inline double ulonglong2double(ulonglong value) } #define my_off_t2double(A) ulonglong2double(A) #endif /* _WIN64 */ -#else -#define inline __inline -#endif /* __cplusplus */ #if SIZEOF_OFF_T > 4 #define lseek(A,B,C) _lseeki64((A),(longlong) (B),(C)) diff --git a/include/m_ctype.h b/include/m_ctype.h index d99fda8d2b9..ee6a50e6b8d 100644 --- a/include/m_ctype.h +++ b/include/m_ctype.h @@ -49,6 +49,9 @@ typedef struct unicase_info_st { #define MY_CS_TOOSMALL -1 #define MY_CS_TOOFEW(n) (-1-(n)) +#define MY_SEQ_INTTAIL 1 +#define MY_SEQ_SPACES 2 + /* My charsets_list flags */ #define MY_NO_SETS 0 #define MY_CS_COMPILED 1 /* compiled-in sets */ @@ -57,6 +60,7 @@ typedef struct unicase_info_st { #define MY_CS_LOADED 8 /* sets that are currently loaded */ #define MY_CS_BINSORT 16 /* if binary sort order */ #define MY_CS_PRIMARY 32 /* if primary collation */ +#define MY_CS_STRNXFRM 64 /* if strnxfrm is used for sort */ #define MY_CHARSET_UNDEFINED 0 #define MY_CHARSET_CURRENT (default_charset_info->number) @@ -133,15 +137,17 @@ typedef struct charset_info_st /* Charset dependant snprintf() */ int (*snprintf)(struct charset_info_st *, char *to, uint n, const char *fmt, ...); - int (*l10tostr)(struct charset_info_st *, char *to, uint n, int radix, long int val); - int (*ll10tostr)(struct charset_info_st *, char *to, uint n, int radix, longlong val); + int (*long10_to_str)(struct charset_info_st *, char *to, uint n, int radix, long int val); + int (*longlong10_to_str)(struct charset_info_st *, char *to, uint n, int radix, longlong val); /* String-to-number convertion routines */ - long (*strntol)(struct charset_info_st *, const char *s, uint l,char **e, int base); - ulong (*strntoul)(struct charset_info_st *, const char *s, uint l, char **e, int base); - longlong (*strntoll)(struct charset_info_st *, const char *s, uint l, char **e, int base); - ulonglong (*strntoull)(struct charset_info_st *, const char *s, uint l, char **e, int base); - double (*strntod)(struct charset_info_st *, char *s, uint l, char **e); + long (*strntol)(struct charset_info_st *, const char *s, uint l, int base, char **e, int *err); + ulong (*strntoul)(struct charset_info_st *, const char *s, uint l, int base, char **e, int *err); + longlong (*strntoll)(struct charset_info_st *, const char *s, uint l, int base, char **e, int *err); + ulonglong (*strntoull)(struct charset_info_st *, const char *s, uint l, int base, char **e, int *err); + double (*strntod)(struct charset_info_st *, char *s, uint l, char **e, int *err); + + ulong (*scan)(struct charset_info_st *, const char *b, const char *e, int sq); } CHARSET_INFO; @@ -180,16 +186,18 @@ extern int my_strncasecmp_8bit(CHARSET_INFO * cs, const char *, const char *, ui int my_mb_wc_8bit(CHARSET_INFO *cs,my_wc_t *wc, const uchar *s,const uchar *e); int my_wc_mb_8bit(CHARSET_INFO *cs,my_wc_t wc, uchar *s, uchar *e); +ulong my_scan_8bit(CHARSET_INFO *cs, const char *b, const char *e, int sq); + int my_snprintf_8bit(struct charset_info_st *, char *to, uint n, const char *fmt, ...); -long my_strntol_8bit(CHARSET_INFO *, const char *s, uint l,char **e, int base); -ulong my_strntoul_8bit(CHARSET_INFO *, const char *s, uint l,char **e, int base); -longlong my_strntoll_8bit(CHARSET_INFO *, const char *s, uint l,char **e, int base); -ulonglong my_strntoull_8bit(CHARSET_INFO *, const char *s, uint l,char **e, int base); -double my_strntod_8bit(CHARSET_INFO *, char *s, uint l,char **e); +long my_strntol_8bit(CHARSET_INFO *, const char *s, uint l, int base, char **e, int *err); +ulong my_strntoul_8bit(CHARSET_INFO *, const char *s, uint l, int base, char **e, int *err); +longlong my_strntoll_8bit(CHARSET_INFO *, const char *s, uint l, int base, char **e, int *err); +ulonglong my_strntoull_8bit(CHARSET_INFO *, const char *s, uint l, int base, char **e, int *err); +double my_strntod_8bit(CHARSET_INFO *, char *s, uint l,char **e, int *err); -int my_l10tostr_8bit(CHARSET_INFO *, char *to, uint l, int radix, long int val); -int my_ll10tostr_8bit(CHARSET_INFO *, char *to, uint l, int radix, longlong val); +int my_long10_to_str_8bit(CHARSET_INFO *, char *to, uint l, int radix, long int val); +int my_longlong10_to_str_8bit(CHARSET_INFO *, char *to, uint l, int radix, longlong val); my_bool my_like_range_simple(CHARSET_INFO *cs, const char *ptr, uint ptr_length, @@ -253,7 +261,7 @@ int my_wildcmp_mb(CHARSET_INFO *, #define my_isvar(s,c) (my_isalnum(s,c) || (c) == '_') #define my_isvar_start(s,c) (my_isalpha(s,c) || (c) == '_') -#define use_strnxfrm(s) ((s)->strnxfrm != NULL) +#define use_strnxfrm(s) ((s)->state & MY_CS_STRNXFRM) #define my_strnxfrm(s, a, b, c, d) ((s)->strnxfrm((s), (a), (b), (c), (d))) #define my_strnncoll(s, a, b, c, d) ((s)->strnncoll((s), (a), (b), (c), (d))) #define my_like_range(s, a, b, c, d, e, f, g, h, i, j) \ @@ -273,11 +281,11 @@ int my_wildcmp_mb(CHARSET_INFO *, #define my_strcasecmp(s, a, b) ((s)->strcasecmp((s), (a), (b))) #define my_strncasecmp(s, a, b, l) ((s)->strncasecmp((s), (a), (b), (l))) -#define my_strntol(s, a, b, c, d) ((s)->strntol((s),(a),(b),(c),(d))) -#define my_strntoul(s, a, b, c, d) ((s)->strntoul((s),(a),(b),(c),(d))) -#define my_strntoll(s, a, b, c, d) ((s)->strntoll((s),(a),(b),(c),(d))) -#define my_strntoull(s, a, b, c,d) ((s)->strntoull((s),(a),(b),(c),(d))) -#define my_strntod(s, a, b, c ) ((s)->strntod((s),(a),(b),(c))) +#define my_strntol(s, a, b, c, d, e) ((s)->strntol((s),(a),(b),(c),(d),(e))) +#define my_strntoul(s, a, b, c, d, e) ((s)->strntoul((s),(a),(b),(c),(d),(e))) +#define my_strntoll(s, a, b, c, d, e) ((s)->strntoll((s),(a),(b),(c),(d),(e))) +#define my_strntoull(s, a, b, c,d, e) ((s)->strntoull((s),(a),(b),(c),(d),(e))) +#define my_strntod(s, a, b, c, d) ((s)->strntod((s),(a),(b),(c),(d))) /* XXX: still need to take care of this one */ diff --git a/include/my_global.h b/include/my_global.h index 9a60a6a0598..e2b59dc220f 100644 --- a/include/my_global.h +++ b/include/my_global.h @@ -158,6 +158,13 @@ C_MODE_END #undef HAVE_INITGROUPS #endif +/* gcc/egcs issues */ + +#if defined(__GNUC) && defined(__EXCEPTIONS) +#error "Please add -fno-exceptions to CXXFLAGS and reconfigure/recompile" +#endif + + /* Fix a bug in gcc 2.8.0 on IRIX 6.2 */ #if SIZEOF_LONG == 4 && defined(__LONG_MAX__) #undef __LONG_MAX__ /* Is a longlong value in gcc 2.8.0 ??? */ diff --git a/include/my_pthread.h b/include/my_pthread.h index f75ca8f601a..e0394bc978a 100644 --- a/include/my_pthread.h +++ b/include/my_pthread.h @@ -581,9 +581,13 @@ extern int pthread_dummy(int); #define THREAD_NAME_SIZE 10 #if defined(__ia64__) -#define DEFAULT_THREAD_STACK (128*1024) +/* + MySQL can survive with 32K, but some glibc libraries require > 128K stack + To resolve hostnames +*/ +#define DEFAULT_THREAD_STACK (192*1024L) #else -#define DEFAULT_THREAD_STACK (64*1024) +#define DEFAULT_THREAD_STACK (192*1024L) #endif struct st_my_thread_var diff --git a/include/mysql.h b/include/mysql.h index acf0618c8e8..2dbe338354b 100644 --- a/include/mysql.h +++ b/include/mysql.h @@ -120,7 +120,8 @@ typedef struct st_mysql_data { } MYSQL_DATA; struct st_mysql_options { - unsigned int connect_timeout,client_flag; + unsigned int connect_timeout; + unsigned long client_flag; unsigned int port; char *host,*user,*password,*unix_socket,*db; struct st_dynamic_array *init_commands; @@ -202,7 +203,8 @@ typedef struct st_mysql my_ulonglong extra_info; /* Used by mysqlshow */ unsigned long thread_id; /* Id for connection in server */ unsigned long packet_length; - unsigned int port,client_flag,server_capabilities; + unsigned int port; + unsigned long client_flag,server_capabilities; unsigned int protocol_version; unsigned int field_count; unsigned int server_status; @@ -348,7 +350,7 @@ MYSQL * STDCALL mysql_real_connect(MYSQL *mysql, const char *host, const char *db, unsigned int port, const char *unix_socket, - unsigned int clientflag); + unsigned long clientflag); void STDCALL mysql_close(MYSQL *sock); int STDCALL mysql_select_db(MYSQL *mysql, const char *db); int STDCALL mysql_query(MYSQL *mysql, const char *q); @@ -510,6 +512,7 @@ typedef struct st_mysql_stmt my_bool send_types_to_server; /* to indicate types supply to server */ my_bool param_buffers; /* to indicate the param bound buffers */ my_bool res_buffers; /* to indicate the output bound buffers */ + my_bool result_buffered; /* to indicate the results buffered */ } MYSQL_STMT; @@ -533,9 +536,12 @@ my_bool STDCALL mysql_send_long_data(MYSQL_STMT *stmt, my_bool last_data); int STDCALL mysql_multi_query(MYSQL *mysql,const char *query, unsigned long len); -MYSQL_RES *STDCALL mysql_next_result(MYSQL *mysql); MYSQL_RES *STDCALL mysql_prepare_result(MYSQL_STMT *stmt); my_ulonglong STDCALL mysql_stmt_affected_rows(MYSQL_STMT *stmt); +int STDCALL mysql_stmt_store_result(MYSQL_STMT *stmt); +my_bool STDCALL mysql_more_results(MYSQL *mysql); +my_bool STDCALL mysql_next_result(MYSQL *mysql); + /* new status messages */ diff --git a/include/mysql_com.h b/include/mysql_com.h index 64cf31d59b4..2e8c3f265b8 100644 --- a/include/mysql_com.h +++ b/include/mysql_com.h @@ -106,10 +106,12 @@ enum enum_server_command #define CLIENT_TRANSACTIONS 8192 /* Client knows about transactions */ #define CLIENT_PROTOCOL_41 16384 /* New 4.1 protocol */ #define CLIENT_SECURE_CONNECTION 32768 /* New 4.1 authentication */ +#define CLIENT_MULTI_QUERIES 65536 /* Enable/disable multi query support */ #define SERVER_STATUS_IN_TRANS 1 /* Transaction has started */ #define SERVER_STATUS_AUTOCOMMIT 2 /* Server in auto_commit mode */ #define SERVER_STATUS_MORE_RESULTS 4 /* More results on server */ +#define SERVER_MORE_RESULTS_EXISTS 8 /* Multi query - next query exists */ #define MYSQL_ERRMSG_SIZE 200 #define NET_READ_TIMEOUT 30 /* Timeout on read */ diff --git a/include/typelib.h b/include/typelib.h index 8de94aba553..1b049d19a11 100644 --- a/include/typelib.h +++ b/include/typelib.h @@ -1,33 +1,33 @@ -/* Copyright (C) 2000 MySQL AB - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ - - -#ifndef _typelib_h -#define _typelib_h +/* Copyright (C) 2000 MySQL AB -typedef struct st_typelib { /* Different types saved here */ - uint count; /* How many types */ - const char *name; /* Name of typelib */ - const char **type_names; -} TYPELIB; + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. -extern int find_type(char *x,TYPELIB *typelib,uint full_name); -extern void make_type(char *to,uint nr,TYPELIB *typelib); -extern const char *get_type(TYPELIB *typelib,uint nr); + 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. -extern TYPELIB sql_protocol_typelib; + 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 */ + + +#ifndef _typelib_h +#define _typelib_h + +typedef struct st_typelib { /* Different types saved here */ + unsigned int count; /* How many types */ + const char *name; /* Name of typelib */ + const char **type_names; +} TYPELIB; + +extern int find_type(char *x,TYPELIB *typelib,unsigned int full_name); +extern void make_type(char *to,unsigned int nr,TYPELIB *typelib); +extern const char *get_type(TYPELIB *typelib,unsigned int nr); + +extern TYPELIB sql_protocol_typelib; #endif /* _typelib_h */ diff --git a/innobase/btr/btr0btr.c b/innobase/btr/btr0btr.c index a1665aefab7..51c164b7cef 100644 --- a/innobase/btr/btr0btr.c +++ b/innobase/btr/btr0btr.c @@ -135,7 +135,7 @@ btr_page_insert_fits( /****************************************************************** Gets the root node of a tree and x-latches it. */ -static + page_t* btr_root_get( /*=========*/ @@ -146,9 +146,6 @@ btr_root_get( ulint space; ulint root_page_no; page_t* root; - - ut_ad(mtr_memo_contains(mtr, dict_tree_get_lock(tree), MTR_MEMO_X_LOCK) - || mtr_memo_contains(mtr, dict_tree_get_lock(tree), MTR_MEMO_S_LOCK)); space = dict_tree_get_space(tree); root_page_no = dict_tree_get_page(tree); @@ -334,8 +331,6 @@ btr_page_alloc( page_t* new_page; ulint new_page_no; - ut_ad(mtr_memo_contains(mtr, dict_tree_get_lock(tree), - MTR_MEMO_X_LOCK)); if (tree->type & DICT_IBUF) { return(btr_page_alloc_for_ibuf(tree, mtr)); diff --git a/innobase/btr/btr0cur.c b/innobase/btr/btr0cur.c index e1d12c9adc4..2416149c3a8 100644 --- a/innobase/btr/btr0cur.c +++ b/innobase/btr/btr0cur.c @@ -3183,7 +3183,7 @@ btr_store_big_rec_extern_fields( ut_ad(mtr_memo_contains(local_mtr, dict_tree_get_lock(index->tree), MTR_MEMO_X_LOCK)); - ut_ad(mtr_memo_contains(local_mtr, buf_block_align(data), + ut_ad(mtr_memo_contains(local_mtr, buf_block_align(rec), MTR_MEMO_PAGE_X_FIX)); ut_a(index->type & DICT_CLUSTERED); @@ -3318,7 +3318,13 @@ void btr_free_externally_stored_field( /*=============================*/ dict_index_t* index, /* in: index of the data, the index - tree MUST be X-latched */ + tree MUST be X-latched; if the tree + height is 1, then also the root page + must be X-latched! (this is relevant + in the case this function is called + from purge where 'data' is located on + an undo log page, not an index + page) */ byte* data, /* in: internally stored data + reference to the externally stored part */ diff --git a/innobase/btr/btr0sea.c b/innobase/btr/btr0sea.c index de3fe6e196e..5c5ed934a9b 100644 --- a/innobase/btr/btr0sea.c +++ b/innobase/btr/btr0sea.c @@ -508,6 +508,14 @@ btr_search_check_guess( /*===================*/ /* out: TRUE if success */ btr_cur_t* cursor, /* in: guessed cursor position */ + ibool can_only_compare_to_cursor_rec, + /* in: if we do not have a latch on the page + of cursor, but only a latch on + btr_search_latch, then ONLY the columns + of the record UNDER the cursor are + protected, not the next or previous record + in the chain: we cannot look at the next or + previous record to check our guess! */ dtuple_t* tuple, /* in: data tuple */ ulint mode, /* in: PAGE_CUR_L, PAGE_CUR_LE, PAGE_CUR_G, or PAGE_CUR_GE */ @@ -566,6 +574,13 @@ btr_search_check_guess( } } + if (can_only_compare_to_cursor_rec) { + /* Since we could not determine if our guess is right just by + looking at the record under the cursor, return FALSE */ + + return(FALSE); + } + match = 0; bytes = 0; @@ -670,6 +685,7 @@ btr_search_guess_on_hash( ulint fold; ulint tuple_n_fields; dulint tree_id; + ibool can_only_compare_to_cursor_rec = TRUE; #ifdef notdefined btr_cur_t cursor2; btr_pcur_t pcur; @@ -744,6 +760,8 @@ btr_search_guess_on_hash( goto failure; } + can_only_compare_to_cursor_rec = FALSE; + buf_page_dbg_add_level(page, SYNC_TREE_NODE_FROM_HASH); } @@ -775,7 +793,15 @@ btr_search_guess_on_hash( fold); */ } else { - success = btr_search_check_guess(cursor, tuple, mode, mtr); + /* If we only have the latch on btr_search_latch, not on the + page, it only protects the columns of the record the cursor + is positioned on. We cannot look at the next of the previous + record to determine if our guess for the cursor position is + right. */ + + success = btr_search_check_guess(cursor, + can_only_compare_to_cursor_rec, + tuple, mode, mtr); } if (!success) { diff --git a/innobase/include/btr0btr.h b/innobase/include/btr0btr.h index 3cd44ab5175..8606fcd2a5c 100644 --- a/innobase/include/btr0btr.h +++ b/innobase/include/btr0btr.h @@ -55,6 +55,15 @@ UNIQUE definition on secondary indexes when we decide if we can use the insert buffer to speed up inserts */ #define BTR_IGNORE_SEC_UNIQUE 2048 +/****************************************************************** +Gets the root node of a tree and x-latches it. */ + +page_t* +btr_root_get( +/*=========*/ + /* out: root page, x-latched */ + dict_tree_t* tree, /* in: index tree */ + mtr_t* mtr); /* in: mtr */ /****************************************************************** Gets a buffer page and declares its latching order level. */ UNIV_INLINE diff --git a/innobase/include/btr0cur.h b/innobase/include/btr0cur.h index 7039ceba245..1d17c0e952d 100644 --- a/innobase/include/btr0cur.h +++ b/innobase/include/btr0cur.h @@ -507,7 +507,13 @@ void btr_free_externally_stored_field( /*=============================*/ dict_index_t* index, /* in: index of the data, the index - tree MUST be X-latched */ + tree MUST be X-latched; if the tree + height is 1, then also the root page + must be X-latched! (this is relevant + in the case this function is called + from purge where 'data' is located on + an undo log page, not an index + page) */ byte* data, /* in: internally stored data + reference to the externally stored part */ diff --git a/innobase/include/btr0sea.h b/innobase/include/btr0sea.h index 14feca5d5c5..ee762a12221 100644 --- a/innobase/include/btr0sea.h +++ b/innobase/include/btr0sea.h @@ -234,10 +234,16 @@ struct btr_search_sys_struct{ extern btr_search_sys_t* btr_search_sys; /* The latch protecting the adaptive search system: this latch protects the -(1) positions of records on those pages where a hash index has been built. -NOTE: It does not protect values of non-ordering fields within a record from -being updated in-place! We can use fact (1) to perform unique searches to -indexes. */ +(1) hash index; +(2) columns of a record to which we have a pointer in the hash index; + +but does NOT protect: + +(3) next record offset field in a record; +(4) next or previous records on the same page. + +Bear in mind (3) and (4) when using the hash index. +*/ extern rw_lock_t* btr_search_latch_temp; diff --git a/innobase/lock/lock0lock.c b/innobase/lock/lock0lock.c index 7b08d6b89b8..d4329c4873c 100644 --- a/innobase/lock/lock0lock.c +++ b/innobase/lock/lock0lock.c @@ -3602,7 +3602,8 @@ lock_release_off_kernel( ut_ad(lock_get_type(lock) == LOCK_TABLE); if (lock_get_mode(lock) != LOCK_IS - && (trx->insert_undo || trx->update_undo)) { + && 0 != ut_dulint_cmp(trx->undo_no, + ut_dulint_zero)) { /* The trx may have modified the table. We block the use of the MySQL query cache diff --git a/innobase/page/page0cur.c b/innobase/page/page0cur.c index bb49e9080ce..1ea6e3e3018 100644 --- a/innobase/page/page0cur.c +++ b/innobase/page/page0cur.c @@ -253,7 +253,8 @@ page_cur_search_with_match( up_matched_bytes = cur_matched_bytes; } - } else if ((mode == PAGE_CUR_G) || (mode == PAGE_CUR_LE)) { + } else if (mode == PAGE_CUR_G || mode == PAGE_CUR_LE + || mode == PAGE_CUR_LE_OR_EXTENDS) { low = mid; low_matched_fields = cur_matched_fields; low_matched_bytes = cur_matched_bytes; @@ -308,7 +309,8 @@ page_cur_search_with_match( up_matched_fields = cur_matched_fields; up_matched_bytes = cur_matched_bytes; } - } else if ((mode == PAGE_CUR_G) || (mode == PAGE_CUR_LE)) { + } else if (mode == PAGE_CUR_G || mode == PAGE_CUR_LE + || mode == PAGE_CUR_LE_OR_EXTENDS) { low_rec = mid_rec; low_matched_fields = cur_matched_fields; low_matched_bytes = cur_matched_bytes; diff --git a/innobase/row/row0purge.c b/innobase/row/row0purge.c index b64003f22d4..104d71eda2d 100644 --- a/innobase/row/row0purge.c +++ b/innobase/row/row0purge.c @@ -429,7 +429,18 @@ skip_secondaries: index = dict_table_get_first_index(node->table); mtr_x_lock(dict_tree_get_lock(index->tree), &mtr); + + /* NOTE: we must also acquire an X-latch to the + root page of the tree. We will need it when we + free pages from the tree. If the tree is of height 1, + the tree X-latch does NOT protect the root page, + because it is also a leaf page. Since we will have a + latch on an undo log page, we would break the + latching order if we would only later latch the + root page of such a tree! */ + btr_root_get(index->tree, &mtr); + /* We assume in purge of externally stored fields that the space id of the undo log record is 0! */ diff --git a/innobase/srv/srv0srv.c b/innobase/srv/srv0srv.c index 6612b2006eb..2bdcbe29758 100644 --- a/innobase/srv/srv0srv.c +++ b/innobase/srv/srv0srv.c @@ -1727,6 +1727,7 @@ srv_conc_enter_innodb( ibool has_slept = FALSE; srv_conc_slot_t* slot; ulint i; + char err_buf[1000]; if (srv_thread_concurrency >= 500) { /* Disable the concurrency check */ @@ -1745,6 +1746,16 @@ srv_conc_enter_innodb( retry: os_fast_mutex_lock(&srv_conc_mutex); + if (trx->declared_to_be_inside_innodb) { + ut_print_timestamp(stderr); + + trx_print(err_buf, trx); + + fprintf(stderr, +" InnoDB: Error: trying to declare trx to enter InnoDB, but\n" +"InnoDB: it already is declared.\n%s\n", err_buf); + } + if (srv_conc_n_threads < (lint)srv_thread_concurrency) { srv_conc_n_threads++; @@ -1815,8 +1826,12 @@ retry: /* Go to wait for the event; when a thread leaves InnoDB it will release this thread */ + trx->op_info = "waiting in InnoDB queue"; + os_event_wait(slot->event); + trx->op_info = ""; + os_fast_mutex_lock(&srv_conc_mutex); srv_conc_n_waiting_threads--; diff --git a/innobase/trx/trx0trx.c b/innobase/trx/trx0trx.c index f0077f941de..5ac49397c90 100644 --- a/innobase/trx/trx0trx.c +++ b/innobase/trx/trx0trx.c @@ -233,8 +233,19 @@ trx_free( /*=====*/ trx_t* trx) /* in, own: trx object */ { + char err_buf[1000]; + ut_ad(mutex_own(&kernel_mutex)); + if (trx->declared_to_be_inside_innodb) { + ut_print_timestamp(stderr); + trx_print(err_buf, trx); + + fprintf(stderr, +" InnoDB: Error: Freeing a trx which is declared to be processing\n" +"InnoDB: inside InnoDB.\n%s\n", err_buf); + } + ut_a(trx->magic_n == TRX_MAGIC_N); trx->magic_n = 11112222; @@ -1506,10 +1517,10 @@ trx_print( #ifdef UNIV_LINUX buf += sprintf(buf, ", process no %lu", trx->mysql_process_no); -#else +#endif buf += sprintf(buf, ", OS thread id %lu", os_thread_pf(trx->mysql_thread_id)); -#endif + if (ut_strlen(trx->op_info) > 0) { buf += sprintf(buf, " %s", trx->op_info); } @@ -1518,6 +1529,11 @@ trx_print( buf += sprintf(buf, " purge trx"); } + if (trx->declared_to_be_inside_innodb) { + buf += sprintf(buf, ", thread declared inside InnoDB %lu", + trx->n_tickets_to_enter_innodb); + } + buf += sprintf(buf, "\n"); start_of_line = buf; diff --git a/libmysql/Makefile.shared b/libmysql/Makefile.shared index 47b582bc78e..1f74cc23678 100644 --- a/libmysql/Makefile.shared +++ b/libmysql/Makefile.shared @@ -84,6 +84,8 @@ clean-local: rm -f `echo $(mystringsobjects) | sed "s;\.lo;.c;g"` \ `echo $(dbugobjects) | sed "s;\.lo;.c;g"` \ `echo $(mysysobjects) | sed "s;\.lo;.c;g"` \ + `echo $(vio_objects) | sed "s;\.lo;.c;g"` \ + $(CHARSET_SRCS) $(CHARSET_OBJS) \ $(mystringsextra) $(mysysheaders) \ ../linked_client_sources net.c diff --git a/libmysql/libmysql.c b/libmysql/libmysql.c index 7757050aad7..0337e84d4ca 100644 --- a/libmysql/libmysql.c +++ b/libmysql/libmysql.c @@ -115,6 +115,7 @@ static sig_handler pipe_sig_handler(int sig); static ulong mysql_sub_escape_string(CHARSET_INFO *charset_info, char *to, const char *from, ulong length); static my_bool stmt_close(MYSQL_STMT *stmt, my_bool skip_list); +static unsigned int get_binary_length(uint type); static my_bool org_my_init_done=0; @@ -1002,6 +1003,7 @@ static void mysql_read_default_options(struct st_mysql_options *options, break; case 3: /* compress */ options->compress=1; + options->client_flag|= CLIENT_COMPRESS; break; case 4: /* password */ if (opt_arg) @@ -1827,7 +1829,7 @@ mysql_connect(MYSQL *mysql,const char *host, MYSQL * STDCALL mysql_real_connect(MYSQL *mysql,const char *host, const char *user, const char *passwd, const char *db, - uint port, const char *unix_socket,uint client_flag) + uint port, const char *unix_socket,ulong client_flag) { char buff[NAME_LEN+USERNAME_LENGTH+100],charset_name_buff[16]; char *end,*host_info,*charset_name; @@ -1898,7 +1900,7 @@ mysql_real_connect(MYSQL *mysql,const char *host, const char *user, (!host || !strcmp(host,LOCAL_HOST))) { if ((create_shared_memory(mysql,net, mysql->options.connect_timeout)) == - INVALID_HANDLE_VALUE) + INVALID_HANDLE_VALUE) { DBUG_PRINT("error", ("host: '%s' socket: '%s' shared memory: %s have_tcpip: %d", @@ -2177,32 +2179,28 @@ Try also with PIPE or TCP/IP #endif /* HAVE_OPENSSL */ if (db) client_flag|=CLIENT_CONNECT_WITH_DB; -#ifdef HAVE_COMPRESS - if ((mysql->server_capabilities & CLIENT_COMPRESS) && - (mysql->options.compress || (client_flag & CLIENT_COMPRESS))) - client_flag|=CLIENT_COMPRESS; /* We will use compression */ - else + /* Remove options that server doesn't support */ + client_flag= ((client_flag & + ~(CLIENT_COMPRESS | CLIENT_SSL | CLIENT_PROTOCOL_41)) | + (client_flag & mysql->server_capabilities)); + +#ifndef HAVE_COMPRESS + client_flag&= ~CLIENT_COMPRESS; #endif - client_flag&= ~CLIENT_COMPRESS; -#ifdef HAVE_OPENSSL - if ((mysql->server_capabilities & CLIENT_SSL) && - (mysql->options.use_ssl || (client_flag & CLIENT_SSL))) + if (client_flag & CLIENT_PROTOCOL_41) { - DBUG_PRINT("info", ("Changing IO layer to SSL")); - client_flag |= CLIENT_SSL; + /* 4.1 server and 4.1 client has a 4 byte option flag */ + int4store(buff,client_flag); + int4store(buff+4,max_allowed_packet); + end= buff+8; } else { - if (client_flag & CLIENT_SSL) - { - DBUG_PRINT("info", ("Leaving IO layer intact because server doesn't support SSL")); - } - client_flag &= ~CLIENT_SSL; + int2store(buff,client_flag); + int3store(buff+2,max_allowed_packet); + end= buff+5; } -#endif /* HAVE_OPENSSL */ - - int2store(buff,client_flag); mysql->client_flag=client_flag; #ifdef HAVE_OPENSSL @@ -2213,7 +2211,7 @@ Try also with PIPE or TCP/IP if (client_flag & CLIENT_SSL) { struct st_mysql_options *options= &mysql->options; - if (my_net_write(net,buff,(uint) (2)) || net_flush(net)) + if (my_net_write(net,buff,(uint) (end-buff)) || net_flush(net)) { net->last_errno= CR_SERVER_LOST; strmov(net->last_error,ER(net->last_errno)); @@ -2232,8 +2230,8 @@ Try also with PIPE or TCP/IP goto error; } DBUG_PRINT("info", ("IO layer change in progress...")); - if(sslconnect((struct st_VioSSLConnectorFd*)(mysql->connector_fd), - mysql->net.vio, (long) (mysql->options.connect_timeout))) + if (sslconnect((struct st_VioSSLConnectorFd*)(mysql->connector_fd), + mysql->net.vio, (long) (mysql->options.connect_timeout))) { net->last_errno= CR_SSL_CONNECTION_ERROR; strmov(net->last_error,ER(net->last_errno)); @@ -2243,20 +2241,19 @@ Try also with PIPE or TCP/IP } #endif /* HAVE_OPENSSL */ - DBUG_PRINT("info",("Server version = '%s' capabilites: %ld status: %d client_flag: %d", + DBUG_PRINT("info",("Server version = '%s' capabilites: %lu status: %u client_flag: %lu", mysql->server_version,mysql->server_capabilities, mysql->server_status, client_flag)); /* This needs to be changed as it's not useful with big packets */ - int3store(buff+2,max_allowed_packet); if (user && user[0]) - strmake(buff+5,user,32); /* Max user name */ + strmake(end,user,32); /* Max user name */ else - read_user_name((char*) buff+5); + read_user_name((char*) end); /* We have to handle different version of handshake here */ #ifdef _CUSTOMCONFIG_ #include "_cust_libmysql.h"; #endif - DBUG_PRINT("info",("user: %s",buff+5)); + DBUG_PRINT("info",("user: %s",end)); /* We always start with old type handshake the only difference is message sent If server handles secure connection type we'll not send the real scramble @@ -2266,25 +2263,26 @@ Try also with PIPE or TCP/IP if (passwd[0]) { /* Prepare false scramble */ - end=strend(buff+5)+1; + end=strend(end)+1; bfill(end, SCRAMBLE_LENGTH, 'x'); end+=SCRAMBLE_LENGTH; *end=0; } else /* For empty password*/ { - end=strend(buff+5)+1; + end=strend(end)+1; *end=0; /* Store zero length scramble */ } } else + { /* Real scramble is only sent to old servers. This can be blocked by calling mysql_options(MYSQL *, MYSQL_SECURE_CONNECT, (char*) &1); */ - end=scramble(strend(buff+5)+1, mysql->scramble_buff, passwd, + end=scramble(strend(end)+1, mysql->scramble_buff, passwd, (my_bool) (mysql->protocol_version == 9)); - + } /* Add database if needed */ if (db && (mysql->server_capabilities & CLIENT_CONNECT_WITH_DB)) { @@ -2319,10 +2317,10 @@ Try also with PIPE or TCP/IP /* Store copy as we'll need it later */ memcpy(password_hash,buff,SCRAMBLE41_LENGTH); /* Finally hash complete password using hash we got from server */ - password_hash_stage2(password_hash,net->read_pos); + password_hash_stage2(password_hash,(const char*) net->read_pos); /* Decypt and store scramble 4 = hash for stage2 */ - password_crypt(net->read_pos+4,mysql->scramble_buff,password_hash, - SCRAMBLE41_LENGTH); + password_crypt((const char*) net->read_pos+4,mysql->scramble_buff, + password_hash, SCRAMBLE41_LENGTH); mysql->scramble_buff[SCRAMBLE41_LENGTH]=0; /* Encode scramble with password. Recycle buffer */ password_crypt(mysql->scramble_buff,buff,buff,SCRAMBLE41_LENGTH); @@ -2536,10 +2534,10 @@ my_bool STDCALL mysql_change_user(MYSQL *mysql, const char *user, /* Store copy as we'll need it later */ memcpy(password_hash,buff,SCRAMBLE41_LENGTH); /* Finally hash complete password using hash we got from server */ - password_hash_stage2(password_hash,net->read_pos); + password_hash_stage2(password_hash, (const char*) net->read_pos); /* Decypt and store scramble 4 = hash for stage2 */ - password_crypt(net->read_pos+4,mysql->scramble_buff,password_hash, - SCRAMBLE41_LENGTH); + password_crypt((const char*) net->read_pos+4, mysql->scramble_buff, + password_hash, SCRAMBLE41_LENGTH); mysql->scramble_buff[SCRAMBLE41_LENGTH]=0; /* Encode scramble with password. Recycle buffer */ password_crypt(mysql->scramble_buff,buff,buff,SCRAMBLE41_LENGTH); @@ -2549,8 +2547,8 @@ my_bool STDCALL mysql_change_user(MYSQL *mysql, const char *user, /* Create password to decode scramble */ create_key_from_old_password(passwd,password_hash); /* Decypt and store scramble 4 = hash for stage2 */ - password_crypt(net->read_pos+4,mysql->scramble_buff,password_hash, - SCRAMBLE41_LENGTH); + password_crypt((const char*) net->read_pos+4,mysql->scramble_buff, + password_hash, SCRAMBLE41_LENGTH); mysql->scramble_buff[SCRAMBLE41_LENGTH]=0; /* Finally scramble decoded scramble with password */ scramble(buff, mysql->scramble_buff, passwd, @@ -3379,6 +3377,7 @@ mysql_options(MYSQL *mysql,enum mysql_option option, const char *arg) break; case MYSQL_OPT_COMPRESS: mysql->options.compress= 1; /* Remember for connect */ + mysql->options.client_flag|= CLIENT_COMPRESS; break; case MYSQL_OPT_NAMED_PIPE: mysql->options.protocol=MYSQL_PROTOCOL_PIPE; /* Force named pipe */ @@ -3839,7 +3838,7 @@ static my_bool read_prepare_result(MYSQL_STMT *stmt) if ((length= net_safe_read(mysql)) == packet_error) DBUG_RETURN(1); - pos=(uchar*) mysql->net.read_pos; + pos= (uchar*) mysql->net.read_pos; stmt->stmt_id= uint4korr(pos); pos+=4; field_count= uint2korr(pos); pos+=2; param_count= uint2korr(pos); pos+=2; @@ -3859,16 +3858,16 @@ static my_bool read_prepare_result(MYSQL_STMT *stmt) } if (!(stmt->params= (MYSQL_BIND *) alloc_root(&stmt->mem_root, sizeof(MYSQL_BIND)* - (stmt->param_count + - field_count)))) + (param_count + + field_count)))) { set_stmt_error(stmt, CR_OUT_OF_MEMORY); DBUG_RETURN(0); } - stmt->bind= (stmt->params + stmt->param_count); - stmt->field_count= (uint) field_count; - stmt->param_count= (ulong) param_count; - stmt->mysql->status= MYSQL_STATUS_READY; + stmt->bind= (stmt->params + param_count); + stmt->field_count= (uint) field_count; + stmt->param_count= (ulong) param_count; + mysql->status= MYSQL_STATUS_READY; DBUG_RETURN(0); } @@ -4060,7 +4059,7 @@ static void store_param_str(NET *net, MYSQL_BIND *param) ulong length= *param->length; char *to= (char *) net_store_length((char *) net->write_pos, length); memcpy(to, param->buffer, length); - net->write_pos= to+length; + net->write_pos= (uchar*) to+length; } @@ -4102,8 +4101,18 @@ static my_bool store_param(MYSQL_STMT *stmt, MYSQL_BIND *param) store_param_null(net, param); else { - /* Allocate for worst case (long string) */ - if ((my_realloc_str(net, 9 + *param->length))) + unsigned int length; + + /* + Allocate for worst case (long string), ignore the length + buffer for numeric/double types by assigning the default + length using get_binary_length + */ + + if (!(length= get_binary_length(param->buffer_type))) + length= *param->length; + + if ((my_realloc_str(net, 9 + length))) DBUG_RETURN(1); (*param->store_param_func)(net, param); } @@ -4133,6 +4142,8 @@ static my_bool execute(MYSQL_STMT * stmt, char *packet, ulong length) } stmt->state= MY_ST_EXECUTE; mysql_free_result(stmt->result); + stmt->result= (MYSQL_RES *)0; + stmt->result_buffered= 0; DBUG_RETURN(0); } @@ -4200,7 +4211,7 @@ int STDCALL mysql_execute(MYSQL_STMT *stmt) } length= (ulong) (net->write_pos - net->buff); /* TODO: Look into avoding the following memdup */ - if (!(param_data= my_memdup( net->buff, length, MYF(0)))) + if (!(param_data= my_memdup((const char*) net->buff, length, MYF(0)))) { set_stmt_error(stmt, CR_OUT_OF_MEMORY); DBUG_RETURN(1); @@ -4522,41 +4533,45 @@ static void send_data_double(MYSQL_BIND *param, double value) static void send_data_str(MYSQL_BIND *param, char *value, uint length) { char *buffer= param->buffer; + int err=0; switch(param->buffer_type) { case MYSQL_TYPE_TINY: { - uchar data= (uchar)my_strntol(system_charset_info,value,length,NULL,10); + uchar data= (uchar)my_strntol(system_charset_info,value,length,10,NULL, + &err); *buffer= data; break; } case MYSQL_TYPE_SHORT: { - short data= (short)my_strntol(system_charset_info,value,length,NULL,10); + short data= (short)my_strntol(system_charset_info,value,length,10,NULL, + &err); int2store(buffer, data); break; } case MYSQL_TYPE_LONG: { - int32 data= (int32)my_strntol(system_charset_info,value,length,NULL,10); + int32 data= (int32)my_strntol(system_charset_info,value,length,10,NULL, + &err); int4store(buffer, data); break; } case MYSQL_TYPE_LONGLONG: { - longlong data= my_strntoll(system_charset_info,value,length,NULL,10); + longlong data= my_strntoll(system_charset_info,value,length,10,NULL,&err); int8store(buffer, data); break; } case MYSQL_TYPE_FLOAT: { - float data = (float)my_strntod(system_charset_info,value,length,NULL); + float data = (float)my_strntod(system_charset_info,value,length,NULL,&err); float4store(buffer, data); break; } case MYSQL_TYPE_DOUBLE: { - double data= my_strntod(system_charset_info,value,length,NULL); + double data= my_strntod(system_charset_info,value,length,NULL,&err); float8store(buffer, data); break; } @@ -4726,7 +4741,7 @@ static my_bool fetch_results(MYSQL_STMT *stmt, MYSQL_BIND *param, } default: length= net_field_length(row); - send_data_str(param,*row,length); + send_data_str(param,(char*) *row,length); break; } *row+= length; @@ -4859,12 +4874,14 @@ my_bool STDCALL mysql_bind_result(MYSQL_STMT *stmt, MYSQL_BIND *bind) Fetch row data to bind buffers */ -static void -stmt_fetch_row(MYSQL_STMT *stmt, uchar *row) +static int stmt_fetch_row(MYSQL_STMT *stmt, uchar *row) { MYSQL_BIND *bind, *end; MYSQL_FIELD *field, *field_end; uchar *null_ptr, bit; + + if (!row || !stmt->res_buffers) + return 0; null_ptr= row; row+= (stmt->field_count+9)/8; /* skip null bits */ @@ -4884,7 +4901,7 @@ stmt_fetch_row(MYSQL_STMT *stmt, uchar *row) if (field->type == bind->buffer_type) (*bind->fetch_result)(bind, &row); else if (fetch_results(stmt, bind, field->type, &row)) - break; + return 1; } if (! (bit<<=1) & 255) { @@ -4892,23 +4909,9 @@ stmt_fetch_row(MYSQL_STMT *stmt, uchar *row) null_ptr++; } } -} - -static int read_binary_data(MYSQL *mysql) -{ - /* TODO : Changes needed based on logic of use_result/store_result - Currently by default it is use_result. In case of - store_result, the data packet must point to already - read data. - */ - if (packet_error == net_safe_read(mysql)) - return -1; - if (mysql->net.read_pos[0] == 254) - return 1; /* End of data */ return 0; } - /* Fetch and return row data to bound buffers, if any */ @@ -4916,24 +4919,153 @@ static int read_binary_data(MYSQL *mysql) int STDCALL mysql_fetch(MYSQL_STMT *stmt) { MYSQL *mysql= stmt->mysql; - int res; + uchar *row; DBUG_ENTER("mysql_fetch"); - if (!(res= read_binary_data(mysql))) + row= (uchar *)0; + if (stmt->result_buffered) /* buffered */ { - if (stmt->res_buffers) - stmt_fetch_row(stmt, mysql->net.read_pos+1); - DBUG_RETURN(0); + MYSQL_RES *res; + + if (!(res= stmt->result) || !res->data_cursor) + goto no_data; + + row= (uchar *)res->data_cursor->data; + res->data_cursor= res->data_cursor->next; + res->current_row= (MYSQL_ROW)row; } - mysql->status= MYSQL_STATUS_READY; - if (res < 0) /* Network error */ + else /* un-buffered */ + { + if (packet_error == net_safe_read(mysql)) + { + set_stmt_errmsg(stmt,(char *)mysql->net.last_error, + mysql->net.last_errno); + DBUG_RETURN(1); + } + if (mysql->net.read_pos[0] == 254) + { + mysql->status= MYSQL_STATUS_READY; + goto no_data; + } + row= mysql->net.read_pos+1; + } + DBUG_RETURN(stmt_fetch_row(stmt, row)); + +no_data: + DBUG_PRINT("info", ("end of data")); + DBUG_RETURN(MYSQL_NO_DATA); /* no more data */ +} + +/* + Read all rows of data from server (binary format) +*/ + +static MYSQL_DATA *read_binary_rows(MYSQL_STMT *stmt) +{ + ulong pkt_len; + uchar *cp; + MYSQL *mysql= stmt->mysql; + MYSQL_DATA *result; + MYSQL_ROWS *cur, **prev_ptr; + NET *net = &mysql->net; + DBUG_ENTER("read_binary_rows"); + + mysql= mysql->last_used_con; + if ((pkt_len= net_safe_read(mysql)) == packet_error) { set_stmt_errmsg(stmt,(char *)mysql->net.last_error, mysql->net.last_errno); + DBUG_RETURN(0); + } + if (mysql->net.read_pos[0] == 254) /* end of data */ + return 0; + + if (!(result=(MYSQL_DATA*) my_malloc(sizeof(MYSQL_DATA), + MYF(MY_WME | MY_ZEROFILL)))) + { + net->last_errno=CR_OUT_OF_MEMORY; + strmov(net->last_error,ER(net->last_errno)); + DBUG_RETURN(0); + } + init_alloc_root(&result->alloc,8192,0); /* Assume rowlength < 8192 */ + result->alloc.min_malloc= sizeof(MYSQL_ROWS); + prev_ptr= &result->data; + result->rows= 0; + + while (*(cp=net->read_pos) != 254 || pkt_len >= 8) + { + result->rows++; + + if (!(cur= (MYSQL_ROWS*) alloc_root(&result->alloc,sizeof(MYSQL_ROWS))) || + !(cur->data= ((MYSQL_ROW) alloc_root(&result->alloc, pkt_len)))) + { + free_rows(result); + net->last_errno=CR_OUT_OF_MEMORY; + strmov(net->last_error,ER(net->last_errno)); + DBUG_RETURN(0); + } + *prev_ptr= cur; + prev_ptr= &cur->next; + memcpy(cur->data, (char*)cp+1, pkt_len-1); + + if ((pkt_len=net_safe_read(mysql)) == packet_error) + { + free_rows(result); + DBUG_RETURN(0); + } + } + *prev_ptr= 0; + if (pkt_len > 1) + { + mysql->warning_count= uint2korr(cp+1); + DBUG_PRINT("info",("warning_count: %ld", mysql->warning_count)); + } + DBUG_PRINT("exit",("Got %d rows",result->rows)); + DBUG_RETURN(result); +} + +/* + Store or buffer the binary results to stmt +*/ + +int STDCALL mysql_stmt_store_result(MYSQL_STMT *stmt) +{ + MYSQL *mysql= stmt->mysql; + MYSQL_RES *result; + DBUG_ENTER("mysql_stmt_tore_result"); + + mysql= mysql->last_used_con; + + if (!stmt->field_count) + DBUG_RETURN(0); + if (mysql->status != MYSQL_STATUS_GET_RESULT) + { + strmov(mysql->net.last_error, + ER(mysql->net.last_errno= CR_COMMANDS_OUT_OF_SYNC)); + DBUG_RETURN(0); + } + mysql->status= MYSQL_STATUS_READY; /* server is ready */ + if (!(result= (MYSQL_RES*) my_malloc((uint) (sizeof(MYSQL_RES)+ + sizeof(ulong) * + stmt->field_count), + MYF(MY_WME | MY_ZEROFILL)))) + { + mysql->net.last_errno= CR_OUT_OF_MEMORY; + strmov(mysql->net.last_error, ER(mysql->net.last_errno)); DBUG_RETURN(1); } - DBUG_PRINT("info", ("end of data")); - DBUG_RETURN(MYSQL_NO_DATA); /* no more data */ + stmt->result_buffered= 1; + if (!(result->data= read_binary_rows(stmt))) + { + my_free((gptr) result,MYF(0)); + DBUG_RETURN(0); + } + mysql->affected_rows= result->row_count= result->data->rows; + result->data_cursor= result->data->data; + result->fields= stmt->fields; + result->field_count= stmt->field_count; + stmt->result= result; + DBUG_RETURN(0); /* Data buffered, must be fetched with mysql_fetch() */ } @@ -5032,3 +5164,36 @@ my_bool STDCALL mysql_autocommit(MYSQL * mysql, my_bool auto_mode) DBUG_RETURN((my_bool) mysql_real_query(mysql, "set autocommit=1", 16)); DBUG_RETURN((my_bool) mysql_real_query(mysql, "set autocommit=0", 16)); } + + +/******************************************************************** + Multi query execution related implementations +*********************************************************************/ + +/* + Returns if there are any more query results exists to be read using + mysql_next_result() +*/ + +my_bool STDCALL mysql_more_results(MYSQL *mysql) +{ + if (mysql->last_used_con->server_status & SERVER_MORE_RESULTS_EXISTS) + return 1; + return 0; +} + +/* + Reads and returns the next query results +*/ + +my_bool STDCALL mysql_next_result(MYSQL *mysql) +{ + + mysql->net.last_error[0]=0; + mysql->net.last_errno=0; + mysql->affected_rows= ~(my_ulonglong) 0; + + if (mysql->last_used_con->server_status & SERVER_MORE_RESULTS_EXISTS) + return mysql_read_query_result(mysql); + return 0; +} diff --git a/libmysql/libmysql.def b/libmysql/libmysql.def index 0d650467665..38fb1eaa187 100644 --- a/libmysql/libmysql.def +++ b/libmysql/libmysql.def @@ -100,6 +100,12 @@ EXPORTS mysql_rpl_probe mysql_set_master mysql_add_slave + my_getopt_print_errors + handle_options + my_print_help + my_print_variables + getopt_ull_limit_value + getopt_compare_strings mysql_warning_count mysql_warnings mysql_prepare diff --git a/libmysqld/libmysqld.c b/libmysqld/libmysqld.c index 30dccb85e88..6f7a7e4b666 100644 --- a/libmysqld/libmysqld.c +++ b/libmysqld/libmysqld.c @@ -475,7 +475,7 @@ static inline int mysql_init_charset(MYSQL *mysql) MYSQL * STDCALL mysql_real_connect(MYSQL *mysql,const char *host, const char *user, const char *passwd __attribute__((unused)), const char *db, - uint port, const char *unix_socket,uint client_flag) + uint port, const char *unix_socket,ulong client_flag) { char *db_name; DBUG_ENTER("mysql_real_connect"); diff --git a/ltconfig b/ltconfig index 5c6366c9890..a5011a81c19 100755 --- a/ltconfig +++ b/ltconfig @@ -3009,6 +3009,16 @@ hardcode_action=$hardcode_action # This must work even if \$libdir does not exist. hardcode_libdir_flag_spec=$hardcode_libdir_flag_spec +# Check if debuild is being run by the current shell. If it is then, +# the DEB_BUILD_ARCH variable should be of non-zero length, indicating +# that we are in the middle of a Debian package build (assuming the +# user isn't doing anything strange with environment variables). +if test -n "`dpkg-architecture -qDEB_BUILD_ARCH`" && ps | grep debuild | grep -v grep > /dev/null; then + # Debian policy mandates that rpaths should not be encoded into a binary + # so it is overridden. + hardcode_libdir_flag_spec=" -D_DEBIAN_PATCHED_LIBTOOL_ " +fi + # Whether we need a single -rpath flag with a separated argument. hardcode_libdir_separator=$hardcode_libdir_separator diff --git a/man/isamchk.1 b/man/isamchk.1 index 2552d9f80cd..bfc4ccd9c08 100644 --- a/man/isamchk.1 +++ b/man/isamchk.1 @@ -1,4 +1,4 @@ -.TH ISAMCHK 1 "19 December 2000" +.TH isamchk 1 "19 December 2000" "MySQL 3.23" "MySQL database" .SH NAME .BR isamchk \- Description, check and repair of ISAM tables. diff --git a/man/isamlog.1 b/man/isamlog.1 index ef6ceaff8da..a386f11c010 100644 --- a/man/isamlog.1 +++ b/man/isamlog.1 @@ -1,4 +1,4 @@ -.TH ISAMLOG 1 "20 December 2000" +.TH isamlog 1 "19 December 2000" "MySQL 3.23" "MySQL database" .SH NAME isamlog - Write info about whats in a nisam log file. .SH USAGE diff --git a/man/mysql.1 b/man/mysql.1 index e10fd589092..6664581072f 100644 --- a/man/mysql.1 +++ b/man/mysql.1 @@ -1,4 +1,4 @@ -.TH MYSQL 1 "13 June 1997" +.TH mysql 1 "19 December 2000" "MySQL 3.23" "MySQL database" .SH NAME mysql \- text-based client for mysqld, a SQL-based relational database daemon .SH SYNOPSIS diff --git a/man/mysql_zap.1 b/man/mysql_zap.1 index e57eb7a4d07..144fc212372 100644 --- a/man/mysql_zap.1 +++ b/man/mysql_zap.1 @@ -1,4 +1,4 @@ -.TH ZAP 1 "20 December 2000" +.TH zap 1 "19 December 2000" "MySQL 3.23" "MySQL database" .SH NAME zap - a perl script used to kill processes .SH USAGE diff --git a/man/mysqlaccess.1 b/man/mysqlaccess.1 index 0ae06dca137..c1c61d4a8a7 100644 --- a/man/mysqlaccess.1 +++ b/man/mysqlaccess.1 @@ -1,4 +1,4 @@ -.TH MYSQLACCESS 1 "19 December 2000" +.TH mysqlaccess 1 "19 December 2000" "MySQL 3.23" "MySQL database" .SH NAME .BR mysqlaccess \- Create new users to mysql. diff --git a/man/mysqladmin.1 b/man/mysqladmin.1 index 1e435006bb2..9d7d73aad21 100644 --- a/man/mysqladmin.1 +++ b/man/mysqladmin.1 @@ -1,4 +1,4 @@ -.TH MYSQLADMIN 1 "18 December 2000" +.TH mysqladmin 1 "19 December 2000" "MySQL 3.23" "MySQL database" .SH NAME mysqladmin [OPTIONS] command command.... \- A utility for performing administrative operations .SH OPTION SYNOPSIS diff --git a/man/mysqld.1 b/man/mysqld.1 index 1f87eb9cf32..0a6fcccbef2 100644 --- a/man/mysqld.1 +++ b/man/mysqld.1 @@ -1,4 +1,4 @@ -.TH MYSQLD 1 "19 December 2000" +.TH mysqld 1 "19 December 2000" "MySQL 3.23" "MySQL database" .SH NAME .BR mysqld \- Starts the MySQL server demon diff --git a/man/mysqld_multi.1 b/man/mysqld_multi.1 index b7aa77f656d..68b9d1e876f 100644 --- a/man/mysqld_multi.1 +++ b/man/mysqld_multi.1 @@ -1,4 +1,4 @@ -.TH MYSQLD_MULTI 1 "20 December 2000" +.TH mysqld_multi 1 "19 December 2000" "MySQL 3.23" "MySQL database" .SH NAME mysqld_multi - is meant for managing several mysqld processes running in different UNIX sockets and TCP/IP ports. .SH USAGE diff --git a/man/mysqld_safe.1 b/man/mysqld_safe.1 index c900d193929..b8271c848cc 100644 --- a/man/mysqld_safe.1 +++ b/man/mysqld_safe.1 @@ -1,4 +1,4 @@ -.TH SAFE_MYSQLD 1 "19 December 2000" "safe_mysqld (mysql)" mysql.com +.TH safe_mysqld 1 "19 December 2000" "MySQL 3.23" "MySQL database" .SH NAME mysqld_safe \- start the mysqld daemon on Unix. .SH SYNOPSIS diff --git a/man/mysqldump.1 b/man/mysqldump.1 index b9e5aa33791..b4aba2ade13 100644 --- a/man/mysqldump.1 +++ b/man/mysqldump.1 @@ -1,4 +1,4 @@ -.TH MYSQLDUMP 1 "19 December 2000" +.TH mysqldump 1 "19 December 2000" "MySQL 3.23" "MySQL database" .SH NAME mysqldump \- text-based client for dumping or backing up mysql databases , tables and or data. @@ -123,7 +123,7 @@ Connect to host. Lock all tables for read. .TP .BR \-n | \-\-no\-create\-db -'CREATE DATABASE /*!32312 IF NOT EXISTS*/ db_name;' +\&'CREATE DATABASE /*!32312 IF NOT EXISTS*/ db_name;' will not be put in the output. The above line will be added otherwise, if .BR \-\-databases @@ -270,4 +270,4 @@ Manual page by L. (Kill-9) Pedersen (kill-9@kill-9.dk), Mercurmedia Data Model Architect / system developer (http://www.mercurmedia.com) -.\" end of man page \ No newline at end of file +.\" end of man page diff --git a/man/mysqlshow.1 b/man/mysqlshow.1 index 661b2cd02c8..b6aceec82e3 100644 --- a/man/mysqlshow.1 +++ b/man/mysqlshow.1 @@ -1,4 +1,4 @@ -.TH MYSQLSHOW 1 "19 December 2000" +.TH mysqlshow 1 "19 December 2000" "MySQL 3.23" "MySQL database" .SH NAME .BR mysqlshow \- Shows the structure of a mysql database (databases,tables and columns) diff --git a/man/perror.1 b/man/perror.1 index 7adf99ea772..38a51593ba1 100644 --- a/man/perror.1 +++ b/man/perror.1 @@ -1,4 +1,4 @@ -.TH PERROR 1 "19 December 2000" +.TH perror 1 "19 December 2000" "MySQL 3.23" "MySQL database" .SH NAME .BR perror can be used to display a description for a system error code, or an MyISAM/ISAM table handler error code. The error messages are mostly system dependent. diff --git a/man/replace.1 b/man/replace.1 index 38ffe998027..7c3b79f605b 100644 --- a/man/replace.1 +++ b/man/replace.1 @@ -1,4 +1,4 @@ -.TH REPLACE 1 "20 December 2000" +.TH replace 1 "19 December 2000" "MySQL 3.23" "MySQL database" .SH NAME .TP replace - A utility program that is used by msql2mysql, but that has more general applicability as well. replace changes strings in place in files or on the standard input. Uses a finite state machine to match longer strings first. Can be used to swap strings. diff --git a/myisam/mi_check.c b/myisam/mi_check.c index 28c28e628ea..700ac5b0b62 100644 --- a/myisam/mi_check.c +++ b/myisam/mi_check.c @@ -62,22 +62,6 @@ static SORT_KEY_BLOCKS *alloc_key_blocks(MI_CHECK *param, uint blocks, static ha_checksum mi_byte_checksum(const byte *buf, uint length); static void set_data_file_type(SORT_INFO *sort_info, MYISAM_SHARE *share); -#ifdef __WIN__ -static double ulonglong2double(ulonglong value) -{ - longlong nr=(longlong) value; - if (nr >= 0) - return (double) nr; - return (18446744073709551616.0 + (double) nr); -} - -#if SIZEOF_OFF_T > 4 -#define my_off_t2double(A) ulonglong2double(A) -#else -#define my_off_t2double(A) ((double) (A)) -#endif /* SIZEOF_OFF_T > 4 */ -#endif /* __WIN__ */ - void myisamchk_init(MI_CHECK *param) { bzero((gptr) param,sizeof(*param)); diff --git a/myisam/mi_create.c b/myisam/mi_create.c index 8087d17904a..8b33930bcf3 100644 --- a/myisam/mi_create.c +++ b/myisam/mi_create.c @@ -230,7 +230,7 @@ int mi_create(const char *name,uint keys,MI_KEYDEF *keydefs, if (uniques) { max_key_block_length= myisam_block_size; - max_key_length= MI_UNIQUE_HASH_LENGTH; + max_key_length= MI_UNIQUE_HASH_LENGTH + pointer; } for (i=0, keydef=keydefs ; i < keys ; i++ , keydef++) diff --git a/myisam/mi_unique.c b/myisam/mi_unique.c index 682f946eba7..3eb25aefe0e 100644 --- a/myisam/mi_unique.c +++ b/myisam/mi_unique.c @@ -24,7 +24,7 @@ my_bool mi_check_unique(MI_INFO *info, MI_UNIQUEDEF *def, byte *record, { my_off_t lastpos=info->lastpos; MI_KEYDEF *key= &info->s->keyinfo[def->key]; - uchar *key_buff=info->lastkey+info->s->base.max_key_length; + uchar *key_buff=info->lastkey2; DBUG_ENTER("mi_check_unique"); mi_unique_store(record+key->seg->start, unique_hash); @@ -80,7 +80,16 @@ ha_checksum mi_unique_hash(MI_UNIQUEDEF *def, const byte *record) if (keyseg->null_bit) { if (record[keyseg->null_pos] & keyseg->null_bit) + { + /* + Change crc in a way different from an empty string or 0. + (This is an optimisation; The code will work even if this isn't + done) + */ + crc=((crc << 8) + 511+ + (crc >> (8*sizeof(ha_checksum)-8))); continue; + } } pos= record+keyseg->start; if (keyseg->flag & HA_VAR_LENGTH) diff --git a/myisam/myisamchk.c b/myisam/myisamchk.c index 281cb90d9bf..1b180a728ba 100644 --- a/myisam/myisamchk.c +++ b/myisam/myisamchk.c @@ -130,7 +130,7 @@ int main(int argc, char **argv) char buff[22],buff2[22]; if (!(check_param.testflag & T_SILENT) || check_param.testflag & T_INFO) puts("\n---------\n"); - printf("\nTotal of all %d ISAM-files:\nData records: %9s Deleted blocks: %9s\n",check_param.total_files,llstr(check_param.total_records,buff), + printf("\nTotal of all %d MyISAM-files:\nData records: %9s Deleted blocks: %9s\n",check_param.total_files,llstr(check_param.total_records,buff), llstr(check_param.total_deleted,buff2)); } free_defaults(default_argv); @@ -330,7 +330,7 @@ static void usage(void) print_version(); puts("By Monty, for your professional use"); puts("This software comes with NO WARRANTY: see the PUBLIC for details.\n"); - puts("Description, check and repair of ISAM tables."); + puts("Description, check and repair of MyISAM tables."); puts("Used without options all tables on the command will be checked for errors"); printf("Usage: %s [OPTIONS] tables[.MYI]\n", my_progname); puts("\nGlobal options:\n\ @@ -1693,7 +1693,7 @@ void mi_check_print_error(MI_CHECK *param, const char *fmt,...) if (!param->warning_printed && !param->error_printed) { if (param->testflag & T_SILENT) - fprintf(stderr,"%s: ISAM file %s\n",my_progname,param->isam_file_name); + fprintf(stderr,"%s: MyISAM file %s\n",my_progname,param->isam_file_name); param->out_flag|= O_DATA_LOST; } param->error_printed|=1; diff --git a/myisam/rt_mbr.c b/myisam/rt_mbr.c index a6467500183..bb13c0769b3 100644 --- a/myisam/rt_mbr.c +++ b/myisam/rt_mbr.c @@ -161,25 +161,25 @@ end: return 0; } -#define RT_VOL_KORR(type, korr_func, len) \ +#define RT_VOL_KORR(type, korr_func, len, cast) \ { \ type amin, amax; \ amin = korr_func(a); \ a += len; \ amax = korr_func(a); \ a += len; \ - res *= ((double)amax - (double)amin); \ + res *= (cast(amax) - cast(amin)); \ break; \ } -#define RT_VOL_GET(type, get_func, len) \ +#define RT_VOL_GET(type, get_func, len, cast) \ { \ type amin, amax; \ get_func(amin, a); \ a += len; \ get_func(amax, a); \ a += len; \ - res *= ((double)amax - (double)amin); \ + res *= (cast(amax) - cast(amin)); \ break; \ } @@ -213,27 +213,27 @@ double rtree_rect_volume(HA_KEYSEG *keyseg, uchar *a, uint key_length) break; } case HA_KEYTYPE_SHORT_INT: - RT_VOL_KORR(int16, mi_sint2korr, 2); + RT_VOL_KORR(int16, mi_sint2korr, 2, (double)); case HA_KEYTYPE_USHORT_INT: - RT_VOL_KORR(uint16, mi_uint2korr, 2); + RT_VOL_KORR(uint16, mi_uint2korr, 2, (double)); case HA_KEYTYPE_INT24: - RT_VOL_KORR(int32, mi_sint3korr, 3); + RT_VOL_KORR(int32, mi_sint3korr, 3, (double)); case HA_KEYTYPE_UINT24: - RT_VOL_KORR(uint32, mi_uint3korr, 3); + RT_VOL_KORR(uint32, mi_uint3korr, 3, (double)); case HA_KEYTYPE_LONG_INT: - RT_VOL_KORR(int32, mi_sint4korr, 4); + RT_VOL_KORR(int32, mi_sint4korr, 4, (double)); case HA_KEYTYPE_ULONG_INT: - RT_VOL_KORR(uint32, mi_uint4korr, 4); + RT_VOL_KORR(uint32, mi_uint4korr, 4, (double)); #ifdef HAVE_LONG_LONG case HA_KEYTYPE_LONGLONG: - RT_VOL_KORR(longlong, mi_sint8korr, 8); + RT_VOL_KORR(longlong, mi_sint8korr, 8, (double)); case HA_KEYTYPE_ULONGLONG: - RT_VOL_KORR(ulonglong, mi_uint8korr, 8); + RT_VOL_KORR(longlong, mi_sint8korr, 8, ulonglong2double); #endif case HA_KEYTYPE_FLOAT: - RT_VOL_GET(float, mi_float4get, 4); + RT_VOL_GET(float, mi_float4get, 4, (double)); case HA_KEYTYPE_DOUBLE: - RT_VOL_GET(double, mi_float8get, 8); + RT_VOL_GET(double, mi_float8get, 8, (double)); case HA_KEYTYPE_END: key_length = 0; break; @@ -242,27 +242,27 @@ double rtree_rect_volume(HA_KEYSEG *keyseg, uchar *a, uint key_length) return res; } -#define RT_D_MBR_KORR(type, korr_func, len) \ +#define RT_D_MBR_KORR(type, korr_func, len, cast) \ { \ type amin, amax; \ amin = korr_func(a); \ a += len; \ amax = korr_func(a); \ a += len; \ - *res++ = (double)amin; \ - *res++ = (double)amax; \ + *res++ = cast(amin); \ + *res++ = cast(amax); \ break; \ } -#define RT_D_MBR_GET(type, get_func, len) \ +#define RT_D_MBR_GET(type, get_func, len, cast) \ { \ type amin, amax; \ get_func(amin, a); \ a += len; \ get_func(amax, a); \ a += len; \ - *res++ = (double)amin; \ - *res++ = (double)amax; \ + *res++ = cast(amin); \ + *res++ = cast(amax); \ break; \ } @@ -296,27 +296,27 @@ int rtree_d_mbr(HA_KEYSEG *keyseg, uchar *a, uint key_length, double *res) break; } case HA_KEYTYPE_SHORT_INT: - RT_D_MBR_KORR(int16, mi_sint2korr, 2); + RT_D_MBR_KORR(int16, mi_sint2korr, 2, (double)); case HA_KEYTYPE_USHORT_INT: - RT_D_MBR_KORR(uint16, mi_uint2korr, 2); + RT_D_MBR_KORR(uint16, mi_uint2korr, 2, (double)); case HA_KEYTYPE_INT24: - RT_D_MBR_KORR(int32, mi_sint3korr, 3); + RT_D_MBR_KORR(int32, mi_sint3korr, 3, (double)); case HA_KEYTYPE_UINT24: - RT_D_MBR_KORR(uint32, mi_uint3korr, 3); + RT_D_MBR_KORR(uint32, mi_uint3korr, 3, (double)); case HA_KEYTYPE_LONG_INT: - RT_D_MBR_KORR(int32, mi_sint4korr, 4); + RT_D_MBR_KORR(int32, mi_sint4korr, 4, (double)); case HA_KEYTYPE_ULONG_INT: - RT_D_MBR_KORR(uint32, mi_uint4korr, 4); + RT_D_MBR_KORR(uint32, mi_uint4korr, 4, (double)); #ifdef HAVE_LONG_LONG case HA_KEYTYPE_LONGLONG: - RT_D_MBR_KORR(longlong, mi_sint8korr, 8); + RT_D_MBR_KORR(longlong, mi_sint8korr, 8, (double)); case HA_KEYTYPE_ULONGLONG: - RT_D_MBR_KORR(ulonglong, mi_uint8korr, 8); + RT_D_MBR_KORR(longlong, mi_sint8korr, 8, ulonglong2double); #endif case HA_KEYTYPE_FLOAT: - RT_D_MBR_GET(float, mi_float4get, 4); + RT_D_MBR_GET(float, mi_float4get, 4, (double)); case HA_KEYTYPE_DOUBLE: - RT_D_MBR_GET(double, mi_float8get, 8); + RT_D_MBR_GET(double, mi_float8get, 8, (double)); case HA_KEYTYPE_END: key_length = 0; break; @@ -362,12 +362,13 @@ int rtree_d_mbr(HA_KEYSEG *keyseg, uchar *a, uint key_length, double *res) } /* -Creates common minimal bounding rectungle -for two input rectagnles a and b -Result is written to c + Creates common minimal bounding rectungle + for two input rectagnles a and b + Result is written to c */ + int rtree_combine_rect(HA_KEYSEG *keyseg, uchar* a, uchar* b, uchar* c, - uint key_length) + uint key_length) { for ( ; (int) key_length > 0 ; keyseg += 2) @@ -515,7 +516,7 @@ double rtree_overlapping_area(HA_KEYSEG *keyseg, uchar* a, uchar* b, case HA_KEYTYPE_LONGLONG: RT_OVL_AREA_KORR(longlong, mi_sint8korr, 8); case HA_KEYTYPE_ULONGLONG: - RT_OVL_AREA_KORR(ulonglong, mi_uint8korr, 8); + RT_OVL_AREA_KORR(longlong, mi_sint8korr, 8); #endif case HA_KEYTYPE_FLOAT: RT_OVL_AREA_GET(float, mi_float4get, 4); @@ -613,7 +614,7 @@ double rtree_area_increase(HA_KEYSEG *keyseg, uchar* a, uchar* b, case HA_KEYTYPE_LONGLONG: RT_AREA_INC_KORR(longlong, mi_sint8korr, 8); case HA_KEYTYPE_ULONGLONG: - RT_AREA_INC_KORR(ulonglong, mi_uint8korr, 8); + RT_AREA_INC_KORR(longlong, mi_sint8korr, 8); #endif case HA_KEYTYPE_FLOAT: RT_AREA_INC_GET(float, mi_float4get, 4); diff --git a/mysql-test/mysql-test-run.sh b/mysql-test/mysql-test-run.sh index edd573f50a3..1d730e3f7c5 100644 --- a/mysql-test/mysql-test-run.sh +++ b/mysql-test/mysql-test-run.sh @@ -332,7 +332,7 @@ while test $# -gt 0; do VALGRIND="valgrind --alignment=8 --leak-check=yes" EXTRA_MASTER_MYSQLD_OPT="$EXTRA_MASTER_MYSQLD_OPT --skip-safemalloc" EXTRA_SLAVE_MYSQLD_OPT="$EXTRA_SLAVE_MYSQLD_OPT --skip-safemalloc" - #SLEEP_TIME_AFTER_RESTART=120 + SLEEP_TIME_AFTER_RESTART=10 SLEEP_TIME_FOR_DELETE=60 ;; --valgrind-options=*) diff --git a/mysql-test/r/group_by.result b/mysql-test/r/group_by.result index aa3de48c09e..a63054da88b 100644 --- a/mysql-test/r/group_by.result +++ b/mysql-test/r/group_by.result @@ -468,3 +468,17 @@ NOT NULL); max(value) 4 drop table t1,t2,t3; +create table t1 (a blob null); +insert into t1 values (NULL),(NULL),(NULL),(NULL),(NULL),(NULL),(NULL),(NULL),(NULL),(""),(""),(""),("b"); +select a,count(*) from t1 group by a; +a count(*) +NULL 9 + 3 +b 1 +set option sql_big_tables=1; +select a,count(*) from t1 group by a; +a count(*) +NULL 9 + 3 +b 1 +drop table t1; diff --git a/mysql-test/r/merge.result b/mysql-test/r/merge.result index 9199f291c08..b28607218d1 100644 --- a/mysql-test/r/merge.result +++ b/mysql-test/r/merge.result @@ -260,7 +260,7 @@ t3 CREATE TABLE `t3` ( `othr` int(11) NOT NULL default '0' ) TYPE=MRG_MyISAM CHARSET=latin1 UNION=(t1,t2) drop table t3,t2,t1; -create table t1 (a int not null) type=merge; +create table t1 (a int not null, key(a)) type=merge; select * from t1; a drop table t1; @@ -536,7 +536,7 @@ INSERT INTO t2 VALUES (1,2), (2,2); CREATE TABLE t3 ( a int(11) NOT NULL default '0', b int(11) NOT NULL default '0', KEY a (a,b)) TYPE=MRG_MyISAM UNION=(t1,t2); select max(b) from t3 where a = 2; max(b) -NULL +2 select max(b) from t1 where a = 2; max(b) 1 diff --git a/mysql-test/r/null.result b/mysql-test/r/null.result index 91af34b6681..08612fa191f 100644 --- a/mysql-test/r/null.result +++ b/mysql-test/r/null.result @@ -73,3 +73,39 @@ b ifnull(t2.b,"this is null") NULL this is null NULL this is null drop table t1; +CREATE TABLE t1 (a varchar(16) NOT NULL, b smallint(6) NOT NULL, c datetime NOT NULL, d smallint(6) NOT NULL); +INSERT INTO t1 SET a = "", d= "2003-01-14 03:54:55"; +UPDATE t1 SET d=1/NULL; +UPDATE t1 SET d=NULL; +INSERT INTO t1 (a) values (null); +Column 'a' cannot be null +INSERT INTO t1 (a) values (1/null); +Column 'a' cannot be null +INSERT INTO t1 (a) values (null),(null); +INSERT INTO t1 (b) values (null); +Column 'b' cannot be null +INSERT INTO t1 (b) values (1/null); +Column 'b' cannot be null +INSERT INTO t1 (b) values (null),(null); +INSERT INTO t1 (c) values (null); +Column 'c' cannot be null +INSERT INTO t1 (c) values (1/null); +Column 'c' cannot be null +INSERT INTO t1 (c) values (null),(null); +INSERT INTO t1 (d) values (null); +Column 'd' cannot be null +INSERT INTO t1 (d) values (1/null); +Column 'd' cannot be null +INSERT INTO t1 (d) values (null),(null); +select * from t1; +a b c d + 0 0000-00-00 00:00:00 0 + 0 0000-00-00 00:00:00 0 + 0 0000-00-00 00:00:00 0 + 0 0000-00-00 00:00:00 0 + 0 0000-00-00 00:00:00 0 + 0 0000-00-00 00:00:00 0 + 0 0000-00-00 00:00:00 0 + 0 0000-00-00 00:00:00 0 + 0 0000-00-00 00:00:00 0 +drop table t1; diff --git a/mysql-test/r/rpl_loaddata.result b/mysql-test/r/rpl_loaddata.result new file mode 100644 index 00000000000..8735cae47d5 --- /dev/null +++ b/mysql-test/r/rpl_loaddata.result @@ -0,0 +1,13 @@ +stop slave; +drop table if exists t1,t2,t3,t4,t5,t6,t7,t8,t9; +reset master; +reset slave; +drop table if exists t1,t2,t3,t4,t5,t6,t7,t8,t9; +start slave; +create table t1(a int not null auto_increment, b int, primary key(a) ); +load data infile '../../std_data/rpl_loaddata.dat' into table t1; +select * from t1; +a b +1 10 +2 15 +drop table t1; diff --git a/mysql-test/r/rpl_temporary.result b/mysql-test/r/rpl_temporary.result index 470a6302a2b..c3243d3a227 100644 --- a/mysql-test/r/rpl_temporary.result +++ b/mysql-test/r/rpl_temporary.result @@ -22,7 +22,7 @@ f 7 show binlog events; Log_name Pos Event_type Server_id Orig_log_pos Info -master-bin.000001 4 Start 1 4 Server ver: 4.1.0-alpha-debug-log, Binlog ver: 3 +master-bin.000001 4 Start 1 4 Server ver: VERSION, Binlog ver: 3 master-bin.000001 79 Query 1 79 use `test`; create table t1(f int) master-bin.000001 136 Query 1 136 use `test`; create table t2(f int) master-bin.000001 193 Query 1 193 use `test`; insert into t1 values (1),(2),(3),(4),(5),(6),(7),(8),(9),(10) diff --git a/mysql-test/r/sql_mode.result b/mysql-test/r/sql_mode.result new file mode 100644 index 00000000000..8ded3daf114 --- /dev/null +++ b/mysql-test/r/sql_mode.result @@ -0,0 +1,87 @@ +drop table if exists t1; +CREATE TABLE `t1` ( +a int not null auto_increment, +`pseudo` varchar(35) character set latin2 NOT NULL default '', +`email` varchar(60) character set latin2 NOT NULL default '', +PRIMARY KEY (a), +UNIQUE KEY `email` USING BTREE (`email`) +) TYPE=HEAP CHARSET=latin1 ROW_FORMAT DYNAMIC; +set @@sql_mode=""; +show variables like 'sql_mode'; +Variable_name Value +sql_mode +show create table t1; +Table Create Table +t1 CREATE TABLE `t1` ( + `a` int(11) NOT NULL auto_increment, + `pseudo` varchar(35) character set latin2 NOT NULL default '', + `email` varchar(60) character set latin2 NOT NULL default '', + PRIMARY KEY (`a`), + UNIQUE KEY `email` TYPE BTREE (`email`) +) TYPE=HEAP CHARSET=latin1 ROW_FORMAT=DYNAMIC +set @@sql_mode="ansi_quotes"; +show variables like 'sql_mode'; +Variable_name Value +sql_mode ANSI_QUOTES +show create table t1; +Table Create Table +t1 CREATE TABLE "t1" ( + "a" int(11) NOT NULL auto_increment, + "pseudo" varchar(35) character set latin2 NOT NULL default '', + "email" varchar(60) character set latin2 NOT NULL default '', + PRIMARY KEY ("a"), + UNIQUE KEY "email" TYPE BTREE ("email") +) TYPE=HEAP CHARSET=latin1 ROW_FORMAT=DYNAMIC +set @@sql_mode="no_table_options"; +show variables like 'sql_mode'; +Variable_name Value +sql_mode NO_TABLE_OPTIONS +show create table t1; +Table Create Table +t1 CREATE TABLE `t1` ( + `a` int(11) NOT NULL auto_increment, + `pseudo` varchar(35) character set latin2 NOT NULL default '', + `email` varchar(60) character set latin2 NOT NULL default '', + PRIMARY KEY (`a`), + UNIQUE KEY `email` TYPE BTREE (`email`) +) +set @@sql_mode="no_key_options"; +show variables like 'sql_mode'; +Variable_name Value +sql_mode NO_KEY_OPTIONS +show create table t1; +Table Create Table +t1 CREATE TABLE `t1` ( + `a` int(11) NOT NULL auto_increment, + `pseudo` varchar(35) character set latin2 NOT NULL default '', + `email` varchar(60) character set latin2 NOT NULL default '', + PRIMARY KEY (`a`), + UNIQUE KEY `email` (`email`) +) TYPE=HEAP CHARSET=latin1 ROW_FORMAT=DYNAMIC +set @@sql_mode="no_field_options,mysql323,mysql40"; +show variables like 'sql_mode'; +Variable_name Value +sql_mode NO_FIELD_OPTIONS,MYSQL323,MYSQL40 +show create table t1; +Table Create Table +t1 CREATE TABLE `t1` ( + `a` int(11) NOT NULL auto_increment, + `pseudo` varchar(35) NOT NULL default '', + `email` varchar(60) NOT NULL default '', + PRIMARY KEY (`a`), + UNIQUE KEY `email` (`email`) +) TYPE=HEAP ROW_FORMAT=DYNAMIC +set @@sql_mode="postgresql,oracle,mssql,db2,sapdb"; +show variables like 'sql_mode'; +Variable_name Value +sql_mode POSTGRESQL,ORACLE,MSSQL,DB2,SAPDB +show create table t1; +Table Create Table +t1 CREATE TABLE "t1" ( + "a" int(11) NOT NULL, + "pseudo" varchar(35) NOT NULL default '', + "email" varchar(60) NOT NULL default '', + PRIMARY KEY ("a"), + UNIQUE KEY "email" ("email") +) +drop table t1; diff --git a/mysql-test/std_data/rpl_loaddata.dat b/mysql-test/std_data/rpl_loaddata.dat new file mode 100644 index 00000000000..a70a059c2ab --- /dev/null +++ b/mysql-test/std_data/rpl_loaddata.dat @@ -0,0 +1,2 @@ +\N 10 +\N 15 diff --git a/mysql-test/t/group_by.test b/mysql-test/t/group_by.test index a34c3a12363..bca86f1ef37 100644 --- a/mysql-test/t/group_by.test +++ b/mysql-test/t/group_by.test @@ -363,3 +363,14 @@ m.c1id = c1.id AND c1.active = 'Yes' LEFT JOIN t3 AS c2 ON m.c2id = c2.id AND c2.active = 'Yes' WHERE m.pid=1 AND (c1.id IS NOT NULL OR c2.id IS NOT NULL); drop table t1,t2,t3; + +# +# Test bug in GROUP BY on BLOB that is NULL or empty +# + +create table t1 (a blob null); +insert into t1 values (NULL),(NULL),(NULL),(NULL),(NULL),(NULL),(NULL),(NULL),(NULL),(""),(""),(""),("b"); +select a,count(*) from t1 group by a; +set option sql_big_tables=1; +select a,count(*) from t1 group by a; +drop table t1; diff --git a/mysql-test/t/merge.test b/mysql-test/t/merge.test index f84e10b0e3c..7a7678afca1 100644 --- a/mysql-test/t/merge.test +++ b/mysql-test/t/merge.test @@ -109,7 +109,7 @@ drop table t3,t2,t1; # # Test table without unions # -create table t1 (a int not null) type=merge; +create table t1 (a int not null, key(a)) type=merge; select * from t1; drop table t1; diff --git a/mysql-test/t/null.test b/mysql-test/t/null.test index 087ef81e13e..fa36249dce0 100644 --- a/mysql-test/t/null.test +++ b/mysql-test/t/null.test @@ -52,3 +52,34 @@ insert into t1 values(10,null); select t2.b, ifnull(t2.b,"this is null") from t1 as t2 left join t1 as t3 on t2.b=t3.a order by 1; drop table t1; + +# +# Test inserting and updating with NULL +# +CREATE TABLE t1 (a varchar(16) NOT NULL, b smallint(6) NOT NULL, c datetime NOT NULL, d smallint(6) NOT NULL); +INSERT INTO t1 SET a = "", d= "2003-01-14 03:54:55"; +UPDATE t1 SET d=1/NULL; +UPDATE t1 SET d=NULL; +--error 1048 +INSERT INTO t1 (a) values (null); +--error 1048 +INSERT INTO t1 (a) values (1/null); +INSERT INTO t1 (a) values (null),(null); +--error 1048 +INSERT INTO t1 (b) values (null); +--error 1048 +INSERT INTO t1 (b) values (1/null); +INSERT INTO t1 (b) values (null),(null); +--error 1048 +INSERT INTO t1 (c) values (null); +--error 1048 +INSERT INTO t1 (c) values (1/null); +INSERT INTO t1 (c) values (null),(null); +--error 1048 +INSERT INTO t1 (d) values (null); +--error 1048 +INSERT INTO t1 (d) values (1/null); +INSERT INTO t1 (d) values (null),(null); +select * from t1; +drop table t1; + diff --git a/mysql-test/t/rpl_loaddata.test b/mysql-test/t/rpl_loaddata.test new file mode 100644 index 00000000000..d7fc2a10ca4 --- /dev/null +++ b/mysql-test/t/rpl_loaddata.test @@ -0,0 +1,16 @@ +# See if replication of a "LOAD DATA in an autoincrement column" +# Honours autoincrement values +# i.e. if the master and slave have the same sequence +source include/master-slave.inc; + +create table t1(a int not null auto_increment, b int, primary key(a) ); +load data infile '../../std_data/rpl_loaddata.dat' into table t1; +save_master_pos; +connection slave; +sync_with_master; +select * from t1; +connection master; +drop table t1; +save_master_pos; +connection slave; +sync_with_master; diff --git a/mysql-test/t/rpl_log-master.opt b/mysql-test/t/rpl_log-master.opt new file mode 100644 index 00000000000..e0d075c3fbd --- /dev/null +++ b/mysql-test/t/rpl_log-master.opt @@ -0,0 +1 @@ +--skip-external-locking diff --git a/mysql-test/t/rpl_temporary.test b/mysql-test/t/rpl_temporary.test index 75519b75f75..f91880537e6 100644 --- a/mysql-test/t/rpl_temporary.test +++ b/mysql-test/t/rpl_temporary.test @@ -3,6 +3,8 @@ source include/master-slave.inc; connect (con1,localhost,root,,); connect (con2,localhost,root,,); +let $VERSION=`select version()`; + --disable_warnings drop table if exists t1,t2; --enable_warnings @@ -38,6 +40,7 @@ drop temporary table t3; select * from t2; +--replace_result $VERSION VERSION show binlog events; drop table t1, t2; diff --git a/mysql-test/t/sql_mode.test b/mysql-test/t/sql_mode.test new file mode 100644 index 00000000000..fd464f74de4 --- /dev/null +++ b/mysql-test/t/sql_mode.test @@ -0,0 +1,30 @@ +--disable_warnings +drop table if exists t1; +--enable_warnings + +CREATE TABLE `t1` ( + a int not null auto_increment, + `pseudo` varchar(35) character set latin2 NOT NULL default '', + `email` varchar(60) character set latin2 NOT NULL default '', + PRIMARY KEY (a), + UNIQUE KEY `email` USING BTREE (`email`) +) TYPE=HEAP CHARSET=latin1 ROW_FORMAT DYNAMIC; +set @@sql_mode=""; +show variables like 'sql_mode'; +show create table t1; +set @@sql_mode="ansi_quotes"; +show variables like 'sql_mode'; +show create table t1; +set @@sql_mode="no_table_options"; +show variables like 'sql_mode'; +show create table t1; +set @@sql_mode="no_key_options"; +show variables like 'sql_mode'; +show create table t1; +set @@sql_mode="no_field_options,mysql323,mysql40"; +show variables like 'sql_mode'; +show create table t1; +set @@sql_mode="postgresql,oracle,mssql,db2,sapdb"; +show variables like 'sql_mode'; +show create table t1; +drop table t1; diff --git a/mysys/Makefile.am b/mysys/Makefile.am index b93ddd2241d..97eb06c7aa7 100644 --- a/mysys/Makefile.am +++ b/mysys/Makefile.am @@ -60,7 +60,7 @@ noinst_PROGRAMS = charset2html @THREAD_LPROGRAMS@ # test_dir_DEPENDENCIES= $(LIBRARIES) # testhash_DEPENDENCIES= $(LIBRARIES) # test_charset_DEPENDENCIES= $(LIBRARIES) -charset2html_DEPENDENCIES= $(LIBRARIES) +# charset2html_DEPENDENCIES= $(LIBRARIES) EXTRA_PROGRAMS = DEFS = -DDEFAULT_BASEDIR=\"$(prefix)\" \ -DDATADIR="\"$(MYSQLDATAdir)\"" \ diff --git a/mysys/charset.c b/mysys/charset.c index c89e1d60c7e..591ba568e3d 100644 --- a/mysys/charset.c +++ b/mysys/charset.c @@ -22,6 +22,17 @@ #include +/* + + The code below implements this functionality: + + - Initializing charset related structures + - Loading dynamic charsets + - Searching for a proper CHARSET_INFO + using charset name, collation name or collatio ID + - Setting server default character set +*/ + static void set_max_sort_char(CHARSET_INFO *cs) { @@ -45,9 +56,13 @@ static void set_max_sort_char(CHARSET_INFO *cs) static void simple_cs_init_functions(CHARSET_INFO *cs) { + + cs->strnxfrm = my_strnxfrm_simple; + cs->strnncoll = my_strnncoll_simple; cs->like_range = my_like_range_simple; cs->wildcmp = my_wildcmp_8bit; - cs->strnncoll = my_strnncoll_simple; + cs->mb_wc = my_mb_wc_8bit; + cs->wc_mb = my_wc_mb_8bit; cs->caseup_str = my_caseup_str_8bit; cs->casedn_str = my_casedn_str_8bit; cs->caseup = my_caseup_8bit; @@ -55,16 +70,17 @@ static void simple_cs_init_functions(CHARSET_INFO *cs) cs->tosort = my_tosort_8bit; cs->strcasecmp = my_strcasecmp_8bit; cs->strncasecmp = my_strncasecmp_8bit; - cs->mb_wc = my_mb_wc_8bit; - cs->wc_mb = my_wc_mb_8bit; cs->hash_caseup = my_hash_caseup_simple; cs->hash_sort = my_hash_sort_simple; cs->snprintf = my_snprintf_8bit; + cs->long10_to_str= my_long10_to_str_8bit; + cs->longlong10_to_str= my_longlong10_to_str_8bit; cs->strntol = my_strntol_8bit; cs->strntoul = my_strntoul_8bit; cs->strntoll = my_strntoll_8bit; cs->strntoull = my_strntoull_8bit; cs->strntod = my_strntod_8bit; + cs->scan = my_scan_8bit; cs->mbmaxlen = 1; } diff --git a/mysys/queues.c b/mysys/queues.c index fe642131d74..f077b38ca0b 100644 --- a/mysys/queues.c +++ b/mysys/queues.c @@ -24,7 +24,26 @@ #include -/* Init queue */ +/* + Init queue + + SYNOPSIS + init_queue() + queue Queue to initialise + max_elements Max elements that will be put in queue + offset_to_key Offset to key in element stored in queue + Used when sending pointers to compare function + max_at_top Set to 1 if you want biggest element on top. + compare Compare function for elements, takes 3 arguments. + first_cmp_arg First argument to compare function + + NOTES + Will allocate max_element pointers for queue array + + RETURN + 0 ok + 1 Could not allocate memory +*/ int init_queue(QUEUE *queue, uint max_elements, uint offset_to_key, pbool max_at_top, int (*compare) (void *, byte *, byte *), @@ -43,15 +62,32 @@ int init_queue(QUEUE *queue, uint max_elements, uint offset_to_key, DBUG_RETURN(0); } + /* - Reinitialize queue for new usage; Note that you can't currently resize - the number of elements! If you need this, fix it :) + Reinitialize queue for other usage (deletes all elements) + + SYNOPSIS + reinit_queue() + queue Queue to initialise + max_elements Max elements that will be put in queue + offset_to_key Offset to key in element stored in queue + Used when sending pointers to compare function + max_at_top Set to 1 if you want biggest element on top. + compare Compare function for elements, takes 3 arguments. + first_cmp_arg First argument to compare function + + NOTES + You can't currently resize the number of elements! If you need this, + fix it :) + + RETURN + 0 ok + EE_OUTOFMEMORY Wrong max_elements */ - int reinit_queue(QUEUE *queue, uint max_elements, uint offset_to_key, - pbool max_at_top, int (*compare) (void *, byte *, byte *), - void *first_cmp_arg) + pbool max_at_top, int (*compare) (void *, byte *, byte *), + void *first_cmp_arg) { DBUG_ENTER("reinit_queue"); if (queue->max_elements < max_elements) @@ -66,6 +102,21 @@ int reinit_queue(QUEUE *queue, uint max_elements, uint offset_to_key, DBUG_RETURN(0); } + +/* + Delete queue + + SYNOPSIS + delete_queue() + queue Queue to delete + + IMPLEMENTATION + Just free allocated memory. + + NOTES + Can be called safely multiple times +*/ + void delete_queue(QUEUE *queue) { DBUG_ENTER("delete_queue"); @@ -116,7 +167,7 @@ byte *queue_remove(register QUEUE *queue, uint idx) return 0; #endif { - byte *element=queue->root[++idx]; /* Intern index starts from 1 */ + byte *element=queue->root[++idx]; /* Intern index starts from 1 */ queue->root[idx]=queue->root[queue->elements--]; _downheap(queue,idx); return element; @@ -126,8 +177,7 @@ byte *queue_remove(register QUEUE *queue, uint idx) /* Fix when element on top has been replaced */ #ifndef queue_replaced -void queue_replaced(queue) -QUEUE *queue; +void queue_replaced(QUEUE *queue) { _downheap(queue,1); } @@ -169,8 +219,8 @@ void _downheap(register QUEUE *queue, uint idx) static int queue_fix_cmp(QUEUE *queue, void **a, void **b) { return queue->compare(queue->first_cmp_arg, - (char*) (*a)+queue->offset_to_key, - (char*) (*b)+queue->offset_to_key); + (byte*) (*a)+queue->offset_to_key, + (byte*) (*b)+queue->offset_to_key); } /* diff --git a/scripts/mysql_fix_privilege_tables.sh b/scripts/mysql_fix_privilege_tables.sh index 3ed295170ad..83ee5023b89 100644 --- a/scripts/mysql_fix_privilege_tables.sh +++ b/scripts/mysql_fix_privilege_tables.sh @@ -1,19 +1,27 @@ #!/bin/sh echo "This scripts updates the mysql.user, mysql.db, mysql.host and the" -echo "mysql.func table to MySQL 3.22.14 and above." +echo "mysql.func tables to MySQL 3.22.14 and above." echo "" echo "This is needed if you want to use the new GRANT functions," -echo "CREATE AGGREAGATE FUNCTION or want to use the more secure passwords in 3.23" +echo "CREATE AGGREGATE FUNCTION or want to use the more secure passwords in 3.23" echo "" -echo "If you get Access denied errors, you should run this script again" -echo "and give the MySQL root user password as a argument!" +echo "If you get 'Access denied' errors, you should run this script again" +echo "and give the MySQL root user password as an argument!" root_password="$1" host="localhost" +user="root" + +if test -z $1 ; then + cmd="@bindir@/mysql -f --user=$user --host=$host mysql" +else + root_password="$1" + cmd="@bindir@/mysql -f --user=$user --password=$root_password --host=$host mysql" +fi echo "Converting all privilege tables to MyISAM format" -@bindir@/mysql -f --user=root --password="$root_password" --host="$host" mysql < columns_priv.Column_priv" echo "You can ignore any Unknown column errors from this" -@bindir@/mysql -f --user=root --password="$root_password" --host="$host" mysql <""; END_OF_DATA echo "" @@ -168,7 +176,7 @@ fi # Add fields that can be used to limit number of questions and connections # for some users. -@bindir@/mysql -f --user=root --password="$root_password" --host="$host" mysql <table_charset) + else if (field_charset != table->table_charset && + !(current_thd->variables.sql_mode & MODE_NO_FIELD_OPTIONS) && + !(current_thd->variables.sql_mode & MODE_MYSQL323) && + !(current_thd->variables.sql_mode & MODE_MYSQL40) && + !(current_thd->variables.sql_mode & MODE_POSTGRESQL) && + !(current_thd->variables.sql_mode & MODE_ORACLE) && + !(current_thd->variables.sql_mode & MODE_MSSQL) && + !(current_thd->variables.sql_mode & MODE_DB2) && + !(current_thd->variables.sql_mode & MODE_SAPDB)) { res.append(" character set "); res.append(field_charset->csname); @@ -832,15 +840,19 @@ int Field_decimal::store(longlong nr) double Field_decimal::val_real(void) { - return my_strntod(my_charset_bin, ptr, field_length, NULL); + int not_used; + return my_strntod(my_charset_bin, ptr, field_length, NULL, ¬_used); } longlong Field_decimal::val_int(void) { + int not_used; if (unsigned_flag) - return my_strntoull(my_charset_bin, ptr, field_length, NULL, 10); + return my_strntoull(my_charset_bin, ptr, field_length, 10, NULL, + ¬_used); else - return my_strntoll( my_charset_bin, ptr, field_length, NULL, 10); + return my_strntoll( my_charset_bin, ptr, field_length, 10, NULL, + ¬_used); } @@ -942,8 +954,9 @@ void Field_decimal::sql_type(String &res) const int Field_tiny::store(const char *from,uint len,CHARSET_INFO *cs) { + int not_used; // We can ignore result from str2int char *end; - long tmp= my_strntol(cs, from, len, &end,10); + long tmp= my_strntol(cs, from, len, 10, &end, ¬_used); int error= 0; if (unsigned_flag) @@ -1097,9 +1110,9 @@ String *Field_tiny::val_str(String *val_buffer, char *to=(char*) val_buffer->ptr(); if (unsigned_flag) - length= (uint) cs->l10tostr(cs,to,mlength, 10,(long) *((uchar*) ptr)); + length= (uint) cs->long10_to_str(cs,to,mlength, 10,(long) *((uchar*) ptr)); else - length= (uint) cs->l10tostr(cs,to,mlength,-10,(long) *((signed char*) ptr)); + length= (uint) cs->long10_to_str(cs,to,mlength,-10,(long) *((signed char*) ptr)); val_buffer->length(length); if (zerofill) @@ -1143,9 +1156,11 @@ void Field_tiny::sql_type(String &res) const int Field_short::store(const char *from,uint len,CHARSET_INFO *cs) { + int not_used; // We can ignore result from str2int char *end; - long tmp= my_strntol(cs, from, len, &end, 10); + long tmp= my_strntol(cs, from, len, 10, &end, ¬_used); int error= 0; + if (unsigned_flag) { if (tmp < 0) @@ -1340,9 +1355,9 @@ String *Field_short::val_str(String *val_buffer, shortget(j,ptr); if (unsigned_flag) - length=(uint) cs->l10tostr(cs, to, mlength, 10, (long) (uint16) j); + length=(uint) cs->long10_to_str(cs, to, mlength, 10, (long) (uint16) j); else - length=(uint) cs->l10tostr(cs, to, mlength,-10, (long) j); + length=(uint) cs->long10_to_str(cs, to, mlength,-10, (long) j); val_buffer->length(length); if (zerofill) prepend_zeros(val_buffer); @@ -1415,8 +1430,9 @@ void Field_short::sql_type(String &res) const int Field_medium::store(const char *from,uint len,CHARSET_INFO *cs) { + int not_used; // We can ignore result from str2int char *end; - long tmp= my_strntol(cs, from, len, &end, 10); + long tmp= my_strntol(cs, from, len, 10, &end, ¬_used); int error= 0; if (unsigned_flag) @@ -1577,7 +1593,7 @@ String *Field_medium::val_str(String *val_buffer, char *to=(char*) val_buffer->ptr(); long j= unsigned_flag ? (long) uint3korr(ptr) : sint3korr(ptr); - length=(uint) cs->l10tostr(cs,to,mlength,-10,j); + length=(uint) cs->long10_to_str(cs,to,mlength,-10,j); val_buffer->length(length); if (zerofill) prepend_zeros(val_buffer); /* purecov: inspected */ @@ -1651,16 +1667,15 @@ int Field_long::store(const char *from,uint len,CHARSET_INFO *cs) error= 1; } else - tmp=(long) my_strntoul(cs,from,len,&end,10); + tmp=(long) my_strntoul(cs,from,len,10,&end,&error); } else - tmp=my_strntol(cs,from,len,&end,10); - if (my_errno || + tmp=my_strntol(cs,from,len,10,&end,&error); + if (error || (from+len != end && current_thd->count_cuted_fields && !test_if_int(from,len,end,cs))) { current_thd->cuted_fields++; - error= 1; } #ifdef WORDS_BIGENDIAN if (table->db_low_byte_first) @@ -1816,9 +1831,9 @@ String *Field_long::val_str(String *val_buffer, longget(j,ptr); if (unsigned_flag) - length=cs->l10tostr(cs,to,mlength, 10,(long) (uint32)j); + length=cs->long10_to_str(cs,to,mlength, 10,(long) (uint32)j); else - length=cs->l10tostr(cs,to,mlength,-10,(long) j); + length=cs->long10_to_str(cs,to,mlength,-10,(long) j); val_buffer->length(length); if (zerofill) prepend_zeros(val_buffer); @@ -1910,11 +1925,11 @@ int Field_longlong::store(const char *from,uint len,CHARSET_INFO *cs) error= 1; } else - tmp=(longlong) my_strntoull(cs,from,len,&end,10); + tmp=(longlong) my_strntoull(cs,from,len,10,&end,&error); } else - tmp=my_strntoll(cs,from,len,&end,10); - if (my_errno || + tmp=my_strntoll(cs,from,len,10,&end,&error); + if (error || (from+len != end && current_thd->count_cuted_fields && !test_if_int(from,len,end,cs))) current_thd->cuted_fields++; @@ -2038,7 +2053,7 @@ String *Field_longlong::val_str(String *val_buffer, #endif longlongget(j,ptr); - length=(uint) cs->ll10tostr(cs,to,mlength,unsigned_flag ? 10 : -10, j); + length=(uint) cs->longlong10_to_str(cs,to,mlength,unsigned_flag ? 10 : -10, j); val_buffer->length(length); if (zerofill) prepend_zeros(val_buffer); @@ -2122,14 +2137,14 @@ void Field_longlong::sql_type(String &res) const int Field_float::store(const char *from,uint len,CHARSET_INFO *cs) { - errno=0; // my_strntod() changes errno - Field_float::store(my_strntod(cs,(char*) from,len,(char**)NULL)); - if (errno || current_thd->count_cuted_fields && !test_if_real(from,len,cs)) + int err; + Field_float::store(my_strntod(cs,(char*) from,len,(char**)NULL,&err)); + if (err || current_thd->count_cuted_fields && !test_if_real(from,len,cs)) { current_thd->cuted_fields++; return 1; } - return (errno) ? 1 : 0; + return (err) ? 1 : 0; } @@ -2395,19 +2410,17 @@ void Field_float::sql_type(String &res) const int Field_double::store(const char *from,uint len,CHARSET_INFO *cs) { - errno=0; // my_strntod() changes errno - int error= 0; - double j= my_strntod(cs,(char*) from,len,(char**)0); - if (errno || current_thd->count_cuted_fields && !test_if_real(from,len,cs)) + int err; + double j= my_strntod(cs,(char*) from,len,(char**)0,&err); + if (err || current_thd->count_cuted_fields && !test_if_real(from,len,cs)) { current_thd->cuted_fields++; - error= 1; } if (unsigned_flag && j < 0) { current_thd->cuted_fields++; j=0; - error= 1; + err= 1; } #ifdef WORDS_BIGENDIAN if (table->db_low_byte_first) @@ -2417,7 +2430,7 @@ int Field_double::store(const char *from,uint len,CHARSET_INFO *cs) else #endif doublestore(ptr,j); - return error; + return err; } @@ -3183,8 +3196,9 @@ void Field_time::sql_type(String &res) const int Field_year::store(const char *from, uint len,CHARSET_INFO *cs) { + int not_used; // We can ignore result from str2int char *end; - long nr= my_strntol(cs, from, len, &end, 10); + long nr= my_strntol(cs, from, len, 10, &end, ¬_used); if (nr < 0 || nr >= 100 && nr <= 1900 || nr > 2155) { @@ -3914,22 +3928,24 @@ int Field_string::store(longlong nr) char buff[64]; int l; CHARSET_INFO *cs=charset(); - l=cs->ll10tostr(cs,buff,sizeof(buff),-10,nr); + l=cs->longlong10_to_str(cs,buff,sizeof(buff),-10,nr); return Field_string::store(buff,(uint)l,cs); } double Field_string::val_real(void) { + int not_used; CHARSET_INFO *cs=charset(); - return my_strntod(cs,ptr,field_length,(char**)0); + return my_strntod(cs,ptr,field_length,(char**)0,¬_used); } longlong Field_string::val_int(void) { + int not_used; CHARSET_INFO *cs=charset(); - return my_strntoll(cs,ptr,field_length,NULL,10); + return my_strntoll(cs,ptr,field_length,10,NULL,¬_used); } @@ -3956,23 +3972,11 @@ int Field_string::cmp(const char *a_ptr, const char *b_ptr) void Field_string::sort_string(char *to,uint length) { - if (binary()) - memcpy((byte*) to,(byte*) ptr,(size_t) length); - else - { -#ifdef USE_STRCOLL - if (use_strnxfrm(field_charset)) { - uint tmp=my_strnxfrm(field_charset, + uint tmp=my_strnxfrm(field_charset, (unsigned char *)to, length, (unsigned char *) ptr, field_length); - if (tmp < length) - bzero(to + tmp, length - tmp); - } - else -#endif - for (char *from=ptr,*end=ptr+length ; from != end ;) - *to++=(char) field_charset->sort_order[(uint) (uchar) *from++]; - } + if (tmp < length) + bzero(to + tmp, length - tmp); } @@ -4016,7 +4020,6 @@ int Field_string::pack_cmp(const char *a, const char *b, uint length) { uint a_length= (uint) (uchar) *a++; uint b_length= (uint) (uchar) *b++; - return my_strnncoll(field_charset, (const uchar*)a,a_length, (const uchar*)b,b_length); @@ -4030,7 +4033,6 @@ int Field_string::pack_cmp(const char *b, uint length) while (end > ptr && end[-1] == ' ') end--; uint a_length = (uint) (end - ptr); - return my_strnncoll(field_charset, (const uchar*)ptr,a_length, (const uchar*)b, b_length); @@ -4093,24 +4095,26 @@ int Field_varstring::store(longlong nr) char buff[64]; int l; CHARSET_INFO *cs=charset(); - l=cs->ll10tostr(cs,buff,sizeof(buff),-10,nr); + l=cs->longlong10_to_str(cs,buff,sizeof(buff),-10,nr); return Field_varstring::store(buff,(uint)l,cs); } double Field_varstring::val_real(void) { + int not_used; uint length=uint2korr(ptr)+2; CHARSET_INFO *cs=charset(); - return my_strntod(cs,ptr+2,length,(char**)0); + return my_strntod(cs,ptr+2,length,(char**)0, ¬_used); } longlong Field_varstring::val_int(void) { + int not_used; uint length=uint2korr(ptr)+2; CHARSET_INFO *cs=charset(); - return my_strntoll(cs,ptr+2,length,NULL,10); + return my_strntoll(cs,ptr+2,length,10,NULL, ¬_used); } @@ -4137,27 +4141,9 @@ int Field_varstring::cmp(const char *a_ptr, const char *b_ptr) void Field_varstring::sort_string(char *to,uint length) { uint tot_length=uint2korr(ptr); - if (binary()) - memcpy((byte*) to,(byte*) ptr+2,(size_t) tot_length); - else - { -#ifdef USE_STRCOLL - if (use_strnxfrm(field_charset)) - tot_length=my_strnxfrm(field_charset, + tot_length=my_strnxfrm(field_charset, (unsigned char *) to, length, (unsigned char *)ptr+2, tot_length); - else - { -#endif - char *tmp=to; - if (tot_length > length) - tot_length=length; - for (char *from=ptr+2,*end=from+tot_length ; from != end ;) - *tmp++=(char) field_charset->sort_order[(uint) (uchar) *from++]; -#ifdef USE_STRCOLL - } -#endif - } if (tot_length < length) bzero(to+tot_length,length-tot_length); } @@ -4436,24 +4422,26 @@ int Field_blob::store(longlong nr) double Field_blob::val_real(void) { + int not_used; char *blob; memcpy_fixed(&blob,ptr+packlength,sizeof(char*)); if (!blob) return 0.0; uint32 length=get_length(ptr); CHARSET_INFO *cs=charset(); - return my_strntod(cs,blob,length,(char**)0); + return my_strntod(cs,blob,length,(char**)0, ¬_used); } longlong Field_blob::val_int(void) { + int not_used; char *blob; memcpy_fixed(&blob,ptr+packlength,sizeof(char*)); if (!blob) return 0; uint32 length=get_length(ptr); - return my_strntoll(charset(),blob,length,NULL,10); + return my_strntoll(charset(),blob,length,10,NULL,¬_used); } @@ -4618,39 +4606,18 @@ void Field_blob::sort_string(char *to,uint length) { char *blob; uint blob_length=get_length(); -#ifdef USE_STRCOLL - uint blob_org_length=blob_length; -#endif + if (!blob_length) bzero(to,length); else { - if (blob_length > length) - blob_length=length; memcpy_fixed(&blob,ptr+packlength,sizeof(char*)); - if (binary()) - { - memcpy(to,blob,blob_length); - to+=blob_length; - } - else - { -#ifdef USE_STRCOLL - if (use_strnxfrm(field_charset)) - { - blob_length=my_strnxfrm(field_charset, - (unsigned char *)to, length, - (unsigned char *)blob, blob_org_length); - if (blob_length >= length) - return; - to+=blob_length; - } - else -#endif - for (char *end=blob+blob_length ; blob != end ;) - *to++=(char) field_charset->sort_order[(uint) (uchar) *blob++]; - } - bzero(to,length-blob_length); + + blob_length=my_strnxfrm(field_charset, + (unsigned char *)to, length, + (unsigned char *)blob, blob_length); + if (blob_length < length) + bzero(to+blob_length, length-blob_length); } } @@ -4892,7 +4859,7 @@ uint find_enum(TYPELIB *lib,const char *x, uint length) int Field_enum::store(const char *from,uint length,CHARSET_INFO *cs) { - int error= 0; + int err= 0; uint tmp=find_enum(typelib,from,length); if (!tmp) { @@ -4900,20 +4867,18 @@ int Field_enum::store(const char *from,uint length,CHARSET_INFO *cs) { /* This is for reading numbers with LOAD DATA INFILE */ char *end; - my_errno=0; - tmp=(uint) my_strntoul(cs,from,length,&end,10); - if (my_errno || end != from+length || tmp > typelib->count) + tmp=(uint) my_strntoul(cs,from,length,10,&end,&err); + if (err || end != from+length || tmp > typelib->count) { tmp=0; current_thd->cuted_fields++; - error=1; } } else current_thd->cuted_fields++; } store_type((ulonglong) tmp); - return error; + return err; } @@ -5042,38 +5007,52 @@ void Field_enum::sql_type(String &res) const } -/**************************************************************************** -** set type. -** This is a string which can have a collection of different values. -** Each string value is separated with a ','. -** For example "One,two,five" -** If one uses this string in a number context one gets the bits as a longlong -** number. -****************************************************************************/ +/* + set type. + This is a string which can have a collection of different values. + Each string value is separated with a ','. + For example "One,two,five" + If one uses this string in a number context one gets the bits as a longlong + number. -ulonglong find_set(TYPELIB *lib,const char *x,uint length) + If there was a value in string that wasn't in set, the 'err_pos' points to + the last invalid value found. 'err_len' will be set to length of the + error string. +*/ + +ulonglong find_set(TYPELIB *lib, const char *x, uint length, char **err_pos, + uint *err_len) { - const char *end=x+length; + const char *end= x + length; + *err_pos= 0; // No error yet while (end > x && my_isspace(system_charset_info, end[-1])) end--; - ulonglong found=0; + *err_len= 0; + ulonglong found= 0; if (x != end) { - const char *start=x; + const char *start= x; bool error= 0; for (;;) { - const char *pos=start; - for (; pos != end && *pos != field_separator ; pos++) ; - uint find=find_enum(lib,start,(uint) (pos-start)); + const char *pos= start; + uint var_len; + + for (; pos != end && *pos != field_separator; pos++) ; + var_len= (uint) (pos - start); + uint find= find_enum(lib, start, var_len); if (!find) - error=1; + { + *err_pos= (char*) start; + *err_len= var_len; + error= 1; + } else - found|= ((longlong) 1 << (find-1)); + found|= ((longlong) 1 << (find - 1)); if (pos == end) - break; - start=pos+1; + break; + start= pos + 1; } if (error) current_thd->cuted_fields++; @@ -5084,25 +5063,26 @@ ulonglong find_set(TYPELIB *lib,const char *x,uint length) int Field_set::store(const char *from,uint length,CHARSET_INFO *cs) { - int error= 0; - ulonglong tmp=find_set(typelib,from,length); + int err= 0; + char *not_used; + uint not_used2; + + ulonglong tmp= find_set(typelib, from, length, ¬_used, ¬_used2); if (!tmp && length && length < 22) { /* This is for reading numbers with LOAD DATA INFILE */ char *end; - my_errno=0; - tmp=my_strntoull(cs,from,length,&end,10); - if (my_errno || end != from+length || + tmp=my_strntoull(cs,from,length,10,&end,&err); + if (err || end != from+length || tmp > (ulonglong) (((longlong) 1 << typelib->count) - (longlong) 1)) { tmp=0; - error=1; } else current_thd->cuted_fields--; // Remove warning from find_set } store_type(tmp); - return error; + return err; } diff --git a/sql/field.h b/sql/field.h index 6e049f45814..97900938e9d 100644 --- a/sql/field.h +++ b/sql/field.h @@ -1097,7 +1097,8 @@ uint32 calc_pack_length(enum_field_types type,uint32 length); bool set_field_to_null(Field *field); bool set_field_to_null_with_conversions(Field *field, bool no_conversions); uint find_enum(TYPELIB *typelib,const char *x, uint length); -ulonglong find_set(TYPELIB *typelib,const char *x, uint length); +ulonglong find_set(TYPELIB *typelib,const char *x, uint length, + char **err_pos, uint *err_len); bool test_if_int(const char *str, int length, const char *int_end, CHARSET_INFO *cs); diff --git a/sql/field_conv.cc b/sql/field_conv.cc index 95d06176cd2..47996606638 100644 --- a/sql/field_conv.cc +++ b/sql/field_conv.cc @@ -118,6 +118,15 @@ set_field_to_null(Field *field) field->reset(); return 0; } + field->reset(); + if (current_thd->count_cuted_fields) + { + current_thd->cuted_fields++; // Increment error counter + return 0; + } + if (!current_thd->no_errors) + my_printf_error(ER_BAD_NULL_ERROR,ER(ER_BAD_NULL_ERROR),MYF(0), + field->field_name); return 1; } diff --git a/sql/ha_innodb.cc b/sql/ha_innodb.cc index 68de2a529c9..a6cf68b33b0 100644 --- a/sql/ha_innodb.cc +++ b/sql/ha_innodb.cc @@ -2274,19 +2274,7 @@ convert_search_mode_to_innobase( case HA_READ_AFTER_KEY: return(PAGE_CUR_G); case HA_READ_BEFORE_KEY: return(PAGE_CUR_L); case HA_READ_PREFIX: return(PAGE_CUR_GE); - case HA_READ_PREFIX_LAST: - /* ut_print_timestamp(stderr); - fprintf(stderr, - " InnoDB: Warning: Using HA_READ_PREFIX_LAST\n"); */ - return(PAGE_CUR_LE); - - /* InnoDB does not yet support ..PREFIX_LAST! - We have to add a new search flag - PAGE_CUR_LE_OR_PREFIX to InnoDB. */ - - /* the above PREFIX flags mean that the last - field in the key value may just be a prefix - of the complete fixed length field */ + case HA_READ_PREFIX_LAST: return(PAGE_CUR_LE_OR_EXTENDS); default: assert(0); } @@ -2491,6 +2479,9 @@ ha_innobase::change_active_index( (trx_t*) current_thd->transaction.all.innobase_tid); ut_a(user_thd == current_thd); + ut_a(prebuilt->trx == + (trx_t*) current_thd->transaction.all.innobase_tid); + active_index = keynr; if (keynr != MAX_KEY && table->keys > 0) { @@ -2522,6 +2513,11 @@ ha_innobase::change_active_index( the flag ROW_MYSQL_WHOLE_ROW below, but that caused unnecessary copying. Starting from MySQL-4.1 we use a more efficient flag here. */ + /* + TODO: In 4.0 the below user_thd was changed to NULL. + Heikki, please delete this comment after you have read this and may + acted upon it. + */ build_template(prebuilt, user_thd, table, ROW_MYSQL_REC_FIELDS); DBUG_RETURN(0); @@ -3027,6 +3023,7 @@ ha_innobase::create( { int error; dict_table_t* innobase_table; + trx_t* parent_trx; trx_t* trx; int primary_key_no; uint i; @@ -3038,6 +3035,16 @@ ha_innobase::create( DBUG_ASSERT(thd != NULL); + /* Get the transaction associated with the current thd, or create one + if not yet created */ + + parent_trx = check_trx_exists(current_thd); + + /* In case MySQL calls this in the middle of a SELECT query, release + possible adaptive hash latch to avoid deadlocks of threads */ + + trx_search_latch_release_if_reserved(parent_trx); + trx = trx_allocate_for_mysql(); if (thd->options & OPTION_NO_FOREIGN_KEY_CHECKS) { @@ -3198,11 +3205,22 @@ ha_innobase::delete_table( { ulint name_len; int error; + trx_t* parent_trx; trx_t* trx; char norm_name[1000]; DBUG_ENTER("ha_innobase::delete_table"); + /* Get the transaction associated with the current thd, or create one + if not yet created */ + + parent_trx = check_trx_exists(current_thd); + + /* In case MySQL calls this in the middle of a SELECT query, release + possible adaptive hash latch to avoid deadlocks of threads */ + + trx_search_latch_release_if_reserved(parent_trx); + if (lower_case_table_names) { srv_lower_case_table_names = TRUE; } else { @@ -3257,11 +3275,22 @@ innobase_drop_database( the database name is 'test' */ { ulint len = 0; + trx_t* parent_trx; trx_t* trx; char* ptr; int error; char namebuf[10000]; + /* Get the transaction associated with the current thd, or create one + if not yet created */ + + parent_trx = check_trx_exists(current_thd); + + /* In case MySQL calls this in the middle of a SELECT query, release + possible adaptive hash latch to avoid deadlocks of threads */ + + trx_search_latch_release_if_reserved(parent_trx); + ptr = strend(path) - 2; while (ptr >= path && *ptr != '\\' && *ptr != '/') { @@ -3313,12 +3342,23 @@ ha_innobase::rename_table( ulint name_len1; ulint name_len2; int error; + trx_t* parent_trx; trx_t* trx; char norm_from[1000]; char norm_to[1000]; DBUG_ENTER("ha_innobase::rename_table"); + /* Get the transaction associated with the current thd, or create one + if not yet created */ + + parent_trx = check_trx_exists(current_thd); + + /* In case MySQL calls this in the middle of a SELECT query, release + possible adaptive hash latch to avoid deadlocks of threads */ + + trx_search_latch_release_if_reserved(parent_trx); + if (lower_case_table_names) { srv_lower_case_table_names = TRUE; } else { @@ -3365,8 +3405,8 @@ Estimates the number of index records in a range. */ ha_rows ha_innobase::records_in_range( /*==========================*/ - /* out: estimated number of rows, - currently 32-bit int or uint */ + /* out: estimated number of + rows */ int keynr, /* in: index number */ const mysql_byte* start_key, /* in: start key value of the range, may also be empty */ @@ -3397,9 +3437,18 @@ ha_innobase::records_in_range( DBUG_ENTER("records_in_range"); - /* Warning: since it is not sure that MySQL calls external_lock - before calling this function, the trx field in prebuilt can be - obsolete! */ + /* We do not know if MySQL can call this function before calling + external_lock(). To be safe, update the thd of the current table + handle. */ + + update_thd(current_thd); + + prebuilt->trx->op_info = (char*)"estimating records in index range"; + + /* In case MySQL calls this in the middle of a SELECT query, release + possible adaptive hash latch to avoid deadlocks of threads */ + + trx_search_latch_release_if_reserved(prebuilt->trx); active_index = keynr; @@ -3433,6 +3482,8 @@ ha_innobase::records_in_range( my_free((char*) key_val_buff2, MYF(0)); + prebuilt->trx->op_info = (char*)""; + DBUG_RETURN((ha_rows) n_rows); } @@ -3453,11 +3504,21 @@ ha_innobase::estimate_number_of_rows(void) ulonglong estimate; ulonglong local_data_file_length; - /* Warning: since it is not sure that MySQL calls external_lock - before calling this function, the trx field in prebuilt can be - obsolete! */ + DBUG_ENTER("estimate_number_of_rows"); - DBUG_ENTER("info"); + /* We do not know if MySQL can call this function before calling + external_lock(). To be safe, update the thd of the current table + handle. */ + + update_thd(current_thd); + + prebuilt->trx->op_info = (char*) + "calculating upper bound for table rows"; + + /* In case MySQL calls this in the middle of a SELECT query, release + possible adaptive hash latch to avoid deadlocks of threads */ + + trx_search_latch_release_if_reserved(prebuilt->trx); index = dict_table_get_first_index_noninline(prebuilt->table); @@ -3472,6 +3533,8 @@ ha_innobase::estimate_number_of_rows(void) estimate = 2 * local_data_file_length / dict_index_calc_min_rec_len(index); + prebuilt->trx->op_info = (char*)""; + DBUG_RETURN((ha_rows) estimate); } @@ -3522,9 +3585,18 @@ ha_innobase::info( return; } - /* Warning: since it is not sure that MySQL calls external_lock - before calling this function, the trx field in prebuilt can be - obsolete! */ + /* We do not know if MySQL can call this function before calling + external_lock(). To be safe, update the thd of the current table + handle. */ + + update_thd(current_thd); + + /* In case MySQL calls this in the middle of a SELECT query, release + possible adaptive hash latch to avoid deadlocks of threads */ + + prebuilt->trx->op_info = (char*)"returning various info to MySQL"; + + trx_search_latch_release_if_reserved(prebuilt->trx); ib_table = prebuilt->table; @@ -3532,7 +3604,12 @@ ha_innobase::info( /* In sql_show we call with this flag: update then statistics so that they are up-to-date */ + prebuilt->trx->op_info = (char*)"updating table statistics"; + dict_update_statistics(ib_table); + + prebuilt->trx->op_info = (char*) + "returning various info to MySQL"; } if (flag & HA_STATUS_VARIABLE) { @@ -3592,12 +3669,6 @@ ha_innobase::info( } } - /* The trx struct in InnoDB contains a pthread mutex embedded: - in the debug version of MySQL that it replaced by a 'safe mutex' - which is of a different size. We have to use a function to access - trx fields. Otherwise trx->error_info will be a random - pointer and cause a seg fault. */ - if (flag & HA_STATUS_ERRKEY) { ut_a(prebuilt->trx && prebuilt->trx->magic_n == TRX_MAGIC_N); @@ -3606,6 +3677,8 @@ ha_innobase::info( trx_get_error_info(prebuilt->trx)); } + prebuilt->trx->op_info = (char*)""; + DBUG_VOID_RETURN; } @@ -3663,11 +3736,22 @@ ha_innobase::update_table_comment( char* str = my_malloc(length + 16500, MYF(0)); char* pos; - /* Warning: since it is not sure that MySQL calls external_lock - before calling this function, the trx field in prebuilt can be - obsolete! */ + /* We do not know if MySQL can call this function before calling + external_lock(). To be safe, update the thd of the current table + handle. */ + + update_thd(current_thd); + + prebuilt->trx->op_info = (char*)"returning table comment"; + + /* In case MySQL calls this in the middle of a SELECT query, release + possible adaptive hash latch to avoid deadlocks of threads */ + + trx_search_latch_release_if_reserved(prebuilt->trx); if (!str) { + prebuilt->trx->op_info = (char*)""; + return((char*)comment); } @@ -3691,6 +3775,8 @@ ha_innobase::update_table_comment( prebuilt->table); } + prebuilt->trx->op_info = (char*)""; + return(str); } @@ -3706,20 +3792,30 @@ ha_innobase::get_foreign_key_create_info(void) { row_prebuilt_t* prebuilt = (row_prebuilt_t*)innobase_prebuilt; char* str; + + ut_a(prebuilt != NULL); + + /* We do not know if MySQL can call this function before calling + external_lock(). To be safe, update the thd of the current table + handle. */ + + update_thd(current_thd); + + prebuilt->trx->op_info = (char*)"getting info on foreign keys"; + + /* In case MySQL calls this in the middle of a SELECT query, release + possible adaptive hash latch to avoid deadlocks of threads */ + + trx_search_latch_release_if_reserved(prebuilt->trx); - if (prebuilt == NULL) { - fprintf(stderr, -"InnoDB: Error: cannot get create info for foreign keys\n"); - - return(NULL); - } - str = (char*)ut_malloc(10000); str[0] = '\0'; dict_print_info_on_foreign_keys(TRUE, str, 9000, prebuilt->table); + prebuilt->trx->op_info = (char*)""; + return(str); } @@ -3795,9 +3891,10 @@ ha_innobase::reset(void) } /********************************************************************** -When we create a temporary table inside MySQL LOCK TABLES, MySQL will -not call external_lock for the temporary table when it uses it. Instead, -it will call this function. */ +Inside LOCK TABLES MySQL will not call external_lock() between SQL +statements. It will call this function at the start of each SQL statement. +Note also a spacial case: if a temporary table is created inside LOCK +TABLES, MySQL has not called external_lock() at all on that table. */ int ha_innobase::start_stmt( @@ -3935,8 +4032,8 @@ ha_innobase::external_lock( trx->mysql_n_tables_locked = 0; - /* Here we release the search latch, auto_inc_lock, - and InnoDB thread FIFO ticket if they were reserved. */ + /* Here we release the search latch and InnoDB + thread FIFO ticket if they were reserved. */ innobase_release_stat_resources(trx); @@ -3980,12 +4077,12 @@ innodb_show_status( DBUG_RETURN(-1); } - /* We let the InnoDB Monitor to output at most 100 kB of text, add + /* We let the InnoDB Monitor to output at most 200 kB of text, add a safety margin of 10 kB for buffer overruns */ - buf = (char*)ut_malloc(110 * 1024); + buf = (char*)ut_malloc(210 * 1024); - srv_sprintf_innodb_monitor(buf, 100 * 1024); + srv_sprintf_innodb_monitor(buf, 200 * 1024); List field_list; @@ -3993,6 +4090,7 @@ innodb_show_status( if (protocol->send_fields(&field_list, 1)) { + ut_free(buf); DBUG_RETURN(-1); } @@ -4243,4 +4341,4 @@ ha_innobase::get_auto_increment() } #endif /* HAVE_INNOBASE_DB */ - + diff --git a/sql/ha_myisammrg.cc b/sql/ha_myisammrg.cc index 3bd812821f6..00052239f0b 100644 --- a/sql/ha_myisammrg.cc +++ b/sql/ha_myisammrg.cc @@ -241,7 +241,7 @@ void ha_myisammrg::info(uint flag) #endif if (flag & HA_STATUS_CONST) { - if (table->key_parts) + if (table->key_parts && info.rec_per_key) memcpy((char*) table->key_info[0].rec_per_key, (char*) info.rec_per_key, sizeof(table->key_info[0].rec_per_key)*table->key_parts); diff --git a/sql/item.cc b/sql/item.cc index 99f6a6f8313..7a2f8e26567 100644 --- a/sql/item.cc +++ b/sql/item.cc @@ -378,10 +378,11 @@ int Item_param::save_in_field(Field *field, bool no_conversions) double Item_param::val() { + int err; switch (item_result_type) { case STRING_RESULT: return (double) my_strntod(str_value.charset(), (char*) str_value.ptr(), - str_value.length(), (char**) 0); + str_value.length(), (char**) 0, &err); case INT_RESULT: return (double)int_value; default: @@ -392,9 +393,12 @@ double Item_param::val() longlong Item_param::val_int() { + int err; switch (item_result_type) { case STRING_RESULT: - return my_strntoll(str_value.charset(),str_value.ptr(),str_value.length(),(char**) 0,10); + return my_strntoll(str_value.charset(), + str_value.ptr(),str_value.length(),10, + (char**) 0,&err); case REAL_RESULT: return (longlong) (real_value+(real_value > 0 ? 0.5 : -0.5)); default: @@ -1265,17 +1269,19 @@ void Item_cache_str::store(Item *item) } double Item_cache_str::val() { + int err; if (value) return my_strntod(value->charset(), (char*) value->ptr(), - value->length(), (char**) 0); + value->length(), (char**) 0, &err); else return (double)0; } longlong Item_cache_str::val_int() { + int err; if (value) return my_strntoll(value->charset(), value->ptr(), - value->length(), (char**) 0, 10); + value->length(), 10, (char**) 0, &err); else return (longlong)0; } diff --git a/sql/item.h b/sql/item.h index 0a236189063..09882b26724 100644 --- a/sql/item.h +++ b/sql/item.h @@ -344,13 +344,15 @@ public: enum Type type() const { return STRING_ITEM; } double val() { + int err; return my_strntod(str_value.charset(), (char*) str_value.ptr(), - str_value.length(), (char**) 0); + str_value.length(), (char**) 0, &err); } longlong val_int() { + int err; return my_strntoll(str_value.charset(), str_value.ptr(), - str_value.length(), (char**) 0, 10); + str_value.length(), 10, (char**) 0, &err); } String *val_str(String*) { return (String*) &str_value; } int save_in_field(Field *field, bool no_conversions); @@ -599,12 +601,16 @@ public: enum_field_types field_type() const { return cached_field_type; } double val() { + int err; return (null_value ? 0.0 : my_strntod(str_value.charset(), (char*) str_value.ptr(), - str_value.length(),NULL)); + str_value.length(),NULL,&err)); } longlong val_int() - { return null_value ? LL(0) : my_strntoll(str_value.charset(),str_value.ptr(),str_value.length(),(char**) 0,10); } + { + int err; + return null_value ? LL(0) : my_strntoll(str_value.charset(),str_value.ptr(),str_value.length(),10, (char**) 0,&err); + } String *val_str(String*); void make_field(Send_field *field) { item->make_field(field); } void copy(); diff --git a/sql/item_cmpfunc.cc b/sql/item_cmpfunc.cc index 3d65c05b9cf..79807653317 100644 --- a/sql/item_cmpfunc.cc +++ b/sql/item_cmpfunc.cc @@ -1898,7 +1898,7 @@ Item_func_regex::~Item_func_regex() Precomputation dependent only on pattern_len. **********************************************************************/ -void Item_func_like::turboBM_compute_suffixes(int* suff) +void Item_func_like::turboBM_compute_suffixes(int *suff) { const int plm1 = pattern_len - 1; int f = 0; @@ -1940,8 +1940,8 @@ void Item_func_like::turboBM_compute_suffixes(int* suff) if (i < g) g = i; // g = min(i, g) f = i; - while (g >= 0 && likeconv(cs, pattern[g]) == - likeconv(cs, pattern[g + plm1 - f])) + while (g >= 0 && + likeconv(cs, pattern[g]) == likeconv(cs, pattern[g + plm1 - f])) g--; suff[i] = f - g; } @@ -1955,12 +1955,12 @@ void Item_func_like::turboBM_compute_suffixes(int* suff) Precomputation dependent only on pattern_len. **********************************************************************/ -void Item_func_like::turboBM_compute_good_suffix_shifts(int* suff) +void Item_func_like::turboBM_compute_good_suffix_shifts(int *suff) { turboBM_compute_suffixes(suff); - int* end = bmGs + pattern_len; - int* k; + int *end = bmGs + pattern_len; + int *k; for (k = bmGs; k < end; k++) *k = pattern_len; @@ -1974,14 +1974,14 @@ void Item_func_like::turboBM_compute_good_suffix_shifts(int* suff) { for (tmp = plm1 - i; j < tmp; j++) { - int* tmp2 = bmGs + j; + int *tmp2 = bmGs + j; if (*tmp2 == pattern_len) *tmp2 = tmp; } } } - int* tmp2; + int *tmp2; for (tmp = plm1 - i; j < tmp; j++) { tmp2 = bmGs + j; @@ -2014,12 +2014,12 @@ void Item_func_like::turboBM_compute_bad_character_shifts() if (binary()) { for (j = 0; j < plm1; j++) - bmBc[pattern[j]] = plm1 - j; + bmBc[(uint) (uchar) pattern[j]] = plm1 - j; } else { for (j = 0; j < plm1; j++) - bmBc[likeconv(cs,pattern[j])] = plm1 - j; + bmBc[(uint) likeconv(cs,pattern[j])] = plm1 - j; } } @@ -2038,27 +2038,27 @@ bool Item_func_like::turboBM_matches(const char* text, int text_len) const int u = 0; CHARSET_INFO *cs=system_charset_info; // QQ Needs to be fixed - const int plm1 = pattern_len - 1; - const int tlmpl = text_len - pattern_len; + const int plm1= pattern_len - 1; + const int tlmpl= text_len - pattern_len; /* Searching */ if (binary()) { while (j <= tlmpl) { - register int i = plm1; + register int i= plm1; while (i >= 0 && pattern[i] == text[i + j]) { i--; if (i == plm1 - shift) - i -= u; + i-= u; } if (i < 0) return 1; register const int v = plm1 - i; turboShift = u - v; - bcShift = bmBc[text[i + j]] - plm1 + i; + bcShift = bmBc[(uint) (uchar) text[i + j]] - plm1 + i; shift = max(turboShift, bcShift); shift = max(shift, bmGs[i]); if (shift == bmGs[i]) @@ -2069,7 +2069,7 @@ bool Item_func_like::turboBM_matches(const char* text, int text_len) const shift = max(shift, u + 1); u = 0; } - j += shift; + j+= shift; } return 0; } @@ -2082,14 +2082,14 @@ bool Item_func_like::turboBM_matches(const char* text, int text_len) const { i--; if (i == plm1 - shift) - i -= u; + i-= u; } if (i < 0) return 1; register const int v = plm1 - i; turboShift = u - v; - bcShift = bmBc[likeconv(cs, text[i + j])] - plm1 + i; + bcShift = bmBc[(uint) likeconv(cs, text[i + j])] - plm1 + i; shift = max(turboShift, bcShift); shift = max(shift, bmGs[i]); if (shift == bmGs[i]) @@ -2100,7 +2100,7 @@ bool Item_func_like::turboBM_matches(const char* text, int text_len) const shift = max(shift, u + 1); u = 0; } - j += shift; + j+= shift; } return 0; } diff --git a/sql/item_func.cc b/sql/item_func.cc index 47d26169d06..a5d45d42635 100644 --- a/sql/item_func.cc +++ b/sql/item_func.cc @@ -394,7 +394,7 @@ void Item_func_minus::fix_length_and_dec() { Item_num_op::fix_length_and_dec(); if (unsigned_flag && - (current_thd->sql_mode & MODE_NO_UNSIGNED_SUBTRACTION)) + (current_thd->variables.sql_mode & MODE_NO_UNSIGNED_SUBTRACTION)) unsigned_flag=0; } diff --git a/sql/item_func.h b/sql/item_func.h index 11793b11bdb..a015f6e69ce 100644 --- a/sql/item_func.h +++ b/sql/item_func.h @@ -812,13 +812,15 @@ public: String *val_str(String *); double val() { + int err; String *res; res=val_str(&str_value); - return res ? my_strntod(res->charset(),(char*) res->ptr(),res->length(),0) : 0.0; + return res ? my_strntod(res->charset(),(char*) res->ptr(),res->length(),0,&err) : 0.0; } longlong val_int() { + int err; String *res; res=val_str(&str_value); - return res ? my_strntoll(res->charset(),res->ptr(),res->length(),(char**) 0,10) : (longlong) 0; + return res ? my_strntoll(res->charset(),res->ptr(),res->length(),10,(char**) 0,&err) : (longlong) 0; } enum Item_result result_type () const { return STRING_RESULT; } void fix_length_and_dec(); diff --git a/sql/item_strfunc.cc b/sql/item_strfunc.cc index 4358f14f08e..748ef096dbe 100644 --- a/sql/item_strfunc.cc +++ b/sql/item_strfunc.cc @@ -52,17 +52,19 @@ uint nr_of_decimals(const char *str) double Item_str_func::val() { + int err; String *res; res=val_str(&str_value); return res ? my_strntod(res->charset(), (char*) res->ptr(),res->length(), - NULL) : 0.0; + NULL, &err) : 0.0; } longlong Item_str_func::val_int() { + int err; String *res; res=val_str(&str_value); - return res ? my_strntoll(res->charset(),res->ptr(),res->length(),NULL,10) : (longlong) 0; + return res ? my_strntoll(res->charset(),res->ptr(),res->length(),10,NULL,&err) : (longlong) 0; } @@ -1272,8 +1274,13 @@ String *Item_func_trim::val_str(String *str) return &tmp_value; } +void Item_func_password::fix_length_and_dec() +{ + max_length= get_password_length(use_old_passwords); +} + /* - Password() function can have 2 args now. Second argument can be used + Password() function has 2 arguments. Second argument can be used to make results repeatable */ @@ -1290,9 +1297,9 @@ String *Item_func_password::val_str(String *str) { if (res->length() == 0) return &empty_string; - make_scrambled_password(tmp_value,res->c_ptr(),opt_old_passwords, + make_scrambled_password(tmp_value,res->c_ptr(),use_old_passwords, ¤t_thd->rand); - str->set(tmp_value,get_password_length(opt_old_passwords),res->charset()); + str->set(tmp_value,get_password_length(use_old_passwords),res->charset()); return str; } else @@ -1321,9 +1328,9 @@ String *Item_func_password::val_str(String *str) /* Use constants which allow nice random values even with small seed */ randominit(&rand_st,seed*111111+33333333L,seed*1111+55555555L); - make_scrambled_password(tmp_value,res->c_ptr(),opt_old_passwords, + make_scrambled_password(tmp_value,res->c_ptr(),use_old_passwords, &rand_st); - str->set(tmp_value,get_password_length(opt_old_passwords),res->charset()); + str->set(tmp_value,get_password_length(use_old_passwords),res->charset()); return str; } } @@ -1956,6 +1963,7 @@ String *Item_func_conv::val_str(String *str) longlong dec; int from_base= (int) args[1]->val_int(); int to_base= (int) args[2]->val_int(); + int err; if (args[0]->null_value || args[1]->null_value || args[2]->null_value || abs(to_base) > 36 || abs(to_base) < 2 || @@ -1966,9 +1974,9 @@ String *Item_func_conv::val_str(String *str) } null_value=0; if (from_base < 0) - dec= my_strntoll(res->charset(),res->ptr(),res->length(),&endptr,-from_base); + dec= my_strntoll(res->charset(),res->ptr(),res->length(),-from_base,&endptr,&err); else - dec= (longlong) my_strntoull(res->charset(),res->ptr(),res->length(),&endptr,from_base); + dec= (longlong) my_strntoull(res->charset(),res->ptr(),res->length(),from_base,&endptr,&err); ptr= longlong2str(dec,ans,to_base); if (str->copy(ans,(uint32) (ptr-ans), thd_charset())) return &empty_string; diff --git a/sql/item_strfunc.h b/sql/item_strfunc.h index c90ff6e2295..c56c59bcaef 100644 --- a/sql/item_strfunc.h +++ b/sql/item_strfunc.h @@ -21,9 +21,6 @@ #pragma interface /* gcc class implementation */ #endif -extern my_bool opt_old_passwords; /* Need this variable for some functions */ - - class Item_str_func :public Item_func { public: @@ -264,7 +261,7 @@ public: Item_func_password(Item *a) :Item_str_func(a) {} Item_func_password(Item *a, Item *b) :Item_str_func(a,b) {} String *val_str(String *); - void fix_length_and_dec() { max_length = get_password_length(opt_old_passwords); } + void fix_length_and_dec(); const char *func_name() const { return "password"; } }; diff --git a/sql/item_sum.cc b/sql/item_sum.cc index b15fceda686..2a96594af4e 100644 --- a/sql/item_sum.cc +++ b/sql/item_sum.cc @@ -336,13 +336,14 @@ void Item_sum_variance::update_field(int offset) double Item_sum_hybrid::val() { + int err; if (null_value) return 0.0; switch (hybrid_type) { case STRING_RESULT: String *res; res=val_str(&str_value); return (res ? my_strntod(res->charset(), (char*) res->ptr(),res->length(), - (char**) 0) : 0.0); + (char**) 0, &err) : 0.0); case INT_RESULT: if (unsigned_flag) return ulonglong2double(sum_int); diff --git a/sql/item_sum.h b/sql/item_sum.h index ffc9558822d..c49311082e8 100644 --- a/sql/item_sum.h +++ b/sql/item_sum.h @@ -483,14 +483,16 @@ public: String *val_str(String *); double val() { + int err; String *res; res=val_str(&str_value); return res ? my_strntod(res->charset(),(char*) res->ptr(),res->length(), - (char**) 0) : 0.0; + (char**) 0, &err) : 0.0; } longlong val_int() { + int err; String *res; res=val_str(&str_value); - return res ? my_strntoll(res->charset(),res->ptr(),res->length(),(char**) 0,10) : (longlong) 0; + return res ? my_strntoll(res->charset(),res->ptr(),res->length(),10, (char**) 0, &err) : (longlong) 0; } enum Item_result result_type () const { return STRING_RESULT; } void fix_length_and_dec(); diff --git a/sql/log_event.cc b/sql/log_event.cc index 36fca5250ac..bf32aec91fb 100644 --- a/sql/log_event.cc +++ b/sql/log_event.cc @@ -95,27 +95,29 @@ inline int ignored_error_code(int err_code) ****************************************************************************/ #ifndef MYSQL_CLIENT -static void pretty_print_str(String* packet, char* str, int len) +static char* pretty_print_str(char* packet, char* str, int len) { char* end = str + len; - packet->append('\''); + char* pos= packet; + *pos++= '\''; while (str < end) { char c; switch ((c=*str++)) { - case '\n': packet->append( "\\n"); break; - case '\r': packet->append( "\\r"); break; - case '\\': packet->append( "\\\\"); break; - case '\b': packet->append( "\\b"); break; - case '\t': packet->append( "\\t"); break; - case '\'': packet->append( "\\'"); break; - case 0 : packet->append( "\\0"); break; + case '\n': pos= strmov(pos, "\\n"); break; + case '\r': pos= strmov(pos, "\\r"); break; + case '\\': pos= strmov(pos, "\\\\"); break; + case '\b': pos= strmov(pos, "\\b"); break; + case '\t': pos= strmov(pos, "\\t"); break; + case '\'': pos= strmov(pos, "\\'"); break; + case 0 : pos= strmov(pos, "\\0"); break; default: - packet->append((char)c); + *pos++= (char)c; break; } } - packet->append('\''); + *pos++= '\''; + return pos; } #endif // !MYSQL_CLIENT @@ -575,7 +577,7 @@ Log_event* Log_event::read_log_event(const char* buf, int event_len, ev = new Query_log_event(buf, event_len, old_format); break; case LOAD_EVENT: - ev = new Create_file_log_event(buf, event_len, old_format); + ev = new Load_log_event(buf, event_len, old_format); break; case NEW_LOAD_EVENT: ev = new Load_log_event(buf, event_len, old_format); @@ -702,19 +704,24 @@ void Log_event::set_log_pos(MYSQL_LOG* log) ****************************************************************************/ void Query_log_event::pack_info(Protocol *protocol) { - char buf[256]; - String tmp(buf, sizeof(buf), log_cs); - tmp.length(0); + char *buf, *pos; + if (!(buf= my_malloc(9 + db_len + q_len, MYF(MY_WME)))) + return; + pos= buf; if (db && db_len) { - tmp.append("use `", 5); - tmp.append(db, db_len); - tmp.append("`; ", 3); + pos= strmov(buf, "use `"); + memcpy(pos, db, db_len); + pos+= db_len; + pos= strmov(pos, "`; "); } - if (query && q_len) - tmp.append(query, q_len); - protocol->store((char*) tmp.ptr(), tmp.length()); + { + memcpy(pos, query, q_len); + pos+= q_len; + } + protocol->store(buf, pos-buf); + my_free(buf, MYF(MY_ALLOW_ZERO_PTR)); } #endif @@ -869,10 +876,11 @@ int Query_log_event::exec_event(struct st_relay_log_info* rli) if (db_ok(thd->db, replicate_do_db, replicate_ignore_db)) { - thd->query = (char*)query; thd->set_time((time_t)when); thd->current_tablenr = 0; + thd->query_length= q_len; VOID(pthread_mutex_lock(&LOCK_thread_count)); + thd->query = (char*)query; thd->query_id = query_id++; VOID(pthread_mutex_unlock(&LOCK_thread_count)); thd->query_error= 0; // clear error @@ -930,7 +938,9 @@ int Query_log_event::exec_event(struct st_relay_log_info* rli) else { // master could be inconsistent, abort and tell DBA to check/fix it + VOID(pthread_mutex_lock(&LOCK_thread_count)); thd->db = thd->query = 0; + VOID(pthread_mutex_unlock(&LOCK_thread_count)); thd->variables.convert_set = 0; close_thread_tables(thd); free_root(&thd->mem_root,0); @@ -938,7 +948,9 @@ int Query_log_event::exec_event(struct st_relay_log_info* rli) } } thd->db= 0; // prevent db from being freed + VOID(pthread_mutex_lock(&LOCK_thread_count)); thd->query= 0; // just to be sure + VOID(pthread_mutex_unlock(&LOCK_thread_count)); // assume no convert for next query unless set explictly thd->variables.convert_set = 0; close_thread_tables(thd); @@ -973,16 +985,12 @@ int Query_log_event::exec_event(struct st_relay_log_info* rli) #if defined(HAVE_REPLICATION) && !defined(MYSQL_CLIENT) void Start_log_event::pack_info(Protocol *protocol) { - char buf1[256]; - String tmp(buf1, sizeof(buf1), log_cs); - tmp.length(0); - char buf[22]; - - tmp.append("Server ver: "); - tmp.append(server_version); - tmp.append(", Binlog ver: "); - tmp.append(llstr(binlog_version, buf)); - protocol->store(tmp.ptr(), tmp.length()); + char buf[12 + ST_SERVER_VER_LEN + 14 + 22], *pos; + pos= strmov(buf, "Server ver: "); + pos= strmov(pos, server_version); + pos= strmov(pos, ", Binlog ver: "); + pos=int10_to_str(binlog_version, pos, 10); + protocol->store(buf, pos-buf); } #endif @@ -1084,78 +1092,105 @@ int Start_log_event::exec_event(struct st_relay_log_info* rli) #if defined(HAVE_REPLICATION) && !defined(MYSQL_CLIENT) void Load_log_event::pack_info(Protocol *protocol) { - char buf[256]; - String tmp(buf, sizeof(buf), log_cs); - tmp.length(0); + char *buf, *pos; + uint buf_len; + + buf_len= + 5 + db_len + 3 + // "use DB; " + 18 + fname_len + 2 + // "LOAD DATA INFILE 'file''" + 9 + // " REPLACE or IGNORE " + 11 + table_name_len + // "INTO TABLE table" + 21 + sql_ex.field_term_len*4 + 2 + // " FIELDS TERMINATED BY 'str'" + 23 + sql_ex.enclosed_len*4 + 2 + // " OPTIONALLY ENCLOSED BY 'str'" + 12 + sql_ex.escaped_len*4 + 2 + // " ESCAPED BY 'str'" + 21 + sql_ex.line_term_len*4 + 2 + // " FIELDS TERMINATED BY 'str'" + 19 + sql_ex.line_start_len*4 + 2 + // " LINES STARTING BY 'str'" + 15 + 22 + // " IGNORE xxx LINES" + 3 + (num_fields-1)*2 + field_block_len; // " (field1, field2, ...)" + + buf= my_malloc(buf_len, MYF(MY_WME)); + if (!buf) + return; + pos= buf; if (db && db_len) { - tmp.append("use "); - tmp.append(db, db_len); - tmp.append("; ", 2); + pos= strmov(pos, "use `"); + memcpy(pos, db, db_len); + pos+= db_len; + pos= strmov(pos, "`; "); } - tmp.append("LOAD DATA INFILE '"); - tmp.append(fname, fname_len); - tmp.append("' ", 2); + pos= strmov(pos, "LOAD DATA INFILE '"); + memcpy(pos, fname, fname_len); + pos+= fname_len; + pos= strmov(pos, "' "); + if (sql_ex.opt_flags && REPLACE_FLAG ) - tmp.append(" REPLACE "); + pos= strmov(pos, " REPLACE "); else if (sql_ex.opt_flags && IGNORE_FLAG ) - tmp.append(" IGNORE "); - - tmp.append("INTO TABLE "); - tmp.append(table_name); + pos= strmov(pos, " IGNORE "); + + pos= strmov(pos ,"INTO TABLE "); + memcpy(pos, table_name, table_name_len); + pos+= table_name_len; + if (sql_ex.field_term_len) { - tmp.append(" FIELDS TERMINATED BY "); - pretty_print_str(&tmp, sql_ex.field_term, sql_ex.field_term_len); + pos= strmov(pos, " FIELDS TERMINATED BY "); + pos= pretty_print_str(pos, sql_ex.field_term, sql_ex.field_term_len); } if (sql_ex.enclosed_len) { if (sql_ex.opt_flags && OPT_ENCLOSED_FLAG ) - tmp.append(" OPTIONALLY "); - tmp.append( " ENCLOSED BY "); - pretty_print_str(&tmp, sql_ex.enclosed, sql_ex.enclosed_len); + pos= strmov(pos, " OPTIONALLY "); + pos= strmov(pos, " ENCLOSED BY "); + pos= pretty_print_str(pos, sql_ex.enclosed, sql_ex.enclosed_len); } - + if (sql_ex.escaped_len) { - tmp.append( " ESCAPED BY "); - pretty_print_str(&tmp, sql_ex.escaped, sql_ex.escaped_len); + pos= strmov(pos, " ESCAPED BY "); + pos= pretty_print_str(pos, sql_ex.escaped, sql_ex.escaped_len); } - + if (sql_ex.line_term_len) { - tmp.append(" LINES TERMINATED BY "); - pretty_print_str(&tmp, sql_ex.line_term, sql_ex.line_term_len); + pos= strmov(pos, " LINES TERMINATED BY "); + pos= pretty_print_str(pos, sql_ex.line_term, sql_ex.line_term_len); } if (sql_ex.line_start_len) { - tmp.append(" LINES STARTING BY "); - pretty_print_str(&tmp, sql_ex.line_start, sql_ex.line_start_len); + pos= strmov(pos, " LINES STARTING BY "); + pos= pretty_print_str(pos, sql_ex.line_start, sql_ex.line_start_len); } - + if ((int)skip_lines > 0) - tmp.append( " IGNORE %ld LINES ", (long) skip_lines); + { + pos= strmov(pos, " IGNORE "); + pos= longlong10_to_str((long) skip_lines, pos, 10); + pos= strmov(pos," LINES "); + } if (num_fields) { uint i; const char* field = fields; - tmp.append(" ("); + pos= strmov(pos, " ("); for (i = 0; i < num_fields; i++) { if (i) - tmp.append(" ,"); - tmp.append( field); - + pos= strmov(pos, " ,"); + memcpy(pos, field, field_lens[i]); + pos+= field_lens[i]; field += field_lens[i] + 1; } - tmp.append(')'); + *pos++= ')'; } - protocol->store(tmp.ptr(), tmp.length()); + protocol->store(buf, pos-buf); + my_free(buf, MYF(MY_ALLOW_ZERO_PTR)); } #endif @@ -1311,8 +1346,8 @@ int Load_log_event::copy_log_event(const char *buf, ulong event_len, { uint data_len; char* buf_end = (char*)buf + event_len; - const char* data_head = buf + ((old_format) ? - OLD_HEADER_LEN : LOG_EVENT_HEADER_LEN); + uint header_len= old_format ? OLD_HEADER_LEN : LOG_EVENT_HEADER_LEN; + const char* data_head = buf + header_len; thread_id = uint4korr(data_head + L_THREAD_ID_OFFSET); exec_time = uint4korr(data_head + L_EXEC_TIME_OFFSET); skip_lines = uint4korr(data_head + L_SKIP_LINES_OFFSET); @@ -1321,7 +1356,7 @@ int Load_log_event::copy_log_event(const char *buf, ulong event_len, num_fields = uint4korr(data_head + L_NUM_FIELDS_OFFSET); int body_offset = ((buf[EVENT_TYPE_OFFSET] == LOAD_EVENT) ? - LOAD_HEADER_LEN + OLD_HEADER_LEN : + LOAD_HEADER_LEN + header_len : get_data_body_offset()); if ((int) event_len < body_offset) @@ -1375,7 +1410,10 @@ void Load_log_event::print(FILE* file, bool short_form, char* last_db) if (db && db[0] && !same_db) fprintf(file, "use %s;\n", db); - fprintf(file, "LOAD DATA INFILE '%-*s' ", fname_len, fname); + fprintf(file, "LOAD "); + if (check_fname_outside_temp_buf()) + fprintf(file, "LOCAL "); + fprintf(file, "DATA INFILE '%-*s' ", fname_len, fname); if (sql_ex.opt_flags && REPLACE_FLAG ) fprintf(file," REPLACE "); @@ -1488,7 +1526,8 @@ int Load_log_event::exec_event(NET* net, struct st_relay_log_info* rli, { init_sql_alloc(&thd->mem_root, 8192,0); thd->db = rewrite_db((char*)db); - thd->query = 0; + DBUG_ASSERT(thd->query == 0); + thd->query = 0; // Should not be needed thd->query_error = 0; if (db_ok(thd->db, replicate_do_db, replicate_ignore_db)) @@ -1614,15 +1653,18 @@ int Load_log_event::exec_event(NET* net, struct st_relay_log_info* rli, #if defined(HAVE_REPLICATION) && !defined(MYSQL_CLIENT) void Rotate_log_event::pack_info(Protocol *protocol) { - char buf1[256], buf[22]; - String tmp(buf1, sizeof(buf1), log_cs); - tmp.length(0); - tmp.append(new_log_ident, ident_len); - tmp.append(";pos="); - tmp.append(llstr(pos,buf)); + char *buf, *b_pos; + if (!(buf= my_malloc(ident_len + 45, MYF(MY_WME)))) + return; + b_pos= buf; + memcpy(buf, new_log_ident, ident_len); + b_pos+= ident_len; + b_pos= strmov(b_pos, ";pos="); + b_pos=int10_to_str(pos, b_pos, 10); if (flags & LOG_EVENT_FORCED_ROTATE_F) - tmp.append("; forced by master"); - protocol->store(tmp.ptr(), tmp.length()); + b_pos= strmov(b_pos ,"; forced by master"); + protocol->store(buf, b_pos-buf); + my_free(buf, MYF(MY_ALLOW_ZERO_PTR)); } #endif @@ -1752,13 +1794,11 @@ int Rotate_log_event::exec_event(struct st_relay_log_info* rli) #if defined(HAVE_REPLICATION) && !defined(MYSQL_CLIENT) void Intvar_log_event::pack_info(Protocol *protocol) { - char buf1[256], buf[22]; - String tmp(buf1, sizeof(buf1), log_cs); - tmp.length(0); - tmp.append(get_var_type_name()); - tmp.append('='); - tmp.append(llstr(val, buf)); - protocol->store(tmp.ptr(), tmp.length()); + char buf[64], *pos; + pos= strmov(buf, get_var_type_name()); + *(pos++)='='; + pos=int10_to_str(val, pos, -10); + protocol->store(buf, pos-buf); } #endif @@ -1962,19 +2002,16 @@ int Rand_log_event::exec_event(struct st_relay_log_info* rli) #ifndef MYSQL_CLIENT void Slave_log_event::pack_info(Protocol *protocol) { - char buf1[256], buf[22], *end; - String tmp(buf1, sizeof(buf1), log_cs); - tmp.length(0); - tmp.append("host="); - tmp.append(master_host); - tmp.append(",port="); - end= int10_to_str((long) master_port, buf, 10); - tmp.append(buf, (uint32) (end-buf)); - tmp.append(",log="); - tmp.append(master_log); - tmp.append(",pos="); - tmp.append(llstr(master_pos,buf)); - protocol->store(tmp.ptr(), tmp.length()); + char buf[256], *pos; + pos= strmov(buf, "host="); + pos= strnmov(pos, master_host, HOSTNAME_LENGTH); + pos= strmov(pos, ",port="); + pos= int10_to_str((long) master_port, pos, 10); + pos= strmov(pos, ",log="); + pos= strmov(pos, master_log); + pos= strmov(pos, ",pos="); + pos= int10_to_str(master_pos, pos, 10); + protocol->store(buf, pos-buf); } #endif // !MYSQL_CLIENT @@ -2293,13 +2330,31 @@ Create_file_log_event::Create_file_log_event(const char* buf, int len, ****************************************************************************/ #ifdef MYSQL_CLIENT +void Create_file_log_event::print(FILE* file, bool short_form, + char* last_db, bool enable_local) +{ + if (short_form) + { + if (enable_local && check_fname_outside_temp_buf()) + Load_log_event::print(file, 1, last_db); + return; + } + + if (enable_local) + { + if (!check_fname_outside_temp_buf()) + fprintf(file, "#"); + Load_log_event::print(file, 1, last_db); + fprintf(file, "#"); + } + + fprintf(file, " file_id: %d block_len: %d\n", file_id, block_len); +} + void Create_file_log_event::print(FILE* file, bool short_form, char* last_db) { - if (short_form) - return; - Load_log_event::print(file, 1, last_db); - fprintf(file, " file_id: %d block_len: %d\n", file_id, block_len); + print(file,short_form,last_db,0); } #endif // MYSQL_CLIENT @@ -2311,20 +2366,18 @@ void Create_file_log_event::print(FILE* file, bool short_form, #if defined(HAVE_REPLICATION) && !defined(MYSQL_CLIENT) void Create_file_log_event::pack_info(Protocol *protocol) { - char buf1[256],buf[22], *end; - String tmp(buf1, sizeof(buf1), log_cs); - tmp.length(0); - tmp.append("db="); - tmp.append(db, db_len); - tmp.append(";table="); - tmp.append(table_name, table_name_len); - tmp.append(";file_id="); - end= int10_to_str((long) file_id, buf, 10); - tmp.append(buf, (uint32) (end-buf)); - tmp.append(";block_len="); - end= int10_to_str((long) block_len, buf, 10); - tmp.append(buf, (uint32) (end-buf)); - protocol->store((char*) tmp.ptr(), tmp.length()); + char buf[NAME_LEN*2 + 30 + 21*2], *pos; + pos= strmov(buf, "db="); + memcpy(pos, db, db_len); + pos+= db_len; + pos= strmov(pos, ";table="); + memcpy(pos, table_name, table_name_len); + pos+= table_name_len; + pos= strmov(pos, ";file_id="); + pos= int10_to_str((long) file_id, pos, 10); + pos= strmov(pos, ";block_len="); + pos= int10_to_str((long) block_len, pos, 10); + protocol->store(buf, pos-buf); } #endif diff --git a/sql/log_event.h b/sql/log_event.h index c5ff3ffd4da..29fdf80b03b 100644 --- a/sql/log_event.h +++ b/sql/log_event.h @@ -454,6 +454,13 @@ public: uint32 skip_lines; sql_ex_info sql_ex; + /* fname doesn't point to memory inside Log_event::temp_buf */ + void set_fname_outside_temp_buf(const char *afname, uint alen) + {fname=afname;fname_len=alen;} + /* fname doesn't point to memory inside Log_event::temp_buf */ + int check_fname_outside_temp_buf() + {return fnametemp_buf+cached_event_len;} + #ifndef MYSQL_CLIENT String field_lens_buf; String fields_buf; @@ -713,6 +720,7 @@ public: #endif /* HAVE_REPLICATION */ #else void print(FILE* file, bool short_form = 0, char* last_db = 0); + void print(FILE* file, bool short_form, char* last_db, bool enable_local); #endif Create_file_log_event(const char* buf, int event_len, bool old_format); diff --git a/sql/mysql_priv.h b/sql/mysql_priv.h index cdc668d9b28..7a354ef5ff1 100644 --- a/sql/mysql_priv.h +++ b/sql/mysql_priv.h @@ -209,6 +209,11 @@ char* query_table_status(THD *thd,const char *db,const char *table_name); #define MODE_MSSQL 512 #define MODE_DB2 1024 #define MODE_SAPDB 2048 +#define MODE_NO_KEY_OPTIONS 4096 +#define MODE_NO_TABLE_OPTIONS 8192 +#define MODE_NO_FIELD_OPTIONS 16384 +#define MODE_MYSQL323 32768 +#define MODE_MYSQL40 65536 #define RAID_BLOCK_SIZE 1024 @@ -713,6 +718,7 @@ extern my_bool opt_sql_bin_update, opt_safe_user_create, opt_no_mix_types; extern my_bool opt_safe_show_db, opt_local_infile, lower_case_table_names; extern my_bool opt_slave_compressed_protocol, use_temp_pool; extern my_bool opt_enable_named_pipe; +extern my_bool opt_old_passwords, use_old_passwords; extern char *shared_memory_base_name; extern bool opt_enable_shared_memory; extern char f_fyllchar; diff --git a/sql/mysqld.cc b/sql/mysqld.cc index 059b690fe34..c97799c0d6e 100644 --- a/sql/mysqld.cc +++ b/sql/mysqld.cc @@ -288,7 +288,7 @@ static my_bool opt_noacl=0, opt_bootstrap=0, opt_myisam_log=0; my_bool opt_safe_user_create = 0, opt_no_mix_types = 0; my_bool lower_case_table_names, opt_old_rpl_compat; my_bool opt_show_slave_auth_info, opt_sql_bin_update = 0; -my_bool opt_log_slave_updates= 0, opt_old_passwords=0; +my_bool opt_log_slave_updates= 0, opt_old_passwords=0, use_old_passwords=0; volatile bool mqh_used = 0; FILE *bootstrap_file=0; @@ -424,12 +424,12 @@ double log_10[32]; /* 10 potences */ I_List threads,thread_cache; time_t start_time; -ulong opt_sql_mode = 0L; const char *sql_mode_names[] = { "REAL_AS_FLOAT", "PIPES_AS_CONCAT", "ANSI_QUOTES", "IGNORE_SPACE", "SERIALIZE", "ONLY_FULL_GROUP_BY", "NO_UNSIGNED_SUBTRACTION", - "POSTGRESQL", "ORACLE", "MSSQL", "SAPDB", + "POSTGRESQL", "ORACLE", "MSSQL", "DB2", "SAPDB", "NO_KEY_OPTIONS", + "NO_TABLE_OPTIONS", "NO_FIELD_OPTIONS", "MYSQL323", "MYSQL40", NullS }; TYPELIB sql_mode_typelib= {array_elements(sql_mode_names)-1,"", @@ -773,7 +773,7 @@ static void *kill_server(void *sig_ptr) #define RETURN_FROM_KILL_SERVER DBUG_RETURN(0) #else static void __cdecl kill_server(int sig_ptr) -#define RETURN_FROM_KILL_SERVER DBUG_RETURN +#define RETURN_FROM_KILL_SERVER DBUG_VOID_RETURN #endif { int sig=(int) (long) sig_ptr; // This is passed a int @@ -3614,7 +3614,7 @@ struct my_option my_long_options[] = GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0}, {"safe-mode", OPT_SAFE, "Skip some optimize stages (for testing).", 0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0}, - {"old-passwords", OPT_OLD_PASSWORDS, "Use old password encryption method (needed for old clients)", + {"old-passwords", OPT_OLD_PASSWORDS, "Use old password encryption method (needed for 4.0 and older clients)", (gptr*) &opt_old_passwords, (gptr*) &opt_old_passwords, 0, GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0}, #ifndef TO_BE_DELETED {"safe-show-database", OPT_SAFE_SHOW_DB, @@ -4359,9 +4359,10 @@ get_one_option(int optid, const struct my_option *opt __attribute__((unused)), opt_endinfo=1; /* unireg: memory allocation */ break; case 'a': - opt_sql_mode = (MODE_REAL_AS_FLOAT | MODE_PIPES_AS_CONCAT | - MODE_ANSI_QUOTES | MODE_IGNORE_SPACE | MODE_SERIALIZABLE | - MODE_ONLY_FULL_GROUP_BY); + global_system_variables.sql_mode= + (MODE_REAL_AS_FLOAT | MODE_PIPES_AS_CONCAT | + MODE_ANSI_QUOTES | MODE_IGNORE_SPACE | MODE_SERIALIZABLE | + MODE_ONLY_FULL_GROUP_BY); global_system_variables.tx_isolation= ISO_SERIALIZABLE; break; case 'b': @@ -4727,9 +4728,10 @@ get_one_option(int optid, const struct my_option *opt __attribute__((unused)), berkeley_lock_type=berkeley_lock_types[type-1]; else { + int err; char *end; uint length= strlen(argument); - long value= my_strntol(my_charset_latin1, argument, length, &end, 10); + long value= my_strntol(my_charset_latin1, argument, length, 10, &end, &err); if (test_if_int(argument,(uint) length, end, my_charset_latin1)) berkeley_lock_scan_time= value; else @@ -4792,16 +4794,17 @@ get_one_option(int optid, const struct my_option *opt __attribute__((unused)), } case OPT_SQL_MODE: { - sql_mode_str = argument; - if ((opt_sql_mode = - find_bit_type(argument, &sql_mode_typelib)) == ~(ulong) 0) + sql_mode_str= argument; + if ((global_system_variables.sql_mode= + find_bit_type(argument, &sql_mode_typelib)) == ~(ulong) 0) { fprintf(stderr, "Unknown option to sql-mode: %s\n", argument); exit(1); } - global_system_variables.tx_isolation= ((opt_sql_mode & MODE_SERIALIZABLE) ? - ISO_SERIALIZABLE : - ISO_REPEATABLE_READ); + global_system_variables.tx_isolation= + ((global_system_variables.sql_mode & MODE_SERIALIZABLE) ? + ISO_SERIALIZABLE : + ISO_REPEATABLE_READ); break; } case OPT_MASTER_PASSWORD: diff --git a/sql/opt_sum.cc b/sql/opt_sum.cc index b1bb36353bf..492834b5bb9 100644 --- a/sql/opt_sum.cc +++ b/sql/opt_sum.cc @@ -38,7 +38,7 @@ int opt_sum_query(TABLE_LIST *tables, List &all_fields,COND *conds) table_map removed_tables=0; Item *item; COND *org_conds= conds; - + /* Add all ON conditions to WHERE condition */ for (TABLE_LIST *tl=tables; tl ; tl= tl->next) { @@ -165,10 +165,9 @@ int opt_sum_query(TABLE_LIST *tables, List &all_fields,COND *conds) error=table->file->index_last(table->record[0]) !=0; else { - (void)table->file->index_read(table->record[0], key_buff, + error = table->file->index_read(table->record[0], key_buff, ref.key_length, - HA_READ_AFTER_KEY); - error=table->file->index_prev(table->record[0]) || + HA_READ_PREFIX_LAST) || key_cmp(table,key_buff,ref.key,ref.key_length); } if (table->key_read) diff --git a/sql/password.c b/sql/password.c index da7a499ba09..9b6189a161c 100644 --- a/sql/password.c +++ b/sql/password.c @@ -236,7 +236,7 @@ void password_hash_stage1(char *to, const char *password) { if (*password == ' ' || *password == '\t') continue;/* skip space in password */ - sha1_input(&context,(int8*)&password[0],1); + sha1_input(&context,(uint8*) &password[0],1); } sha1_result(&context,(uint8*)to); } @@ -259,9 +259,9 @@ void password_hash_stage2(char *to,const char *salt) { SHA1_CONTEXT context; sha1_reset(&context); - sha1_input(&context,(uint8*)salt,4); - sha1_input(&context,to,SHA1_HASH_SIZE); - sha1_result(&context,(uint8*)to); + sha1_input(&context,(uint8*) salt, 4); + sha1_input(&context,(uint8*) to, SHA1_HASH_SIZE); + sha1_result(&context,(uint8*) to); } @@ -295,14 +295,14 @@ void make_scrambled_password(char *to,const char *password, else /* New password 4.1 password scrambling */ { to[0]=PVERSION41_CHAR; /* New passwords have version prefix */ - /* Random returns number from 0 to 1 so this would be good salt generation.*/ + /* Rnd returns number from 0 to 1 so this would be good salt generation.*/ salt=rnd(rand_st)*65535+1; /* Use only 2 first bytes from it */ sprintf(to+1,"%04x",salt); /* First hasing is done without salt */ - password_hash_stage1(digest,password); + password_hash_stage1((char*) digest, password); /* Second stage is done with salt */ - password_hash_stage2(digest,(char*)to+1), + password_hash_stage2((char*) digest,(char*)to+1), /* Print resulting hash into the password*/ sprintf(to+5, "%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x", @@ -376,7 +376,7 @@ my_bool validate_password(const char* password, const char* message, password_hash_stage2(buffer,tmpsalt); /* Convert password to salt to compare */ - get_salt_from_bin_password(salt_candidate,buffer,salt[0]); + get_salt_from_bin_password(salt_candidate,(uchar*) buffer,salt[0]); /* Now we shall get exactly the same password as we have stored for user */ for (salt_end=salt+5 ; salt < salt_end; ) diff --git a/sql/procedure.h b/sql/procedure.h index bc77803230f..03a45488b03 100644 --- a/sql/procedure.h +++ b/sql/procedure.h @@ -59,7 +59,7 @@ public: void set(double nr) { value=nr; } void set(longlong nr) { value=(double) nr; } void set(const char *str,uint length,CHARSET_INFO *cs) - { value=my_strntod(cs,(char*) str,length,(char**)0); } + { int err; value=my_strntod(cs,(char*) str,length,(char**)0,&err); } double val() { return value; } longlong val_int() { return (longlong) value; } String *val_str(String *s) { s->set(value,decimals,thd_charset()); return s; } @@ -77,7 +77,7 @@ public: void set(double nr) { value=(longlong) nr; } void set(longlong nr) { value=nr; } void set(const char *str,uint length, CHARSET_INFO *cs) - { value=my_strntoll(cs,str,length,NULL,10); } + { int err; value=my_strntoll(cs,str,length,10,NULL,&err); } double val() { return (double) value; } longlong val_int() { return value; } String *val_str(String *s) { s->set(value, thd_charset()); return s; } @@ -98,14 +98,16 @@ public: { str_value.copy(str,length,cs); } double val() { + int err; CHARSET_INFO *cs=str_value.charset(); return my_strntod(cs, (char*) str_value.ptr(), str_value.length(), - (char**) 0); + (char**) 0, &err); } longlong val_int() { + int err; CHARSET_INFO *cs=str_value.charset(); - return my_strntoll(cs,str_value.ptr(),str_value.length(),NULL,10); + return my_strntoll(cs,str_value.ptr(),str_value.length(),10,NULL,&err); } String *val_str(String*) { diff --git a/sql/set_var.cc b/sql/set_var.cc index 0cf2b50a6ea..89eb155bc63 100644 --- a/sql/set_var.cc +++ b/sql/set_var.cc @@ -218,8 +218,10 @@ sys_var_long_ptr sys_slow_launch_time("slow_launch_time", &slow_launch_time); sys_var_thd_ulong sys_sort_buffer("sort_buffer_size", &SV::sortbuff_size); -sys_var_thd_enum sys_table_type("table_type", &SV::table_type, - &ha_table_typelib); +sys_var_thd_sql_mode sys_sql_mode("sql_mode", + &SV::sql_mode); +sys_var_thd_enum sys_table_type("table_type", &SV::table_type, + &ha_table_typelib); sys_var_long_ptr sys_table_cache_size("table_cache", &table_cache_size); sys_var_long_ptr sys_thread_cache_size("thread_cache_size", @@ -398,6 +400,7 @@ sys_var *sys_variables[]= &sys_sql_big_tables, &sys_sql_low_priority_updates, &sys_sql_max_join_size, + &sys_sql_mode, &sys_sql_warnings, &sys_table_cache_size, &sys_table_type, @@ -552,7 +555,7 @@ struct show_var_st init_vars[]= { {"socket", (char*) &mysql_unix_port, SHOW_CHAR_PTR}, #endif {sys_sort_buffer.name, (char*) &sys_sort_buffer, SHOW_SYS}, - {"sql_mode", (char*) &opt_sql_mode, SHOW_LONG}, + {sys_sql_mode.name, (char*) &sys_sql_mode, SHOW_SYS}, {"table_cache", (char*) &table_cache_size, SHOW_LONG}, {sys_table_type.name, (char*) &sys_table_type, SHOW_SYS}, {sys_thread_cache_size.name,(char*) &sys_thread_cache_size, SHOW_SYS}, @@ -946,6 +949,44 @@ err: return 1; } + + +bool sys_var::check_set(THD *thd, set_var *var, TYPELIB *enum_names) +{ + char buff[80], *value, *error= 0; + uint error_len= 0; + String str(buff, sizeof(buff), system_charset_info), *res; + + if (var->value->result_type() == STRING_RESULT) + { + if (!(res= var->value->val_str(&str))) + goto err; + (long) var->save_result.ulong_value= (ulong) + find_set(enum_names, res->c_ptr(), res->length(), &error, &error_len); + if (error_len) + { + strmake(buff, error, min(sizeof(buff), error_len)); + goto err; + } + } + else + { + ulonglong tmp= var->value->val_int(); + if (tmp >= enum_names->count) + { + llstr(tmp, buff); + goto err; + } + var->save_result.ulong_value= (ulong) tmp; // Save for update + } + return 0; + +err: + my_error(ER_WRONG_VALUE_FOR_VAR, MYF(0), name, buff); + return 1; +} + + /* Return an Item for a variable. Used with @@[global.]variable_name @@ -1022,6 +1063,40 @@ byte *sys_var_thd_enum::value_ptr(THD *thd, enum_var_type type) } +byte *sys_var_thd_sql_mode::value_ptr(THD *thd, enum_var_type type) +{ + ulong val; + char buff[256]; + String tmp(buff, sizeof(buff), default_charset_info); + my_bool found= 0; + + tmp.length(0); + val= ((type == OPT_GLOBAL) ? global_system_variables.*offset : + thd->variables.*offset); + for (uint i= 0; val; val>>= 1, i++) + { + if (val & 1) + { + tmp.append(enum_names->type_names[i]); + tmp.append(','); + } + } + if (tmp.length()) + tmp.length(tmp.length() - 1); + return (byte*) thd->strdup(tmp.c_ptr()); +} + + +void sys_var_thd_sql_mode::set_default(THD *thd, enum_var_type type) +{ + if (type == OPT_GLOBAL) + global_system_variables.*offset= 0; + else + thd->variables.*offset= global_system_variables.*offset; +} + + + bool sys_var_thd_bit::update(THD *thd, set_var *var) { int res= (*update_func)(thd, var); diff --git a/sql/set_var.h b/sql/set_var.h index f9a275941ef..3b4ec9d5bfe 100644 --- a/sql/set_var.h +++ b/sql/set_var.h @@ -28,7 +28,7 @@ class sys_var; class set_var; typedef struct system_variables SV; -extern TYPELIB bool_typelib, delay_key_write_typelib; +extern TYPELIB bool_typelib, delay_key_write_typelib, sql_mode_typelib; enum enum_var_type { @@ -56,6 +56,7 @@ public: virtual ~sys_var() {} virtual bool check(THD *thd, set_var *var) { return 0; } bool check_enum(THD *thd, set_var *var, TYPELIB *enum_names); + bool check_set(THD *thd, set_var *var, TYPELIB *enum_names); virtual bool update(THD *thd, set_var *var)=0; virtual void set_default(THD *thd, enum_var_type type) {} virtual SHOW_TYPE type() { return SHOW_UNDEF; } @@ -273,6 +274,7 @@ public: class sys_var_thd_enum :public sys_var_thd { +protected: ulong SV::*offset; TYPELIB *enum_names; public: @@ -297,6 +299,21 @@ public: }; +class sys_var_thd_sql_mode :public sys_var_thd_enum +{ +public: + sys_var_thd_sql_mode(const char *name_arg, ulong SV::*offset_arg) + :sys_var_thd_enum(name_arg, offset_arg, &sql_mode_typelib) + {} + bool check(THD *thd, set_var *var) + { + return check_set(thd, var, enum_names); + } + void set_default(THD *thd, enum_var_type type); + byte *value_ptr(THD *thd, enum_var_type type); +}; + + class sys_var_thd_bit :public sys_var_thd { sys_update_func update_func; diff --git a/sql/slave.cc b/sql/slave.cc index 3e28c21c9cc..bb53461a120 100644 --- a/sql/slave.cc +++ b/sql/slave.cc @@ -1014,6 +1014,7 @@ static int create_table_from_dump(THD* thd, NET* net, const char* db, const char* table_name) { ulong packet_len = my_net_read(net); // read create table statement + char *query; Vio* save_vio; HA_CHECK_OPT check_opt; TABLE_LIST tables; @@ -1033,15 +1034,23 @@ static int create_table_from_dump(THD* thd, NET* net, const char* db, return 1; } thd->command = COM_TABLE_DUMP; - thd->query = sql_alloc(packet_len + 1); - if (!thd->query) + /* Note that we should not set thd->query until the area is initalized */ + if (!(query = sql_alloc(packet_len + 1))) { sql_print_error("create_table_from_dump: out of memory"); net_printf(thd, ER_GET_ERRNO, "Out of memory"); return 1; } - memcpy(thd->query, net->read_pos, packet_len); - thd->query[packet_len] = 0; + memcpy(query, net->read_pos, packet_len); + query[packet_len]= 0; + thd->query_length= packet_len; + /* + We make the following lock in an attempt to ensure that the compiler will + not rearrange the code so that thd->query is set too soon + */ + VOID(pthread_mutex_lock(&LOCK_thread_count)); + thd->query= query; + VOID(pthread_mutex_unlock(&LOCK_thread_count)); thd->current_tablenr = 0; thd->query_error = 0; thd->net.no_send_ok = 1; @@ -2275,7 +2284,9 @@ err: // print the current replication position sql_print_error("Slave I/O thread exiting, read up to log '%s', position %s", IO_RPL_LOG_NAME, llstr(mi->master_log_pos,llbuff)); + VOID(pthread_mutex_lock(&LOCK_thread_count)); thd->query = thd->db = 0; // extra safety + VOID(pthread_mutex_unlock(&LOCK_thread_count)); if (mysql) { mc_mysql_close(mysql); @@ -2410,7 +2421,9 @@ the slave SQL thread with \"SLAVE START\". We stopped at log \ RPL_LOG_NAME, llstr(rli->master_log_pos,llbuff)); err: + VOID(pthread_mutex_lock(&LOCK_thread_count)); thd->query = thd->db = 0; // extra safety + VOID(pthread_mutex_unlock(&LOCK_thread_count)); thd->proc_info = "Waiting for slave mutex on exit"; pthread_mutex_lock(&rli->run_lock); DBUG_ASSERT(rli->slave_running == 1); // tracking buffer overrun diff --git a/sql/sql_acl.cc b/sql/sql_acl.cc index b87f620901d..93f2dda1283 100644 --- a/sql/sql_acl.cc +++ b/sql/sql_acl.cc @@ -55,7 +55,7 @@ static DYNAMIC_ARRAY acl_hosts,acl_users,acl_dbs; static MEM_ROOT mem, memex; static bool initialized=0; static bool allow_all_hosts=1; -static HASH acl_check_hosts, hash_tables; +static HASH acl_check_hosts, column_priv_hash; static DYNAMIC_ARRAY acl_wild_hosts; static hash_filo *acl_cache; static uint grant_version=0; @@ -104,7 +104,7 @@ my_bool acl_init(THD *org_thd, bool dont_read_acl_tables) DBUG_RETURN(0); /* purecov: tested */ } - priv_version++; /* Priveleges updated */ + priv_version++; /* Privileges updated */ /* To be able to run this from boot, we allocate a temporary THD @@ -112,6 +112,8 @@ my_bool acl_init(THD *org_thd, bool dont_read_acl_tables) if (!(thd=new THD)) DBUG_RETURN(1); /* purecov: inspected */ thd->store_globals(); + /* Use passwords according to command line option */ + use_old_passwords= opt_old_passwords; acl_cache->clear(1); // Clear locked hostname cache thd->db= my_strdup("mysql",MYF(0)); @@ -176,7 +178,14 @@ my_bool acl_init(THD *org_thd, bool dont_read_acl_tables) protocol_version=9; /* purecov: tested */ } - DBUG_PRINT("info",("user table fields: %d",table->fields)); + DBUG_PRINT("info",("user table fields: %d, password length: %d", + table->fields, table->field[2]->field_length)); + if (table->field[2]->field_length < 45 && !use_old_passwords) + { + sql_print_error("mysql.user table is not updated to new password format; Disabling new password usage until mysql_fix_privilege_tables is run"); + use_old_passwords= 1; + } + allow_all_hosts=0; while (!(read_record_info.read_record(&read_record_info))) { @@ -192,7 +201,7 @@ my_bool acl_init(THD *org_thd, bool dont_read_acl_tables) "Found old style password for user '%s'. Ignoring user. (You may want to restart mysqld using --old-protocol)", user.user ? user.user : ""); /* purecov: tested */ } - else /* non emptpy and not short passwords */ + else /* non empty and not short passwords */ { user.pversion=get_password_version(user.password); /* Only passwords of specific lengths depending on version are allowed */ @@ -358,6 +367,7 @@ void acl_reload(THD *thd) if (acl_init(thd, 0)) { // Error. Revert to old list + DBUG_PRINT("error",("Reverting to old privileges")); acl_free(); /* purecov: inspected */ acl_hosts=old_acl_hosts; acl_users=old_acl_users; @@ -1733,10 +1743,12 @@ static GRANT_TABLE *table_hash_search(const char *host,const char* ip, GRANT_TABLE *grant_table,*found=0; len = (uint) (strmov(strmov(strmov(helping,user)+1,db)+1,tname)-helping)+ 1; - for (grant_table=(GRANT_TABLE*) hash_search(&hash_tables,(byte*) helping, + for (grant_table=(GRANT_TABLE*) hash_search(&column_priv_hash, + (byte*) helping, len) ; grant_table ; - grant_table= (GRANT_TABLE*) hash_next(&hash_tables,(byte*) helping,len)) + grant_table= (GRANT_TABLE*) hash_next(&column_priv_hash,(byte*) helping, + len)) { if (exact) { @@ -2033,7 +2045,7 @@ static int replace_table_table(THD *thd, GRANT_TABLE *grant_table, } else { - hash_delete(&hash_tables,(byte*) grant_table); + hash_delete(&column_priv_hash,(byte*) grant_table); } DBUG_RETURN(0); @@ -2179,7 +2191,7 @@ int mysql_table_grant (THD *thd, TABLE_LIST *table_list, result= -1; /* purecov: deadcode */ continue; /* purecov: deadcode */ } - hash_insert(&hash_tables,(byte*) grant_table); + hash_insert(&column_priv_hash,(byte*) grant_table); } /* If revoke_grant, calculate the new column privilege for tables_priv */ @@ -2334,7 +2346,7 @@ void grant_free(void) { DBUG_ENTER("grant_free"); grant_option = FALSE; - hash_free(&hash_tables); + hash_free(&column_priv_hash); free_root(&memex,MYF(0)); DBUG_VOID_RETURN; } @@ -2352,7 +2364,7 @@ my_bool grant_init(THD *org_thd) DBUG_ENTER("grant_init"); grant_option = FALSE; - (void) hash_init(&hash_tables,my_charset_latin1, + (void) hash_init(&column_priv_hash,my_charset_latin1, 0,0,0, (hash_get_key) get_grant_table, (hash_free_key) free_grant_table,0); init_sql_alloc(&memex,1024,0); @@ -2387,6 +2399,7 @@ my_bool grant_init(THD *org_thd) if (t_table->file->index_first(t_table->record[0])) { t_table->file->index_end(); + return_val= 0; goto end_unlock; } grant_option= TRUE; @@ -2398,7 +2411,7 @@ my_bool grant_init(THD *org_thd) { GRANT_TABLE *mem_check; if (!(mem_check=new GRANT_TABLE(t_table,c_table)) || - mem_check->ok() && hash_insert(&hash_tables,(byte*) mem_check)) + mem_check->ok() && hash_insert(&column_priv_hash,(byte*) mem_check)) { /* This could only happen if we are out memory */ grant_option = FALSE; /* purecov: deadcode */ @@ -2427,11 +2440,12 @@ end: } -/* Reload grant array if possible */ +/* Reload grant array (table and column privileges) if possible */ void grant_reload(THD *thd) { - HASH old_hash_tables;bool old_grant_option; + HASH old_column_priv_hash; + bool old_grant_option; MEM_ROOT old_mem; DBUG_ENTER("grant_reload"); @@ -2439,20 +2453,21 @@ void grant_reload(THD *thd) rw_wrlock(&LOCK_grant); grant_version++; - old_hash_tables=hash_tables; + old_column_priv_hash= column_priv_hash; old_grant_option = grant_option; old_mem = memex; if (grant_init(thd)) { // Error. Revert to old hash + DBUG_PRINT("error",("Reverting to old privileges")); grant_free(); /* purecov: deadcode */ - hash_tables=old_hash_tables; /* purecov: deadcode */ + column_priv_hash= old_column_priv_hash; /* purecov: deadcode */ grant_option = old_grant_option; /* purecov: deadcode */ memex = old_mem; /* purecov: deadcode */ } else { - hash_free(&old_hash_tables); + hash_free(&old_column_priv_hash); free_root(&old_mem,MYF(0)); } rw_unlock(&LOCK_grant); @@ -2676,9 +2691,10 @@ bool check_grant_db(THD *thd,const char *db) len = (uint) (strmov(strmov(helping,thd->priv_user)+1,db)-helping)+ 1; rw_rdlock(&LOCK_grant); - for (uint idx=0 ; idx < hash_tables.records ; idx++) + for (uint idx=0 ; idx < column_priv_hash.records ; idx++) { - GRANT_TABLE *grant_table = (GRANT_TABLE*) hash_element(&hash_tables,idx); + GRANT_TABLE *grant_table= (GRANT_TABLE*) hash_element(&column_priv_hash, + idx); if (len < grant_table->key_length && !memcmp(grant_table->hash_key,helping,len) && (thd->host && !wild_case_compare(my_charset_latin1, @@ -3004,10 +3020,11 @@ int mysql_show_grants(THD *thd,LEX_USER *lex_user) } /* Add column access */ - for (index=0 ; index < hash_tables.records ; index++) + for (index=0 ; index < column_priv_hash.records ; index++) { const char *user,*host; - GRANT_TABLE *grant_table= (GRANT_TABLE*) hash_element(&hash_tables,index); + GRANT_TABLE *grant_table= (GRANT_TABLE*) hash_element(&column_priv_hash, + index); if (!(user=grant_table->user)) user=""; diff --git a/sql/sql_base.cc b/sql/sql_base.cc index 47cbaf088a3..13c31046819 100644 --- a/sql/sql_base.cc +++ b/sql/sql_base.cc @@ -241,6 +241,7 @@ static void free_cache_entry(TABLE *table) DBUG_VOID_RETURN; } +/* Free resources allocated by filesort() and read_record() */ void free_io_cache(TABLE *table) { diff --git a/sql/sql_cache.cc b/sql/sql_cache.cc index 2089b381156..ef763bbeffe 100644 --- a/sql/sql_cache.cc +++ b/sql/sql_cache.cc @@ -357,12 +357,6 @@ TODO list: #define DUMP(C) #endif -#ifdef FN_NO_CASE_SENCE -#define DB_NAME_PREPROCESS(C) tolower(C) -#else -#define DB_NAME_PREPROCESS(C) (C) -#endif - const char *query_cache_type_names[]= { "OFF", "ON", "DEMAND",NullS }; TYPELIB query_cache_type_typelib= { @@ -2480,12 +2474,18 @@ TABLE_COUNTER_TYPE Query_cache::is_cacheable(THD *thd, uint32 query_len, if (tables_used->table->db_type == DB_TYPE_MRG_ISAM || tables_used->table->tmp_table != NO_TMP_TABLE || - (tables_used->db_length == 5 && - DB_NAME_PREPROCESS(tables_used->db[0])=='m' && - DB_NAME_PREPROCESS(tables_used->db[1])=='y' && - DB_NAME_PREPROCESS(tables_used->db[2])=='s' && - DB_NAME_PREPROCESS(tables_used->db[3])=='q' && - DB_NAME_PREPROCESS(tables_used->db[4])=='l')) + (tables_used->db_length == 5 && +#ifdef FN_NO_CASE_SENCE + // TODO: latin1 charset should be replaced with system charset + my_strncasecmp(my_charset_latin1,tables_used->db,"mysql",5) == 0 +#else + tables_used->db[0]=='m' && + tables_used->db[1]=='y' && + tables_used->db[2]=='s' && + tables_used->db[3]=='q' && + tables_used->db[4]=='l' +#endif + )) { DBUG_PRINT("qcache", ("select not cacheable: used MRG_ISAM, temporary or system table(s)")); diff --git a/sql/sql_class.cc b/sql/sql_class.cc index bc5715aaab7..6d6d2004be5 100644 --- a/sql/sql_class.cc +++ b/sql/sql_class.cc @@ -195,7 +195,6 @@ void THD::init(void) pthread_mutex_unlock(&LOCK_global_system_variables); server_status= SERVER_STATUS_AUTOCOMMIT; options= thd_startup_options; - sql_mode=(uint) opt_sql_mode; open_options=ha_open_options; update_lock_default= (variables.low_priority_updates ? TL_WRITE_LOW_PRIORITY : diff --git a/sql/sql_class.h b/sql/sql_class.h index fbe3a1f049a..216720da801 100644 --- a/sql/sql_class.h +++ b/sql/sql_class.h @@ -379,6 +379,7 @@ struct system_variables ulong table_type; ulong tmp_table_size; ulong tx_isolation; + ulong sql_mode; /* In slave thread we need to know in behalf of which @@ -441,9 +442,8 @@ public: /* points to host if host is available, otherwise points to ip */ const char *host_or_ip; - uint client_capabilities; /* What the client supports */ + ulong client_capabilities; /* What the client supports */ /* Determines if which non-standard SQL behaviour should be enabled */ - uint sql_mode; ulong max_client_packet_length; ulong master_access; /* Global privileges from mysql.user */ ulong db_access; /* Privileges for current db */ diff --git a/sql/sql_lex.cc b/sql/sql_lex.cc index b656f698e89..e4fc3a5aec3 100644 --- a/sql/sql_lex.cc +++ b/sql/sql_lex.cc @@ -107,7 +107,7 @@ void lex_init(void) state_map[i]=(uchar) STATE_CHAR; } state_map[(uchar)'_']=state_map[(uchar)'$']=(uchar) STATE_IDENT; - state_map[(uchar)'\'']=state_map[(uchar)'"']=(uchar) STATE_STRING; + state_map[(uchar)'\'']=(uchar) STATE_STRING; state_map[(uchar)'-']=state_map[(uchar)'+']=(uchar) STATE_SIGNED_NUMBER; state_map[(uchar)'.']=(uchar) STATE_REAL_OR_POINT; state_map[(uchar)'>']=state_map[(uchar)'=']=state_map[(uchar)'!']= (uchar) STATE_CMP_OP; @@ -122,10 +122,7 @@ void lex_init(void) state_map[(uchar)'*']= (uchar) STATE_END_LONG_COMMENT; state_map[(uchar)'@']= (uchar) STATE_USER_END; state_map[(uchar) '`']= (uchar) STATE_USER_VARIABLE_DELIMITER; - if (opt_sql_mode & MODE_ANSI_QUOTES) - { - state_map[(uchar) '"'] = STATE_USER_VARIABLE_DELIMITER; - } + state_map[(uchar)'"']= (uchar) STAT_STRING_OR_DELIMITER; /* Create a second map to make it faster to find identifiers @@ -167,7 +164,7 @@ LEX *lex_start(THD *thd, uchar *buf,uint length) lex->convert_set= (lex->thd= thd)->variables.convert_set; lex->thd_charset= lex->thd->variables.thd_charset; lex->yacc_yyss=lex->yacc_yyvs=0; - lex->ignore_space=test(thd->sql_mode & MODE_IGNORE_SPACE); + lex->ignore_space=test(thd->variables.sql_mode & MODE_IGNORE_SPACE); lex->slave_thd_opt=0; lex->sql_command=SQLCOM_END; lex->safe_to_cache_query= 1; @@ -222,7 +219,7 @@ static int find_keyword(LEX *lex, uint len, bool function) /* make a copy of token before ptr and set yytoklen */ -LEX_STRING get_token(LEX *lex,uint length) +static LEX_STRING get_token(LEX *lex,uint length) { LEX_STRING tmp; yyUnget(); // ptr points now after last token char @@ -231,8 +228,27 @@ LEX_STRING get_token(LEX *lex,uint length) return tmp; } -/* Return an unescaped text literal without quotes */ -/* Fix sometimes to do only one scan of the string */ +static LEX_STRING get_quoted_token(LEX *lex,uint length, char quote) +{ + LEX_STRING tmp; + byte *from, *to, *end; + yyUnget(); // ptr points now after last token char + tmp.length=lex->yytoklen=length; + tmp.str=(char*) lex->thd->alloc(tmp.length+1); + for (from= (byte*) lex->tok_start, to= tmp.str, end= to+length ; to != end ;) + { + if ((*to++= *from++) == quote) + from++; // Skip double quotes + } + *to= 0; // End null for safety + return tmp; +} + + +/* + Return an unescaped text literal without quotes + Fix sometimes to do only one scan of the string +*/ static char *get_text(LEX *lex) { @@ -652,12 +668,13 @@ int yylex(void *arg, void *yythd) return(IDENT); case STATE_USER_VARIABLE_DELIMITER: + { + char delim= c; // Used char lex->tok_start=lex->ptr; // Skip first ` #ifdef USE_MB if (use_mb(system_charset_info)) { - while ((c=yyGet()) && state_map[c] != STATE_USER_VARIABLE_DELIMITER && - c != (uchar) NAMES_SEP_CHAR) + while ((c=yyGet()) && c != delim && c != (uchar) NAMES_SEP_CHAR) { if (my_ismbhead(system_charset_info, c)) { @@ -669,20 +686,38 @@ int yylex(void *arg, void *yythd) lex->ptr += l-1; } } + yylval->lex_str=get_token(lex,yyLength()); } else #endif { - while ((c=yyGet()) && state_map[c] != STATE_USER_VARIABLE_DELIMITER && - c != (uchar) NAMES_SEP_CHAR) ; + uint double_quotes= 0; + char quote_char= c; + while ((c=yyGet())) + { + if (c == quote_char) + { + if (yyPeek() != quote_char) + break; + c=yyGet(); + double_quotes++; + continue; + } + if (c == (uchar) NAMES_SEP_CHAR) + break; + } + if (double_quotes) + yylval->lex_str=get_quoted_token(lex,yyLength() - double_quotes, + quote_char); + else + yylval->lex_str=get_token(lex,yyLength()); } - yylval->lex_str=get_token(lex,yyLength()); if (lex->convert_set) lex->convert_set->convert((char*) yylval->lex_str.str,lex->yytoklen); - if (state_map[c] == STATE_USER_VARIABLE_DELIMITER) + if (c == delim) yySkip(); // Skip end ` return(IDENT); - + } case STATE_SIGNED_NUMBER: // Incomplete signed number if (prev_state == STATE_OPERATOR_OR_IDENT) { @@ -795,6 +830,13 @@ int yylex(void *arg, void *yythd) lex->next_state= STATE_START; // Allow signed numbers return(tokval); + case STAT_STRING_OR_DELIMITER: + if (((THD *) yythd)->variables.sql_mode & MODE_ANSI_QUOTES) + { + state= STATE_USER_VARIABLE_DELIMITER; + break; + } + /* " used for strings */ case STATE_STRING: // Incomplete text string if (!(yylval->lex_str.str = get_text(lex))) { @@ -864,7 +906,15 @@ int yylex(void *arg, void *yythd) case STATE_COLON: // optional line terminator if (yyPeek()) { - state=STATE_CHAR; // Return ';' + if (((THD *)yythd)->client_capabilities & CLIENT_MULTI_QUERIES) + { + lex->found_colon=(char*)lex->ptr; + ((THD *)yythd)->server_status |= SERVER_MORE_RESULTS_EXISTS; + lex->next_state=STATE_END; + return(END_OF_INPUT); + } + else + state=STATE_CHAR; // Return ';' break; } /* fall true */ @@ -889,6 +939,7 @@ int yylex(void *arg, void *yythd) switch (state_map[yyPeek()]) { case STATE_STRING: case STATE_USER_VARIABLE_DELIMITER: + case STAT_STRING_OR_DELIMITER: break; case STATE_USER_END: lex->next_state=STATE_SYSTEM_VAR; diff --git a/sql/sql_lex.h b/sql/sql_lex.h index e4a17838cd2..9fc00e5f56d 100644 --- a/sql/sql_lex.h +++ b/sql/sql_lex.h @@ -86,7 +86,8 @@ enum lex_states STATE_REAL_OR_POINT, STATE_BOOL, STATE_EOL, STATE_ESCAPE, STATE_LONG_COMMENT, STATE_END_LONG_COMMENT, STATE_COLON, STATE_SET_VAR, STATE_USER_END, STATE_HOSTNAME, STATE_SKIP, STATE_USER_VARIABLE_DELIMITER, STATE_SYSTEM_VAR, - STATE_IDENT_OR_KEYWORD, STATE_IDENT_OR_HEX, STATE_IDENT_OR_BIN + STATE_IDENT_OR_KEYWORD, STATE_IDENT_OR_HEX, STATE_IDENT_OR_BIN, + STAT_STRING_OR_DELIMITER }; @@ -413,6 +414,7 @@ typedef struct st_lex char *backup_dir; /* For RESTORE/BACKUP */ char* to_log; /* For PURGE MASTER LOGS TO */ char* x509_subject,*x509_issuer,*ssl_cipher; + char* found_colon; /* For multi queries - next query */ enum SSL_type ssl_type; /* defined in violite.h */ String *wild; sql_exchange *exchange; diff --git a/sql/sql_load.cc b/sql/sql_load.cc index 0a4c6330154..71d8a3e181b 100644 --- a/sql/sql_load.cc +++ b/sql/sql_load.cc @@ -287,6 +287,20 @@ int mysql_load(THD *thd,sql_exchange *ex,TABLE_LIST *table_list, { if (lf_info.wrote_create_file) { + /* + Make sure last block (the one which caused the error) gets logged. + This is needed because otherwise after write of + (to the binlog, not to read_info (which is a cache)) + Delete_file_log_event the bad block will remain in read_info. + At the end of mysql_load(), the destructor of read_info will call + end_io_cache() which will flush read_info, so we will finally have + this in the binlog: + Append_block # The last successfull block + Delete_file + Append_block # The failing block + which is nonsense. + */ + read_info.end_io_cache(); Delete_file_log_event d(thd, log_delayed); mysql_bin_log.write(&d); } @@ -348,8 +362,10 @@ read_fixed_length(THD *thd,COPY_INFO &info,TABLE *table,List &fields, { List_iterator_fast it(fields); Item_field *sql_field; + ulonglong id; DBUG_ENTER("read_fixed_length"); + id=0; /* No fields can be null in this format. mark all fields as not null */ while ((sql_field= (Item_field*) it++)) sql_field->field->set_notnull(); @@ -392,6 +408,14 @@ read_fixed_length(THD *thd,COPY_INFO &info,TABLE *table,List &fields, thd->cuted_fields++; /* To long row */ if (write_record(table,&info)) DBUG_RETURN(1); + /* + If auto_increment values are used, save the first one + for LAST_INSERT_ID() and for the binary/update log. + We can't use insert_id() as we don't want to touch the + last_insert_id_used flag. + */ + if (!id && thd->insert_id_used) + id= thd->last_insert_id; if (table->next_number_field) table->next_number_field->reset(); // Clear for next record if (read_info.next_line()) // Skip to next line @@ -399,6 +423,8 @@ read_fixed_length(THD *thd,COPY_INFO &info,TABLE *table,List &fields, if (read_info.line_cuted) thd->cuted_fields++; /* To long row */ } + if (id && !read_info.error) + thd->insert_id(id); // For binary/update log DBUG_RETURN(test(read_info.error)); } @@ -412,10 +438,12 @@ read_sep_field(THD *thd,COPY_INFO &info,TABLE *table, List_iterator_fast it(fields); Item_field *sql_field; uint enclosed_length; + ulonglong id; DBUG_ENTER("read_sep_field"); enclosed_length=enclosed.length(); - + id=0; + for (;;it.rewind()) { if (thd->killed) @@ -468,6 +496,14 @@ read_sep_field(THD *thd,COPY_INFO &info,TABLE *table, } if (write_record(table,&info)) DBUG_RETURN(1); + /* + If auto_increment values are used, save the first one + for LAST_INSERT_ID() and for the binary/update log. + We can't use insert_id() as we don't want to touch the + last_insert_id_used flag. + */ + if (!id && thd->insert_id_used) + id= thd->last_insert_id; if (table->next_number_field) table->next_number_field->reset(); // Clear for next record if (read_info.next_line()) // Skip to next line @@ -475,6 +511,8 @@ read_sep_field(THD *thd,COPY_INFO &info,TABLE *table, if (read_info.line_cuted) thd->cuted_fields++; /* To long row */ } + if (id && !read_info.error) + thd->insert_id(id); // For binary/update log DBUG_RETURN(test(read_info.error)); } diff --git a/sql/sql_parse.cc b/sql/sql_parse.cc index be90e6d6e16..509e1ac972c 100644 --- a/sql/sql_parse.cc +++ b/sql/sql_parse.cc @@ -206,10 +206,11 @@ static int check_user(THD *thd,enum_server_command command, const char *user, } thd->master_access=acl_getroot(thd, thd->host, thd->ip, thd->user, passwd, thd->scramble, &thd->priv_user, - protocol_version == 9 || - !(thd->client_capabilities & - CLIENT_LONG_PASSWORD),&ur,crypted_scramble, - cur_priv_version,hint_user); + (protocol_version == 9 || + !(thd->client_capabilities & + CLIENT_LONG_PASSWORD)), + &ur,crypted_scramble, + cur_priv_version,hint_user); DBUG_PRINT("info", ("Capabilities: %d packet_length: %ld Host: '%s' User: '%s' Using password: %s Access: %u db: '%s'", @@ -218,12 +219,18 @@ static int check_user(THD *thd,enum_server_command command, const char *user, had_password ? "yes": "no", thd->master_access, thd->db ? thd->db : "*none*")); - /* in case we're going to retry we should not send error message at this point */ + /* + In case we're going to retry we should not send error message at this + point + */ if (thd->master_access & NO_ACCESS) { if (do_send_error) { - /* Old client should get nicer error message if password version is not supported*/ + /* + Old client should get nicer error message if password version is + not supported + */ if (simple_connect && *hint_user && (*hint_user)->pversion) { net_printf(thd, ER_NOT_SUPPORTED_AUTH_MODE); @@ -494,11 +501,14 @@ check_connections(THD *thd) { uint connect_errors=0; NET *net= &thd->net; - /* Store the connection details */ + char *end, *user, *passwd, *db; + char prepared_scramble[SCRAMBLE41_LENGTH+4]; /* Buffer for scramble&hash */ + ACL_USER* cached_user=NULL; /* Initialise to NULL for first stage */ + uint cur_priv_version; DBUG_PRINT("info", (("check_connections called by thread %d"), - thd->thread_id)); + thd->thread_id)); DBUG_PRINT("info",("New connection received on %s", - vio_description(net->vio))); + vio_description(net->vio))); if (!thd->host) // If TCP/IP connection { char ip[30]; @@ -543,8 +553,7 @@ check_connections(THD *thd) ulong pkt_len=0; { /* buff[] needs to big enough to hold the server_version variable */ - char buff[SERVER_VERSION_LENGTH + - SCRAMBLE_LENGTH+64],*end; + char buff[SERVER_VERSION_LENGTH + SCRAMBLE_LENGTH+64]; int client_flags = CLIENT_LONG_FLAG | CLIENT_CONNECT_WITH_DB | CLIENT_PROTOCOL_41 | CLIENT_SECURE_CONNECTION; @@ -588,8 +597,20 @@ check_connections(THD *thd) return(ER_OUT_OF_RESOURCES); thd->client_capabilities=uint2korr(net->read_pos); + if (thd->client_capabilities & CLIENT_PROTOCOL_41) + { + thd->client_capabilities|= ((ulong) uint2korr(net->read_pos+2)) << 16; + thd->max_client_packet_length= uint4korr(net->read_pos+4); + end= (char*) net->read_pos+8; + } + else + { + thd->max_client_packet_length= uint3korr(net->read_pos+2); + end= (char*) net->read_pos+5; + } + if (thd->client_capabilities & CLIENT_IGNORE_SPACE) - thd->sql_mode|= MODE_IGNORE_SPACE; + thd->variables.sql_mode|= MODE_IGNORE_SPACE; #ifdef HAVE_OPENSSL DBUG_PRINT("info", ("client capabilities: %d", thd->client_capabilities)); if (thd->client_capabilities & CLIENT_SSL) @@ -613,21 +634,17 @@ check_connections(THD *thd) return(ER_HANDSHAKE_ERROR); } } - else - { - DBUG_PRINT("info", ("Leaving IO layer intact")); - if (pkt_len < NORMAL_HANDSHAKE_SIZE) - { - inc_host_errors(&thd->remote.sin_addr); - return ER_HANDSHAKE_ERROR; - } - } #endif - thd->max_client_packet_length=uint3korr(net->read_pos+2); - char *user= (char*) net->read_pos+5; - char *passwd= strend(user)+1; - char *db=0; + if (end >= (char*) net->read_pos+ pkt_len +2) + { + inc_host_errors(&thd->remote.sin_addr); + return(ER_HANDSHAKE_ERROR); + } + + user= end; + passwd= strend(user)+1; + db=0; if (thd->client_capabilities & CLIENT_CONNECT_WITH_DB) db=strend(passwd)+1; @@ -642,11 +659,6 @@ check_connections(THD *thd) net->return_status= &thd->server_status; net->read_timeout=(uint) thd->variables.net_read_timeout; - char prepared_scramble[SCRAMBLE41_LENGTH+4]; /* Buffer for scramble and hash */ - - ACL_USER* cached_user=NULL; /* Initialise to NULL as first stage indication */ - uint cur_priv_version; - /* Simple connect only for old clients. New clients always use secure auth */ bool simple_connect=(!(thd->client_capabilities & CLIENT_SECURE_CONNECTION)); @@ -657,15 +669,14 @@ check_connections(THD *thd) simple_connect, prepared_scramble, using_password, &cur_priv_version, &cached_user)<0) { + /* Store current used and database as they are erased with next packet */ + char tmp_user[USERNAME_LENGTH+1]; + char tmp_db[NAME_LEN+1]; + /* If The client is old we just have to return error */ if (simple_connect) return -1; - /* Store current used and database as they are erased with next packet */ - - char tmp_user[USERNAME_LENGTH+1]; - char tmp_db[NAME_LEN+1]; - tmp_user[0]=0; if (user) strmake(tmp_user,user,USERNAME_LENGTH); @@ -682,17 +693,17 @@ check_connections(THD *thd) return ER_HANDSHAKE_ERROR; } /* Reading packet back */ - if ((pkt_len=my_net_read(net)) == packet_error) - { - inc_host_errors(&thd->remote.sin_addr); - return ER_HANDSHAKE_ERROR; - } + if ((pkt_len= my_net_read(net)) == packet_error) + { + inc_host_errors(&thd->remote.sin_addr); + return ER_HANDSHAKE_ERROR; + } /* We have to get very specific packet size */ - if (pkt_len!=SCRAMBLE41_LENGTH) - { - inc_host_errors(&thd->remote.sin_addr); - return ER_HANDSHAKE_ERROR; - } + if (pkt_len != SCRAMBLE41_LENGTH) + { + inc_host_errors(&thd->remote.sin_addr); + return ER_HANDSHAKE_ERROR; + } /* Final attempt to check the user based on reply */ if (check_user(thd,COM_CONNECT, tmp_user, (char*)net->read_pos, tmp_db, 1, 0, 1, prepared_scramble, using_password, &cur_priv_version, @@ -940,7 +951,7 @@ int mysql_table_dump(THD* thd, char* db, char* tbl_name, int fd) goto err; } net_flush(&thd->net); - if ((error = table->file->dump(thd,fd))) + if ((error= table->file->dump(thd,fd))) my_error(ER_GET_ERRNO, MYF(0)); err: @@ -1049,7 +1060,6 @@ bool dispatch_command(enum enum_server_command command, THD *thd, tbl_name[tbl_len] = 0; if (mysql_table_dump(thd, db, tbl_name, -1)) send_error(thd); // dump to NET - break; } #ifndef EMBEDDED_LIBRARY @@ -1074,7 +1084,11 @@ bool dispatch_command(enum enum_server_command command, THD *thd, USER_CONN *save_uc= thd->user_connect; bool simple_connect; bool using_password; - + char prepared_scramble[SCRAMBLE41_LENGTH+4];/* Buffer for scramble,hash */ + char tmp_user[USERNAME_LENGTH+1]; + char tmp_db[NAME_LEN+1]; + ACL_USER* cached_user ; /* Cached user */ + uint cur_priv_version; /* Cached grant version */ ulong pkt_len=0; /* Length of reply packet */ /* Small check for incomming packet */ @@ -1088,10 +1102,7 @@ bool dispatch_command(enum enum_server_command command, THD *thd, if (passwd[0] && strlen(passwd)!=SCRAMBLE_LENGTH) goto restore_user_err; - char prepared_scramble[SCRAMBLE41_LENGTH+4];/* Buffer for scramble,hash */ - ACL_USER* cached_user ; /* Cached user */ cached_user= NULL; - uint cur_priv_version; /* Cached grant version */ /* Simple connect only for old clients. New clients always use sec. auth*/ simple_connect=(!(thd->client_capabilities & CLIENT_SECURE_CONNECTION)); @@ -1103,8 +1114,8 @@ bool dispatch_command(enum enum_server_command command, THD *thd, memcpy(thd->scramble,thd->old_scramble,9); /* - Check user permissions. If password failure we'll get scramble back - Do not retry if we already have sent error (result>0) + Check user permissions. If password failure we'll get scramble back + Do not retry if we already have sent error (result>0) */ if (check_user(thd,COM_CHANGE_USER, user, passwd, db, 0, simple_connect, simple_connect, prepared_scramble, using_password, &cur_priv_version, @@ -1115,10 +1126,6 @@ bool dispatch_command(enum enum_server_command command, THD *thd, goto restore_user; /* Error is already reported */ /* Store current used and database as they are erased with next packet */ - - char tmp_user[USERNAME_LENGTH+1]; - char tmp_db[NAME_LEN+1]; - tmp_user[0]=0; if (user) strmake(tmp_user,user,USERNAME_LENGTH); @@ -1142,8 +1149,9 @@ bool dispatch_command(enum enum_server_command command, THD *thd, goto restore_user; /* Final attempt to check the user based on reply */ - if (check_user(thd,COM_CHANGE_USER, tmp_user, (char*)net->read_pos, - tmp_db, 0, 0, 1, prepared_scramble, using_password, &cur_priv_version, + if (check_user(thd,COM_CHANGE_USER, tmp_user, (char*) net->read_pos, + tmp_db, 0, 0, 1, prepared_scramble, using_password, + &cur_priv_version, &cached_user)) goto restore_user; } @@ -1430,6 +1438,14 @@ bool dispatch_command(enum enum_server_command command, THD *thd, mysql_slow_log.write(thd, thd->query, thd->query_length, start_of_query); } } + if (command == COM_QUERY && thd->lex.found_colon) + { + /* + Multiple queries exits, execute them individually + */ + uint length= thd->query_length-(uint)(thd->lex.found_colon-thd->query)+1; + dispatch_command(command, thd, thd->lex.found_colon, length); + } thd->proc_info="cleaning up"; VOID(pthread_mutex_lock(&LOCK_thread_count)); // For process list thd->proc_info=0; @@ -3111,6 +3127,7 @@ mysql_init_query(THD *thd) lex->olap=lex->describe=0; lex->derived_tables= false; lex->lock_option=TL_READ; + lex->found_colon=0; thd->check_loops_counter= thd->select_number= lex->select_lex.select_number= 1; thd->free_list= 0; @@ -3119,6 +3136,7 @@ mysql_init_query(THD *thd) thd->sent_row_count= thd->examined_row_count= 0; thd->fatal_error= thd->rand_used= 0; thd->possible_loops= 0; + thd->server_status &= ~SERVER_MORE_RESULTS_EXISTS; DBUG_VOID_RETURN; } @@ -3499,10 +3517,14 @@ bool add_field_to_list(THD *thd, char *field_name, enum_field_types type, set_if_smaller(new_field->length,MAX_FIELD_WIDTH-1); if (default_value) { + char *not_used; + uint not_used2; + thd->cuted_fields=0; String str,*res; res=default_value->val_str(&str); - (void) find_set(interval,res->ptr(),res->length()); + (void) find_set(interval, res->ptr(), res->length(), ¬_used, + ¬_used2); if (thd->cuted_fields) { net_printf(thd,ER_INVALID_DEFAULT,field_name); diff --git a/sql/sql_prepare.cc b/sql/sql_prepare.cc index 3a5bf8ebaad..2c5b049e51b 100644 --- a/sql/sql_prepare.cc +++ b/sql/sql_prepare.cc @@ -66,6 +66,7 @@ Long data handling: #include "mysql_priv.h" #include "sql_acl.h" +#include "sql_select.h" // for JOIN #include // for DEBUG_ASSERT() #include // for isspace() @@ -343,53 +344,6 @@ static bool setup_params_data(PREP_STMT *stmt) DBUG_RETURN(0); } -/* - Validates insert fields -*/ - -static int check_prepare_fields(THD *thd,TABLE *table, List &fields, - List &values, ulong counter) -{ - if (fields.elements == 0 && values.elements != 0) - { - if (values.elements != table->fields) - { - my_printf_error(ER_WRONG_VALUE_COUNT_ON_ROW, - ER(ER_WRONG_VALUE_COUNT_ON_ROW), - MYF(0),counter); - return -1; - } - } - else - { - if (fields.elements != values.elements) - { - my_printf_error(ER_WRONG_VALUE_COUNT_ON_ROW, - ER(ER_WRONG_VALUE_COUNT_ON_ROW), - MYF(0),counter); - return -1; - } - TABLE_LIST table_list; - bzero((char*) &table_list,sizeof(table_list)); - table_list.db= table->table_cache_key; - table_list.real_name= table_list.alias= table->table_name; - table_list.table= table; - table_list.grant= table->grant; - - thd->dupp_field=0; - if (setup_tables(&table_list) || - setup_fields(thd,&table_list,fields,1,0,0)) - return -1; - if (thd->dupp_field) - { - my_error(ER_FIELD_SPECIFIED_TWICE,MYF(0), thd->dupp_field->field_name); - return -1; - } - } - return 0; -} - - /* Validate the following information for INSERT statement: - field existance @@ -459,7 +413,7 @@ static bool mysql_test_upd_fields(PREP_STMT *stmt, TABLE_LIST *table_list, DBUG_RETURN(1); if (setup_tables(table_list) || setup_fields(thd,table_list,fields,1,0,0) || - setup_conds(thd,table_list,&conds)) + setup_conds(thd,table_list,&conds) || thd->net.report_error) DBUG_RETURN(1); /* @@ -483,39 +437,41 @@ static bool mysql_test_upd_fields(PREP_STMT *stmt, TABLE_LIST *table_list, And send column list fields info back to client. */ - static bool mysql_test_select_fields(PREP_STMT *stmt, TABLE_LIST *tables, - List &fields, List &values, - COND *conds, ORDER *order, ORDER *group, - Item *having) + List &fields, COND *conds, + ORDER *order, ORDER *group, + Item *having, ORDER *proc, + ulong select_options, + SELECT_LEX_UNIT *unit, + SELECT_LEX *select_lex) { - bool hidden_group_fields; THD *thd= stmt->thd; - List all_fields(fields); + LEX *lex= &thd->lex; + select_result *result= thd->lex.result; DBUG_ENTER("mysql_test_select_fields"); + if ((&lex->select_lex != lex->all_selects_list && + lex->unit.create_total_list(thd, lex, &tables))) + DBUG_RETURN(1); + if (open_and_lock_tables(thd, tables)) DBUG_RETURN(1); - thd->used_tables=0; // Updated by setup_fields - if (setup_tables(tables) || - setup_fields(thd,tables,fields,1,&all_fields,1) || - setup_conds(thd,tables,&conds) || - setup_order(thd,tables,fields,all_fields,order) || - setup_group(thd,tables,fields,all_fields,group,&hidden_group_fields)) - DBUG_RETURN(1); + fix_tables_pointers(thd->lex.all_selects_list); - if (having) + if (!result && !(result= new select_send())) { - thd->where="having clause"; - thd->allow_sum_func=1; - if (having->check_cols(1) || having->fix_fields(thd, tables, &having) - || thd->fatal_error) - DBUG_RETURN(1); - if (having->with_sum_func) - having->split_sum_func(all_fields); + delete select_lex->having; + delete select_lex->where; + send_error(thd, ER_OUT_OF_RESOURCES); + DBUG_RETURN(1); } - if (setup_ftfuncs(&thd->lex.select_lex)) + + JOIN *join= new JOIN(thd, fields, select_options, result); + thd->used_tables= 0; // Updated by setup_fields + + if (join->prepare(tables, conds, order, group, having, proc, + select_lex, unit, 0)) DBUG_RETURN(1); /* @@ -523,27 +479,14 @@ static bool mysql_test_select_fields(PREP_STMT *stmt, TABLE_LIST *tables, sending any info on where clause. */ if (send_prep_stmt(stmt, fields.elements) || - thd->protocol_simple.send_fields(&fields,0) || + thd->protocol_simple.send_fields(&fields, 0) || send_item_params(stmt)) DBUG_RETURN(1); + join->cleanup(thd); DBUG_RETURN(0); } -/* - Check the access privileges -*/ - -static bool check_prepare_access(THD *thd, TABLE_LIST *tables, - uint type) -{ - if (check_access(thd,type,tables->db,&tables->grant.privilege)) - return 1; - if (grant_option && check_grant(thd,type,tables)) - return 1; - return 0; -} - /* Send the prepare query results back to client */ @@ -586,10 +529,15 @@ static bool send_prepare_results(PREP_STMT *stmt) break; case SQLCOM_SELECT: - if (mysql_test_select_fields(stmt, tables, select_lex->item_list, - lex->value_list, select_lex->where, - (ORDER*) select_lex->order_list.first, - (ORDER*) select_lex->group_list.first, select_lex->having)) + if (mysql_test_select_fields(stmt, tables, + select_lex->item_list, + select_lex->where, + (ORDER*) select_lex->order_list.first, + (ORDER*) select_lex->group_list.first, + select_lex->having, + (ORDER*)lex->proc_list.first, + select_lex->options | thd->options, + &(lex->unit), select_lex)) goto abort; break; @@ -750,6 +698,10 @@ void mysql_stmt_execute(THD *thd, char *packet) DBUG_VOID_RETURN; } + if (my_pthread_setspecific_ptr(THR_THD, stmt->thd) || + my_pthread_setspecific_ptr(THR_MALLOC, &stmt->thd->mem_root)) + DBUG_VOID_RETURN; + init_stmt_execute(stmt); if (stmt->param_count && setup_params_data(stmt)) @@ -764,13 +716,17 @@ void mysql_stmt_execute(THD *thd, char *packet) mysql_delete(), mysql_update() and mysql_select() to not to have re-check on setup_* and other things .. */ - stmt->thd->protocol= &thd->protocol_prep; // Switch to binary protocol - mysql_execute_command(stmt->thd); - stmt->thd->protocol= &thd->protocol_simple; // Use normal protocol + THD *cur_thd= stmt->thd; + cur_thd->protocol= &cur_thd->protocol_prep; // Switch to binary protocol + mysql_execute_command(cur_thd); + cur_thd->protocol= &cur_thd->protocol_simple; // Use normal protocol if (!(specialflag & SPECIAL_NO_PRIOR)) my_pthread_setprio(pthread_self(), WAIT_PRIOR); - + + my_pthread_setspecific_ptr(THR_THD, thd); + my_pthread_setspecific_ptr(THR_MALLOC, &thd->mem_root); + DBUG_VOID_RETURN; } diff --git a/sql/sql_select.cc b/sql/sql_select.cc index f6f7c473b07..0243cd7cfb6 100644 --- a/sql/sql_select.cc +++ b/sql/sql_select.cc @@ -6952,7 +6952,7 @@ setup_group(THD *thd,TABLE_LIST *tables,List &fields, if (!order) return 0; /* Everything is ok */ - if (thd->sql_mode & MODE_ONLY_FULL_GROUP_BY) + if (thd->variables.sql_mode & MODE_ONLY_FULL_GROUP_BY) { Item *item; List_iterator li(fields); @@ -6974,7 +6974,7 @@ setup_group(THD *thd,TABLE_LIST *tables,List &fields, return 1; } } - if (thd->sql_mode & MODE_ONLY_FULL_GROUP_BY) + if (thd->variables.sql_mode & MODE_ONLY_FULL_GROUP_BY) { /* Don't allow one to use fields that is not used in GROUP BY */ Item *item; diff --git a/sql/sql_show.cc b/sql/sql_show.cc index bc86f0bfb3c..e77947e70f6 100644 --- a/sql/sql_show.cc +++ b/sql/sql_show.cc @@ -994,11 +994,22 @@ mysqld_dump_create_info(THD *thd, TABLE *table, int fd) static void append_identifier(THD *thd, String *packet, const char *name) { + char qtype; + if ((thd->variables.sql_mode & MODE_ANSI_QUOTES) || + (thd->variables.sql_mode & MODE_POSTGRESQL) || + (thd->variables.sql_mode & MODE_ORACLE) || + (thd->variables.sql_mode & MODE_MSSQL) || + (thd->variables.sql_mode & MODE_DB2) || + (thd->variables.sql_mode & MODE_SAPDB)) + qtype= '\"'; + else + qtype= '`'; + if (thd->options & OPTION_QUOTE_SHOW_CREATE) { - packet->append("`", 1); + packet->append(&qtype, 1); packet->append(name); - packet->append("`", 1); + packet->append(&qtype, 1); } else { @@ -1010,6 +1021,16 @@ append_identifier(THD *thd, String *packet, const char *name) static int store_create_info(THD *thd, TABLE *table, String *packet) { + my_bool foreign_db_mode= ((thd->variables.sql_mode & MODE_POSTGRESQL) || + (thd->variables.sql_mode & MODE_ORACLE) || + (thd->variables.sql_mode & MODE_MSSQL) || + (thd->variables.sql_mode & MODE_DB2) || + (thd->variables.sql_mode & MODE_SAPDB)); + my_bool limited_mysql_mode= ((thd->variables.sql_mode & + MODE_NO_FIELD_OPTIONS) || + (thd->variables.sql_mode & MODE_MYSQL323) || + (thd->variables.sql_mode & MODE_MYSQL40)); + DBUG_ENTER("store_create_info"); DBUG_PRINT("enter",("table: %s",table->real_name)); @@ -1050,9 +1071,10 @@ store_create_info(THD *thd, TABLE *table, String *packet) For string types dump collation name only if collation is not primary for the given charset */ - if (!field->binary() && !(field->charset()->state & MY_CS_PRIMARY)) + if (!field->binary() && !(field->charset()->state & MY_CS_PRIMARY) && + !limited_mysql_mode && !foreign_db_mode) { - packet->append(" collate ",9); + packet->append(" collate ", 9); packet->append(field->charset()->name); } if (flags & NOT_NULL_FLAG) @@ -1076,7 +1098,7 @@ store_create_info(THD *thd, TABLE *table, String *packet) packet->append(tmp,0); } - if (field->unireg_check == Field::NEXT_NUMBER) + if (field->unireg_check == Field::NEXT_NUMBER && !foreign_db_mode) packet->append(" auto_increment", 15 ); if (field->comment.length) @@ -1110,17 +1132,20 @@ store_create_info(THD *thd, TABLE *table, String *packet) packet->append("KEY ", 4); if (!found_primary) - append_identifier(thd,packet,key_info->name); - - if (table->db_type == DB_TYPE_HEAP && - key_info->algorithm == HA_KEY_ALG_BTREE) - packet->append(" USING BTREE", 12); - - // +BAR: send USING only in non-default case: non-spatial rtree - if ((key_info->algorithm == HA_KEY_ALG_RTREE) && - !(key_info->flags & HA_SPATIAL)) - packet->append(" USING RTREE",12); + append_identifier(thd, packet, key_info->name); + if (!(thd->variables.sql_mode & MODE_NO_KEY_OPTIONS) && + !limited_mysql_mode && !foreign_db_mode) + { + if (table->db_type == DB_TYPE_HEAP && + key_info->algorithm == HA_KEY_ALG_BTREE) + packet->append(" TYPE BTREE", 11); + + // +BAR: send USING only in non-default case: non-spatial rtree + if ((key_info->algorithm == HA_KEY_ALG_RTREE) && + !(key_info->flags & HA_SPATIAL)) + packet->append(" TYPE RTREE", 11); + } packet->append(" (", 2); for (uint j=0 ; j < key_info->key_parts ; j++,key_part++) @@ -1159,67 +1184,72 @@ store_create_info(THD *thd, TABLE *table, String *packet) } packet->append("\n)", 2); - packet->append(" TYPE=", 6); - packet->append(file->table_type()); - char buff[128]; - char* p; - - if (table->table_charset) + if (!(thd->variables.sql_mode & MODE_NO_TABLE_OPTIONS) && !foreign_db_mode) { - packet->append(" CHARSET="); - packet->append(table->table_charset->csname); - if (!(table->table_charset->state & MY_CS_PRIMARY)) + packet->append(" TYPE=", 6); + packet->append(file->table_type()); + char buff[128]; + char* p; + + if (table->table_charset && + !(thd->variables.sql_mode & MODE_MYSQL323) && + !(thd->variables.sql_mode & MODE_MYSQL40)) { - packet->append(" COLLATE="); - packet->append(table->table_charset->name); + packet->append(" CHARSET="); + packet->append(table->table_charset->csname); + if (!(table->table_charset->state & MY_CS_PRIMARY)) + { + packet->append(" COLLATE="); + packet->append(table->table_charset->name); + } } - } - if (table->min_rows) - { - packet->append(" MIN_ROWS="); - p = longlong10_to_str(table->min_rows, buff, 10); - packet->append(buff, (uint) (p - buff)); - } + if (table->min_rows) + { + packet->append(" MIN_ROWS="); + p = longlong10_to_str(table->min_rows, buff, 10); + packet->append(buff, (uint) (p - buff)); + } - if (table->max_rows) - { - packet->append(" MAX_ROWS="); - p = longlong10_to_str(table->max_rows, buff, 10); - packet->append(buff, (uint) (p - buff)); - } - if (table->avg_row_length) - { - packet->append(" AVG_ROW_LENGTH="); - p=longlong10_to_str(table->avg_row_length, buff,10); - packet->append(buff, (uint) (p - buff)); - } + if (table->max_rows) + { + packet->append(" MAX_ROWS="); + p = longlong10_to_str(table->max_rows, buff, 10); + packet->append(buff, (uint) (p - buff)); + } + if (table->avg_row_length) + { + packet->append(" AVG_ROW_LENGTH="); + p=longlong10_to_str(table->avg_row_length, buff,10); + packet->append(buff, (uint) (p - buff)); + } - if (table->db_create_options & HA_OPTION_PACK_KEYS) - packet->append(" PACK_KEYS=1", 12); - if (table->db_create_options & HA_OPTION_NO_PACK_KEYS) - packet->append(" PACK_KEYS=0", 12); - if (table->db_create_options & HA_OPTION_CHECKSUM) - packet->append(" CHECKSUM=1", 11); - if (table->db_create_options & HA_OPTION_DELAY_KEY_WRITE) - packet->append(" DELAY_KEY_WRITE=1",18); - if (table->row_type != ROW_TYPE_DEFAULT) - { - packet->append(" ROW_FORMAT=",12); - packet->append(ha_row_type[(uint) table->row_type]); - } - table->file->append_create_info(packet); - if (table->comment && table->comment[0]) - { - packet->append(" COMMENT=", 9); - append_unescaped(packet, table->comment, strlen(table->comment)); - } - if (file->raid_type) - { - char buff[100]; - sprintf(buff," RAID_TYPE=%s RAID_CHUNKS=%d RAID_CHUNKSIZE=%ld", - my_raid_type(file->raid_type), file->raid_chunks, file->raid_chunksize/RAID_BLOCK_SIZE); - packet->append(buff); + if (table->db_create_options & HA_OPTION_PACK_KEYS) + packet->append(" PACK_KEYS=1", 12); + if (table->db_create_options & HA_OPTION_NO_PACK_KEYS) + packet->append(" PACK_KEYS=0", 12); + if (table->db_create_options & HA_OPTION_CHECKSUM) + packet->append(" CHECKSUM=1", 11); + if (table->db_create_options & HA_OPTION_DELAY_KEY_WRITE) + packet->append(" DELAY_KEY_WRITE=1",18); + if (table->row_type != ROW_TYPE_DEFAULT) + { + packet->append(" ROW_FORMAT=",12); + packet->append(ha_row_type[(uint) table->row_type]); + } + table->file->append_create_info(packet); + if (table->comment && table->comment[0]) + { + packet->append(" COMMENT=", 9); + append_unescaped(packet, table->comment, strlen(table->comment)); + } + if (file->raid_type) + { + char buff[100]; + sprintf(buff," RAID_TYPE=%s RAID_CHUNKS=%d RAID_CHUNKSIZE=%ld", + my_raid_type(file->raid_type), file->raid_chunks, file->raid_chunksize/RAID_BLOCK_SIZE); + packet->append(buff); + } } DBUG_RETURN(0); } diff --git a/sql/sql_string.cc b/sql/sql_string.cc index 4c499af8f9e..646621ac2eb 100644 --- a/sql/sql_string.cc +++ b/sql/sql_string.cc @@ -100,7 +100,7 @@ bool String::set(longlong num, CHARSET_INFO *cs) if (alloc(l)) return TRUE; - str_length=(uint32) cs->ll10tostr(cs,Ptr,l,-10,num); + str_length=(uint32) cs->longlong10_to_str(cs,Ptr,l,-10,num); str_charset=cs; return FALSE; } @@ -111,7 +111,7 @@ bool String::set(ulonglong num, CHARSET_INFO *cs) if (alloc(l)) return TRUE; - str_length=(uint32) cs->ll10tostr(cs,Ptr,l,10,num); + str_length=(uint32) cs->longlong10_to_str(cs,Ptr,l,10,num); str_charset=cs; return FALSE; } diff --git a/sql/sql_table.cc b/sql/sql_table.cc index 291976fe154..f4642870af3 100644 --- a/sql/sql_table.cc +++ b/sql/sql_table.cc @@ -1236,7 +1236,6 @@ static int mysql_admin_table(THD* thd, TABLE_LIST* tables, thd->net.last_errno= 0; // these errors shouldn't get client #endif thd->open_options&= ~extra_open_options; - protocol->prepare_for_resend(); if (prepare_func) { @@ -2046,12 +2045,9 @@ int mysql_alter_table(THD *thd,char *new_db, char *new_name, /* We changed a temporary table */ if (error) { - /* - * The following function call will also free a - * new_table pointer. - * Therefore, here new_table pointer is not free'd as it is - * free'd in close_temporary() which is called by by the - * close_temporary_table() function. + /* + The following function call will free the new_table pointer, + in close_temporary_table(), so we can safely directly jump to err */ close_temporary_table(thd,new_db,tmp_name); goto err; diff --git a/sql/sql_test.cc b/sql/sql_test.cc index 758778ecfa8..8984bcc6463 100644 --- a/sql/sql_test.cc +++ b/sql/sql_test.cc @@ -276,7 +276,6 @@ static void display_table_locks (void) VOID(pthread_mutex_unlock(&lock->mutex)); } VOID(pthread_mutex_unlock(&THR_LOCK_lock)); - uint i; if (!saved_table_locks.elements) goto end; qsort((gptr) dynamic_element(&saved_table_locks,0,TABLE_LOCK_INFO *),saved_table_locks.elements,sizeof(TABLE_LOCK_INFO),(qsort_cmp) dl_compare); @@ -284,6 +283,7 @@ static void display_table_locks (void) puts("\nThread database.table_name Locked/Waiting Lock_type\n"); + unsigned int i; for (i=0 ; i < saved_table_locks.elements ; i++) { TABLE_LOCK_INFO *dl_ptr=dynamic_element(&saved_table_locks,i,TABLE_LOCK_INFO*); diff --git a/sql/sql_yacc.yy b/sql/sql_yacc.yy index 318d563b88a..a424aefd45f 100644 --- a/sql/sql_yacc.yy +++ b/sql/sql_yacc.yy @@ -45,7 +45,7 @@ int yylex(void *yylval, void *yythd); inline Item *or_or_concat(THD *thd, Item* A, Item* B) { - return (thd->sql_mode & MODE_PIPES_AS_CONCAT ? + return (thd->variables.sql_mode & MODE_PIPES_AS_CONCAT ? (Item*) new Item_func_concat(A,B) : (Item*) new Item_cond_or(A,B)); } @@ -1112,6 +1112,8 @@ type: $$=FIELD_TYPE_TINY; } | BOOL_SYM { Lex->length=(char*) "1"; $$=FIELD_TYPE_TINY; } + | BOOLEAN_SYM { Lex->length=(char*) "1"; + $$=FIELD_TYPE_TINY; } | char '(' NUM ')' opt_binary { Lex->length=$3.str; $$=FIELD_TYPE_STRING; } | char opt_binary { Lex->length=(char*) "1"; @@ -1129,7 +1131,7 @@ type: | TIME_SYM { $$=FIELD_TYPE_TIME; } | TIMESTAMP { - if (YYTHD->sql_mode & MODE_SAPDB) + if (YYTHD->variables.sql_mode & MODE_SAPDB) $$=FIELD_TYPE_DATETIME; else $$=FIELD_TYPE_TIMESTAMP; @@ -1200,7 +1202,7 @@ int_type: | BIGINT { $$=FIELD_TYPE_LONGLONG; }; real_type: - REAL { $$= YYTHD->sql_mode & MODE_REAL_AS_FLOAT ? + REAL { $$= YYTHD->variables.sql_mode & MODE_REAL_AS_FLOAT ? FIELD_TYPE_FLOAT : FIELD_TYPE_DOUBLE; } | DOUBLE_SYM { $$=FIELD_TYPE_DOUBLE; } | DOUBLE_SYM PRECISION { $$=FIELD_TYPE_DOUBLE; }; @@ -4106,7 +4108,8 @@ text_or_password: else { char *buff=(char*) YYTHD->alloc(HASH_PASSWORD_LENGTH+1); - make_scrambled_password(buff,$3.str,opt_old_passwords,¤t_thd->rand); + make_scrambled_password(buff,$3.str,use_old_passwords, + &YYTHD->rand); $$=buff; } } @@ -4408,7 +4411,8 @@ grant_user: char *buff=(char*) YYTHD->alloc(HASH_PASSWORD_LENGTH+1); if (buff) { - make_scrambled_password(buff,$4.str,opt_old_passwords,¤t_thd->rand); + make_scrambled_password(buff,$4.str,use_old_passwords, + &YYTHD->rand); $1->password.str=buff; $1->password.length=HASH_PASSWORD_LENGTH; } diff --git a/strings/Makefile.am b/strings/Makefile.am index dea9effdfeb..c78be3d9cb4 100644 --- a/strings/Makefile.am +++ b/strings/Makefile.am @@ -40,6 +40,7 @@ endif libmystrings_a_SOURCES = $(ASRCS) $(CSRCS) noinst_PROGRAMS = conf_to_src +DISTCLEANFILES = ctype_autoconf.c # Default charset definitions EXTRA_DIST = ctype-big5.c ctype-czech.c ctype-euc_kr.c ctype-win1250ch.c \ ctype-gb2312.c ctype-gbk.c ctype-sjis.c ctype-utf8.c \ diff --git a/strings/ctype-big5.c b/strings/ctype-big5.c index 408c7f8fe35..a475a36d049 100644 --- a/strings/ctype-big5.c +++ b/strings/ctype-big5.c @@ -6218,7 +6218,7 @@ my_mb_wc_big5(CHARSET_INFO *cs __attribute__((unused)), CHARSET_INFO my_charset_big5 = { 1, /* number */ - MY_CS_COMPILED|MY_CS_PRIMARY, /* state */ + MY_CS_COMPILED|MY_CS_PRIMARY|MY_CS_STRNXFRM, /* state */ "big5", /* cs name */ "big5", /* name */ "", /* comment */ @@ -6250,14 +6250,15 @@ CHARSET_INFO my_charset_big5 = my_hash_sort_simple, 0, my_snprintf_8bit, - my_l10tostr_8bit, - my_ll10tostr_8bit, + my_long10_to_str_8bit, + my_longlong10_to_str_8bit, my_strntol_8bit, my_strntoul_8bit, my_strntoll_8bit, my_strntoull_8bit, - my_strntod_8bit + my_strntod_8bit, + my_scan_8bit }; diff --git a/strings/ctype-bin.c b/strings/ctype-bin.c index e20afff1470..7d174d510e2 100644 --- a/strings/ctype-bin.c +++ b/strings/ctype-bin.c @@ -254,7 +254,14 @@ static int my_wildcmp_bin(CHARSET_INFO *cs, return(str != str_end ? 1 : 0); } - +static int my_strnxfrm_bin(CHARSET_INFO *cs __attribute__((unused)), + uchar * dest, uint len, + const uchar *src, + uint srclen __attribute__((unused))) +{ + memcpy(dest,src,len= min(len,srclen)); + return len; +} static CHARSET_INFO my_charset_bin_st = { @@ -271,7 +278,7 @@ static CHARSET_INFO my_charset_bin_st = NULL, /* tab_from_uni */ 0, /* strxfrm_multiply */ my_strnncoll_binary, /* strnncoll */ - NULL, /* strxnfrm */ + my_strnxfrm_bin, /* strxnfrm */ my_like_range_simple, /* like_range */ my_wildcmp_bin, /* wildcmp */ 1, /* mbmaxlen */ @@ -291,13 +298,14 @@ static CHARSET_INFO my_charset_bin_st = my_hash_sort_bin, /* hash_sort */ 255, /* max_sort_char */ my_snprintf_8bit, /* snprintf */ - my_l10tostr_8bit, - my_ll10tostr_8bit, + my_long10_to_str_8bit, + my_longlong10_to_str_8bit, my_strntol_8bit, my_strntoul_8bit, my_strntoll_8bit, my_strntoull_8bit, my_strntod_8bit, + my_scan_8bit }; diff --git a/strings/ctype-czech.c b/strings/ctype-czech.c index bb0afe98032..6397d3d902e 100644 --- a/strings/ctype-czech.c +++ b/strings/ctype-czech.c @@ -596,7 +596,7 @@ static MY_UNI_IDX idx_uni_8859_2[]={ CHARSET_INFO my_charset_czech = { 2, /* number */ - MY_CS_COMPILED, /* state */ + MY_CS_COMPILED|MY_CS_STRNXFRM, /* state */ "latin2", /* cs name */ "czech", /* name */ "", /* comment */ @@ -628,13 +628,14 @@ CHARSET_INFO my_charset_czech = my_hash_sort_simple, 0, my_snprintf_8bit, - my_l10tostr_8bit, - my_ll10tostr_8bit, + my_long10_to_str_8bit, + my_longlong10_to_str_8bit, my_strntol_8bit, my_strntoul_8bit, my_strntoll_8bit, my_strntoull_8bit, - my_strntod_8bit + my_strntod_8bit, + my_scan_8bit }; #endif diff --git a/strings/ctype-euc_kr.c b/strings/ctype-euc_kr.c index 814f43166c4..ee75673e1c5 100644 --- a/strings/ctype-euc_kr.c +++ b/strings/ctype-euc_kr.c @@ -8648,7 +8648,7 @@ CHARSET_INFO my_charset_euc_kr = NULL, /* tab_from_uni */ 0, /* strxfrm_multiply */ my_strnncoll_simple,/* strnncoll */ - NULL, /* strnxfrm */ + my_strnxfrm_simple, /* strnxfrm */ my_like_range_simple,/* like_range */ my_wildcmp_mb, /* wildcmp */ 2, /* mbmaxlen */ @@ -8668,13 +8668,14 @@ CHARSET_INFO my_charset_euc_kr = my_hash_sort_simple, 0, my_snprintf_8bit, - my_l10tostr_8bit, - my_ll10tostr_8bit, + my_long10_to_str_8bit, + my_longlong10_to_str_8bit, my_strntol_8bit, my_strntoul_8bit, my_strntoll_8bit, my_strntoull_8bit, - my_strntod_8bit + my_strntod_8bit, + my_scan_8bit }; #endif diff --git a/strings/ctype-gb2312.c b/strings/ctype-gb2312.c index d575e48a59a..0820d03b2d0 100644 --- a/strings/ctype-gb2312.c +++ b/strings/ctype-gb2312.c @@ -5698,7 +5698,7 @@ CHARSET_INFO my_charset_gb2312 = NULL, /* tab_from_uni */ 0, /* strxfrm_multiply */ my_strnncoll_simple,/* strnncoll */ - NULL, /* strnxfrm */ + my_strnxfrm_simple, /* strnxfrm */ my_like_range_simple,/* like_range */ my_wildcmp_mb, /* wildcmp */ 2, /* mbmaxlen */ @@ -5718,13 +5718,14 @@ CHARSET_INFO my_charset_gb2312 = my_hash_sort_simple, 0, my_snprintf_8bit, - my_l10tostr_8bit, - my_ll10tostr_8bit, + my_long10_to_str_8bit, + my_longlong10_to_str_8bit, my_strntol_8bit, my_strntoul_8bit, my_strntoll_8bit, my_strntoull_8bit, - my_strntod_8bit + my_strntod_8bit, + my_scan_8bit }; #endif diff --git a/strings/ctype-gbk.c b/strings/ctype-gbk.c index 0094a93e5f8..3aff098cd14 100644 --- a/strings/ctype-gbk.c +++ b/strings/ctype-gbk.c @@ -9873,7 +9873,7 @@ my_mb_wc_gbk(CHARSET_INFO *cs __attribute__((unused)), CHARSET_INFO my_charset_gbk = { 28, /* number */ - MY_CS_COMPILED|MY_CS_PRIMARY, /* state */ + MY_CS_COMPILED|MY_CS_PRIMARY|MY_CS_STRNXFRM, /* state */ "gbk", /* cs name */ "gbk", /* name */ "", /* comment */ @@ -9905,13 +9905,14 @@ CHARSET_INFO my_charset_gbk = my_hash_sort_simple, 0, my_snprintf_8bit, - my_l10tostr_8bit, - my_ll10tostr_8bit, + my_long10_to_str_8bit, + my_longlong10_to_str_8bit, my_strntol_8bit, my_strntoul_8bit, my_strntoll_8bit, my_strntoull_8bit, my_strntod_8bit, + my_scan_8bit }; diff --git a/strings/ctype-latin1_de.c b/strings/ctype-latin1_de.c index 166e059ef42..6a5a9b78e8d 100644 --- a/strings/ctype-latin1_de.c +++ b/strings/ctype-latin1_de.c @@ -414,7 +414,7 @@ static my_bool my_like_range_latin1_de(CHARSET_INFO *cs __attribute__((unused)), CHARSET_INFO my_charset_latin1_de = { 31, /* number */ - MY_CS_COMPILED, /* state */ + MY_CS_COMPILED|MY_CS_STRNXFRM, /* state */ "latin1", /* cs name */ "latin1_de", /* name */ "", /* comment */ @@ -446,13 +446,14 @@ CHARSET_INFO my_charset_latin1_de = my_hash_sort_simple, 0, my_snprintf_8bit, - my_l10tostr_8bit, - my_ll10tostr_8bit, + my_long10_to_str_8bit, + my_longlong10_to_str_8bit, my_strntol_8bit, my_strntoul_8bit, my_strntoll_8bit, my_strntoull_8bit, - my_strntod_8bit + my_strntod_8bit, + my_scan_8bit }; #endif diff --git a/strings/ctype-simple.c b/strings/ctype-simple.c index 9bcafa9f164..99d03e187b1 100644 --- a/strings/ctype-simple.c +++ b/strings/ctype-simple.c @@ -17,7 +17,6 @@ #include #include "m_string.h" #include "m_ctype.h" -#include "my_sys.h" /* defines errno */ #include #include "stdarg.h" @@ -29,11 +28,13 @@ int my_strnxfrm_simple(CHARSET_INFO * cs, const uchar *src, uint srclen) { uchar *map= cs->sort_order; + const uchar *end; DBUG_ASSERT(len >= srclen); - for ( ; len > 0 ; len-- ) + len= min(len,srclen); + for ( end=src+len; src < end ; ) *dest++= map[*src++]; - return srclen; + return len; } int my_strnncoll_simple(CHARSET_INFO * cs, const uchar *s, uint slen, @@ -201,7 +202,8 @@ void my_hash_sort_simple(CHARSET_INFO *cs, long my_strntol_8bit(CHARSET_INFO *cs, - const char *nptr, uint l, char **endptr, int base) + const char *nptr, uint l, int base, + char **endptr, int *err) { int negative; register ulong cutoff; @@ -211,10 +213,13 @@ long my_strntol_8bit(CHARSET_INFO *cs, register unsigned char c; const char *save, *e; int overflow; - + + *err= 0; /* Initialize error indicator */ +#ifdef NOT_USED if (base < 0 || base == 1 || base > 36) base = 10; - +#endif + s = nptr; e = nptr+l; @@ -239,9 +244,12 @@ long my_strntol_8bit(CHARSET_INFO *cs, else negative = 0; +#ifdef NOT_USED if (base == 16 && s[0] == '0' && (s[1]=='X' || s[1]=='x')) s += 2; +#endif +#ifdef NOT_USED if (base == 0) { if (*s == '0') @@ -257,6 +265,7 @@ long my_strntol_8bit(CHARSET_INFO *cs, else base = 10; } +#endif save = s; cutoff = ((ulong)~0L) / (unsigned long int) base; @@ -301,14 +310,14 @@ long my_strntol_8bit(CHARSET_INFO *cs, if (overflow) { - my_errno=(ERANGE); + err[0]= ERANGE; return negative ? LONG_MIN : LONG_MAX; } return (negative ? -((long) i) : (long) i); noconv: - my_errno=(EDOM); + err[0]= EDOM; if (endptr != NULL) *endptr = (char *) nptr; return 0L; @@ -316,7 +325,8 @@ noconv: ulong my_strntoul_8bit(CHARSET_INFO *cs, - const char *nptr, uint l, char **endptr, int base) + const char *nptr, uint l, int base, + char **endptr, int *err) { int negative; register ulong cutoff; @@ -327,9 +337,12 @@ ulong my_strntoul_8bit(CHARSET_INFO *cs, const char *save, *e; int overflow; + *err= 0; /* Initialize error indicator */ +#ifdef NOT_USED if (base < 0 || base == 1 || base > 36) base = 10; - +#endif + s = nptr; e = nptr+l; @@ -353,9 +366,12 @@ ulong my_strntoul_8bit(CHARSET_INFO *cs, else negative = 0; +#ifdef NOT_USED if (base == 16 && s[0] == '0' && (s[1]=='X' || s[1]=='x')) s += 2; +#endif +#ifdef NOT_USED if (base == 0) { if (*s == '0') @@ -371,6 +387,7 @@ ulong my_strntoul_8bit(CHARSET_INFO *cs, else base = 10; } +#endif save = s; cutoff = ((ulong)~0L) / (unsigned long int) base; @@ -407,14 +424,14 @@ ulong my_strntoul_8bit(CHARSET_INFO *cs, if (overflow) { - my_errno=(ERANGE); + err[0]= ERANGE; return ((ulong)~0L); } return (negative ? -((long) i) : (long) i); noconv: - my_errno=(EDOM); + err[0]= EDOM; if (endptr != NULL) *endptr = (char *) nptr; return 0L; @@ -422,7 +439,8 @@ noconv: longlong my_strntoll_8bit(CHARSET_INFO *cs __attribute__((unused)), - const char *nptr, uint l, char **endptr, int base) + const char *nptr, uint l, int base, + char **endptr,int *err) { int negative; register ulonglong cutoff; @@ -433,8 +451,11 @@ longlong my_strntoll_8bit(CHARSET_INFO *cs __attribute__((unused)), const char *save; int overflow; + *err= 0; /* Initialize error indicator */ +#ifdef NOT_USED if (base < 0 || base == 1 || base > 36) base = 10; +#endif s = nptr; e = nptr+l; @@ -459,9 +480,12 @@ longlong my_strntoll_8bit(CHARSET_INFO *cs __attribute__((unused)), else negative = 0; +#ifdef NOT_USED if (base == 16 && s[0] == '0' && (s[1]=='X'|| s[1]=='x')) s += 2; +#endif +#ifdef NOT_USED if (base == 0) { if (*s == '0') @@ -477,6 +501,7 @@ longlong my_strntoll_8bit(CHARSET_INFO *cs __attribute__((unused)), else base = 10; } +#endif save = s; @@ -522,14 +547,14 @@ longlong my_strntoll_8bit(CHARSET_INFO *cs __attribute__((unused)), if (overflow) { - my_errno=(ERANGE); + err[0]= ERANGE; return negative ? LONGLONG_MIN : LONGLONG_MAX; } return (negative ? -((longlong) i) : (longlong) i); noconv: - my_errno=(EDOM); + err[0]= EDOM; if (endptr != NULL) *endptr = (char *) nptr; return 0L; @@ -537,7 +562,8 @@ noconv: ulonglong my_strntoull_8bit(CHARSET_INFO *cs, - const char *nptr, uint l, char **endptr, int base) + const char *nptr, uint l, int base, + char **endptr, int *err) { int negative; register ulonglong cutoff; @@ -548,8 +574,11 @@ ulonglong my_strntoull_8bit(CHARSET_INFO *cs, const char *save; int overflow; + *err= 0; /* Initialize error indicator */ +#ifdef NOT_USED if (base < 0 || base == 1 || base > 36) base = 10; +#endif s = nptr; e = nptr+l; @@ -574,9 +603,12 @@ ulonglong my_strntoull_8bit(CHARSET_INFO *cs, else negative = 0; +#ifdef NOT_USED if (base == 16 && s[0] == '0' && (s[1]=='X' || s[1]=='x')) s += 2; +#endif +#ifdef NOT_USED if (base == 0) { if (*s == '0') @@ -592,6 +624,7 @@ ulonglong my_strntoull_8bit(CHARSET_INFO *cs, else base = 10; } +#endif save = s; @@ -629,14 +662,14 @@ ulonglong my_strntoull_8bit(CHARSET_INFO *cs, if (overflow) { - my_errno=(ERANGE); + err[0]= ERANGE; return (~(ulonglong) 0); } return (negative ? -((longlong) i) : (longlong) i); noconv: - my_errno=(EDOM); + err[0]= EDOM; if (endptr != NULL) *endptr = (char *) nptr; return 0L; @@ -650,7 +683,8 @@ noconv: cs Character set information str String to convert to double length Optional length for string. - end pointer to end of converted string + end result pointer to end of converted string + err Error number if failed conversion NOTES: If length is not INT_MAX32 or str[length] != 0 then the given str must @@ -660,22 +694,28 @@ noconv: It's implemented this way to save a buffer allocation and a memory copy. RETURN - value of number in string + Value of number in string */ double my_strntod_8bit(CHARSET_INFO *cs __attribute__((unused)), - char *str, uint length, char **end) + char *str, uint length, + char **end, int *err) { char end_char; double result; + errno= 0; /* Safety */ if (length == INT_MAX32 || str[length] == 0) - return strtod(str, end); - end_char= str[length]; - str[length]= 0; - result= strtod(str, end); - str[length]= end_char; /* Restore end char */ + result= strtod(str, end); + else + { + end_char= str[length]; + str[length]= 0; + result= strtod(str, end); + str[length]= end_char; /* Restore end char */ + } + *err= errno; return result; } @@ -686,7 +726,7 @@ double my_strntod_8bit(CHARSET_INFO *cs __attribute__((unused)), Assume len >= 1 */ -int my_l10tostr_8bit(CHARSET_INFO *cs __attribute__((unused)), +int my_long10_to_str_8bit(CHARSET_INFO *cs __attribute__((unused)), char *dst, uint len, int radix, long int val) { char buffer[66]; @@ -725,7 +765,7 @@ int my_l10tostr_8bit(CHARSET_INFO *cs __attribute__((unused)), } -int my_ll10tostr_8bit(CHARSET_INFO *cs __attribute__((unused)), +int my_longlong10_to_str_8bit(CHARSET_INFO *cs __attribute__((unused)), char *dst, uint len, int radix, longlong val) { char buffer[65]; @@ -938,3 +978,29 @@ my_bool my_like_range_simple(CHARSET_INFO *cs, *min_str++ = *max_str++ = ' '; // Because if key compression return 0; } + + +ulong my_scan_8bit(CHARSET_INFO *cs, const char *str, const char *end, int sq) +{ + const char *str0= str; + switch (sq) + { + case MY_SEQ_INTTAIL: + if (*str == '.') + { + for(str++ ; str != end && *str == '0' ; str++); + return str-str0; + } + return 0; + + case MY_SEQ_SPACES: + for (str++ ; str != end ; str++) + { + if (!my_isspace(cs,*str)) + break; + } + return str-str0; + default: + return 0; + } +} diff --git a/strings/ctype-sjis.c b/strings/ctype-sjis.c index 3949be5e215..b19bcf1a45a 100644 --- a/strings/ctype-sjis.c +++ b/strings/ctype-sjis.c @@ -4460,7 +4460,7 @@ my_mb_wc_sjis(CHARSET_INFO *cs __attribute__((unused)), CHARSET_INFO my_charset_sjis = { 13, /* number */ - MY_CS_COMPILED|MY_CS_PRIMARY, /* state */ + MY_CS_COMPILED|MY_CS_PRIMARY|MY_CS_STRNXFRM, /* state */ "sjis", /* cs name */ "sjis", /* name */ "", /* comment */ @@ -4492,13 +4492,14 @@ CHARSET_INFO my_charset_sjis = my_hash_sort_simple, 0, my_snprintf_8bit, - my_l10tostr_8bit, - my_ll10tostr_8bit, + my_long10_to_str_8bit, + my_longlong10_to_str_8bit, my_strntol_8bit, my_strntoul_8bit, my_strntoll_8bit, my_strntoull_8bit, - my_strntod_8bit + my_strntod_8bit, + my_scan_8bit }; #endif diff --git a/strings/ctype-tis620.c b/strings/ctype-tis620.c index 7168026eea5..ac959105736 100644 --- a/strings/ctype-tis620.c +++ b/strings/ctype-tis620.c @@ -688,7 +688,7 @@ void ThNormalize(uchar* ptr, uint field_length, const uchar* from, uint length) CHARSET_INFO my_charset_tis620 = { 18, /* number */ - MY_CS_COMPILED|MY_CS_PRIMARY, /* state */ + MY_CS_COMPILED|MY_CS_PRIMARY|MY_CS_STRNXFRM, /* state */ "tis620", /* cs name */ "tis620", /* name */ "", /* comment */ @@ -720,13 +720,14 @@ CHARSET_INFO my_charset_tis620 = my_hash_sort_simple, 0, my_snprintf_8bit, - my_l10tostr_8bit, - my_ll10tostr_8bit, + my_long10_to_str_8bit, + my_longlong10_to_str_8bit, my_strntol_8bit, my_strntoul_8bit, my_strntoll_8bit, my_strntoull_8bit, - my_strntod_8bit + my_strntod_8bit, + my_scan_8bit }; diff --git a/strings/ctype-ujis.c b/strings/ctype-ujis.c index 0a0024594e4..12e177a89be 100644 --- a/strings/ctype-ujis.c +++ b/strings/ctype-ujis.c @@ -8442,7 +8442,7 @@ CHARSET_INFO my_charset_ujis = NULL, /* tab_from_uni */ 0, /* strxfrm_multiply */ NULL, /* strnncoll */ - NULL, /* strnxfrm */ + my_strnxfrm_simple, /* strnxfrm */ my_like_range_simple,/* like_range */ my_wildcmp_mb, /* wildcmp */ 3, /* mbmaxlen */ @@ -8462,13 +8462,14 @@ CHARSET_INFO my_charset_ujis = my_hash_sort_simple, 0, my_snprintf_8bit, - my_l10tostr_8bit, - my_ll10tostr_8bit, + my_long10_to_str_8bit, + my_longlong10_to_str_8bit, my_strntol_8bit, my_strntoul_8bit, my_strntoll_8bit, my_strntoull_8bit, - my_strntod_8bit + my_strntod_8bit, + my_scan_8bit }; diff --git a/strings/ctype-utf8.c b/strings/ctype-utf8.c index 4f72a4c2334..a237b6f14a0 100644 --- a/strings/ctype-utf8.c +++ b/strings/ctype-utf8.c @@ -21,9 +21,12 @@ #include #include "m_string.h" #include "m_ctype.h" -#include "my_sys.h" /* defines errno */ #include +#ifndef EILSEQ +#define EILSEQ ENOENT +#endif + #ifdef HAVE_CHARSET_utf8 #define HAVE_UNIDATA #endif @@ -1958,7 +1961,7 @@ static int my_mbcharlen_utf8(CHARSET_INFO *cs __attribute__((unused)) , uint c) CHARSET_INFO my_charset_utf8 = { 33, /* number */ - MY_CS_COMPILED|MY_CS_PRIMARY, /* state */ + MY_CS_COMPILED|MY_CS_PRIMARY|MY_CS_STRNXFRM, /* state */ "utf8", /* cs name */ "utf8", /* name */ "", /* comment */ @@ -1990,13 +1993,14 @@ CHARSET_INFO my_charset_utf8 = my_hash_sort_utf8, /* hash_sort */ 0, my_snprintf_8bit, - my_l10tostr_8bit, - my_ll10tostr_8bit, + my_long10_to_str_8bit, + my_longlong10_to_str_8bit, my_strntol_8bit, my_strntoul_8bit, my_strntoll_8bit, my_strntoull_8bit, my_strntod_8bit, + my_scan_8bit }; @@ -2446,7 +2450,8 @@ static int my_snprintf_ucs2(CHARSET_INFO *cs __attribute__((unused)) long my_strntol_ucs2(CHARSET_INFO *cs, - const char *nptr, uint l, char **endptr, int base) + const char *nptr, uint l, int base, + char **endptr, int *err) { int negative=0; int overflow; @@ -2455,11 +2460,13 @@ long my_strntol_ucs2(CHARSET_INFO *cs, register unsigned int cutlim; register ulong cutoff; register ulong res; - register const char *s=nptr; - register const char *e=nptr+l; - const char *save; + register const uchar *s= (const uchar*) nptr; + register const uchar *e= (const uchar*) nptr+l; + const uchar *save; - do { + *err= 0; + do + { if ((cnv=cs->mb_wc(cs,&wc,s,e))>0) { switch (wc) @@ -2475,16 +2482,18 @@ long my_strntol_ucs2(CHARSET_INFO *cs, { if (endptr !=NULL ) *endptr = (char*)s; - my_errno = (cnv==MY_CS_ILSEQ) ? EILSEQ : EDOM; + err[0] = (cnv==MY_CS_ILSEQ) ? EILSEQ : EDOM; return 0; } s+=cnv; } while (1); bs: - + +#ifdef NOT_USED if (base <= 0 || base == 1 || base > 36) base = 10; +#endif overflow = 0; res = 0; @@ -2518,7 +2527,7 @@ bs: { if (endptr !=NULL ) *endptr = (char*)s; - my_errno=EILSEQ; + err[0]=EILSEQ; return 0; } else @@ -2533,7 +2542,7 @@ bs: if (s == save) { - my_errno=EDOM; + err[0]=EDOM; return 0L; } @@ -2547,7 +2556,7 @@ bs: if (overflow) { - my_errno=(ERANGE); + err[0]=ERANGE; return negative ? LONG_MIN : LONG_MAX; } @@ -2556,7 +2565,8 @@ bs: ulong my_strntoul_ucs2(CHARSET_INFO *cs, - const char *nptr, uint l, char **endptr, int base) + const char *nptr, uint l, int base, + char **endptr, int *err) { int negative=0; int overflow; @@ -2565,11 +2575,13 @@ ulong my_strntoul_ucs2(CHARSET_INFO *cs, register unsigned int cutlim; register ulong cutoff; register ulong res; - register const char *s=nptr; - register const char *e=nptr+l; - const char *save; + register const uchar *s= (const uchar*) nptr; + register const uchar *e= (const uchar*) nptr+l; + const uchar *save; - do { + *err= 0; + do + { if ((cnv=cs->mb_wc(cs,&wc,s,e))>0) { switch (wc) @@ -2585,24 +2597,27 @@ ulong my_strntoul_ucs2(CHARSET_INFO *cs, { if (endptr !=NULL ) *endptr = (char*)s; - my_errno = (cnv==MY_CS_ILSEQ) ? EILSEQ : EDOM; + err[0] = (cnv==MY_CS_ILSEQ) ? EILSEQ : EDOM; return 0; } s+=cnv; } while (1); bs: - + +#ifdef NOT_USED if (base <= 0 || base == 1 || base > 36) base = 10; - +#endif + overflow = 0; res = 0; save = s; cutoff = ((ulong)~0L) / (unsigned long int) base; cutlim = (uint) (((ulong)~0L) % (unsigned long int) base); - do { + do + { if ((cnv=cs->mb_wc(cs,&wc,s,e))>0) { s+=cnv; @@ -2628,7 +2643,7 @@ bs: { if (endptr !=NULL ) *endptr = (char*)s; - my_errno=EILSEQ; + err[0]=EILSEQ; return 0; } else @@ -2643,13 +2658,13 @@ bs: if (s == save) { - my_errno=EDOM; + err[0]=EDOM; return 0L; } if (overflow) { - my_errno=(ERANGE); + err[0]=(ERANGE); return ((ulong)~0L); } @@ -2660,7 +2675,8 @@ bs: longlong my_strntoll_ucs2(CHARSET_INFO *cs, - const char *nptr, uint l, char **endptr, int base) + const char *nptr, uint l, int base, + char **endptr, int *err) { int negative=0; int overflow; @@ -2669,11 +2685,13 @@ longlong my_strntoll_ucs2(CHARSET_INFO *cs, register ulonglong cutoff; register unsigned int cutlim; register ulonglong res; - register const char *s=nptr; - register const char *e=nptr+l; - const char *save; + register const uchar *s= (const uchar*) nptr; + register const uchar *e= (const uchar*) nptr+l; + const uchar *save; - do { + *err= 0; + do + { if ((cnv=cs->mb_wc(cs,&wc,s,e))>0) { switch (wc) @@ -2689,17 +2707,19 @@ longlong my_strntoll_ucs2(CHARSET_INFO *cs, { if (endptr !=NULL ) *endptr = (char*)s; - my_errno = (cnv==MY_CS_ILSEQ) ? EILSEQ : EDOM; + err[0] = (cnv==MY_CS_ILSEQ) ? EILSEQ : EDOM; return 0; } s+=cnv; } while (1); bs: - + +#ifdef NOT_USED if (base <= 0 || base == 1 || base > 36) base = 10; - +#endif + overflow = 0; res = 0; save = s; @@ -2732,7 +2752,7 @@ bs: { if (endptr !=NULL ) *endptr = (char*)s; - my_errno=EILSEQ; + err[0]=EILSEQ; return 0; } else @@ -2747,7 +2767,7 @@ bs: if (s == save) { - my_errno=EDOM; + err[0]=EDOM; return 0L; } @@ -2761,7 +2781,7 @@ bs: if (overflow) { - my_errno=(ERANGE); + err[0]=ERANGE; return negative ? LONGLONG_MIN : LONGLONG_MAX; } @@ -2772,7 +2792,8 @@ bs: ulonglong my_strntoull_ucs2(CHARSET_INFO *cs, - const char *nptr, uint l, char **endptr, int base) + const char *nptr, uint l, int base, + char **endptr, int *err) { int negative=0; int overflow; @@ -2781,11 +2802,13 @@ ulonglong my_strntoull_ucs2(CHARSET_INFO *cs, register ulonglong cutoff; register unsigned int cutlim; register ulonglong res; - register const char *s=nptr; - register const char *e=nptr+l; - const char *save; + register const uchar *s= (const uchar*) nptr; + register const uchar *e= (const uchar*) nptr+l; + const uchar *save; - do { + *err= 0; + do + { if ((cnv=cs->mb_wc(cs,&wc,s,e))>0) { switch (wc) @@ -2801,7 +2824,7 @@ ulonglong my_strntoull_ucs2(CHARSET_INFO *cs, { if (endptr !=NULL ) *endptr = (char*)s; - my_errno = (cnv==MY_CS_ILSEQ) ? EILSEQ : EDOM; + err[0]= (cnv==MY_CS_ILSEQ) ? EILSEQ : EDOM; return 0; } s+=cnv; @@ -2809,16 +2832,19 @@ ulonglong my_strntoull_ucs2(CHARSET_INFO *cs, bs: +#ifdef NOT_USED if (base <= 0 || base == 1 || base > 36) base = 10; - +#endif + overflow = 0; res = 0; save = s; cutoff = (~(ulonglong) 0) / (unsigned long int) base; cutlim = (uint) ((~(ulonglong) 0) % (unsigned long int) base); - do { + do + { if ((cnv=cs->mb_wc(cs,&wc,s,e))>0) { s+=cnv; @@ -2844,7 +2870,7 @@ bs: { if (endptr !=NULL ) *endptr = (char*)s; - my_errno=EILSEQ; + err[0]= EILSEQ; return 0; } else @@ -2859,13 +2885,13 @@ bs: if (s == save) { - my_errno=EDOM; + err[0]= EDOM; return 0L; } if (overflow) { - my_errno=(ERANGE); + err[0]= ERANGE; return (~(ulonglong) 0); } @@ -2874,20 +2900,22 @@ bs: double my_strntod_ucs2(CHARSET_INFO *cs __attribute__((unused)), - char *nptr, uint length, char **endptr) + char *nptr, uint length, + char **endptr, int *err) { char buf[256]; double res; register char *b=buf; - register const char *s=nptr; - register const char *end; + register const uchar *s= (const uchar*) nptr; + register const uchar *end; my_wc_t wc; int cnv; - + + *err= 0; /* Cut too long strings */ if (length >= sizeof(buf)) length= sizeof(buf)-1; - end=nptr+length; + end= s+length; while ((cnv=cs->mb_wc(cs,&wc,s,end)) > 0) { @@ -2898,7 +2926,9 @@ double my_strntod_ucs2(CHARSET_INFO *cs __attribute__((unused)), } *b= 0; + errno= 0; res=strtod(buf, endptr); + *err= errno; if (endptr) *endptr=(char*) (*endptr-buf+nptr); return res; @@ -2947,7 +2977,7 @@ int my_l10tostr_ucs2(CHARSET_INFO *cs, for ( db=dst, de=dst+len ; (dstwc_mb(cs,(my_wc_t)p[0],dst,de); + int cnvres=cs->wc_mb(cs,(my_wc_t)p[0],(uchar*) dst, (uchar*) de); if (cnvres>0) dst+=cnvres; else @@ -3006,7 +3036,7 @@ cnv: for ( db=dst, de=dst+len ; (dstwc_mb(cs,(my_wc_t)p[0],dst,de); + int cnvres=cs->wc_mb(cs, (my_wc_t) p[0], (uchar*) dst, (uchar*) de); if (cnvres>0) dst+=cnvres; else @@ -3019,7 +3049,7 @@ cnv: CHARSET_INFO my_charset_ucs2 = { 35, /* number */ - MY_CS_COMPILED|MY_CS_PRIMARY, /* state */ + MY_CS_COMPILED|MY_CS_PRIMARY|MY_CS_STRNXFRM, /* state */ "ucs2", /* cs name */ "ucs2", /* name */ "", /* comment */ @@ -3057,7 +3087,8 @@ CHARSET_INFO my_charset_ucs2 = my_strntoul_ucs2, my_strntoll_ucs2, my_strntoull_ucs2, - my_strntod_ucs2 + my_strntod_ucs2, + my_scan_8bit }; diff --git a/strings/ctype-win1250ch.c b/strings/ctype-win1250ch.c index 358cede442c..b6e78368dde 100644 --- a/strings/ctype-win1250ch.c +++ b/strings/ctype-win1250ch.c @@ -1,23 +1,33 @@ +/* Copyright (C) 2003 MySQL AB + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ + /* - File strings/ctype-win1250ch.c for MySQL. + Shared, independent copyright: (C) 2001 Jan Pazdziora. - Copyright: (C) 2001 Jan Pazdziora. + Development of this software was supported by Neocortex, s.r.o. + MySQL AB expresses its gratitude to Jan for for giving us this software. - This software is released under the terms of GNU General - Public License. + Bug reports and suggestions are always welcome. - Development of this software was supported by Neocortex, s.r.o. - - Bug reports and suggestions are always welcome. - - This file implements the collating sequence for Windows-1250 - character set. It merely extends the binary sorting of US-ASCII - by adding characters with diacritical marks into proper places. - In addition, it sorts 'ch' between 'h' and 'i', and the sorting - is case sensitive, with uppercase being sorted first, in the - second pass. - - Bug reports and suggestions are always welcome. + This file implements the collating sequence for Windows-1250 + character set. It merely extends the binary sorting of US-ASCII + by adding characters with diacritical marks into proper places. + In addition, it sorts 'ch' between 'h' and 'i', and the sorting + is case sensitive, with uppercase being sorted first, in the + second pass. */ /* @@ -388,16 +398,16 @@ static uchar NEAR _sort_order_win1250ch2[] = { }; struct wordvalue { - const char * word; + const uchar * word; uchar pass1; uchar pass2; }; static struct wordvalue doubles[] = { - { "ch", 0xad, 0x03 }, - { "c", 0xa6, 0x02 }, - { "Ch", 0xad, 0x02 }, - { "CH", 0xad, 0x01 }, - { "C", 0xa6, 0x01 }, + { (uchar*) "ch", 0xad, 0x03 }, + { (uchar*) "c", 0xa6, 0x02 }, + { (uchar*) "Ch", 0xad, 0x02 }, + { (uchar*) "CH", 0xad, 0x01 }, + { (uchar*) "C", 0xa6, 0x01 }, }; #define NEXT_CMP_VALUE(src, p, pass, value, len) \ @@ -412,7 +422,7 @@ static struct wordvalue doubles[] = { int i; \ for (i = 0; i < (int) sizeof(doubles); i++) { \ const uchar * patt = doubles[i].word; \ - const uchar * q = (const char *) p; \ + const uchar * q = (const uchar *) p; \ while (*patt \ && !(IS_END(q, src, len)) \ && (*patt == *q)) { \ @@ -622,7 +632,7 @@ static my_bool my_like_range_win1250ch(CHARSET_INFO *cs __attribute__((unused)), CHARSET_INFO my_charset_win1250ch = { 34, /* number */ - MY_CS_COMPILED, /* state */ + MY_CS_COMPILED|MY_CS_STRNXFRM, /* state */ "cp1250", /* cs name */ "cp1250_czech", /* name */ "", /* comment */ @@ -654,13 +664,14 @@ CHARSET_INFO my_charset_win1250ch = my_hash_sort_simple, 0, my_snprintf_8bit, - my_l10tostr_8bit, - my_ll10tostr_8bit, + my_long10_to_str_8bit, + my_longlong10_to_str_8bit, my_strntol_8bit, my_strntoul_8bit, my_strntoll_8bit, my_strntoull_8bit, - my_strntod_8bit + my_strntod_8bit, + my_scan_8bit }; diff --git a/strings/ctype.c b/strings/ctype.c index c2a7e928493..0873429f9ab 100644 --- a/strings/ctype.c +++ b/strings/ctype.c @@ -2823,7 +2823,7 @@ static CHARSET_INFO compiled_charsets[] = { idx_uni_8859_1, /* tab_from_uni */ 0, /* strxfrm_multiply */ my_strnncoll_simple,/* strnncoll */ - NULL, /* strnxfrm */ + my_strnxfrm_simple, /* strnxfrm */ my_like_range_simple,/* like_range */ my_wildcmp_8bit, /* wildcmp */ 1, /* mbmaxlen */ @@ -2843,13 +2843,14 @@ static CHARSET_INFO compiled_charsets[] = { my_hash_sort_simple, 0, my_snprintf_8bit, - my_l10tostr_8bit, - my_ll10tostr_8bit, + my_long10_to_str_8bit, + my_longlong10_to_str_8bit, my_strntol_8bit, my_strntoul_8bit, my_strntoll_8bit, my_strntoull_8bit, - my_strntod_8bit + my_strntod_8bit, + my_scan_8bit }, #endif @@ -2869,7 +2870,7 @@ static CHARSET_INFO compiled_charsets[] = { idx_uni_cp1251, /* tab_from_uni */ 0, /* strxfrm_multiply */ my_strnncoll_simple,/* strnncoll */ - NULL, /* strnxfrm */ + my_strnxfrm_simple, /* strnxfrm */ my_like_range_simple,/* like_range */ my_wildcmp_8bit, /* wildcmp */ 1, /* mbmaxlen */ @@ -2889,13 +2890,14 @@ static CHARSET_INFO compiled_charsets[] = { my_hash_sort_simple, 0, my_snprintf_8bit, - my_l10tostr_8bit, - my_ll10tostr_8bit, + my_long10_to_str_8bit, + my_longlong10_to_str_8bit, my_strntol_8bit, my_strntoul_8bit, my_strntoll_8bit, my_strntoull_8bit, - my_strntod_8bit + my_strntod_8bit, + my_scan_8bit }, #endif @@ -2914,7 +2916,7 @@ static CHARSET_INFO compiled_charsets[] = { idx_uni_cp1257, /* tab_from_uni */ 0, /* strxfrm_multiply */ my_strnncoll_simple,/* strnncoll */ - NULL, /* strnxfrm */ + my_strnxfrm_simple, /* strnxfrm */ my_like_range_simple,/* like_range */ my_wildcmp_8bit, /* wildcmp */ 1, /* mbmaxlen */ @@ -2934,13 +2936,14 @@ static CHARSET_INFO compiled_charsets[] = { my_hash_sort_simple, 0, my_snprintf_8bit, - my_l10tostr_8bit, - my_ll10tostr_8bit, + my_long10_to_str_8bit, + my_longlong10_to_str_8bit, my_strntol_8bit, my_strntoul_8bit, my_strntoll_8bit, my_strntoull_8bit, - my_strntod_8bit + my_strntod_8bit, + my_scan_8bit }, #endif @@ -2959,7 +2962,7 @@ static CHARSET_INFO compiled_charsets[] = { idx_uni_8859_2, /* tab_from_uni */ 0, /* strxfrm_multiply */ my_strnncoll_simple,/* strnncoll */ - NULL, /* strnxfrm */ + my_strnxfrm_simple, /* strnxfrm */ my_like_range_simple,/* like_range */ my_wildcmp_8bit, /* wildcmp */ 1, /* mbmaxlen */ @@ -2979,13 +2982,14 @@ static CHARSET_INFO compiled_charsets[] = { my_hash_sort_simple, 0, my_snprintf_8bit, - my_l10tostr_8bit, - my_ll10tostr_8bit, + my_long10_to_str_8bit, + my_longlong10_to_str_8bit, my_strntol_8bit, my_strntoul_8bit, my_strntoll_8bit, my_strntoull_8bit, - my_strntod_8bit + my_strntod_8bit, + my_scan_8bit }, #endif @@ -3005,7 +3009,7 @@ static CHARSET_INFO compiled_charsets[] = { idx_uni_8859_1, /* tab_from_uni */ 0, /* strxfrm_multiply */ my_strnncoll_simple,/* strnncoll */ - NULL, /* strnxfrm */ + my_strnxfrm_simple, /* strnxfrm */ my_like_range_simple,/* like_range */ my_wildcmp_8bit, /* wildcmp */ 1, /* mbmaxlen */ @@ -3025,13 +3029,14 @@ static CHARSET_INFO compiled_charsets[] = { my_hash_sort_simple, 0, my_snprintf_8bit, - my_l10tostr_8bit, - my_ll10tostr_8bit, + my_long10_to_str_8bit, + my_longlong10_to_str_8bit, my_strntol_8bit, my_strntoul_8bit, my_strntoll_8bit, my_strntoull_8bit, - my_strntod_8bit + my_strntod_8bit, + my_scan_8bit }, #endif @@ -3050,7 +3055,7 @@ static CHARSET_INFO compiled_charsets[] = { NULL, /* tab_from_uni */ 0, /* strxfrm_multiply */ my_strnncoll_simple,/* strnncoll */ - NULL, /* strnxfrm */ + my_strnxfrm_simple, /* strnxfrm */ my_like_range_simple,/* like_range */ my_wildcmp_8bit, /* wildcmp */ 1, /* mbmaxlen */ @@ -3070,13 +3075,14 @@ static CHARSET_INFO compiled_charsets[] = { my_hash_sort_simple, 0, my_snprintf_8bit, - my_l10tostr_8bit, - my_ll10tostr_8bit, + my_long10_to_str_8bit, + my_longlong10_to_str_8bit, my_strntol_8bit, my_strntoul_8bit, my_strntoll_8bit, my_strntoull_8bit, - my_strntod_8bit + my_strntod_8bit, + my_scan_8bit }, #endif @@ -3095,7 +3101,7 @@ static CHARSET_INFO compiled_charsets[] = { NULL, /* tab_from_uni */ 0, /* strxfrm_multiply */ my_strnncoll_simple,/* strnncoll */ - NULL, /* strnxfrm */ + my_strnxfrm_simple, /* strnxfrm */ my_like_range_simple,/* like_range */ my_wildcmp_8bit, /* wildcmp */ 1, /* mbmaxlen */ @@ -3115,13 +3121,14 @@ static CHARSET_INFO compiled_charsets[] = { my_hash_sort_simple, 0, my_snprintf_8bit, - my_l10tostr_8bit, - my_ll10tostr_8bit, + my_long10_to_str_8bit, + my_longlong10_to_str_8bit, my_strntol_8bit, my_strntoul_8bit, my_strntoll_8bit, my_strntoull_8bit, - my_strntod_8bit + my_strntod_8bit, + my_scan_8bit }, #endif @@ -3140,7 +3147,7 @@ static CHARSET_INFO compiled_charsets[] = { NULL, /* tab_from_uni */ 0, /* strxfrm_multiply */ my_strnncoll_simple,/* strnncoll */ - NULL, /* strnxfrm */ + my_strnxfrm_simple, /* strnxfrm */ my_like_range_simple,/* like_range */ my_wildcmp_8bit, /* wildcmp */ 1, /* mbmaxlen */ @@ -3160,13 +3167,14 @@ static CHARSET_INFO compiled_charsets[] = { my_hash_sort_simple, 0, my_snprintf_8bit, - my_l10tostr_8bit, - my_ll10tostr_8bit, + my_long10_to_str_8bit, + my_longlong10_to_str_8bit, my_strntol_8bit, my_strntoul_8bit, my_strntoll_8bit, my_strntoull_8bit, - my_strntod_8bit + my_strntod_8bit, + my_scan_8bit }, #endif @@ -3186,7 +3194,7 @@ static CHARSET_INFO compiled_charsets[] = { idx_uni_8859_1, /* tab_from_uni */ 0, /* strxfrm_multiply */ my_strnncoll_simple,/* strnncoll */ - NULL, /* strnxfrm */ + my_strnxfrm_simple, /* strnxfrm */ my_like_range_simple,/* like_range */ my_wildcmp_8bit, /* wildcmp */ 1, /* mbmaxlen */ @@ -3206,13 +3214,14 @@ static CHARSET_INFO compiled_charsets[] = { my_hash_sort_simple, 0, my_snprintf_8bit, - my_l10tostr_8bit, - my_ll10tostr_8bit, + my_long10_to_str_8bit, + my_longlong10_to_str_8bit, my_strntol_8bit, my_strntoul_8bit, my_strntoll_8bit, my_strntoull_8bit, - my_strntod_8bit + my_strntod_8bit, + my_scan_8bit }, #endif @@ -3231,7 +3240,7 @@ static CHARSET_INFO compiled_charsets[] = { NULL, /* tab_from_uni */ 0, /* strxfrm_multiply */ my_strnncoll_simple,/* strnncoll */ - NULL, /* strnxfrm */ + my_strnxfrm_simple, /* strnxfrm */ my_like_range_simple,/* like_range */ my_wildcmp_8bit, /* wildcmp */ 1, /* mbmaxlen */ @@ -3251,13 +3260,14 @@ static CHARSET_INFO compiled_charsets[] = { my_hash_sort_simple, 0, my_snprintf_8bit, - my_l10tostr_8bit, - my_ll10tostr_8bit, + my_long10_to_str_8bit, + my_longlong10_to_str_8bit, my_strntol_8bit, my_strntoul_8bit, my_strntoll_8bit, my_strntoull_8bit, - my_strntod_8bit + my_strntod_8bit, + my_scan_8bit }, #endif @@ -3276,7 +3286,7 @@ static CHARSET_INFO compiled_charsets[] = { NULL, /* tab_from_uni */ 0, /* strxfrm_multiply */ my_strnncoll_simple,/* strnncoll */ - NULL, /* strnxfrm */ + my_strnxfrm_simple, /* strnxfrm */ my_like_range_simple,/* like_range */ my_wildcmp_8bit, /* wildcmp */ 1, /* mbmaxlen */ @@ -3296,13 +3306,14 @@ static CHARSET_INFO compiled_charsets[] = { my_hash_sort_simple, 0, my_snprintf_8bit, - my_l10tostr_8bit, - my_ll10tostr_8bit, + my_long10_to_str_8bit, + my_longlong10_to_str_8bit, my_strntol_8bit, my_strntoul_8bit, my_strntoll_8bit, my_strntoull_8bit, - my_strntod_8bit + my_strntod_8bit, + my_scan_8bit }, #endif @@ -3321,7 +3332,7 @@ static CHARSET_INFO compiled_charsets[] = { NULL, /* tab_from_uni */ 0, /* strxfrm_multiply */ my_strnncoll_simple,/* strnncoll */ - NULL, /* strnxfrm */ + my_strnxfrm_simple, /* strnxfrm */ my_like_range_simple,/* like_range */ my_wildcmp_8bit, /* wildcmp */ 1, /* mbmaxlen */ @@ -3341,13 +3352,14 @@ static CHARSET_INFO compiled_charsets[] = { my_hash_sort_simple, 0, my_snprintf_8bit, - my_l10tostr_8bit, - my_ll10tostr_8bit, + my_long10_to_str_8bit, + my_longlong10_to_str_8bit, my_strntol_8bit, my_strntoul_8bit, my_strntoll_8bit, my_strntoull_8bit, - my_strntod_8bit + my_strntod_8bit, + my_scan_8bit }, #endif @@ -3366,7 +3378,7 @@ static CHARSET_INFO compiled_charsets[] = { idx_uni_8859_2, /* tab_from_uni */ 0, /* strxfrm_multiply */ my_strnncoll_simple,/* strnncoll */ - NULL, /* strnxfrm */ + my_strnxfrm_simple, /* strnxfrm */ my_like_range_simple,/* like_range */ my_wildcmp_8bit, /* wildcmp */ 1, /* mbmaxlen */ @@ -3386,13 +3398,14 @@ static CHARSET_INFO compiled_charsets[] = { my_hash_sort_simple, 0, my_snprintf_8bit, - my_l10tostr_8bit, - my_ll10tostr_8bit, + my_long10_to_str_8bit, + my_longlong10_to_str_8bit, my_strntol_8bit, my_strntoul_8bit, my_strntoll_8bit, my_strntoull_8bit, - my_strntod_8bit + my_strntod_8bit, + my_scan_8bit }, #endif @@ -3411,7 +3424,7 @@ static CHARSET_INFO compiled_charsets[] = { idx_uni_koi8_r, /* tab_from_uni */ 0, /* strxfrm_multiply */ my_strnncoll_simple,/* strnncoll */ - NULL, /* strnxfrm */ + my_strnxfrm_simple, /* strnxfrm */ my_like_range_simple,/* like_range */ my_wildcmp_8bit, /* wildcmp */ 1, /* mbmaxlen */ @@ -3431,13 +3444,14 @@ static CHARSET_INFO compiled_charsets[] = { my_hash_sort_simple, 0, my_snprintf_8bit, - my_l10tostr_8bit, - my_ll10tostr_8bit, + my_long10_to_str_8bit, + my_longlong10_to_str_8bit, my_strntol_8bit, my_strntoul_8bit, my_strntoll_8bit, my_strntoull_8bit, - my_strntod_8bit + my_strntod_8bit, + my_scan_8bit }, #endif @@ -3456,7 +3470,7 @@ static CHARSET_INFO compiled_charsets[] = { idx_uni_koi8_u, /* tab_from_uni */ 0, /* strxfrm_multiply */ my_strnncoll_simple,/* strnncoll */ - NULL, /* strnxfrm */ + my_strnxfrm_simple, /* strnxfrm */ my_like_range_simple,/* like_range */ my_wildcmp_8bit, /* wildcmp */ 1, /* mbmaxlen */ @@ -3476,13 +3490,14 @@ static CHARSET_INFO compiled_charsets[] = { my_hash_sort_simple, 0, my_snprintf_8bit, - my_l10tostr_8bit, - my_ll10tostr_8bit, + my_long10_to_str_8bit, + my_longlong10_to_str_8bit, my_strntol_8bit, my_strntoul_8bit, my_strntoll_8bit, my_strntoull_8bit, - my_strntod_8bit + my_strntod_8bit, + my_scan_8bit }, #endif @@ -3502,7 +3517,7 @@ static CHARSET_INFO compiled_charsets[] = { idx_uni_8859_2, /* tab_from_uni */ 0, /* strxfrm_multiply */ my_strnncoll_simple,/* strnncoll */ - NULL, /* strnxfrm */ + my_strnxfrm_simple, /* strnxfrm */ my_like_range_simple,/* like_range */ my_wildcmp_8bit, /* wildcmp */ 1, /* mbmaxlen */ @@ -3522,13 +3537,14 @@ static CHARSET_INFO compiled_charsets[] = { my_hash_sort_simple, 0, my_snprintf_8bit, - my_l10tostr_8bit, - my_ll10tostr_8bit, + my_long10_to_str_8bit, + my_longlong10_to_str_8bit, my_strntol_8bit, my_strntoul_8bit, my_strntoll_8bit, my_strntoull_8bit, - my_strntod_8bit + my_strntod_8bit, + my_scan_8bit }, #endif @@ -3547,7 +3563,7 @@ static CHARSET_INFO compiled_charsets[] = { idx_uni_8859_9, /* tab_from_uni */ 0, /* strxfrm_multiply */ my_strnncoll_simple,/* strnncoll */ - NULL, /* strnxfrm */ + my_strnxfrm_simple, /* strnxfrm */ my_like_range_simple,/* like_range */ my_wildcmp_8bit, /* wildcmp */ 1, /* mbmaxlen */ @@ -3567,13 +3583,14 @@ static CHARSET_INFO compiled_charsets[] = { my_hash_sort_simple, 0, my_snprintf_8bit, - my_l10tostr_8bit, - my_ll10tostr_8bit, + my_long10_to_str_8bit, + my_longlong10_to_str_8bit, my_strntol_8bit, my_strntoul_8bit, my_strntoll_8bit, my_strntoull_8bit, - my_strntod_8bit + my_strntod_8bit, + my_scan_8bit }, #endif @@ -3593,7 +3610,7 @@ static CHARSET_INFO compiled_charsets[] = { NULL, /* tab_from_uni */ 0, /* strxfrm_multiply */ my_strnncoll_simple,/* strnncoll */ - NULL, /* strnxfrm */ + my_strnxfrm_simple, /* strnxfrm */ my_like_range_simple,/* like_range */ my_wildcmp_8bit, /* wildcmp */ 1, /* mbmaxlen */ @@ -3613,13 +3630,14 @@ static CHARSET_INFO compiled_charsets[] = { my_hash_sort_simple, 0, my_snprintf_8bit, - my_l10tostr_8bit, - my_ll10tostr_8bit, + my_long10_to_str_8bit, + my_longlong10_to_str_8bit, my_strntol_8bit, my_strntoul_8bit, my_strntoll_8bit, my_strntoull_8bit, - my_strntod_8bit + my_strntod_8bit, + my_scan_8bit }, #endif @@ -3639,7 +3657,7 @@ static CHARSET_INFO compiled_charsets[] = { idx_uni_us_ascii, /* tab_from_uni */ 0, /* strxfrm_multiply */ my_strnncoll_simple,/* strnncoll */ - NULL, /* strnxfrm */ + my_strnxfrm_simple, /* strnxfrm */ my_like_range_simple,/* like_range */ my_wildcmp_8bit, /* wildcmp */ 1, /* mbmaxlen */ @@ -3659,13 +3677,14 @@ static CHARSET_INFO compiled_charsets[] = { my_hash_sort_simple, 0, my_snprintf_8bit, - my_l10tostr_8bit, - my_ll10tostr_8bit, + my_long10_to_str_8bit, + my_longlong10_to_str_8bit, my_strntol_8bit, my_strntoul_8bit, my_strntoll_8bit, my_strntoull_8bit, - my_strntod_8bit + my_strntod_8bit, + my_scan_8bit }, #endif @@ -3684,7 +3703,7 @@ static CHARSET_INFO compiled_charsets[] = { idx_uni_cp1250, /* tab_from_uni */ 0, /* strxfrm_multiply */ my_strnncoll_simple,/* strnncoll */ - NULL, /* strnxfrm */ + my_strnxfrm_simple, /* strnxfrm */ my_like_range_simple,/* like_range */ my_wildcmp_8bit, /* wildcmp */ 1, /* mbmaxlen */ @@ -3704,13 +3723,14 @@ static CHARSET_INFO compiled_charsets[] = { my_hash_sort_simple, 0, my_snprintf_8bit, - my_l10tostr_8bit, - my_ll10tostr_8bit, + my_long10_to_str_8bit, + my_longlong10_to_str_8bit, my_strntol_8bit, my_strntoul_8bit, my_strntoll_8bit, my_strntoull_8bit, - my_strntod_8bit + my_strntod_8bit, + my_scan_8bit }, #endif @@ -3729,7 +3749,7 @@ static CHARSET_INFO compiled_charsets[] = { idx_uni_cp1251, /* tab_from_uni */ 0, /* strxfrm_multiply */ my_strnncoll_simple,/* strnncoll */ - NULL, /* strnxfrm */ + my_strnxfrm_simple, /* strnxfrm */ my_like_range_simple,/* like_range */ my_wildcmp_8bit, /* wildcmp */ 1, /* mbmaxlen */ @@ -3749,13 +3769,14 @@ static CHARSET_INFO compiled_charsets[] = { my_hash_sort_simple, 0, my_snprintf_8bit, - my_l10tostr_8bit, - my_ll10tostr_8bit, + my_long10_to_str_8bit, + my_longlong10_to_str_8bit, my_strntol_8bit, my_strntoul_8bit, my_strntoll_8bit, my_strntoull_8bit, - my_strntod_8bit + my_strntod_8bit, + my_scan_8bit }, #endif @@ -3774,7 +3795,7 @@ static CHARSET_INFO compiled_charsets[] = { idx_uni_armscii_8, /* tab_from_uni */ 0, /* strxfrm_multiply */ my_strnncoll_simple,/* strnncoll */ - NULL, /* strnxfrm */ + my_strnxfrm_simple, /* strnxfrm */ my_like_range_simple,/* like_range */ my_wildcmp_8bit, /* wildcmp */ 1, /* mbmaxlen */ @@ -3794,13 +3815,14 @@ static CHARSET_INFO compiled_charsets[] = { my_hash_sort_simple, 0, my_snprintf_8bit, - my_l10tostr_8bit, - my_ll10tostr_8bit, + my_long10_to_str_8bit, + my_longlong10_to_str_8bit, my_strntol_8bit, my_strntoul_8bit, my_strntoll_8bit, my_strntoull_8bit, - my_strntod_8bit + my_strntod_8bit, + my_scan_8bit }, #endif @@ -3819,7 +3841,7 @@ static CHARSET_INFO compiled_charsets[] = { idx_uni_cp1251, /* tab_from_uni */ 0, /* strxfrm_multiply */ my_strnncoll_simple,/* strnncoll */ - NULL, /* strnxfrm */ + my_strnxfrm_simple, /* strnxfrm */ my_like_range_simple,/* like_range */ my_wildcmp_8bit, /* wildcmp */ 1, /* mbmaxlen */ @@ -3839,13 +3861,14 @@ static CHARSET_INFO compiled_charsets[] = { my_hash_sort_simple, 0, my_snprintf_8bit, - my_l10tostr_8bit, - my_ll10tostr_8bit, + my_long10_to_str_8bit, + my_longlong10_to_str_8bit, my_strntol_8bit, my_strntoul_8bit, my_strntoll_8bit, my_strntoull_8bit, - my_strntod_8bit + my_strntod_8bit, + my_scan_8bit }, #endif @@ -3891,6 +3914,7 @@ static CHARSET_INFO compiled_charsets[] = { NULL, NULL, NULL, + NULL, NULL } }; @@ -3983,6 +4007,7 @@ typedef struct my_cs_file_info static int fill_uchar(uchar *a,uint size,const char *str, uint len) { + int err=0; uint i= 0; const char *s, *b, *e=str+len; @@ -3993,7 +4018,7 @@ static int fill_uchar(uchar *a,uint size,const char *str, uint len) for ( ; (s < e) && !strchr(" \t\r\n",s[0]); s++) ; if (s == b || i > size) break; - a[i]= my_strntoul(my_charset_latin1,b,s-b,NULL,16); + a[i]= my_strntoul(my_charset_latin1,b,s-b,16,NULL,&err); } return 0; } @@ -4001,6 +4026,8 @@ static int fill_uchar(uchar *a,uint size,const char *str, uint len) static int fill_uint16(uint16 *a,uint size,const char *str, uint len) { uint i= 0; + int err; + const char *s, *b, *e=str+len; for (s=str ; s < e ; i++) { @@ -4009,7 +4036,7 @@ static int fill_uint16(uint16 *a,uint size,const char *str, uint len) for ( ; (s < e) && !strchr(" \t\r\n",s[0]); s++) ; if (s == b || i > size) break; - a[i]= my_strntol(my_charset_latin1,b,s-b,NULL,16); + a[i]= my_strntol(my_charset_latin1,b,s-b,16,NULL,&err); } return 0; } @@ -4051,6 +4078,7 @@ static int cs_value(MY_XML_PARSER *st,const char *attr, uint len) struct my_cs_file_info *i= (struct my_cs_file_info *)st->user_data; struct my_cs_file_section_st *s; int state= (s=cs_file_sec(st->attr,strlen(st->attr))) ? s->state : 0; + int err; #ifndef DBUG_OFF if(0){ @@ -4062,7 +4090,7 @@ static int cs_value(MY_XML_PARSER *st,const char *attr, uint len) switch (state) { case _CS_ID: - i->cs.number= my_strntoul(my_charset_latin1,attr,len,(char**)NULL,0); + i->cs.number= my_strntoul(my_charset_latin1,attr,len,10,(char**)NULL,&err); break; case _CS_COLNAME: i->cs.name=mstr(i->name,attr,len,MY_CS_NAME_SIZE-1); diff --git a/tests/client_test.c b/tests/client_test.c index 6f7473cf4b5..35bd5f8639a 100644 --- a/tests/client_test.c +++ b/tests/client_test.c @@ -160,10 +160,11 @@ myassert(stmt == 0);\ *********************************************************/ static void client_connect() { + int rc; char buff[255]; myheader("client_connect"); - if(!(mysql = mysql_init(NULL))) + if (!(mysql = mysql_init(NULL))) { myerror("mysql_init() failed"); exit(0); @@ -180,21 +181,27 @@ static void client_connect() /* set AUTOCOMMIT to ON*/ mysql_autocommit(mysql, true); sprintf(buff,"CREATE DATABASE IF NOT EXISTS %s", current_db); - mysql_query(mysql, buff); + rc = mysql_query(mysql, buff); + myquery(rc); sprintf(buff,"USE %s", current_db); - mysql_query(mysql, buff); + rc = mysql_query(mysql, buff); + myquery(rc); } /******************************************************** * close the connection * *********************************************************/ static void client_disconnect() -{ +{ + myheader("client_disconnect"); + if (mysql) { char buff[255]; + fprintf(stdout, "\n droping the test database '%s'", current_db); sprintf(buff,"DROP DATABASE IF EXISTS %s", current_db); mysql_query(mysql, buff); + fprintf(stdout, "\n closing the connection\n"); mysql_close(mysql); } } @@ -378,6 +385,9 @@ uint my_process_stmt_result(MYSQL_STMT *stmt) rc= mysql_bind_result(stmt,buffer); mystmt(stmt,rc); + rc= mysql_stmt_store_result(stmt); + mystmt(stmt,rc); + mysql_field_seek(result, 0); while (mysql_fetch(stmt) == 0) { @@ -417,6 +427,7 @@ uint my_stmt_result(const char *query, unsigned long length) uint row_count; int rc; + fprintf(stdout,"\n\n %s \n", query); stmt= mysql_prepare(mysql,query,length); mystmt_init(stmt); @@ -550,6 +561,41 @@ static void client_use_result() mysql_free_result(result); } +/* + Separate thread query to test some cases +*/ +static my_bool thread_query(char *query) +{ + MYSQL *l_mysql; + my_bool error; + + error= 0; + fprintf(stdout,"\n in thread_query(%s)", query); + if(!(l_mysql = mysql_init(NULL))) + { + myerror("mysql_init() failed"); + return 1; + } + if (!(mysql_real_connect(l_mysql,opt_host,opt_user, + opt_password, current_db, opt_port, + opt_unix_socket, 0))) + { + myerror("connection failed"); + error= 1; + goto end; + } + if (mysql_query(l_mysql,(char *)query)) + { + fprintf(stderr,"Query failed (%s)\n",mysql_error(l_mysql)); + error= 1; + goto end; + } + mysql_commit(l_mysql); +end: + mysql_close(l_mysql); + return error; +} + /******************************************************** * query processing * @@ -919,15 +965,15 @@ static void test_prepare() MYSQL_STMT *stmt; int rc; char query[200]; - int int_data; - char str_data[50]; - char tiny_data; - short small_data; - longlong big_data; - double real_data; - double double_data; - MYSQL_RES *result; - MYSQL_BIND bind[8]; + int int_data, o_int_data; + char str_data[50], data[50]; + char tiny_data, o_tiny_data; + short small_data, o_small_data; + longlong big_data, o_big_data; + float real_data, o_real_data; + double double_data, o_double_data; + long length[7], len; + MYSQL_BIND bind[7]; myheader("test_prepare"); @@ -956,11 +1002,11 @@ static void test_prepare() /* tinyint */ bind[0].buffer_type=FIELD_TYPE_TINY; - bind[0].buffer=(gptr)&tiny_data; + bind[0].buffer= (gptr)&tiny_data; /* string */ bind[1].buffer_type=FIELD_TYPE_STRING; - bind[1].buffer=str_data; - bind[1].buffer_length=sizeof(str_data); + bind[1].buffer= (gptr)str_data; + bind[1].length= &bind[1].buffer_length; /* integer */ bind[2].buffer_type=FIELD_TYPE_LONG; bind[2].buffer= (gptr)&int_data; @@ -971,7 +1017,7 @@ static void test_prepare() bind[4].buffer_type=FIELD_TYPE_LONGLONG; bind[4].buffer= (gptr)&big_data; /* float */ - bind[5].buffer_type=FIELD_TYPE_DOUBLE; + bind[5].buffer_type=FIELD_TYPE_FLOAT; bind[5].buffer= (gptr)&real_data; /* double */ bind[6].buffer_type=FIELD_TYPE_DOUBLE; @@ -1006,15 +1052,77 @@ static void test_prepare() myquery(rc); /* test the results now, only one row should exists */ - rc = mysql_query(mysql,"SELECT * FROM my_prepare"); - myquery(rc); + myassert(tiny_data == (int) my_stmt_result("SELECT * FROM my_prepare",50)); + + stmt = mysql_prepare(mysql,"SELECT * FROM my_prepare",50); + mystmt_init(stmt); + + for (int_data= 0; int_data < 7; int_data++) + bind[int_data].length= &length[int_data]; + + rc = mysql_bind_result(stmt, bind); + mystmt(stmt, rc); /* get the result */ - result = mysql_store_result(mysql); - mytest(result); + rc = mysql_execute(stmt); + mystmt(stmt, rc); + + o_int_data = 320; + o_small_data = 1867; + o_big_data = 1000; + o_real_data = 2; + o_double_data = 6578.001; - myassert((int)tiny_data == my_process_result_set(result)); - mysql_free_result(result); + /* now, execute the prepared statement to insert 10 records.. */ + for (o_tiny_data=0; o_tiny_data < 100; o_tiny_data++) + { + len = sprintf(data,"MySQL%d",o_int_data); + + rc = mysql_fetch(stmt); + mystmt(stmt, rc); + + fprintf(stdout, "\n tiny : %d (%ld)", tiny_data,length[0]); + fprintf(stdout, "\n short : %d (%ld)", small_data,length[3]); + fprintf(stdout, "\n int : %d (%ld)", int_data,length[2]); + fprintf(stdout, "\n big : %lld (%ld)", big_data,length[4]); + + fprintf(stdout, "\n float : %f (%ld)", real_data,length[5]); + fprintf(stdout, "\n double : %f (%ld)", double_data,length[6]); + + fprintf(stdout, "\n str : %s (%ld)", str_data, length[1]); + + myassert(tiny_data == o_tiny_data); + myassert(length[0] == 1); + + myassert(int_data == o_int_data); + myassert(length[2] == 4); + + myassert(small_data == o_small_data); + myassert(length[3] == 2); + + myassert(big_data == o_big_data); + myassert(length[4] == 8); + + myassert(real_data == o_real_data); + myassert(length[5] == 4); + + myassert(double_data == o_double_data); + myassert(length[6] == 8); + + myassert(strcmp(data,str_data) == 0); + myassert(length[1] == len); + + o_int_data += 25; + o_small_data += 10; + o_big_data += 100; + o_real_data += 1; + o_double_data += 10.09; + } + + rc = mysql_fetch(stmt); + myassert(rc == MYSQL_NO_DATA); + + mysql_stmt_close(stmt); } @@ -1403,6 +1511,8 @@ static void test_select_prepare() mystmt(stmt,rc); my_process_stmt_result(stmt); + + mysql_stmt_close(stmt); } /******************************************************** @@ -1481,7 +1591,6 @@ static void test_select_show() { MYSQL_STMT *stmt; int rc; - MYSQL_RES *result; myheader("test_select_show"); @@ -1496,12 +1605,7 @@ static void test_select_show() rc = mysql_execute(stmt); mystmt(stmt, rc); - /* get the result */ - result = mysql_store_result(mysql); - mytest(result); - - my_process_result_set(result); - mysql_free_result(result); + my_process_stmt_result(stmt); mysql_stmt_close(stmt); } @@ -4359,7 +4463,7 @@ static void test_select_meta() field= mysql_fetch_field(result); mytest_r(field); - +n mysql_free_result(result); mysql_stmt_close(stmt); } @@ -4447,6 +4551,88 @@ static void test_func_fields() /* Multiple stmts .. */ static void test_multi_stmt() { +#if TO_BE_FIXED_IN_SERVER + MYSQL_STMT *stmt, *stmt1; + int rc, id; + long length; + char name[50]={0}; + MYSQL_BIND bind[2]; + + myheader("test_multi_stmt"); + + rc = mysql_query(mysql,"DROP TABLE IF EXISTS test_multi_table"); + myquery(rc); + + rc = mysql_query(mysql,"CREATE TABLE test_multi_table(id int, name char(20))"); + myquery(rc); + + rc = mysql_query(mysql,"INSERT INTO test_multi_table values(10,'mysql')"); + myquery(rc); + + stmt = mysql_prepare(mysql, "SELECT * FROM test_multi_table WHERE id = ?", 100); + mystmt_init(stmt); + + verify_param_count(stmt,1); + + init_bind(bind); + + bind[0].buffer_type = MYSQL_TYPE_SHORT; + bind[0].buffer = (void *)&id; + + bind[1].buffer_type = MYSQL_TYPE_STRING; + bind[1].buffer = (void *)&name; + bind[1].length = &length;; + + rc = mysql_bind_param(stmt, bind); + mystmt(stmt, rc); + + rc = mysql_bind_result(stmt, bind); + mystmt(stmt, rc); + + id = 10; + rc = mysql_execute(stmt); + mystmt(stmt, rc); + + id = 999; + rc = mysql_fetch(stmt); + mystmt(stmt, rc); + + fprintf(stdout, "\n int_data: %d", id); + fprintf(stdout, "\n str_data: %s(%d)", name, length); + myassert(id == 10); + myassert(strcmp(name,"mysql")==0); + + rc = mysql_fetch(stmt); + myassert(rc == MYSQL_NO_DATA); + + /* alter the table schema now */ + stmt1 = mysql_prepare(mysql,"DELETE FROM test_multi_table WHERE id = ? AND name=?",100); + mystmt_init(stmt1); + + verify_param_count(stmt1,2); + + rc = mysql_bind_param(stmt1, bind); + mystmt(stmt1, rc); + + rc = mysql_execute(stmt1); + mystmt(stmt1, rc); + + rc = (int)mysql_stmt_affected_rows(stmt1); + fprintf(stdout,"\n total rows affected(delete): %d", rc); + myassert(rc == 1); + + mysql_stmt_close(stmt1); + + rc = mysql_execute(stmt); + mystmt(stmt, rc); + + rc = mysql_fetch(stmt); + myassert(rc == MYSQL_NO_DATA); + + myassert(0 == my_stmt_result("SELECT * FROM test_multi_table",50)); + + mysql_stmt_close(stmt); +#endif } /******************************************************** @@ -4598,6 +4784,476 @@ static void test_manual_sample() fprintf(stdout, "Success !!!"); } +/******************************************************** +* to test alter table scenario in the middle of prepare * +*********************************************************/ +static void test_prepare_alter() +{ + MYSQL_STMT *stmt; + int rc, id; + long length; + MYSQL_BIND bind[1]; + + myheader("test_prepare_alter"); + + rc = mysql_query(mysql,"DROP TABLE IF EXISTS test_prep_alter"); + myquery(rc); + + rc = mysql_query(mysql,"CREATE TABLE test_prep_alter(id int, name char(20))"); + myquery(rc); + + rc = mysql_query(mysql,"INSERT INTO test_prep_alter values(10,'venu'),(20,'mysql')"); + myquery(rc); + + stmt = mysql_prepare(mysql, "INSERT INTO test_prep_alter VALUES(?,'monty')", 100); + mystmt_init(stmt); + + verify_param_count(stmt,1); + + init_bind(bind); + + bind[0].buffer_type= MYSQL_TYPE_SHORT; + bind[0].length= (long *)&length; + bind[0].buffer= (void *)&id; + + rc = mysql_bind_param(stmt, bind); + mystmt(stmt, rc); + + id = 30; length= 0; + rc = mysql_execute(stmt); + mystmt(stmt, rc); + + if (thread_query((char *)"ALTER TABLE test_prep_alter change id id_new varchar(20)")) + exit(0); + + length = MYSQL_NULL_DATA; + rc = mysql_execute(stmt); + mystmt(stmt, rc); + + myassert(4 == my_stmt_result("SELECT * FROM test_prep_alter",50)); + + mysql_stmt_close(stmt); +} + +/******************************************************** +* to test the support of multi-query executions * +*********************************************************/ +static void test_multi_query() +{ + MYSQL *l_mysql, *org_mysql; + MYSQL_RES *result; + int rc; + + const char *query= "DROP TABLE IF EXISTS test_multi_tab;\ + CREATE TABLE test_multi_tab(id int,name char(20));\ + INSERT INTO test_multi_tab(id) VALUES(10),(20);\ + INSERT INTO test_multi_tab VALUES(20,'insert;comma');\ + SELECT * FROM test_multi_tab;\ + UPDATE test_multi_tab SET unknown_col=100 WHERE id=100;\ + UPDATE test_multi_tab SET name='new;name' WHERE id=20;\ + DELETE FROM test_multi_tab WHERE name='new;name';\ + SELECT * FROM test_multi_tab;\ + DELETE FROM test_multi_tab WHERE id=10;\ + SELECT * FROM test_multi_tab"; + uint count, rows[10]={0,2,1,3,0,2,2,1,1,0}; + + myheader("test_multi_query"); + + rc = mysql_query(mysql, query); /* syntax error */ + myquery_r(rc); + + if(!(l_mysql = mysql_init(NULL))) + { + fprintf(stdout,"\n mysql_init() failed"); + exit(1); + } + if (!(mysql_real_connect(l_mysql,opt_host,opt_user, + opt_password, current_db, opt_port, + opt_unix_socket, CLIENT_MULTI_QUERIES))) /* enable multi queries */ + { + fprintf(stdout,"\n connection failed(%s)", mysql_error(l_mysql)); + exit(1); + } + org_mysql= mysql; + mysql= l_mysql; + + rc = mysql_query(mysql, query); + myquery(rc); + + count= 0; + while(mysql_more_results(mysql) && count < sizeof(rows)/sizeof(uint)) + { + fprintf(stdout,"\n query %d", count); + if ((rc= mysql_next_result(mysql))) + fprintf(stdout, "\n\t failed(%s)", mysql_error(mysql)); + else + fprintf(stdout,"\n\t affected rows: %lld", mysql_affected_rows(mysql)); + if ((result= mysql_store_result(mysql))) + my_process_result_set(result); + if (!rc) + myassert(rows[count] == (uint)mysql_affected_rows(mysql)); + count++; + } + mysql= org_mysql; +} + +/******************************************************** +* to test simple bind store result * +*********************************************************/ +static void test_store_result() +{ + MYSQL_STMT *stmt; + int rc; + const char query[100]; + int nData, length, length1; + char szData[100]; + MYSQL_BIND bind[2]; + + myheader("test_store_result"); + + init_bind(bind); + rc = mysql_query(mysql,"DROP TABLE IF EXISTS test_store_result"); + myquery(rc); + + rc = mysql_commit(mysql); + myquery(rc); + + rc = mysql_query(mysql,"CREATE TABLE test_store_result(col1 int ,col2 varchar(50))"); + myquery(rc); + + rc = mysql_commit(mysql); + myquery(rc); + + rc = mysql_query(mysql,"INSERT INTO test_store_result VALUES(10,'venu'),(20,'mysql')"); + myquery(rc); + + rc = mysql_query(mysql,"INSERT INTO test_store_result(col2) VALUES('monty')"); + myquery(rc); + + rc = mysql_commit(mysql); + myquery(rc); + + /* fetch */ + + bind[0].buffer_type=FIELD_TYPE_LONG; + bind[0].buffer= (gptr) &nData; /* integer data */ + bind[0].length= (long *)&length; + bind[1].buffer_type=FIELD_TYPE_STRING; + bind[1].buffer=szData; /* string data */ + bind[1].buffer_length=sizeof(szData); + bind[1].length=(long *)&length1; + + strcpy((char *)query , "SELECT * FROM test_store_result"); + stmt = mysql_prepare(mysql, query, strlen(query)); + mystmt_init(stmt); + + rc = mysql_bind_result(stmt,bind); + mystmt(stmt, rc); + + rc = mysql_execute(stmt); + mystmt(stmt, rc); + + rc = mysql_stmt_store_result(stmt); + mystmt(stmt, rc); + + rc = mysql_fetch(stmt); + mystmt(stmt,rc); + + fprintf(stdout,"\n row 1: %d,%s(%d)",nData, szData, length1); + myassert(nData == 10); + myassert(strcmp(szData,"venu")==0); + myassert(length1 == 4); + + rc = mysql_fetch(stmt); + mystmt(stmt,rc); + + fprintf(stdout,"\n row 2: %d,%s(%d)",nData, szData, length1); + myassert(nData == 20); + myassert(strcmp(szData,"mysql")==0); + myassert(length1 == 5); + + length=99; + rc = mysql_fetch(stmt); + mystmt(stmt,rc); + + if (length == MYSQL_NULL_DATA) + fprintf(stdout,"\n row 3: NULL,%s(%d)", szData, length1); + else + fprintf(stdout,"\n row 3: %d,%s(%d)", nData, szData, length1); + myassert(length == MYSQL_NULL_DATA); + myassert(strcmp(szData,"monty")==0); + myassert(length1 == 5); + + rc = mysql_fetch(stmt); + myassert(rc == MYSQL_NO_DATA); + + rc = mysql_execute(stmt); + mystmt(stmt, rc); + + rc = mysql_stmt_store_result(stmt); + mystmt(stmt, rc); + + rc = mysql_fetch(stmt); + mystmt(stmt,rc); + + fprintf(stdout,"\n row 1: %d,%s(%d)",nData, szData, length1); + myassert(nData == 10); + myassert(strcmp(szData,"venu")==0); + myassert(length1 == 4); + + rc = mysql_fetch(stmt); + mystmt(stmt,rc); + + fprintf(stdout,"\n row 2: %d,%s(%d)",nData, szData, length1); + myassert(nData == 20); + myassert(strcmp(szData,"mysql")==0); + myassert(length1 == 5); + + length=99; + rc = mysql_fetch(stmt); + mystmt(stmt,rc); + + if (length == MYSQL_NULL_DATA) + fprintf(stdout,"\n row 3: NULL,%s(%d)", szData, length1); + else + fprintf(stdout,"\n row 3: %d,%s(%d)", nData, szData, length1); + myassert(length == MYSQL_NULL_DATA); + myassert(strcmp(szData,"monty")==0); + myassert(length1 == 5); + + rc = mysql_fetch(stmt); + myassert(rc == MYSQL_NO_DATA); + + mysql_stmt_close(stmt); +} + +/******************************************************** +* to test simple bind store result * +*********************************************************/ +static void test_store_result1() +{ + MYSQL_STMT *stmt; + int rc; + + myheader("test_store_result1"); + + rc = mysql_query(mysql,"DROP TABLE IF EXISTS test_store_result"); + myquery(rc); + + rc = mysql_commit(mysql); + myquery(rc); + + rc = mysql_query(mysql,"CREATE TABLE test_store_result(col1 int ,col2 varchar(50))"); + myquery(rc); + + rc = mysql_commit(mysql); + myquery(rc); + + rc = mysql_query(mysql,"INSERT INTO test_store_result VALUES(10,'venu'),(20,'mysql')"); + myquery(rc); + + rc = mysql_query(mysql,"INSERT INTO test_store_result(col2) VALUES('monty')"); + myquery(rc); + + rc = mysql_commit(mysql); + myquery(rc); + + stmt = mysql_prepare(mysql,"SELECT * FROM test_store_result",100); + mystmt_init(stmt); + + rc = mysql_execute(stmt); + mystmt(stmt, rc); + + rc = mysql_stmt_store_result(stmt); + mystmt(stmt, rc); + + rc = 0; + while (mysql_fetch(stmt) != MYSQL_NO_DATA) + rc++; + fprintf(stdout, "\n total rows: %d", rc); + myassert(rc == 3); + + rc = mysql_execute(stmt); + mystmt(stmt, rc); + + rc = mysql_stmt_store_result(stmt); + mystmt(stmt, rc); + + rc = 0; + while (mysql_fetch(stmt) != MYSQL_NO_DATA) + rc++; + fprintf(stdout, "\n total rows: %d", rc); + myassert(rc == 3); + + mysql_stmt_close(stmt); +} + +/******************************************************** +* to test simple bind store result * +*********************************************************/ +static void test_store_result2() +{ + MYSQL_STMT *stmt; + int rc; + int nData; + long length; + MYSQL_BIND bind[1]; + + myheader("test_store_result2"); + + init_bind(bind); + rc = mysql_query(mysql,"DROP TABLE IF EXISTS test_store_result"); + myquery(rc); + + rc = mysql_commit(mysql); + myquery(rc); + + rc = mysql_query(mysql,"CREATE TABLE test_store_result(col1 int ,col2 varchar(50))"); + myquery(rc); + + rc = mysql_commit(mysql); + myquery(rc); + + rc = mysql_query(mysql,"INSERT INTO test_store_result VALUES(10,'venu'),(20,'mysql')"); + myquery(rc); + + rc = mysql_query(mysql,"INSERT INTO test_store_result(col2) VALUES('monty')"); + myquery(rc); + + rc = mysql_commit(mysql); + myquery(rc); + + /* fetch */ + + bind[0].buffer_type=FIELD_TYPE_LONG; + bind[0].buffer= (gptr) &nData; /* integer data */ + bind[0].length= &length; + + strcpy((char *)query , "SELECT col1 FROM test_store_result where col1= ?"); + stmt = mysql_prepare(mysql, query, strlen(query)); + mystmt_init(stmt); + + rc = mysql_bind_param(stmt,bind); + mystmt(stmt, rc); + + rc = mysql_bind_result(stmt,bind); + mystmt(stmt, rc); + + nData = 10; length= 0; + rc = mysql_execute(stmt); + mystmt(stmt, rc); + + nData = 0; + rc = mysql_stmt_store_result(stmt); + mystmt(stmt, rc); + + rc = mysql_fetch(stmt); + mystmt(stmt,rc); + + fprintf(stdout,"\n row 1: %d",nData); + myassert(nData == 10); + + rc = mysql_fetch(stmt); + myassert(rc == MYSQL_NO_DATA); + + nData = 20; + rc = mysql_execute(stmt); + mystmt(stmt, rc); + + nData = 0; + rc = mysql_stmt_store_result(stmt); + mystmt(stmt, rc); + + rc = mysql_fetch(stmt); + mystmt(stmt,rc); + + fprintf(stdout,"\n row 1: %d",nData); + myassert(nData == 20); + + rc = mysql_fetch(stmt); + myassert(rc == MYSQL_NO_DATA); + + mysql_stmt_close(stmt); +} + +/******************************************************** +* to test simple subselect prepare * +*********************************************************/ +static void test_subselect() +{ +#if TO_BE_FIXED_IN_SERVER + MYSQL_STMT *stmt; + int rc; + int id; + long length; + MYSQL_BIND bind[1]; + + myheader("test_subselect"); + + init_bind(bind); + + rc = mysql_query(mysql,"DROP TABLE IF EXISTS test_sub1"); + myquery(rc); + + rc = mysql_query(mysql,"DROP TABLE IF EXISTS test_sub2"); + myquery(rc); + + rc = mysql_commit(mysql); + myquery(rc); + + rc = mysql_query(mysql,"CREATE TABLE test_sub1(id int)"); + myquery(rc); + + rc = mysql_query(mysql,"CREATE TABLE test_sub2(id int, id1 int)"); + myquery(rc); + + rc = mysql_commit(mysql); + myquery(rc); + + rc = mysql_query(mysql,"INSERT INTO test_sub1 values(2)"); + myquery(rc); + + rc = mysql_query(mysql,"INSERT INTO test_sub2 VALUES(1,7),(2,7)"); + myquery(rc); + + rc = mysql_commit(mysql); + myquery(rc); + + /* fetch */ + + bind[0].buffer_type=FIELD_TYPE_LONG; + bind[0].buffer= (gptr) &id; + bind[0].length= &length; + + strcpy((char *)query , "SELECT ROW(1,7) IN (select id, id1 from test_sub2 WHERE id1=?)"); + myassert(1 == my_stmt_result("SELECT ROW(1,7) IN (select id, id1 from test_sub2 WHERE id1=8)",100)); + myassert(1 == my_stmt_result("SELECT ROW(1,7) IN (select id, id1 from test_sub2 WHERE id1=7)",100)); + stmt = mysql_prepare(mysql, query, strlen(query)); + mystmt_init(stmt); + + rc = mysql_bind_param(stmt,bind); + mystmt(stmt, rc); + + rc = mysql_bind_result(stmt,bind); + mystmt(stmt, rc); + + id = 7; + rc = mysql_execute(stmt); + mystmt(stmt, rc); + + rc = mysql_fetch(stmt); + mystmt(stmt,rc); + + fprintf(stdout,"\n row 1: %d(%ld)",id, length); + myassert(id == 1); + + rc = mysql_fetch(stmt); + myassert(rc == MYSQL_NO_DATA); + + mysql_stmt_close(stmt); +#endif +} + static struct my_option myctest_long_options[] = { @@ -4631,7 +5287,7 @@ static void usage(void) puts(" By Monty & Venu \n"); puts("This software comes with ABSOLUTELY NO WARRANTY. This is free software,"); puts("and you are welcome to modify and redistribute it under the GPL license\n"); - puts(" Copyright (C) 1995-2002 MySQL AB "); + puts(" Copyright (C) 1995-2003 MySQL AB "); puts("-----------------------------------------------------------------------\n"); fprintf(stdout,"usage: %s [OPTIONS]\n\n", my_progname); fprintf(stdout,"\ @@ -4707,7 +5363,11 @@ int main(int argc, char **argv) get_options(argc,argv); client_connect(); /* connect to server */ - client_query(); /* simple client query test */ + + /* Start of tests */ + test_multi_query(); + test_select_show(); /* test show syntax */ + test_prepare_alter(); /* change table schema in middle of prepare */ test_manual_sample(); /* sample in the manual */ test_bind_result(); /* result bind test */ test_fetch_null(); /* to fetch null data */ @@ -4721,65 +5381,72 @@ int main(int argc, char **argv) test_fetch_double(); /* to fetch double to all types */ test_bind_result_ext(); /* result bind test - extension */ test_bind_result_ext1(); /* result bind test - extension */ - test_select_direct(); /* direct select - protocol_simple debug */ + test_select_direct(); /* direct select - protocol_simple debug */ test_select_prepare(); /* prepare select - protocol_prep debug */ test_select_direct(); /* direct select - protocol_simple debug */ test_select(); /* simple select test */ test_select_version(); /* select with variables */ - test_set_variable(); /* set variable prepare */ + test_set_variable(); /* set variable prepare */ #if NOT_USED - test_select_meta(); /* select param meta information */ - test_update_meta(); /* update param meta information */ - test_insert_meta(); /* insert param meta information */ + test_select_meta(); /* select param meta information */ + test_update_meta(); /* update param meta information */ + test_insert_meta(); /* insert param meta information */ #endif - test_simple_update(); /* simple update test */ - test_func_fields(); /* test for new 4.1 MYSQL_FIELD members */ - test_long_data(); /* test for sending text data in chunks */ - test_insert(); /* simple insert test - prepare */ - test_set_variable(); /* prepare with set variables */ - test_tran_innodb(); /* test for mysql_commit(), rollback() and autocommit() */ - test_select_show(); /* prepare - show test */ - test_null(); /* test null data handling */ - test_simple_update(); /* simple prepare - update */ - test_prepare_noparam();/* prepare without parameters */ - test_select(); /* simple prepare-select */ - test_insert(); /* prepare with insert */ - test_bind_result(); /* result bind test */ - test_long_data(); /* long data handling in pieces */ - test_prepare_simple();/* simple prepare */ - test_prepare(); /* prepare test */ - test_null(); /* test null data handling */ - test_debug_example(); /* some debugging case */ - test_update(); /* prepare-update test */ - test_simple_update(); /* simple prepare with update */ - test_long_data(); /* long data handling in pieces */ - test_simple_delete(); /* prepare with delete */ - test_field_names(); /* test for field names */ - test_double_compare();/* float comparision */ - client_query(); /* simple client query test */ - client_store_result();/* usage of mysql_store_result() */ - client_use_result(); /* usage of mysql_use_result() */ - test_tran_bdb(); /* transaction test on BDB table type */ - test_tran_innodb(); /* transaction test on InnoDB table type */ - test_prepare_ext(); /* test prepare with all types conversion -- TODO */ - test_prepare_syntax();/* syntax check for prepares */ + test_simple_update(); /* simple update test */ + test_func_fields(); /* test for new 4.1 MYSQL_FIELD members */ + test_long_data(); /* test for sending text data in chunks */ + test_insert(); /* simple insert test - prepare */ + test_set_variable(); /* prepare with set variables */ + test_tran_innodb(); /* test for mysql_commit(), rollback() and autocommit() */ + test_select_show(); /* prepare - show test */ + test_null(); /* test null data handling */ + test_simple_update(); /* simple prepare - update */ + test_prepare_noparam(); /* prepare without parameters */ + test_select(); /* simple prepare-select */ + test_insert(); /* prepare with insert */ + test_bind_result(); /* result bind test */ + test_long_data(); /* long data handling in pieces */ + test_prepare_simple(); /* simple prepare */ + test_prepare(); /* prepare test */ + test_null(); /* test null data handling */ + test_debug_example(); /* some debugging case */ + test_update(); /* prepare-update test */ + test_simple_update(); /* simple prepare with update */ + test_long_data(); /* long data handling in pieces */ + test_simple_delete(); /* prepare with delete */ + test_field_names(); /* test for field names */ + test_double_compare(); /* float comparision */ + client_query(); /* simple client query test */ + client_store_result(); /* usage of mysql_store_result() */ + client_use_result(); /* usage of mysql_use_result() */ + test_tran_bdb(); /* transaction test on BDB table type */ + test_tran_innodb(); /* transaction test on InnoDB table type */ + test_prepare_ext(); /* test prepare with all types conversion -- TODO */ + test_prepare_syntax(); /* syntax check for prepares */ test_prepare_field_result(); /* prepare meta info */ test_prepare_resultset(); /* prepare meta info test */ - test_field_names(); /* test for field names */ - test_field_flags(); /* test to help .NET provider team */ - test_long_data_str(); /* long data handling */ - test_long_data_str1();/* yet another long data handling */ - test_long_data_bin(); /* long binary insertion */ - test_warnings(); /* show warnings test */ - test_errors(); /* show errors test */ - test_select_simple(); /* simple select prepare */ + test_field_names(); /* test for field names */ + test_field_flags(); /* test to help .NET provider team */ + test_long_data_str(); /* long data handling */ + test_long_data_str1(); /* yet another long data handling */ + test_long_data_bin(); /* long binary insertion */ + test_warnings(); /* show warnings test */ + test_errors(); /* show errors test */ + test_select_simple(); /* simple select prepare */ test_prepare_resultset();/* prepare meta info test */ - test_func_fields(); /* FUNCTION field info */ + test_func_fields(); /* FUNCTION field info */ /*test_stmt_close(); */ /* mysql_stmt_close() test -- hangs */ test_prepare_field_result(); /* prepare meta info */ - test_multi_stmt(); /* multi stmt test */ - client_disconnect(); /* disconnect from server */ - + test_multi_stmt(); /* multi stmt test */ + test_store_result(); /* test the store_result */ + test_store_result1(); /* test store result without buffers */ + test_store_result2(); /* test store result for misc case */ + test_multi_stmt(); /* test multi stmt */ + test_subselect(); /* test subselect prepare */ + test_multi_query(); /* test multi query exec */ + /* End of tests */ + + client_disconnect(); /* disconnect from server */ fprintf(stdout,"\n\nSUCCESS !!!\n"); return(0); } diff --git a/tests/grant.res b/tests/grant.res index 3359f970225..15aad01c888 100644 --- a/tests/grant.res +++ b/tests/grant.res @@ -27,11 +27,11 @@ show grants for grant_user@localhost GRANT SELECT ON *.* TO 'grant_user'@'localhost' insert into mysql.user (host,user) values ('error','grant_user') -Error in execute: insert command denied to user: 'grant_user@localhost' for table 'user' +Error in execute: Access denied for user: 'grant_user@localhost' to database 'mysql' update mysql.user set host='error' WHERE user='grant_user' -Error in execute: update command denied to user: 'grant_user@localhost' for table 'user' +Error in execute: Access denied for user: 'grant_user@localhost' to database 'mysql' create table grant_test.test (a int,b int) -Error in execute: create command denied to user: 'grant_user@localhost' for table 'test' +Error in execute: Access denied for user: 'grant_user@localhost' to database 'grant_test' grant select on *.* to grant_user2@localhost Error in execute: Access denied for user: 'grant_user@localhost' (Using password: NO) revoke select on grant_test.test from grant_user@opt_host @@ -106,21 +106,21 @@ select count(*) from grant_test.test 2 select * from mysql.user where user = 'grant_user' -Error in execute: select command denied to user: 'grant_user@localhost' for table 'user' +Error in execute: Access denied for user: 'grant_user@localhost' to database 'mysql' insert into grant_test.test values (4,0) -Error in execute: insert command denied to user: 'grant_user@localhost' for table 'test' +Error in execute: Access denied for user: 'grant_user@localhost' to database 'grant_test' update grant_test.test set a=1 -Error in execute: update command denied to user: 'grant_user@localhost' for table 'test' +Error in execute: Access denied for user: 'grant_user@localhost' to database 'grant_test' delete from grant_test.test -Error in execute: delete command denied to user: 'grant_user@localhost' for table 'test' +Error in execute: Access denied for user: 'grant_user@localhost' to database 'grant_test' create table grant_test.test2 (a int) -Error in execute: create command denied to user: 'grant_user@localhost' for table 'test2' +Error in execute: Access denied for user: 'grant_user@localhost' to database 'grant_test' ALTER TABLE grant_test.test add c int -Error in execute: alter command denied to user: 'grant_user@localhost' for table 'test' +Error in execute: Access denied for user: 'grant_user@localhost' to database 'grant_test' CREATE INDEX dummy ON grant_test.test (a) -Error in execute: index command denied to user: 'grant_user@localhost' for table 'test' +Error in execute: Access denied for user: 'grant_user@localhost' to database 'grant_test' drop table grant_test.test -Error in execute: drop command denied to user: 'grant_user@localhost' for table 'test' +Error in execute: Access denied for user: 'grant_user@localhost' to database 'grant_test' grant ALL PRIVILEGES on grant_test.* to grant_user2@localhost Error in execute: Access denied for user: 'grant_user@localhost' to database 'grant_test' grant ALL PRIVILEGES on grant_test.* to grant_user@localhost WITH GRANT OPTION @@ -133,14 +133,14 @@ REVOKE ALL PRIVILEGES on grant_test.* from grant_user@localhost REVOKE ALL PRIVILEGES on grant_test.* from grant_user@localhost Connecting grant_user insert into grant_test.test values (6,0) -Error in execute: insert command denied to user: 'grant_user@localhost' for table 'test' +Error in execute: Access denied for user: 'grant_user@localhost' to database 'grant_test' REVOKE GRANT OPTION on grant_test.* from grant_user@localhost Connecting grant_user Access denied for user: 'grant_user@localhost' to database 'grant_test' grant ALL PRIVILEGES on grant_test.* to grant_user@localhost Connecting grant_user select * from mysql.user where user = 'grant_user' -Error in execute: select command denied to user: 'grant_user@localhost' for table 'user' +Error in execute: Access denied for user: 'grant_user@localhost' to database 'mysql' insert into grant_test.test values (7,0) update grant_test.test set a=3 where a=2 delete from grant_test.test where a=3 @@ -152,7 +152,7 @@ show tables from grant_test test insert into mysql.user (host,user) values ('error','grant_user',0) -Error in execute: insert command denied to user: 'grant_user@localhost' for table 'user' +Error in execute: Access denied for user: 'grant_user@localhost' to database 'mysql' revoke ALL PRIVILEGES on grant_test.* from grant_user@localhost select * from mysql.user where user = 'grant_user' localhost grant_user N N N N N N N N N N N N N N N N N N N N N 0 0 0 @@ -171,7 +171,7 @@ Error in execute: select command denied to user: 'grant_user@localhost' for tabl show keys from test Error in execute: select command denied to user: 'grant_user@localhost' for table 'test' show columns from test2 -a int(11) 0 +a int(11) binary 0 show keys from test2 select * from test @@ -228,7 +228,7 @@ Error in execute: select command denied to user: 'grant_user@localhost' for tabl select count(*) from test,test2 Error in execute: select command denied to user: 'grant_user@localhost' for table 'test2' replace into test2 SELECT a from test -Error in execute: update command denied to user: 'grant_user@localhost' for table 'test2' +Error in execute: delete command denied to user: 'grant_user@localhost' for table 'test2' grant update on grant_test.test2 to grant_user@localhost replace into test2 SELECT a,a from test Error in execute: delete command denied to user: 'grant_user@localhost' for table 'test2' @@ -314,8 +314,8 @@ revoke GRANT OPTION on grant_test.test from grant_user@localhost Error in execute: There is no such grant defined for user 'grant_user' on host 'localhost' on table 'test' grant select(a) on grant_test.test to grant_user@localhost show full columns from test -a int(11) YES NULL select -b int(11) YES NULL +a int(11) binary YES NULL select +b int(11) binary YES NULL grant insert (b), update (b) on grant_test.test to grant_user@localhost select count(a) from test @@ -472,7 +472,7 @@ GRANT LOCK TABLES ON *.* TO 'grant_user'@'localhost' GRANT SELECT, INSERT ON grant_test.test3 TO 'grant_user'@'localhost' select * from mysql.user where user='grant_user' -127.0.0.1 grant_user 7f70e8b858ee6782 N N N N N N N N N N N N N N N N N N N N N 0 0 0 +127.0.0.1 grant_user *042a99b3d247ae587783f647f2d69496d390aa71eab3 N N N N N N N N N N N N N N N N N N N N N 0 0 0 localhost grant_user N N N N N N N N N N N N N N N N N Y N N N 0 0 0 Connecting grant_user