From 124da02af7a348eed58d7e3580d5176421171070 Mon Sep 17 00:00:00 2001 From: Joel Sherrill Date: Fri, 8 Apr 2011 17:36:40 +0000 Subject: 2011-04-08 Joel Sherrill * bit: Update to libtecl-1.6.1 * libtecla-1.6.1/CHANGES, libtecla-1.6.1/INSTALL, libtecla-1.6.1/LICENSE.TERMS, libtecla-1.6.1/Makefile, libtecla-1.6.1/Makefile.in, libtecla-1.6.1/Makefile.rules, libtecla-1.6.1/Makefile.stub, libtecla-1.6.1/PORTING, libtecla-1.6.1/README, libtecla-1.6.1/RELEASE.NOTES, libtecla-1.6.1/chrqueue.c, libtecla-1.6.1/chrqueue.h, libtecla-1.6.1/config.guess, libtecla-1.6.1/config.sub, libtecla-1.6.1/configure, libtecla-1.6.1/configure.in, libtecla-1.6.1/cplfile.c, libtecla-1.6.1/cplfile.h, libtecla-1.6.1/cplmatch.c, libtecla-1.6.1/cplmatch.h, libtecla-1.6.1/demo.c, libtecla-1.6.1/demo2.c, libtecla-1.6.1/demo3.c, libtecla-1.6.1/direader.c, libtecla-1.6.1/direader.h, libtecla-1.6.1/enhance.c, libtecla-1.6.1/errmsg.c, libtecla-1.6.1/errmsg.h, libtecla-1.6.1/expand.c, libtecla-1.6.1/expand.h, libtecla-1.6.1/freelist.c, libtecla-1.6.1/freelist.h, libtecla-1.6.1/getline.c, libtecla-1.6.1/getline.h, libtecla-1.6.1/hash.c, libtecla-1.6.1/hash.h, libtecla-1.6.1/history.c, libtecla-1.6.1/history.h, libtecla-1.6.1/homedir.c, libtecla-1.6.1/homedir.h, libtecla-1.6.1/install-sh, libtecla-1.6.1/ioutil.c, libtecla-1.6.1/ioutil.h, libtecla-1.6.1/keytab.c, libtecla-1.6.1/keytab.h, libtecla-1.6.1/libtecla.h, libtecla-1.6.1/libtecla.map, libtecla-1.6.1/pathutil.c, libtecla-1.6.1/pathutil.h, libtecla-1.6.1/pcache.c, libtecla-1.6.1/stringrp.c, libtecla-1.6.1/stringrp.h, libtecla-1.6.1/strngmem.c, libtecla-1.6.1/strngmem.h, libtecla-1.6.1/update_html, libtecla-1.6.1/update_version, libtecla-1.6.1/version.c, libtecla-1.6.1/html/changes.html, libtecla-1.6.1/html/cpl_complete_word.html, libtecla-1.6.1/html/ef_expand_file.html, libtecla-1.6.1/html/enhance.html, libtecla-1.6.1/html/gl_get_line.html, libtecla-1.6.1/html/gl_io_mode.html, libtecla-1.6.1/html/index.html, libtecla-1.6.1/html/libtecla.html, libtecla-1.6.1/html/pca_lookup_file.html, libtecla-1.6.1/html/release.html, libtecla-1.6.1/html/tecla.html, libtecla-1.6.1/man/file/teclarc.in, libtecla-1.6.1/man/func/cfc_file_start.in, libtecla-1.6.1/man/func/cfc_literal_escapes.in, libtecla-1.6.1/man/func/cfc_set_check_fn.in, libtecla-1.6.1/man/func/cpl_add_completion.in, libtecla-1.6.1/man/func/cpl_complete_word.in, libtecla-1.6.1/man/func/cpl_file_completions.in, libtecla-1.6.1/man/func/cpl_last_error.in, libtecla-1.6.1/man/func/cpl_list_completions.in, libtecla-1.6.1/man/func/cpl_recall_matches.in, libtecla-1.6.1/man/func/cpl_record_error.in, libtecla-1.6.1/man/func/del_CplFileConf.in, libtecla-1.6.1/man/func/del_ExpandFile.in, libtecla-1.6.1/man/func/del_GetLine.in, libtecla-1.6.1/man/func/del_PathCache.in, libtecla-1.6.1/man/func/del_PcaPathConf.in, libtecla-1.6.1/man/func/del_WordCompletion.in, libtecla-1.6.1/man/func/ef_expand_file.in, libtecla-1.6.1/man/func/ef_last_error.in, libtecla-1.6.1/man/func/ef_list_expansions.in, libtecla-1.6.1/man/func/gl_abandon_line.in, libtecla-1.6.1/man/func/gl_bind_keyseq.in, libtecla-1.6.1/man/func/gl_catch_blocked.in, libtecla-1.6.1/man/func/gl_change_terminal.in, libtecla-1.6.1/man/func/gl_clear_history.in, libtecla-1.6.1/man/func/gl_completion_action.in, libtecla-1.6.1/man/func/gl_configure_getline.in, libtecla-1.6.1/man/func/gl_customize_completion.in, libtecla-1.6.1/man/func/gl_display_text.in, libtecla-1.6.1/man/func/gl_echo_mode.in, libtecla-1.6.1/man/func/gl_erase_terminal.in, libtecla-1.6.1/man/func/gl_error_message.in, libtecla-1.6.1/man/func/gl_get_line.in, libtecla-1.6.1/man/func/gl_group_history.in, libtecla-1.6.1/man/func/gl_handle_signal.in, libtecla-1.6.1/man/func/gl_ignore_signal.in, libtecla-1.6.1/man/func/gl_inactivity_timeout.in, libtecla-1.6.1/man/func/gl_io_mode.in, libtecla-1.6.1/man/func/gl_last_signal.in, libtecla-1.6.1/man/func/gl_limit_history.in, libtecla-1.6.1/man/func/gl_list_signals.in, libtecla-1.6.1/man/func/gl_load_history.in, libtecla-1.6.1/man/func/gl_lookup_history.in, libtecla-1.6.1/man/func/gl_normal_io.in, libtecla-1.6.1/man/func/gl_pending_io.in, libtecla-1.6.1/man/func/gl_prompt_style.in, libtecla-1.6.1/man/func/gl_query_char.in, libtecla-1.6.1/man/func/gl_range_of_history.in, libtecla-1.6.1/man/func/gl_raw_io.in, libtecla-1.6.1/man/func/gl_read_char.in, libtecla-1.6.1/man/func/gl_register_action.in, libtecla-1.6.1/man/func/gl_resize_history.in, libtecla-1.6.1/man/func/gl_return_status.in, libtecla-1.6.1/man/func/gl_save_history.in, libtecla-1.6.1/man/func/gl_set_term_size.in, libtecla-1.6.1/man/func/gl_show_history.in, libtecla-1.6.1/man/func/gl_size_of_history.in, libtecla-1.6.1/man/func/gl_state_of_history.in, libtecla-1.6.1/man/func/gl_terminal_size.in, libtecla-1.6.1/man/func/gl_toggle_history.in, libtecla-1.6.1/man/func/gl_trap_signal.in, libtecla-1.6.1/man/func/gl_tty_signals.in, libtecla-1.6.1/man/func/gl_watch_fd.in, libtecla-1.6.1/man/func/libtecla_version.in, libtecla-1.6.1/man/func/new_CplFileConf.in, libtecla-1.6.1/man/func/new_ExpandFile.in, libtecla-1.6.1/man/func/new_GetLine.in, libtecla-1.6.1/man/func/new_PathCache.in, libtecla-1.6.1/man/func/new_PcaPathConf.in, libtecla-1.6.1/man/func/new_WordCompletion.in, libtecla-1.6.1/man/func/pca_last_error.in, libtecla-1.6.1/man/func/pca_lookup_file.in, libtecla-1.6.1/man/func/pca_path_completions.in, libtecla-1.6.1/man/func/pca_scan_path.in, libtecla-1.6.1/man/func/pca_set_check_fn.in, libtecla-1.6.1/man/func/ppc_file_start.in, libtecla-1.6.1/man/func/ppc_literal_escapes.in, libtecla-1.6.1/man/libr/libtecla.in, libtecla-1.6.1/man/misc/tecla.in, libtecla-1.6.1/man/prog/enhance.in: New files. * libtecla-1.4.1/CHANGES, libtecla-1.4.1/INSTALL, libtecla-1.4.1/LICENSE.TERMS, libtecla-1.4.1/Makefile, libtecla-1.4.1/Makefile.in, libtecla-1.4.1/Makefile.rules, libtecla-1.4.1/Makefile.stub, libtecla-1.4.1/PORTING, libtecla-1.4.1/README, libtecla-1.4.1/RELEASE.NOTES, libtecla-1.4.1/config.guess, libtecla-1.4.1/config.sub, libtecla-1.4.1/configure, libtecla-1.4.1/configure.in, libtecla-1.4.1/cplfile.c, libtecla-1.4.1/cplfile.h, libtecla-1.4.1/cplmatch.c, libtecla-1.4.1/demo.c, libtecla-1.4.1/demo2.c, libtecla-1.4.1/direader.c, libtecla-1.4.1/direader.h, libtecla-1.4.1/enhance.c, libtecla-1.4.1/expand.c, libtecla-1.4.1/freelist.c, libtecla-1.4.1/freelist.h, libtecla-1.4.1/getline.c, libtecla-1.4.1/getline.h, libtecla-1.4.1/hash.c, libtecla-1.4.1/hash.h, libtecla-1.4.1/history.c, libtecla-1.4.1/history.h, libtecla-1.4.1/homedir.c, libtecla-1.4.1/homedir.h, libtecla-1.4.1/install-sh, libtecla-1.4.1/keytab.c, libtecla-1.4.1/keytab.h, libtecla-1.4.1/libtecla.h, libtecla-1.4.1/libtecla.map, libtecla-1.4.1/pathutil.c, libtecla-1.4.1/pathutil.h, libtecla-1.4.1/pcache.c, libtecla-1.4.1/stringrp.c, libtecla-1.4.1/stringrp.h, libtecla-1.4.1/strngmem.c, libtecla-1.4.1/strngmem.h, libtecla-1.4.1/update_html, libtecla-1.4.1/update_version, libtecla-1.4.1/version.c, libtecla-1.4.1/html/changes.html, libtecla-1.4.1/html/cpl_complete_word.html, libtecla-1.4.1/html/ef_expand_file.html, libtecla-1.4.1/html/enhance.html, libtecla-1.4.1/html/gl_get_line.html, libtecla-1.4.1/html/index.html, libtecla-1.4.1/html/libtecla.html, libtecla-1.4.1/html/pca_lookup_file.html, libtecla-1.4.1/html/release.html, libtecla-1.4.1/man3/cfc_file_start.3, libtecla-1.4.1/man3/cfc_literal_escapes.3, libtecla-1.4.1/man3/cfc_set_check_fn.3, libtecla-1.4.1/man3/cpl_add_completion.3, libtecla-1.4.1/man3/cpl_complete_word.3, libtecla-1.4.1/man3/cpl_file_completions.3, libtecla-1.4.1/man3/cpl_last_error.3, libtecla-1.4.1/man3/cpl_list_completions.3, libtecla-1.4.1/man3/cpl_record_error.3, libtecla-1.4.1/man3/del_CplFileConf.3, libtecla-1.4.1/man3/del_ExpandFile.3, libtecla-1.4.1/man3/del_GetLine.3, libtecla-1.4.1/man3/del_PathCache.3, libtecla-1.4.1/man3/del_PcaPathConf.3, libtecla-1.4.1/man3/del_WordCompletion.3, libtecla-1.4.1/man3/ef_expand_file.3, libtecla-1.4.1/man3/ef_last_error.3, libtecla-1.4.1/man3/ef_list_expansions.3, libtecla-1.4.1/man3/enhance.3, libtecla-1.4.1/man3/gl_change_terminal.3, libtecla-1.4.1/man3/gl_clear_history.3, libtecla-1.4.1/man3/gl_configure_getline.3, libtecla-1.4.1/man3/gl_customize_completion.3, libtecla-1.4.1/man3/gl_echo_mode.3, libtecla-1.4.1/man3/gl_get_line.3, libtecla-1.4.1/man3/gl_group_history.3, libtecla-1.4.1/man3/gl_ignore_signal.3, libtecla-1.4.1/man3/gl_last_signal.3, libtecla-1.4.1/man3/gl_limit_history.3, libtecla-1.4.1/man3/gl_load_history.3, libtecla-1.4.1/man3/gl_lookup_history.3, libtecla-1.4.1/man3/gl_prompt_style.3, libtecla-1.4.1/man3/gl_range_of_history.3, libtecla-1.4.1/man3/gl_resize_history.3, libtecla-1.4.1/man3/gl_save_history.3, libtecla-1.4.1/man3/gl_show_history.3, libtecla-1.4.1/man3/gl_size_of_history.3, libtecla-1.4.1/man3/gl_state_of_history.3, libtecla-1.4.1/man3/gl_terminal_size.3, libtecla-1.4.1/man3/gl_toggle_history.3, libtecla-1.4.1/man3/gl_trap_signal.3, libtecla-1.4.1/man3/gl_watch_fd.3, libtecla-1.4.1/man3/libtecla.3, libtecla-1.4.1/man3/libtecla_version.3, libtecla-1.4.1/man3/new_CplFileConf.3, libtecla-1.4.1/man3/new_ExpandFile.3, libtecla-1.4.1/man3/new_GetLine.3, libtecla-1.4.1/man3/new_PathCache.3, libtecla-1.4.1/man3/new_PcaPathConf.3, libtecla-1.4.1/man3/new_WordCompletion.3, libtecla-1.4.1/man3/pca_last_error.3, libtecla-1.4.1/man3/pca_lookup_file.3, libtecla-1.4.1/man3/pca_path_completions.3, libtecla-1.4.1/man3/pca_scan_path.3, libtecla-1.4.1/man3/pca_set_check_fn.3, libtecla-1.4.1/man3/ppc_file_start.3, libtecla-1.4.1/man3/ppc_literal_escapes.3: Removed. --- ChangeLog | 209 + bit | 2 +- libtecla-1.4.1/CHANGES | 1492 --- libtecla-1.4.1/INSTALL | 168 - libtecla-1.4.1/LICENSE.TERMS | 28 - libtecla-1.4.1/Makefile | 3 - libtecla-1.4.1/Makefile.in | 225 - libtecla-1.4.1/Makefile.rules | 142 - libtecla-1.4.1/Makefile.stub | 3 - libtecla-1.4.1/PORTING | 38 - libtecla-1.4.1/README | 53 - libtecla-1.4.1/RELEASE.NOTES | 357 - libtecla-1.4.1/config.guess | 1183 -- libtecla-1.4.1/config.sub | 1268 -- libtecla-1.4.1/configure | 1939 --- libtecla-1.4.1/configure.in | 412 - libtecla-1.4.1/cplfile.c | 874 -- libtecla-1.4.1/cplfile.h | 96 - libtecla-1.4.1/cplmatch.c | 927 -- libtecla-1.4.1/demo.c | 109 - libtecla-1.4.1/demo2.c | 352 - libtecla-1.4.1/direader.c | 299 - libtecla-1.4.1/direader.h | 44 - libtecla-1.4.1/enhance.c | 689 - libtecla-1.4.1/expand.c | 1265 -- libtecla-1.4.1/freelist.c | 393 - libtecla-1.4.1/freelist.h | 82 - libtecla-1.4.1/getline.c | 8346 ------------ libtecla-1.4.1/getline.h | 88 - libtecla-1.4.1/hash.c | 748 -- libtecla-1.4.1/hash.h | 157 - libtecla-1.4.1/history.c | 2003 --- libtecla-1.4.1/history.h | 159 - libtecla-1.4.1/homedir.c | 399 - libtecla-1.4.1/homedir.h | 81 - libtecla-1.4.1/html/changes.html | 1495 --- libtecla-1.4.1/html/cpl_complete_word.html | 423 - libtecla-1.4.1/html/ef_expand_file.html | 267 - libtecla-1.4.1/html/enhance.html | 111 - libtecla-1.4.1/html/gl_get_line.html | 2295 ---- libtecla-1.4.1/html/index.html | 116 - libtecla-1.4.1/html/libtecla.html | 163 - libtecla-1.4.1/html/pca_lookup_file.html | 371 - libtecla-1.4.1/html/release.html | 360 - libtecla-1.4.1/install-sh | 251 - libtecla-1.4.1/keytab.c | 827 -- libtecla-1.4.1/keytab.h | 146 - libtecla-1.4.1/libtecla.h | 1194 -- libtecla-1.4.1/libtecla.map | 124 - libtecla-1.4.1/man3/cfc_file_start.3 | 1 - libtecla-1.4.1/man3/cfc_literal_escapes.3 | 1 - libtecla-1.4.1/man3/cfc_set_check_fn.3 | 1 - libtecla-1.4.1/man3/cpl_add_completion.3 | 1 - libtecla-1.4.1/man3/cpl_complete_word.3 | 405 - libtecla-1.4.1/man3/cpl_file_completions.3 | 1 - libtecla-1.4.1/man3/cpl_last_error.3 | 1 - libtecla-1.4.1/man3/cpl_list_completions.3 | 1 - libtecla-1.4.1/man3/cpl_record_error.3 | 1 - libtecla-1.4.1/man3/del_CplFileConf.3 | 1 - libtecla-1.4.1/man3/del_ExpandFile.3 | 1 - libtecla-1.4.1/man3/del_GetLine.3 | 1 - libtecla-1.4.1/man3/del_PathCache.3 | 1 - libtecla-1.4.1/man3/del_PcaPathConf.3 | 1 - libtecla-1.4.1/man3/del_WordCompletion.3 | 1 - libtecla-1.4.1/man3/ef_expand_file.3 | 245 - libtecla-1.4.1/man3/ef_last_error.3 | 1 - libtecla-1.4.1/man3/ef_list_expansions.3 | 1 - libtecla-1.4.1/man3/enhance.3 | 86 - libtecla-1.4.1/man3/gl_change_terminal.3 | 1 - libtecla-1.4.1/man3/gl_clear_history.3 | 1 - libtecla-1.4.1/man3/gl_configure_getline.3 | 1 - libtecla-1.4.1/man3/gl_customize_completion.3 | 1 - libtecla-1.4.1/man3/gl_echo_mode.3 | 1 - libtecla-1.4.1/man3/gl_get_line.3 | 2329 ---- libtecla-1.4.1/man3/gl_group_history.3 | 1 - libtecla-1.4.1/man3/gl_ignore_signal.3 | 1 - libtecla-1.4.1/man3/gl_last_signal.3 | 1 - libtecla-1.4.1/man3/gl_limit_history.3 | 1 - libtecla-1.4.1/man3/gl_load_history.3 | 1 - libtecla-1.4.1/man3/gl_lookup_history.3 | 1 - libtecla-1.4.1/man3/gl_prompt_style.3 | 1 - libtecla-1.4.1/man3/gl_range_of_history.3 | 1 - libtecla-1.4.1/man3/gl_resize_history.3 | 1 - libtecla-1.4.1/man3/gl_save_history.3 | 1 - libtecla-1.4.1/man3/gl_show_history.3 | 1 - libtecla-1.4.1/man3/gl_size_of_history.3 | 1 - libtecla-1.4.1/man3/gl_state_of_history.3 | 1 - libtecla-1.4.1/man3/gl_terminal_size.3 | 1 - libtecla-1.4.1/man3/gl_toggle_history.3 | 1 - libtecla-1.4.1/man3/gl_trap_signal.3 | 1 - libtecla-1.4.1/man3/gl_watch_fd.3 | 1 - libtecla-1.4.1/man3/libtecla.3 | 160 - libtecla-1.4.1/man3/libtecla_version.3 | 1 - libtecla-1.4.1/man3/new_CplFileConf.3 | 1 - libtecla-1.4.1/man3/new_ExpandFile.3 | 1 - libtecla-1.4.1/man3/new_GetLine.3 | 1 - libtecla-1.4.1/man3/new_PathCache.3 | 1 - libtecla-1.4.1/man3/new_PcaPathConf.3 | 1 - libtecla-1.4.1/man3/new_WordCompletion.3 | 1 - libtecla-1.4.1/man3/pca_last_error.3 | 1 - libtecla-1.4.1/man3/pca_lookup_file.3 | 361 - libtecla-1.4.1/man3/pca_path_completions.3 | 1 - libtecla-1.4.1/man3/pca_scan_path.3 | 1 - libtecla-1.4.1/man3/pca_set_check_fn.3 | 1 - libtecla-1.4.1/man3/ppc_file_start.3 | 1 - libtecla-1.4.1/man3/ppc_literal_escapes.3 | 1 - libtecla-1.4.1/pathutil.c | 532 - libtecla-1.4.1/pathutil.h | 122 - libtecla-1.4.1/pcache.c | 1688 --- libtecla-1.4.1/stringrp.c | 285 - libtecla-1.4.1/stringrp.h | 84 - libtecla-1.4.1/strngmem.c | 226 - libtecla-1.4.1/strngmem.h | 80 - libtecla-1.4.1/update_html | 35 - libtecla-1.4.1/update_version | 82 - libtecla-1.4.1/version.c | 30 - libtecla-1.6.1/CHANGES | 2758 ++++ libtecla-1.6.1/INSTALL | 213 + libtecla-1.6.1/LICENSE.TERMS | 28 + libtecla-1.6.1/Makefile | 12 + libtecla-1.6.1/Makefile.in | 272 + libtecla-1.6.1/Makefile.rules | 169 + libtecla-1.6.1/Makefile.stub | 12 + libtecla-1.6.1/PORTING | 38 + libtecla-1.6.1/README | 53 + libtecla-1.6.1/RELEASE.NOTES | 587 + libtecla-1.6.1/chrqueue.c | 432 + libtecla-1.6.1/chrqueue.h | 106 + libtecla-1.6.1/config.guess | 1410 ++ libtecla-1.6.1/config.sub | 1510 +++ libtecla-1.6.1/configure | 5208 ++++++++ libtecla-1.6.1/configure.in | 582 + libtecla-1.6.1/cplfile.c | 870 ++ libtecla-1.6.1/cplfile.h | 96 + libtecla-1.6.1/cplmatch.c | 1170 ++ libtecla-1.6.1/cplmatch.h | 47 + libtecla-1.6.1/demo.c | 166 + libtecla-1.6.1/demo2.c | 423 + libtecla-1.6.1/demo3.c | 738 ++ libtecla-1.6.1/direader.c | 309 + libtecla-1.6.1/direader.h | 44 + libtecla-1.6.1/enhance.c | 695 + libtecla-1.6.1/errmsg.c | 167 + libtecla-1.6.1/errmsg.h | 85 + libtecla-1.6.1/expand.c | 1448 +++ libtecla-1.6.1/expand.h | 48 + libtecla-1.6.1/freelist.c | 400 + libtecla-1.6.1/freelist.h | 87 + libtecla-1.6.1/getline.c | 12844 +++++++++++++++++++ libtecla-1.6.1/getline.h | 88 + libtecla-1.6.1/hash.c | 737 ++ libtecla-1.6.1/hash.h | 157 + libtecla-1.6.1/history.c | 2842 ++++ libtecla-1.6.1/history.h | 169 + libtecla-1.6.1/homedir.c | 470 + libtecla-1.6.1/homedir.h | 81 + libtecla-1.6.1/html/changes.html | 2761 ++++ libtecla-1.6.1/html/cpl_complete_word.html | 382 + libtecla-1.6.1/html/ef_expand_file.html | 213 + libtecla-1.6.1/html/enhance.html | 75 + libtecla-1.6.1/html/gl_get_line.html | 1996 +++ libtecla-1.6.1/html/gl_io_mode.html | 509 + libtecla-1.6.1/html/index.html | 122 + libtecla-1.6.1/html/libtecla.html | 138 + libtecla-1.6.1/html/pca_lookup_file.html | 312 + libtecla-1.6.1/html/release.html | 590 + libtecla-1.6.1/html/tecla.html | 1120 ++ libtecla-1.6.1/install-sh | 251 + libtecla-1.6.1/ioutil.c | 330 + libtecla-1.6.1/ioutil.h | 73 + libtecla-1.6.1/keytab.c | 1022 ++ libtecla-1.6.1/keytab.h | 157 + libtecla-1.6.1/libtecla.h | 1834 +++ libtecla-1.6.1/libtecla.map | 155 + libtecla-1.6.1/man/file/teclarc.in | 1 + libtecla-1.6.1/man/func/cfc_file_start.in | 1 + libtecla-1.6.1/man/func/cfc_literal_escapes.in | 1 + libtecla-1.6.1/man/func/cfc_set_check_fn.in | 1 + libtecla-1.6.1/man/func/cpl_add_completion.in | 1 + libtecla-1.6.1/man/func/cpl_complete_word.in | 441 + libtecla-1.6.1/man/func/cpl_file_completions.in | 1 + libtecla-1.6.1/man/func/cpl_last_error.in | 1 + libtecla-1.6.1/man/func/cpl_list_completions.in | 1 + libtecla-1.6.1/man/func/cpl_recall_matches.in | 1 + libtecla-1.6.1/man/func/cpl_record_error.in | 1 + libtecla-1.6.1/man/func/del_CplFileConf.in | 1 + libtecla-1.6.1/man/func/del_ExpandFile.in | 1 + libtecla-1.6.1/man/func/del_GetLine.in | 1 + libtecla-1.6.1/man/func/del_PathCache.in | 1 + libtecla-1.6.1/man/func/del_PcaPathConf.in | 1 + libtecla-1.6.1/man/func/del_WordCompletion.in | 1 + libtecla-1.6.1/man/func/ef_expand_file.in | 248 + libtecla-1.6.1/man/func/ef_last_error.in | 1 + libtecla-1.6.1/man/func/ef_list_expansions.in | 1 + libtecla-1.6.1/man/func/gl_abandon_line.in | 1 + libtecla-1.6.1/man/func/gl_bind_keyseq.in | 1 + libtecla-1.6.1/man/func/gl_catch_blocked.in | 1 + libtecla-1.6.1/man/func/gl_change_terminal.in | 1 + libtecla-1.6.1/man/func/gl_clear_history.in | 1 + libtecla-1.6.1/man/func/gl_completion_action.in | 1 + libtecla-1.6.1/man/func/gl_configure_getline.in | 1 + libtecla-1.6.1/man/func/gl_customize_completion.in | 1 + libtecla-1.6.1/man/func/gl_display_text.in | 1 + libtecla-1.6.1/man/func/gl_echo_mode.in | 1 + libtecla-1.6.1/man/func/gl_erase_terminal.in | 1 + libtecla-1.6.1/man/func/gl_error_message.in | 1 + libtecla-1.6.1/man/func/gl_get_line.in | 2236 ++++ libtecla-1.6.1/man/func/gl_group_history.in | 1 + libtecla-1.6.1/man/func/gl_handle_signal.in | 1 + libtecla-1.6.1/man/func/gl_ignore_signal.in | 1 + libtecla-1.6.1/man/func/gl_inactivity_timeout.in | 1 + libtecla-1.6.1/man/func/gl_io_mode.in | 571 + libtecla-1.6.1/man/func/gl_last_signal.in | 1 + libtecla-1.6.1/man/func/gl_limit_history.in | 1 + libtecla-1.6.1/man/func/gl_list_signals.in | 1 + libtecla-1.6.1/man/func/gl_load_history.in | 1 + libtecla-1.6.1/man/func/gl_lookup_history.in | 1 + libtecla-1.6.1/man/func/gl_normal_io.in | 1 + libtecla-1.6.1/man/func/gl_pending_io.in | 1 + libtecla-1.6.1/man/func/gl_prompt_style.in | 1 + libtecla-1.6.1/man/func/gl_query_char.in | 1 + libtecla-1.6.1/man/func/gl_range_of_history.in | 1 + libtecla-1.6.1/man/func/gl_raw_io.in | 1 + libtecla-1.6.1/man/func/gl_read_char.in | 1 + libtecla-1.6.1/man/func/gl_register_action.in | 1 + libtecla-1.6.1/man/func/gl_resize_history.in | 1 + libtecla-1.6.1/man/func/gl_return_status.in | 1 + libtecla-1.6.1/man/func/gl_save_history.in | 1 + libtecla-1.6.1/man/func/gl_set_term_size.in | 1 + libtecla-1.6.1/man/func/gl_show_history.in | 1 + libtecla-1.6.1/man/func/gl_size_of_history.in | 1 + libtecla-1.6.1/man/func/gl_state_of_history.in | 1 + libtecla-1.6.1/man/func/gl_terminal_size.in | 1 + libtecla-1.6.1/man/func/gl_toggle_history.in | 1 + libtecla-1.6.1/man/func/gl_trap_signal.in | 1 + libtecla-1.6.1/man/func/gl_tty_signals.in | 1 + libtecla-1.6.1/man/func/gl_watch_fd.in | 1 + libtecla-1.6.1/man/func/libtecla_version.in | 1 + libtecla-1.6.1/man/func/new_CplFileConf.in | 1 + libtecla-1.6.1/man/func/new_ExpandFile.in | 1 + libtecla-1.6.1/man/func/new_GetLine.in | 1 + libtecla-1.6.1/man/func/new_PathCache.in | 1 + libtecla-1.6.1/man/func/new_PcaPathConf.in | 1 + libtecla-1.6.1/man/func/new_WordCompletion.in | 1 + libtecla-1.6.1/man/func/pca_last_error.in | 1 + libtecla-1.6.1/man/func/pca_lookup_file.in | 365 + libtecla-1.6.1/man/func/pca_path_completions.in | 1 + libtecla-1.6.1/man/func/pca_scan_path.in | 1 + libtecla-1.6.1/man/func/pca_set_check_fn.in | 1 + libtecla-1.6.1/man/func/ppc_file_start.in | 1 + libtecla-1.6.1/man/func/ppc_literal_escapes.in | 1 + libtecla-1.6.1/man/libr/libtecla.in | 168 + libtecla-1.6.1/man/misc/tecla.in | 1201 ++ libtecla-1.6.1/man/prog/enhance.in | 89 + libtecla-1.6.1/pathutil.c | 539 + libtecla-1.6.1/pathutil.h | 122 + libtecla-1.6.1/pcache.c | 1710 +++ libtecla-1.6.1/stringrp.c | 286 + libtecla-1.6.1/stringrp.h | 84 + libtecla-1.6.1/strngmem.c | 218 + libtecla-1.6.1/strngmem.h | 80 + libtecla-1.6.1/update_html | 37 + libtecla-1.6.1/update_version | 82 + libtecla-1.6.1/version.c | 30 + 264 files changed, 58400 insertions(+), 39367 deletions(-) delete mode 100644 libtecla-1.4.1/CHANGES delete mode 100644 libtecla-1.4.1/INSTALL delete mode 100644 libtecla-1.4.1/LICENSE.TERMS delete mode 100644 libtecla-1.4.1/Makefile delete mode 100644 libtecla-1.4.1/Makefile.in delete mode 100644 libtecla-1.4.1/Makefile.rules delete mode 100644 libtecla-1.4.1/Makefile.stub delete mode 100644 libtecla-1.4.1/PORTING delete mode 100644 libtecla-1.4.1/README delete mode 100644 libtecla-1.4.1/RELEASE.NOTES delete mode 100644 libtecla-1.4.1/config.guess delete mode 100644 libtecla-1.4.1/config.sub delete mode 100755 libtecla-1.4.1/configure delete mode 100644 libtecla-1.4.1/configure.in delete mode 100644 libtecla-1.4.1/cplfile.c delete mode 100644 libtecla-1.4.1/cplfile.h delete mode 100644 libtecla-1.4.1/cplmatch.c delete mode 100644 libtecla-1.4.1/demo.c delete mode 100644 libtecla-1.4.1/demo2.c delete mode 100644 libtecla-1.4.1/direader.c delete mode 100644 libtecla-1.4.1/direader.h delete mode 100644 libtecla-1.4.1/enhance.c delete mode 100644 libtecla-1.4.1/expand.c delete mode 100644 libtecla-1.4.1/freelist.c delete mode 100644 libtecla-1.4.1/freelist.h delete mode 100644 libtecla-1.4.1/getline.c delete mode 100644 libtecla-1.4.1/getline.h delete mode 100644 libtecla-1.4.1/hash.c delete mode 100644 libtecla-1.4.1/hash.h delete mode 100644 libtecla-1.4.1/history.c delete mode 100644 libtecla-1.4.1/history.h delete mode 100644 libtecla-1.4.1/homedir.c delete mode 100644 libtecla-1.4.1/homedir.h delete mode 100644 libtecla-1.4.1/html/changes.html delete mode 100644 libtecla-1.4.1/html/cpl_complete_word.html delete mode 100644 libtecla-1.4.1/html/ef_expand_file.html delete mode 100644 libtecla-1.4.1/html/enhance.html delete mode 100644 libtecla-1.4.1/html/gl_get_line.html delete mode 100644 libtecla-1.4.1/html/index.html delete mode 100644 libtecla-1.4.1/html/libtecla.html delete mode 100644 libtecla-1.4.1/html/pca_lookup_file.html delete mode 100644 libtecla-1.4.1/html/release.html delete mode 100755 libtecla-1.4.1/install-sh delete mode 100644 libtecla-1.4.1/keytab.c delete mode 100644 libtecla-1.4.1/keytab.h delete mode 100644 libtecla-1.4.1/libtecla.h delete mode 100644 libtecla-1.4.1/libtecla.map delete mode 100644 libtecla-1.4.1/man3/cfc_file_start.3 delete mode 100644 libtecla-1.4.1/man3/cfc_literal_escapes.3 delete mode 100644 libtecla-1.4.1/man3/cfc_set_check_fn.3 delete mode 100644 libtecla-1.4.1/man3/cpl_add_completion.3 delete mode 100644 libtecla-1.4.1/man3/cpl_complete_word.3 delete mode 100644 libtecla-1.4.1/man3/cpl_file_completions.3 delete mode 100644 libtecla-1.4.1/man3/cpl_last_error.3 delete mode 100644 libtecla-1.4.1/man3/cpl_list_completions.3 delete mode 100644 libtecla-1.4.1/man3/cpl_record_error.3 delete mode 100644 libtecla-1.4.1/man3/del_CplFileConf.3 delete mode 100644 libtecla-1.4.1/man3/del_ExpandFile.3 delete mode 100644 libtecla-1.4.1/man3/del_GetLine.3 delete mode 100644 libtecla-1.4.1/man3/del_PathCache.3 delete mode 100644 libtecla-1.4.1/man3/del_PcaPathConf.3 delete mode 100644 libtecla-1.4.1/man3/del_WordCompletion.3 delete mode 100644 libtecla-1.4.1/man3/ef_expand_file.3 delete mode 100644 libtecla-1.4.1/man3/ef_last_error.3 delete mode 100644 libtecla-1.4.1/man3/ef_list_expansions.3 delete mode 100644 libtecla-1.4.1/man3/enhance.3 delete mode 100644 libtecla-1.4.1/man3/gl_change_terminal.3 delete mode 100644 libtecla-1.4.1/man3/gl_clear_history.3 delete mode 100644 libtecla-1.4.1/man3/gl_configure_getline.3 delete mode 100644 libtecla-1.4.1/man3/gl_customize_completion.3 delete mode 100644 libtecla-1.4.1/man3/gl_echo_mode.3 delete mode 100644 libtecla-1.4.1/man3/gl_get_line.3 delete mode 100644 libtecla-1.4.1/man3/gl_group_history.3 delete mode 100644 libtecla-1.4.1/man3/gl_ignore_signal.3 delete mode 100644 libtecla-1.4.1/man3/gl_last_signal.3 delete mode 100644 libtecla-1.4.1/man3/gl_limit_history.3 delete mode 100644 libtecla-1.4.1/man3/gl_load_history.3 delete mode 100644 libtecla-1.4.1/man3/gl_lookup_history.3 delete mode 100644 libtecla-1.4.1/man3/gl_prompt_style.3 delete mode 100644 libtecla-1.4.1/man3/gl_range_of_history.3 delete mode 100644 libtecla-1.4.1/man3/gl_resize_history.3 delete mode 100644 libtecla-1.4.1/man3/gl_save_history.3 delete mode 100644 libtecla-1.4.1/man3/gl_show_history.3 delete mode 100644 libtecla-1.4.1/man3/gl_size_of_history.3 delete mode 100644 libtecla-1.4.1/man3/gl_state_of_history.3 delete mode 100644 libtecla-1.4.1/man3/gl_terminal_size.3 delete mode 100644 libtecla-1.4.1/man3/gl_toggle_history.3 delete mode 100644 libtecla-1.4.1/man3/gl_trap_signal.3 delete mode 100644 libtecla-1.4.1/man3/gl_watch_fd.3 delete mode 100644 libtecla-1.4.1/man3/libtecla.3 delete mode 100644 libtecla-1.4.1/man3/libtecla_version.3 delete mode 100644 libtecla-1.4.1/man3/new_CplFileConf.3 delete mode 100644 libtecla-1.4.1/man3/new_ExpandFile.3 delete mode 100644 libtecla-1.4.1/man3/new_GetLine.3 delete mode 100644 libtecla-1.4.1/man3/new_PathCache.3 delete mode 100644 libtecla-1.4.1/man3/new_PcaPathConf.3 delete mode 100644 libtecla-1.4.1/man3/new_WordCompletion.3 delete mode 100644 libtecla-1.4.1/man3/pca_last_error.3 delete mode 100644 libtecla-1.4.1/man3/pca_lookup_file.3 delete mode 100644 libtecla-1.4.1/man3/pca_path_completions.3 delete mode 100644 libtecla-1.4.1/man3/pca_scan_path.3 delete mode 100644 libtecla-1.4.1/man3/pca_set_check_fn.3 delete mode 100644 libtecla-1.4.1/man3/ppc_file_start.3 delete mode 100644 libtecla-1.4.1/man3/ppc_literal_escapes.3 delete mode 100644 libtecla-1.4.1/pathutil.c delete mode 100644 libtecla-1.4.1/pathutil.h delete mode 100644 libtecla-1.4.1/pcache.c delete mode 100644 libtecla-1.4.1/stringrp.c delete mode 100644 libtecla-1.4.1/stringrp.h delete mode 100644 libtecla-1.4.1/strngmem.c delete mode 100644 libtecla-1.4.1/strngmem.h delete mode 100755 libtecla-1.4.1/update_html delete mode 100755 libtecla-1.4.1/update_version delete mode 100644 libtecla-1.4.1/version.c create mode 100644 libtecla-1.6.1/CHANGES create mode 100644 libtecla-1.6.1/INSTALL create mode 100644 libtecla-1.6.1/LICENSE.TERMS create mode 100644 libtecla-1.6.1/Makefile create mode 100644 libtecla-1.6.1/Makefile.in create mode 100644 libtecla-1.6.1/Makefile.rules create mode 100644 libtecla-1.6.1/Makefile.stub create mode 100644 libtecla-1.6.1/PORTING create mode 100644 libtecla-1.6.1/README create mode 100644 libtecla-1.6.1/RELEASE.NOTES create mode 100644 libtecla-1.6.1/chrqueue.c create mode 100644 libtecla-1.6.1/chrqueue.h create mode 100644 libtecla-1.6.1/config.guess create mode 100644 libtecla-1.6.1/config.sub create mode 100755 libtecla-1.6.1/configure create mode 100644 libtecla-1.6.1/configure.in create mode 100644 libtecla-1.6.1/cplfile.c create mode 100644 libtecla-1.6.1/cplfile.h create mode 100644 libtecla-1.6.1/cplmatch.c create mode 100644 libtecla-1.6.1/cplmatch.h create mode 100644 libtecla-1.6.1/demo.c create mode 100644 libtecla-1.6.1/demo2.c create mode 100644 libtecla-1.6.1/demo3.c create mode 100644 libtecla-1.6.1/direader.c create mode 100644 libtecla-1.6.1/direader.h create mode 100644 libtecla-1.6.1/enhance.c create mode 100644 libtecla-1.6.1/errmsg.c create mode 100644 libtecla-1.6.1/errmsg.h create mode 100644 libtecla-1.6.1/expand.c create mode 100644 libtecla-1.6.1/expand.h create mode 100644 libtecla-1.6.1/freelist.c create mode 100644 libtecla-1.6.1/freelist.h create mode 100644 libtecla-1.6.1/getline.c create mode 100644 libtecla-1.6.1/getline.h create mode 100644 libtecla-1.6.1/hash.c create mode 100644 libtecla-1.6.1/hash.h create mode 100644 libtecla-1.6.1/history.c create mode 100644 libtecla-1.6.1/history.h create mode 100644 libtecla-1.6.1/homedir.c create mode 100644 libtecla-1.6.1/homedir.h create mode 100644 libtecla-1.6.1/html/changes.html create mode 100644 libtecla-1.6.1/html/cpl_complete_word.html create mode 100644 libtecla-1.6.1/html/ef_expand_file.html create mode 100644 libtecla-1.6.1/html/enhance.html create mode 100644 libtecla-1.6.1/html/gl_get_line.html create mode 100644 libtecla-1.6.1/html/gl_io_mode.html create mode 100644 libtecla-1.6.1/html/index.html create mode 100644 libtecla-1.6.1/html/libtecla.html create mode 100644 libtecla-1.6.1/html/pca_lookup_file.html create mode 100644 libtecla-1.6.1/html/release.html create mode 100644 libtecla-1.6.1/html/tecla.html create mode 100755 libtecla-1.6.1/install-sh create mode 100644 libtecla-1.6.1/ioutil.c create mode 100644 libtecla-1.6.1/ioutil.h create mode 100644 libtecla-1.6.1/keytab.c create mode 100644 libtecla-1.6.1/keytab.h create mode 100644 libtecla-1.6.1/libtecla.h create mode 100644 libtecla-1.6.1/libtecla.map create mode 100644 libtecla-1.6.1/man/file/teclarc.in create mode 100644 libtecla-1.6.1/man/func/cfc_file_start.in create mode 100644 libtecla-1.6.1/man/func/cfc_literal_escapes.in create mode 100644 libtecla-1.6.1/man/func/cfc_set_check_fn.in create mode 100644 libtecla-1.6.1/man/func/cpl_add_completion.in create mode 100644 libtecla-1.6.1/man/func/cpl_complete_word.in create mode 100644 libtecla-1.6.1/man/func/cpl_file_completions.in create mode 100644 libtecla-1.6.1/man/func/cpl_last_error.in create mode 100644 libtecla-1.6.1/man/func/cpl_list_completions.in create mode 100644 libtecla-1.6.1/man/func/cpl_recall_matches.in create mode 100644 libtecla-1.6.1/man/func/cpl_record_error.in create mode 100644 libtecla-1.6.1/man/func/del_CplFileConf.in create mode 100644 libtecla-1.6.1/man/func/del_ExpandFile.in create mode 100644 libtecla-1.6.1/man/func/del_GetLine.in create mode 100644 libtecla-1.6.1/man/func/del_PathCache.in create mode 100644 libtecla-1.6.1/man/func/del_PcaPathConf.in create mode 100644 libtecla-1.6.1/man/func/del_WordCompletion.in create mode 100644 libtecla-1.6.1/man/func/ef_expand_file.in create mode 100644 libtecla-1.6.1/man/func/ef_last_error.in create mode 100644 libtecla-1.6.1/man/func/ef_list_expansions.in create mode 100644 libtecla-1.6.1/man/func/gl_abandon_line.in create mode 100644 libtecla-1.6.1/man/func/gl_bind_keyseq.in create mode 100644 libtecla-1.6.1/man/func/gl_catch_blocked.in create mode 100644 libtecla-1.6.1/man/func/gl_change_terminal.in create mode 100644 libtecla-1.6.1/man/func/gl_clear_history.in create mode 100644 libtecla-1.6.1/man/func/gl_completion_action.in create mode 100644 libtecla-1.6.1/man/func/gl_configure_getline.in create mode 100644 libtecla-1.6.1/man/func/gl_customize_completion.in create mode 100644 libtecla-1.6.1/man/func/gl_display_text.in create mode 100644 libtecla-1.6.1/man/func/gl_echo_mode.in create mode 100644 libtecla-1.6.1/man/func/gl_erase_terminal.in create mode 100644 libtecla-1.6.1/man/func/gl_error_message.in create mode 100644 libtecla-1.6.1/man/func/gl_get_line.in create mode 100644 libtecla-1.6.1/man/func/gl_group_history.in create mode 100644 libtecla-1.6.1/man/func/gl_handle_signal.in create mode 100644 libtecla-1.6.1/man/func/gl_ignore_signal.in create mode 100644 libtecla-1.6.1/man/func/gl_inactivity_timeout.in create mode 100644 libtecla-1.6.1/man/func/gl_io_mode.in create mode 100644 libtecla-1.6.1/man/func/gl_last_signal.in create mode 100644 libtecla-1.6.1/man/func/gl_limit_history.in create mode 100644 libtecla-1.6.1/man/func/gl_list_signals.in create mode 100644 libtecla-1.6.1/man/func/gl_load_history.in create mode 100644 libtecla-1.6.1/man/func/gl_lookup_history.in create mode 100644 libtecla-1.6.1/man/func/gl_normal_io.in create mode 100644 libtecla-1.6.1/man/func/gl_pending_io.in create mode 100644 libtecla-1.6.1/man/func/gl_prompt_style.in create mode 100644 libtecla-1.6.1/man/func/gl_query_char.in create mode 100644 libtecla-1.6.1/man/func/gl_range_of_history.in create mode 100644 libtecla-1.6.1/man/func/gl_raw_io.in create mode 100644 libtecla-1.6.1/man/func/gl_read_char.in create mode 100644 libtecla-1.6.1/man/func/gl_register_action.in create mode 100644 libtecla-1.6.1/man/func/gl_resize_history.in create mode 100644 libtecla-1.6.1/man/func/gl_return_status.in create mode 100644 libtecla-1.6.1/man/func/gl_save_history.in create mode 100644 libtecla-1.6.1/man/func/gl_set_term_size.in create mode 100644 libtecla-1.6.1/man/func/gl_show_history.in create mode 100644 libtecla-1.6.1/man/func/gl_size_of_history.in create mode 100644 libtecla-1.6.1/man/func/gl_state_of_history.in create mode 100644 libtecla-1.6.1/man/func/gl_terminal_size.in create mode 100644 libtecla-1.6.1/man/func/gl_toggle_history.in create mode 100644 libtecla-1.6.1/man/func/gl_trap_signal.in create mode 100644 libtecla-1.6.1/man/func/gl_tty_signals.in create mode 100644 libtecla-1.6.1/man/func/gl_watch_fd.in create mode 100644 libtecla-1.6.1/man/func/libtecla_version.in create mode 100644 libtecla-1.6.1/man/func/new_CplFileConf.in create mode 100644 libtecla-1.6.1/man/func/new_ExpandFile.in create mode 100644 libtecla-1.6.1/man/func/new_GetLine.in create mode 100644 libtecla-1.6.1/man/func/new_PathCache.in create mode 100644 libtecla-1.6.1/man/func/new_PcaPathConf.in create mode 100644 libtecla-1.6.1/man/func/new_WordCompletion.in create mode 100644 libtecla-1.6.1/man/func/pca_last_error.in create mode 100644 libtecla-1.6.1/man/func/pca_lookup_file.in create mode 100644 libtecla-1.6.1/man/func/pca_path_completions.in create mode 100644 libtecla-1.6.1/man/func/pca_scan_path.in create mode 100644 libtecla-1.6.1/man/func/pca_set_check_fn.in create mode 100644 libtecla-1.6.1/man/func/ppc_file_start.in create mode 100644 libtecla-1.6.1/man/func/ppc_literal_escapes.in create mode 100644 libtecla-1.6.1/man/libr/libtecla.in create mode 100644 libtecla-1.6.1/man/misc/tecla.in create mode 100644 libtecla-1.6.1/man/prog/enhance.in create mode 100644 libtecla-1.6.1/pathutil.c create mode 100644 libtecla-1.6.1/pathutil.h create mode 100644 libtecla-1.6.1/pcache.c create mode 100644 libtecla-1.6.1/stringrp.c create mode 100644 libtecla-1.6.1/stringrp.h create mode 100644 libtecla-1.6.1/strngmem.c create mode 100644 libtecla-1.6.1/strngmem.h create mode 100755 libtecla-1.6.1/update_html create mode 100755 libtecla-1.6.1/update_version create mode 100644 libtecla-1.6.1/version.c diff --git a/ChangeLog b/ChangeLog index b88cd6a..8516227 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,212 @@ +2011-04-08 Joel Sherrill + + * bit: Update to libtecl-1.6.1 + * libtecla-1.6.1/CHANGES, libtecla-1.6.1/INSTALL, + libtecla-1.6.1/LICENSE.TERMS, libtecla-1.6.1/Makefile, + libtecla-1.6.1/Makefile.in, libtecla-1.6.1/Makefile.rules, + libtecla-1.6.1/Makefile.stub, libtecla-1.6.1/PORTING, + libtecla-1.6.1/README, libtecla-1.6.1/RELEASE.NOTES, + libtecla-1.6.1/chrqueue.c, libtecla-1.6.1/chrqueue.h, + libtecla-1.6.1/config.guess, libtecla-1.6.1/config.sub, + libtecla-1.6.1/configure, libtecla-1.6.1/configure.in, + libtecla-1.6.1/cplfile.c, libtecla-1.6.1/cplfile.h, + libtecla-1.6.1/cplmatch.c, libtecla-1.6.1/cplmatch.h, + libtecla-1.6.1/demo.c, libtecla-1.6.1/demo2.c, + libtecla-1.6.1/demo3.c, libtecla-1.6.1/direader.c, + libtecla-1.6.1/direader.h, libtecla-1.6.1/enhance.c, + libtecla-1.6.1/errmsg.c, libtecla-1.6.1/errmsg.h, + libtecla-1.6.1/expand.c, libtecla-1.6.1/expand.h, + libtecla-1.6.1/freelist.c, libtecla-1.6.1/freelist.h, + libtecla-1.6.1/getline.c, libtecla-1.6.1/getline.h, + libtecla-1.6.1/hash.c, libtecla-1.6.1/hash.h, + libtecla-1.6.1/history.c, libtecla-1.6.1/history.h, + libtecla-1.6.1/homedir.c, libtecla-1.6.1/homedir.h, + libtecla-1.6.1/install-sh, libtecla-1.6.1/ioutil.c, + libtecla-1.6.1/ioutil.h, libtecla-1.6.1/keytab.c, + libtecla-1.6.1/keytab.h, libtecla-1.6.1/libtecla.h, + libtecla-1.6.1/libtecla.map, libtecla-1.6.1/pathutil.c, + libtecla-1.6.1/pathutil.h, libtecla-1.6.1/pcache.c, + libtecla-1.6.1/stringrp.c, libtecla-1.6.1/stringrp.h, + libtecla-1.6.1/strngmem.c, libtecla-1.6.1/strngmem.h, + libtecla-1.6.1/update_html, libtecla-1.6.1/update_version, + libtecla-1.6.1/version.c, libtecla-1.6.1/html/changes.html, + libtecla-1.6.1/html/cpl_complete_word.html, + libtecla-1.6.1/html/ef_expand_file.html, + libtecla-1.6.1/html/enhance.html, + libtecla-1.6.1/html/gl_get_line.html, + libtecla-1.6.1/html/gl_io_mode.html, libtecla-1.6.1/html/index.html, + libtecla-1.6.1/html/libtecla.html, + libtecla-1.6.1/html/pca_lookup_file.html, + libtecla-1.6.1/html/release.html, libtecla-1.6.1/html/tecla.html, + libtecla-1.6.1/man/file/teclarc.in, + libtecla-1.6.1/man/func/cfc_file_start.in, + libtecla-1.6.1/man/func/cfc_literal_escapes.in, + libtecla-1.6.1/man/func/cfc_set_check_fn.in, + libtecla-1.6.1/man/func/cpl_add_completion.in, + libtecla-1.6.1/man/func/cpl_complete_word.in, + libtecla-1.6.1/man/func/cpl_file_completions.in, + libtecla-1.6.1/man/func/cpl_last_error.in, + libtecla-1.6.1/man/func/cpl_list_completions.in, + libtecla-1.6.1/man/func/cpl_recall_matches.in, + libtecla-1.6.1/man/func/cpl_record_error.in, + libtecla-1.6.1/man/func/del_CplFileConf.in, + libtecla-1.6.1/man/func/del_ExpandFile.in, + libtecla-1.6.1/man/func/del_GetLine.in, + libtecla-1.6.1/man/func/del_PathCache.in, + libtecla-1.6.1/man/func/del_PcaPathConf.in, + libtecla-1.6.1/man/func/del_WordCompletion.in, + libtecla-1.6.1/man/func/ef_expand_file.in, + libtecla-1.6.1/man/func/ef_last_error.in, + libtecla-1.6.1/man/func/ef_list_expansions.in, + libtecla-1.6.1/man/func/gl_abandon_line.in, + libtecla-1.6.1/man/func/gl_bind_keyseq.in, + libtecla-1.6.1/man/func/gl_catch_blocked.in, + libtecla-1.6.1/man/func/gl_change_terminal.in, + libtecla-1.6.1/man/func/gl_clear_history.in, + libtecla-1.6.1/man/func/gl_completion_action.in, + libtecla-1.6.1/man/func/gl_configure_getline.in, + libtecla-1.6.1/man/func/gl_customize_completion.in, + libtecla-1.6.1/man/func/gl_display_text.in, + libtecla-1.6.1/man/func/gl_echo_mode.in, + libtecla-1.6.1/man/func/gl_erase_terminal.in, + libtecla-1.6.1/man/func/gl_error_message.in, + libtecla-1.6.1/man/func/gl_get_line.in, + libtecla-1.6.1/man/func/gl_group_history.in, + libtecla-1.6.1/man/func/gl_handle_signal.in, + libtecla-1.6.1/man/func/gl_ignore_signal.in, + libtecla-1.6.1/man/func/gl_inactivity_timeout.in, + libtecla-1.6.1/man/func/gl_io_mode.in, + libtecla-1.6.1/man/func/gl_last_signal.in, + libtecla-1.6.1/man/func/gl_limit_history.in, + libtecla-1.6.1/man/func/gl_list_signals.in, + libtecla-1.6.1/man/func/gl_load_history.in, + libtecla-1.6.1/man/func/gl_lookup_history.in, + libtecla-1.6.1/man/func/gl_normal_io.in, + libtecla-1.6.1/man/func/gl_pending_io.in, + libtecla-1.6.1/man/func/gl_prompt_style.in, + libtecla-1.6.1/man/func/gl_query_char.in, + libtecla-1.6.1/man/func/gl_range_of_history.in, + libtecla-1.6.1/man/func/gl_raw_io.in, + libtecla-1.6.1/man/func/gl_read_char.in, + libtecla-1.6.1/man/func/gl_register_action.in, + libtecla-1.6.1/man/func/gl_resize_history.in, + libtecla-1.6.1/man/func/gl_return_status.in, + libtecla-1.6.1/man/func/gl_save_history.in, + libtecla-1.6.1/man/func/gl_set_term_size.in, + libtecla-1.6.1/man/func/gl_show_history.in, + libtecla-1.6.1/man/func/gl_size_of_history.in, + libtecla-1.6.1/man/func/gl_state_of_history.in, + libtecla-1.6.1/man/func/gl_terminal_size.in, + libtecla-1.6.1/man/func/gl_toggle_history.in, + libtecla-1.6.1/man/func/gl_trap_signal.in, + libtecla-1.6.1/man/func/gl_tty_signals.in, + libtecla-1.6.1/man/func/gl_watch_fd.in, + libtecla-1.6.1/man/func/libtecla_version.in, + libtecla-1.6.1/man/func/new_CplFileConf.in, + libtecla-1.6.1/man/func/new_ExpandFile.in, + libtecla-1.6.1/man/func/new_GetLine.in, + libtecla-1.6.1/man/func/new_PathCache.in, + libtecla-1.6.1/man/func/new_PcaPathConf.in, + libtecla-1.6.1/man/func/new_WordCompletion.in, + libtecla-1.6.1/man/func/pca_last_error.in, + libtecla-1.6.1/man/func/pca_lookup_file.in, + libtecla-1.6.1/man/func/pca_path_completions.in, + libtecla-1.6.1/man/func/pca_scan_path.in, + libtecla-1.6.1/man/func/pca_set_check_fn.in, + libtecla-1.6.1/man/func/ppc_file_start.in, + libtecla-1.6.1/man/func/ppc_literal_escapes.in, + libtecla-1.6.1/man/libr/libtecla.in, + libtecla-1.6.1/man/misc/tecla.in, libtecla-1.6.1/man/prog/enhance.in: + New files. + * libtecla-1.4.1/CHANGES, libtecla-1.4.1/INSTALL, + libtecla-1.4.1/LICENSE.TERMS, libtecla-1.4.1/Makefile, + libtecla-1.4.1/Makefile.in, libtecla-1.4.1/Makefile.rules, + libtecla-1.4.1/Makefile.stub, libtecla-1.4.1/PORTING, + libtecla-1.4.1/README, libtecla-1.4.1/RELEASE.NOTES, + libtecla-1.4.1/config.guess, libtecla-1.4.1/config.sub, + libtecla-1.4.1/configure, libtecla-1.4.1/configure.in, + libtecla-1.4.1/cplfile.c, libtecla-1.4.1/cplfile.h, + libtecla-1.4.1/cplmatch.c, libtecla-1.4.1/demo.c, + libtecla-1.4.1/demo2.c, libtecla-1.4.1/direader.c, + libtecla-1.4.1/direader.h, libtecla-1.4.1/enhance.c, + libtecla-1.4.1/expand.c, libtecla-1.4.1/freelist.c, + libtecla-1.4.1/freelist.h, libtecla-1.4.1/getline.c, + libtecla-1.4.1/getline.h, libtecla-1.4.1/hash.c, + libtecla-1.4.1/hash.h, libtecla-1.4.1/history.c, + libtecla-1.4.1/history.h, libtecla-1.4.1/homedir.c, + libtecla-1.4.1/homedir.h, libtecla-1.4.1/install-sh, + libtecla-1.4.1/keytab.c, libtecla-1.4.1/keytab.h, + libtecla-1.4.1/libtecla.h, libtecla-1.4.1/libtecla.map, + libtecla-1.4.1/pathutil.c, libtecla-1.4.1/pathutil.h, + libtecla-1.4.1/pcache.c, libtecla-1.4.1/stringrp.c, + libtecla-1.4.1/stringrp.h, libtecla-1.4.1/strngmem.c, + libtecla-1.4.1/strngmem.h, libtecla-1.4.1/update_html, + libtecla-1.4.1/update_version, libtecla-1.4.1/version.c, + libtecla-1.4.1/html/changes.html, + libtecla-1.4.1/html/cpl_complete_word.html, + libtecla-1.4.1/html/ef_expand_file.html, + libtecla-1.4.1/html/enhance.html, + libtecla-1.4.1/html/gl_get_line.html, libtecla-1.4.1/html/index.html, + libtecla-1.4.1/html/libtecla.html, + libtecla-1.4.1/html/pca_lookup_file.html, + libtecla-1.4.1/html/release.html, + libtecla-1.4.1/man3/cfc_file_start.3, + libtecla-1.4.1/man3/cfc_literal_escapes.3, + libtecla-1.4.1/man3/cfc_set_check_fn.3, + libtecla-1.4.1/man3/cpl_add_completion.3, + libtecla-1.4.1/man3/cpl_complete_word.3, + libtecla-1.4.1/man3/cpl_file_completions.3, + libtecla-1.4.1/man3/cpl_last_error.3, + libtecla-1.4.1/man3/cpl_list_completions.3, + libtecla-1.4.1/man3/cpl_record_error.3, + libtecla-1.4.1/man3/del_CplFileConf.3, + libtecla-1.4.1/man3/del_ExpandFile.3, + libtecla-1.4.1/man3/del_GetLine.3, + libtecla-1.4.1/man3/del_PathCache.3, + libtecla-1.4.1/man3/del_PcaPathConf.3, + libtecla-1.4.1/man3/del_WordCompletion.3, + libtecla-1.4.1/man3/ef_expand_file.3, + libtecla-1.4.1/man3/ef_last_error.3, + libtecla-1.4.1/man3/ef_list_expansions.3, + libtecla-1.4.1/man3/enhance.3, + libtecla-1.4.1/man3/gl_change_terminal.3, + libtecla-1.4.1/man3/gl_clear_history.3, + libtecla-1.4.1/man3/gl_configure_getline.3, + libtecla-1.4.1/man3/gl_customize_completion.3, + libtecla-1.4.1/man3/gl_echo_mode.3, + libtecla-1.4.1/man3/gl_get_line.3, + libtecla-1.4.1/man3/gl_group_history.3, + libtecla-1.4.1/man3/gl_ignore_signal.3, + libtecla-1.4.1/man3/gl_last_signal.3, + libtecla-1.4.1/man3/gl_limit_history.3, + libtecla-1.4.1/man3/gl_load_history.3, + libtecla-1.4.1/man3/gl_lookup_history.3, + libtecla-1.4.1/man3/gl_prompt_style.3, + libtecla-1.4.1/man3/gl_range_of_history.3, + libtecla-1.4.1/man3/gl_resize_history.3, + libtecla-1.4.1/man3/gl_save_history.3, + libtecla-1.4.1/man3/gl_show_history.3, + libtecla-1.4.1/man3/gl_size_of_history.3, + libtecla-1.4.1/man3/gl_state_of_history.3, + libtecla-1.4.1/man3/gl_terminal_size.3, + libtecla-1.4.1/man3/gl_toggle_history.3, + libtecla-1.4.1/man3/gl_trap_signal.3, + libtecla-1.4.1/man3/gl_watch_fd.3, libtecla-1.4.1/man3/libtecla.3, + libtecla-1.4.1/man3/libtecla_version.3, + libtecla-1.4.1/man3/new_CplFileConf.3, + libtecla-1.4.1/man3/new_ExpandFile.3, + libtecla-1.4.1/man3/new_GetLine.3, + libtecla-1.4.1/man3/new_PathCache.3, + libtecla-1.4.1/man3/new_PcaPathConf.3, + libtecla-1.4.1/man3/new_WordCompletion.3, + libtecla-1.4.1/man3/pca_last_error.3, + libtecla-1.4.1/man3/pca_lookup_file.3, + libtecla-1.4.1/man3/pca_path_completions.3, + libtecla-1.4.1/man3/pca_scan_path.3, + libtecla-1.4.1/man3/pca_set_check_fn.3, + libtecla-1.4.1/man3/ppc_file_start.3, + libtecla-1.4.1/man3/ppc_literal_escapes.3: Removed. + 2011-04-08 Joel Sherrill * RTEMS_Makefiles/Makefile.gsl: New file. diff --git a/bit b/bit index b5b17aa..2fbdd9c 100755 --- a/bit +++ b/bit @@ -9,7 +9,7 @@ # set -ex -PACKAGES="avl-1.4.0 ncurses-5.9 readline-4.3 libtecla-1.4.1" +PACKAGES="avl-1.4.0 gsl-1.9 ncurses-5.9 readline-6.2 libtecla-1.6.1" # make sure it is clean before we start for p in $PACKAGES diff --git a/libtecla-1.4.1/CHANGES b/libtecla-1.4.1/CHANGES deleted file mode 100644 index 45073e0..0000000 --- a/libtecla-1.4.1/CHANGES +++ /dev/null @@ -1,1492 +0,0 @@ -In the following log, modification dates are listed using the European -convention in which the day comes before the month (ie. DD/MM/YYYY). -The most recent modifications are listed first. - -25/05/2002 mcs@astro.caltech.edu (based on suggestions by Paul Smith) - pathutil.c - Apparently, under QNX pathconf("/",_PC_PATH_MAX) returns - EINVAL. At Paul's suggestion I have modified the code to - silently substitute the existing MAX_PATHLEN_FALLBACK - value if pathconf() returns an error of any kind. - homedir.c - Under QNX, sysconf(_SC_GETPW_R_SIZE_MAX) also apparently - returns EINVAL, so as with pathconf() I modified the code - to substitute a fallback default, rather than - complaining and failing. - enhance.c - Paul told me that the inclusion of sys/termios.h was - causing compilation of enhance.c to fail under QNX. This - line is a bug. The correct thing to do is include - termios.h without a sub-directory prefix, as I was - already doing futher up in the file, so I have just - removed the errant include line. - -12/02/2002 mcs@astro.caltech.edu - getline.c configure.in configure - Mac OS X doesn't have a term.h or termcap.h, but it does - define prototypes for tputs() and setupterm(), so the - default prototypes that I was including if no headers - where available, upset it. I've removed these prototypes. - I also now conditionally include whichever is found of - curses.h and ncurses/curses.h for both termcap and - terminfo (before I wasn't including curses.h when - termcap was selected). - -12/02/2002 mcs@astro.caltech.edu - Updated version number to 1.4.1, ready for a micro - release. - -12/02/2002 mcs@astro.caltech.edu - html/index.html - Added Mac OS X and Cygwin to the list of systems that - can compile libtecla. - -12/02/2002 mcs@astro.caltech.edu - getline.c - Under Mac OS X, the tputs() callback function returns - void, instead of the int return value used by other - systems. This declaration is now used if both __MACH__ - and __APPLE__ are defined. Hopefully these are the - correct system macros to check. Thanks for Stephan - Fiedler for providing information on Mac OS X. - -11/02/2002 mcs@astro.caltech.edu - configure.in configure getline.c - Some systems don't have term.h, and others have it hidden - in an ncurses sub-directory of the standard system include - directory. If term.h can't be found, simply don't include - it. If it is in an ncurses sub-directory, include - ncurses/term.h instead of term.h. - -04/02/2002 mcs@astro.caltech.edu - configure.in configure Makefile.in Makefile.rules - Use ranlib on systems that need it (Mac OS X). Also, - make all components of the installation directories where - needed, instead of assuming that they exist. - -04/02/2002 mcs@astro.caltech.edu - getline.c - When the tab completion binding was unbound from the tab - key, hitting the tab key caused gl_get_line() to ring the - bell instead of inserting a tab character. This is - problematic when using the 'enhance' program with - Jython, since tabs are important in Python. I have - corrected this. - -10/12/2001 Version 1.4.0 released. - -10/12/2001 mcs@astro.caltech.edu - getline.c - If the TIOCGWINSZ ioctl doesn't work, as is the case when - running in an emacs shell, leave the size unchanged, rather - than returning a fatal error. - -07/12/2001 mcs@astro.caltech.edu - configure.in configure - Now that the configure version of CFLAGS is included in - the makefile, I noticed that the optimization flags -g - and -O2 had been added. It turns out that if CFLAGS isn't - already set, the autoconf AC_PROG_CC macro initializes it - with these two optimization flags. Since this would break - backwards compatibility in embedded distributions that - already use the OPT= makefile argument, and because - turning debugging on needlessly bloats the library, I now - make sure that CFLAGS is set before calling this macro. - -07/12/2001 mcs@astro.caltech.edu - enhance.c - Use argv[0] in error reports instead of using a - hardcoded macro. - -07/12/2001 mcs@astro.caltech.edu - getline.c - The cut buffer wasn't being cleared after being - used as a work buffer by gl_load_history(). - -06/12/2001 mcs@astro.caltech.edu - configure.in configure - I removed my now redundant definition of SUN_TPUTS from - CFLAGS. I also added "-I/usr/include" to CFLAGS under - Solaris to prevent gcc from seeing conflicting versions - of system header files in /usr/local/include. - -06/12/2001 Markus Gyger (logged here by mcs) - Lots of files. - Lots of corrections to misspellings and typos in the - comments. - getline.c - Markus reverted a supposed fix that I added a day or two - ago. I had incorrectly thought that in Solaris 8, Sun had - finally brought their declaration of the callback - function of tputs() into line with other systems, but it - turned out that gcc was pulling in a GNU version of - term.h from /usr/local/include, and this was what - confused me. - -05/12/2001 mcs@astro.caltech.edu - Makefile.in - I added @CFLAGS@ to the CFLAGS assignment, so that - if CFLAGS is set as an environment variable when - configure is run, the corresponding make variable - includes its values in the output makefile. - -05/12/2001 mcs@astro.caltech.edu - getline.c libtecla.h libtecla.map man3/gl_get_line.3 - man3/gl_last_signal.3 - I added a function that programs can use to find out - which signal caused gl_get_line() to return EINTR. - -05/12/2001 mcs@astro.caltech.edu - getline.c - When the newline action was triggered by a printable - character, it failed to display that character. It now - does. Also, extra control codes that I had added, to - clear to the end of the display after the carriage return, - but before displaying the prompt, were confusing expect - scripts, so I have removed them. This step is now done - instead in gl_redisplay() after displaying the full input - line. - -05/12/2001 mcs@astro.caltech.edu - getline.c man3/gl_get_line.3 - A user convinced me that continuing to invoke meta - keybindings for meta characters that are printable is a - bad idea, as is allowing users to ask to have setlocale() - called behind the application's back. I have thus changed - this. The setlocale configuration option has gone, and - gl_get_line() is now completely 8-bit clean, by default. - This means that if a meta character is printable, it is - treated as a literal character, rather than a potential - M-c binding. Meta bindings can still be invoked via - their Esc-c equivalents, and indeed most terminal - emulators either output such escape pairs by default when - the meta character is pressed, or can be configured to do - so. I have documented how to configure xterm to do this, - in the man page. - -03/12/2001 mcs@astro.caltech.edu - getline.c man3/gl_get_line.3 - gl_get_line() by default now prints any 8-bit printable - characters that don't match keybindings. Previously - characters > 127 were only printed if preceded by the - literal-next action. Alternatively, by placing the - command literal_if_printable in the tecla configuration - file, all printable characters are treated as literal - characters, even if they are bound to action functions. - - For international users of programs written by - programmers that weren't aware of the need to call - setlocale() to support alternate character sets, the - configuration file can now also contain the single-word - command "setlocale", which tells gl_get_line() to remedy - this. - -27/11/2001 mcs@astro.caltech.edu - demo.c demo2.c enhance man3/gl_get_line.3 - All demos and programs now call setlocale(LC_CTYPE,""). - This makes them support character sets of different - locales, where specified with the LC_CTYPE, LC_ALL, or - LANG environment variables. I also added this to the demo - in the man page, and documented its effect. - -27/11/2001 mcs@astro.caltech.edu - getline.c - When displaying unsigned characters with values over - 127 literally, previously it was assumed that they would - all be displayable. Now isprint() is consulted, and if it - says that a character isn't printable, the character code - is displayed in octal like \307. In non-C locales, some - characters with values > 127 are displayable, and - isprint() tells gl_get_line() which are and which aren't. - -27/11/2001 mcs@astro.caltech.edu - getline.c pathutil.c history.c enhance.c demo2.c - All arguments of the ctype.h character class functions - are now cast to (int)(unsigned char). Previously they - were cast to (int), which doesn't correctly conform to - the requirements of the C standard, and could cause - problems for characters with values > 127 on systems - with signed char's. - -26/11/2001 mcs@astro.caltech.edu - man3/enhance.3 man3/libtecla.3 - I started writing a man page for the enhance program. - -26/11/2001 mcs@astro.caltech.edu - Makefile.in Makefile.rules INSTALL - It is now possible to specify whether the demos and other - programs are to be built, by overriding the default - values of the DEMOS, PROGRAMS and PROGRAMS_R variables. - I have also documented the BINDIR variable and the - install_bin makefile target. - -22/11/2001 mcs@astro.caltech.edu - getline.c libtecla.h libtecla.map man3/gl_get_line.3 - man3/gl_ignore_signal.3 man3/gl_trap_signal.3 - Signal handling has now been modified to be customizable. - Signals that are trapped by default can be removed from - the list of trapped signals, and signals that aren't - currently trapped, can be added to the list. Applications - can also specify the signal and terminal environments in - which an application's signal handler is invoked, and - what gl_get_line() does after the signal handler returns. - -13/11/2001 mcs@astro.caltech.edu - getline.c man3/gl_get_line.3 - Added half-bright, reverse-video and blinking text to the - available prompt formatting options. - getline.c - Removed ^O from the default VT100 sgr0 capability - string. Apparently it can cause problems with some - terminal emulators, and we don't need it, since it turns - off the alternative character set mode, which we don't - use. - getline.c - gl_tigetstr() and gl_tgetstr() didn't guard against the - error returns of tigetstr() and tgetstr() respectively. - They now do. - -11/11/2001 mcs@astro.caltech.edu - getline.c libtecla.h libtecla.map man3/gl_get_line.3 - man3/gl_prompt_style.3 - Although the default remains to display the prompt string - literally, the new gl_prompt_style() function can be used - to enable text attribute formatting directives in prompt - strings, such as underlining, bold font, and highlighting - directives. - -09/11/2001 mcs@astro.caltech.edu - enhance.c Makefile.rules configure.in configure - I added a new program to the distribution that allows one - to run most third party programs with the tecla library - providing command-line editing. - -08/11/2001 mcs@astro.caltech.edu - libtecla.h getline.c man3/gl_get_line.3 history.c history.h - I added a max_lines argument to gl_show_history() and - _glh_show_history(). This can optionally be used to - set a limit on the number of history lines displayed. - libtecla.h getline.c man3/gl_get_line.3 - I added a new function called gl_replace_prompt(). This - can be used by gl_get_line() callback functions to - request that a new prompt be use when they return. - -06/11/2001 mcs@astro.caltech.edu - getline.c man3/gl_get_line.3 - I implemented, bound and documented the list-history - action, used for listing historical lines of the current - history group. - getline.c man3/gl_get_line.3 man3/gl_echo_mode.3 - I wrote functions to specify and query whether subsequent - lines will be visible as they are being typed. - -28/10/2001 mcs@astro.caltech.edu - getline.c man3/gl_get_line.3 - For those cases where a terminal provides its own - high-level terminal editing facilities, you can now - specify an edit-mode argument of 'none'. This disables - all tecla key bindings, and by using canonical terminal - input mode instead of raw input mode, editing is left up - to the terminal driver. - -21/10/2001 mcs@astro.caltech.edu - libtecla.h getline.c history.c history.h - man3/gl_get_line.3 man3/gl_history_info.3 - I added the new gl_state_of_history(), - gl_range_of_history() and gl_size_of_history() - functions for querying information about the - history list. - history.c - While testing the new gl_size_of_history() - function, I noticed that when the history buffer - wrapped, any location nodes of old lines between - the most recent line and the end of the buffer - weren't being removed. This could result in bogus - entries appearing at the start of the history list. - Now fixed. - -20/10/2001 mcs@astro.caltech.edu - - libtecla.h getline.c history.c history.h - man3/gl_get_line.3 man3/gl_lookup_history.3 - I added a function called gl_lookup_history(), that - the application can use to lookup lines in the history - list. - libtecla.h getline.c history.c history.h man3/gl_get_line.3 - gl_show_history() now takes a format string argument - to control how the line is displayed, and with what - information. It also now provides the option of either - displaying all history lines or just those of the - current history group. - getline.c man3/gl_get_line.3 - gl_get_line() only archives lines in the history buffer - if the newline action was invoked by a newline or - carriage return character. - -16/10/2001 mcs@astro.caltech.edu - - history.c history.h getline.c libtecla.h libtecla.map - man3/gl_get_line.3 man3/gl_resize_history.3 - man3/gl_limit_history.3 man3/gl_clear_history.3 - man3/gl_toggle_history.3 - I added a number of miscellaneous history configuration - functions. You can now resize or delete the history - buffer, limit the number of lines that are allowed in the - buffer, clear either all history or just the history of - the current history group, and temporarily enable and - disable the history mechanism. - -13/10/2001 mcs@astro.caltech.edu - - getline.c - tputs_fp is now only declared if using termcap or - terminfo. - getline.c libtecla.map man3/gl_get_line.3 - man3/gl_terminal_size.3 - I added a public gl_terminal_size() function for - updating and querying the current size of the terminal. - update_version configure.in libtecla.h - A user noted that on systems where the configure script - couldn't be used, it was inconvenient to have the version - number macros set by the configure script, so they are - now specified in libtecla.h. To reduce the likelihood - that the various files where the version number now - appears might get out of sync, I have written the - update_version script, which changes the version number - in all of these files to a given value. - -01/10/2001 mcs@astro.caltech.edu - - getline.c history.c history.h man3/gl_get_line.3 - I added a max_lines argument to gl_save_history(), to - allow people to optionally place a ceiling on the number - of history lines saved. Specifying this as -1 sets the - ceiling to infinity. - -01/10/2001 mcs@astro.caltech.edu - - configure.in configure - Under digital unix, getline wouldn't compile with - _POSIX_C_SOURCE set, due to type definitions needed by - select being excluded by this flag. Defining the - _OSF_SOURCE macro as well on this system, resolved this. - -30/09/2001 mcs@astro.caltech.edu - - getline.c libtecla.h history.c history.h man3/gl_get_line.3 - man3/gl_group_history.3 - I implemented history streams. History streams - effectively allow multiple history lists to be stored in - a single history buffer. Lines in the buffer are tagged - with the current stream identification number, and - lookups only consider lines that are marked with the - current stream identifier. - getline.c libtecla.h history.c history.h man3/gl_get_line.3 - man3/gl_show_history.3 - The new gl_show_history function displays the current - history to a given stdio output stream. - -29/09/2001 mcs@astro.caltech.edu - - getline.c - Previously new_GetLine() installed a persistent signal - handler to be sure to catch the SIGWINCH (terminal size - change) signal between calls to gl_get_line(). This had - the drawback that if multiple GetLine objects were - created, only the first GetLine object used after the - signal was received, would see the signal and adapt to - the new terminal size. Instead of this, a signal handler - for sigwinch is only installed while gl_get_line() is - running, and just after installing this handler, - gl_get_line() checks for terminal size changes that - might have occurred while the signal handler wasn't - installed. - getline.c - Dynamically allocated copies of capability strings looked - up in the terminfo or termcap databases are now made, so - that calls to setupterm() etc for one GetLine object - don't get trashed when another GetLine object calls - setupterm() etc. It is now safe to allocate and use - multiple GetLine objects, albeit only within a single - thread. - -28/09/2001 mcs@astro.caltech.edu - - version.c Makefile.rules - I added a function for querying the version number of - the library. - -26/09/2001 mcs@astro.caltech.edu - - getline.c man3/gl_get_line.3 - I added the new gl_watch_fd() function, which allows - applications to register callback functions to be invoked - when activity is seen on arbitrary file descriptors while - gl_get_line() is awaiting keyboard input from the user. - - keytab.c - If a request is received to delete a non-existent - binding, which happens to be an ambiguous prefix of other - bindings no complaint is now generated about it being - ambiguous. - -23/09/2001 mcs@astro.caltech.edu - - getline.c history.c history.h man3/gl_get_line.3 - libtecla.map demo.c - I added new public functions for saving and restoring the - contents of the history list. The demo program now uses - these functions to load and save history in ~/.demo_history. - -23/09/2001 mcs@astro.caltech.edu - - getline.c - On trying the demo for the first time on a KDE konsole - terminal, I discovered that the default M-O binding - to repeat history was hiding the arrow keys, which are - M-OA etc. I have removed this binding. The M-o (ie the - lower case version of this), is still bound. - -18/09/2001 mcs@astro.caltech.edu - - getline.c man3/gl_get_line.3 libtecla.map - Automatic reading of ~/.teclarc is now postponed until - the first call to gl_get_line(), to give the application - the chance to specify alternative configuration sources - with the new function gl_configure_getline(). The latter - function allows configuration to be done with a string, a - specified application-specific file, and/or a specified - user-specific file. I also added a read-init-files action - function, for re-reading the configuration files, if any. - This is by default bound to ^X^R. This is all documented - in gl_get_line.3. - -08/09/2001 mcs@astro.caltech.edu - - getline.c man3/gl_get_line.3 - It is now possible to bind actions to key-sequences - that start with printable characters. Previously - keysequences were required to start with meta or control - characters. This is documented in gl_get_line.3. - - getline.c man3/gl_get_line.3 - A customized completion function can now arrange for - gl_get_line() to return the current input line whenever a - successful completion has been made. This is signalled by - setting the last character of the optional continuation - suffix to a newline character. This is documented in - gl_get_line.3. - -05/07/2001 Bug reported by Mike MacFaden, fixed by mcs - - configure.in - There was a bug in the configure script that only - revealed itself on systems without termcap but not - terminfo (eg. NetBSD). I traced the bug back to a lack of - sufficient quoting of multi-line m4 macro arguments in - configure.in, and have now fixed this and recreated the - configure script. - -05/07/2001 Bug reported and patched by Mike MacFaden (patch modified - by mcs to match original intentions). - - getline.c - getline.c wouldn't compile when termcap was selected as - the terminal information database. setupterm() was being - passed a non-existent variable, in place of the term[] - argument of gl_control_strings(). Also if - gl_change_terminal() is called with term==NULL, "ansi" - is now substituted. - -02/07/2001 Version 1.3.3 released. - -27/06/2001 mcs@astro.caltech.edu - - getline.c expand.c cplmatch.c - Added checks to fprintf() statements that write to the - terminal. - getline.c - Move the cursor to the end of the line before suspending, - so that the cursor doesn't get left in the middle of the - input line. - Makefile.in - On systems that don't support shared libraries, the - distclean target of make deleted libtecla.h. This has - now been fixed. - getline.c - gl_change_terminal() was being called by gl_change_editor(), - with the unwanted side effect that raw terminal modes were - stored as those to be restored later, if called by an - action function. gl_change_terminal() was being called in - this case to re-establish terminal-specific key bindings, - so I have just split this part of the function out into - a separate function for both gl_change_editor() and - gl_change_terminal() to call. - -12/06/2001 mcs@astro.caltech.edu - - getline.c - Signal handling has been improved. Many more signals are - now trapped, and instead of using a simple flag set by a - signal handler, race conditions are avoided by blocking - signals during most of the gl_get_line() code, and - unblocking them via calls to sigsetjmp(), just before - attempting to read each new character from the user. - The matching use of siglongjmp() in the signal - handlers ensures that signals are reblocked correctly - before they are handled. In most cases, signals cause - gl_get_line() to restore the terminal modes and signal - handlers of the calling application, then resend the - signal to the application. In the case of SIGINT, SIGHUP, - SIGPIPE, and SIGQUIT, if the process still exists after - the signals are resent, gl_get_line() immediately returns - with appropriate values assigned to errno. If SIGTSTP, - SIGTTIN or SIGTTOU signals are received, the process is - suspended. If any other signal is received, and the - process continues to exist after the signal is resent to - the calling application, line input is resumed after the - terminal is put back into raw mode, the gl_get_line() - signal handling is restored, and the input line redrawn. - man/gl_get_line(3) - I added a SIGNAL HANDLING section to the gl_get_line() - man page, describing the new signal handling features. - -21/05/2001 Version 1.3.2 released. - -21/05/2001 mcs@astro.caltech.edu - - getline.c - When vi-replace-char was used to replace the character at - the end of the line, it left the cursor one character to - its right instead of on top of it. Now rememdied. - getline.c - When undoing, to properly emulate vi, the cursor is now - left at the leftmost of the saved and current cursor - positions. - getline.c man3/gl_get_line.3 - Implemented find-parenthesis (%), delete-to-paren (M-d%), - vi-change-to-paren (M-c%), copy-to-paren (M-y%). - cplfile.c pcache.c - In three places I was comparing the last argument of - strncmp() to zero instead of the return value of - strncmp(). - -20/05/2001 mcs@astro.caltech.edu - - getline.c man3/gl_get_line.3 - Implemented and documented the vi-repeat-change action, - bound to the period key. This repeats the last action - that modified the input line. - -19/05/2001 mcs@astro.caltech.edu - - man3/gl_get_line.3 - I documented the new action functions and bindings - provided by Tim Eliseo, plus the ring-bell action and - the new "nobeep" configuration option. - getline.c - I modified gl_change_editor() to remove and reinstate the - terminal settings as well as the default bindings, since - these have editor-specific differences. I also modified - it to not abort if a key-sequence can't be bound for some - reason. This allows the new vi-mode and emacs-mode - bindings to be used safely. - getline.c - When the line was re-displayed on receipt of a SIGWINCH - signal, the result wasn't visible until the next - character was typed, since a call to fflush() was needed. - gl_redisplay_line() now calls gl_flush_output() to remedy - this. - -17/05/2001 mcs@astro.catlech.edu - - getline.c - Under Linux, calling fflush(gl->output_fd) hangs if - terminal output has been suspended with ^S. With the - tecla library taking responsability for reading the stop - and start characters this was a problem, because once - hung in fflush(), the keyboard input loop wasn't entered, - so the user couldn't type the start character to resume - output. To remedy this, I now have the terminal process - these characters, rather than the library. - -12/05/2001 mcs@astro.caltech.edu - - getline.c - The literal-next action is now implemented as a single - function which reads the next character itself. - Previously it just set a flag which effected the - interpretation of the next character read by the input - loop. - getline.c - Added a ring-bell action function. This is currently - unbound to any key by default, but it is used internally, - and can be used by users that want to disable any of the - default key-bindings. - -12/05/2001 Tim Eliseo (logged here by mcs) - - getline.c - Don't reset gl->number until after calling an action - function. By looking at whether gl->number is <0 or - not, action functions can then tell whether the count - that they were passed was explicitly specified by the - user, as opposed to being defaulted to 1. - getline.c - In vi, the position at which input mode is entered - acts as a barrier to backward motion for the few - backward moving actions that are enabled in input mode. - Tim added this barrier to getline. - getline.c - In gl_get_line() after reading an input line, or - having the read aborted by a signal, the sig_atomic_t - gl_pending_signal was being compared to zero instead - of -1 to see if no signals had been received. - gl_get_line() will thus have been calling raise(-1), - which luckily didn't seem to do anything. Tim also - arranged for errno to be set to EINTR when a signal - aborts gl_get_line(). - getline.c - The test in gl_add_char_to_line() for detecting - when overwriting a character with a wider character, - had a < where it needed a >. Overwriting with a wider - character thus overwrote trailing characters. Tim also - removed a redundant copy of the character into the - line buffer. - getline.c - gl_cursor_left() and gl->cursor_right() were executing - a lot of redundant code, when the existing call to the - recently added gl_place_cursor() function, does all that - is necessary. - getline.c - Remove redundant code from backward_kill_line() by - re-implimenting in terms of gl_place_cursor() and - gl_delete_chars(). - getline.c - gl_forward_delete_char() now records characters in cut - buffer when in vi command mode. - getline.c - In vi mode gl_backward_delete_char() now only deletes - up to the point at which input mode was entered. Also - gl_delete_chars() restores from the undo buffer when - deleting in vi insert mode. - getline.c - Added action functions, vi-delete-goto-column, - vi-change-to-bol, vi-change-line, emacs-mode, vi-mode, - vi-forward-change-find, vi-backward-change-find, - vi-forward-change-to, vi-backward-change-to, - vi-change-goto-col, forward-delete-find, backward-delete-find, - forward-delete-to, backward-delete-to, - delete-refind, delete-invert-refind, forward-copy-find, - backward-copy-find, forward-copy-to, backward-copy-to - copy-goto-column, copy-rest-of-line, copy-to-bol, copy-line, - history-re-search-forward, history-re-search-backward. - -06/05/2001 Version 1.3.1 released. - -03/05/2001 mcs@astro.caltech.edu - - configure.in - Old versions of GNU ld don't accept version scripts. - Under Linux I thus added a test to try out ld with - the --version-script argument to see if it works. - If not, version scripts aren't used. - configure.in - My test for versions of Solaris earlier than 7 - failed when confronted by a three figure version - number (2.5.1). Fixed. - -30/04/2001 mcs@astro.caltech.edu - - getline.c - In vi mode, history-search-backward and - history-search-forward weren't doing anything when - invoked at the start of an empty line, whereas - they should have acted like up-history and down-history. - Makefile.in Makefile.rules - When shared libraries are being created, the build - procedure now arranges for any alternate library - links to be created as well, before linking the - demos. Without this the demos always linked to the - static libraries (which was perfectly ok, but wasn't a - good example). - Makefile.in Makefile.rules - On systems on which shared libraries were being created, - if there were no alternate list of names, make would - abort due to a Bourne shell 'for' statement that didn't - have any arguments. Currently there are no systems who's - shared library configurations would trigger this - problem. - Makefile.rules - The demos now relink to take account of changes to the - library. - configure.in configure - When determining whether the reentrant version of the - library should be compiled by default, the configure - script now attempts to compile a dummy program that - includes all of the appropriate system headers and - defines _POSIX_C_SOURCE. This should now be a robust test - on systems which use C macros to alias these function - names to other internal functions. - configure.in - Under Solaris 2.6 and earlier, the curses library is in - /usr/ccs/lib. Gcc wasn't finding this. In addition to - remedying this, I had to remove "-z text" from - LINK_SHARED under Solaris to get it to successfully - compile the shared library against the static curses - library. - configure.in - Under Linux the -soname directive was being used - incorrectly, citing the fully qualified name of the - library instead of its major version alias. This will - unfortunately mean that binaries linked with the 1.2.3 - and 1.2.4 versions of the shared library won't use - later versions of the library unless relinked. - -30/04/2001 mcs@astro.caltech.edu - - getline.c - In gl_get_input_line(), don't redundantly copy the - start_line if start_line == gl->line. - -30/04/2001 Version 1.3.0 released. - -28/04/2001 mcs@astro.caltech.edu - - configure.in - I removed the --no-undefined directive from the Linux - LINK_SHARED command. After recent patches to our RedHat - 7.0 systems ld started reporting some internal symbols of - libc as being undefined. Using nm on libc indicated that - the offending symbols are indeed defined, albeit as - "common" symbols, so there appears to be a bug in - RedHat's ld. Removing this flag allows the tecla shared - library to compile, and programs appear to function fine. - man3/gl_get_line.3 - The default key-sequence used to invoke the - read-from-file action was incorrectly cited as ^Xi - instead of ^X^F. - -26/04/2001 mcs@astro.caltech.edu - - getline.c man3/gl_get_line.3 - A new vi-style editing mode was added. This involved - adding many new action functions, adding support for - specifying editing modes in users' ~/.teclarc files, - writing a higher level cursor motion function to support - the different line-end bounds required in vi command - mode, and a few small changes to support the fact that vi - has two modes, input mode and command mode with different - bindings. - - When vi editing mode is enabled, any binding that starts - with an escape or a meta character, is interpreted as a - command-mode binding, and switches the library to vi - command mode if not already in that mode. Once in command - mode the first character of all keysequences entered - until input mode is re-enabled, are quietly coerced to - meta characters before being looked up in the key-binding - table. So, for example, in the key-binding table, the - standard vi command-mode 'w' key, which moves the cursor - one word to the right, is represented by M-w. This - emulates vi's dual sets of bindings in a natural way - without needing large changes to the library, or new - binding syntaxes. Since cursor keys normally emit - keysequences which start with escape, it also does - something sensible when a cursor key is pressed during - input mode (unlike true vi, which gets upset). - - I also added a ^Xg binding for the new list-glob action - to both the emacs and vi key-binding tables. This lists - the files that match the wild-card expression that - precedes it on the command line. - - The function that reads in ~/.teclarc used to tell - new_GetLine() to abort if it encountered anything that it - didn't understand in this file. It now just reports an - error and continues onto the next line. - Makefile.in: - When passing LIBS=$(LIBS) to recursive invokations of - make, quotes weren't included around the $(LIBS) part. - This would cause problems if LIBS ever contained more - than one word (with the supplied configure script this - doesn't happen currently). I added these quotes. - expand.c man3/ef_expand_file.3: - I wrote a new public function called ef_list_expansions(), - to list the matching filenames returned by - ef_expand_file(). - - I also fixed the example in the man page, which cited - exp->file instead of exp->files, and changed the - dangerous name 'exp' with 'expn'. - keytab.c: - Key-binding tables start with 100 elements, and are - supposedly incremented in size by 100 elements whenever - the a table runs out of space. The realloc arguments to - do this were wrong. This would have caused problems if - anybody added a lot of personal bindings in their - ~/.teclarc file. I only noticed it because the number of - key bindings needed by the new vi mode exceeded this - number. - libtecla.map - ef_expand_file() is now reported as having been added in - the upcoming 1.3.0 release. - -25/03/2001 Markus Gyger (logged here by mcs) - - Makefile.in: - Make symbolic links to alternative shared library names - relative instead of absolute. - Makefile.rules: - The HP-UX libtecla.map.opt file should be made in the - compilation directory, to allow the source code directory - to be on a readonly filesystem. - cplmatch.c demo2.c history.c pcache.c - To allow the library to be compiled with a C++ compiler, - without generating warnings, a few casts were added where - void* return values were being assigned directly to - none void* pointer variables. - -25/03/2001 mcs@astro.caltech.edu - - libtecla.map: - Added comment header to explain the purpose of the file. - Also added cpl_init_FileArgs to the list of exported - symbols. This symbol is deprecated, and no longer - documented, but for backwards compatibility, it should - still be exported. - configure: - I had forgotten to run autoconf before releasing version - 1.2.4, so I have just belatedly done so. This enables - Markus' changes to "configure.in" documented previously, - (see 17/03/2001). - -20/03/2001 John Levon (logged here by mcs) - - libtecla.h - A couple of the function prototypes in libtecla.h have - (FILE *) argument declarations, which means that stdio.h - needs to be included. The header file should be self - contained, so libtecla.h now includes stdio.h. - -18/03/2001 Version 1.2.4 released. - - README html/index.html configure.in - Incremented minor version from 3 to 4. - -18/03/2001 mcs@astro.caltech.edu - - getline.c - The fix for the end-of-line problem that I released a - couple of weeks ago, only worked for the first line, - because I was handling this case when the cursor position - was equal to the last column, rather than when the cursor - position modulo ncolumn was zero. - Makefile.in Makefile.rules - The demos are now made by default, their rules now being - int Makefile.rules instead of Makefile.in. - INSTALL - I documented how to compile the library in a different - directory than the distribution directory. - I also documented features designed to facilitate - configuring and building the library as part of another - package. - -17/03/2001 Markus Gyger (logged here by mcs) - - getline.c - Until now cursor motions were done one at a time. Markus - has added code to make use the of the terminfo capability - that moves the cursor by more than one position at a - time. This greatly improves performance when editing near - the start of long lines. - getline.c - To further improve performance, Markus switched from - writing one character at a time to the terminal, using - the write() system call, to using C buffered output - streams. The output buffer is only flushed when - necessary. - Makefile.rules Makefile.in configure.in - Added support for compiling for different architectures - in different directories. Simply create another directory - and run the configure script located in the original - directory. - Makefile.in configure.in libtecla.map - Under Solaris, Linux and HP-UX, symbols that are to be - exported by tecla shared libraries are explicitly specified - via symbol map files. Only publicly documented functions - are thus visible to applications. - configure.in - When linking shared libraries under Solaris SPARC, - registers that are reserved for applications are marked - as off limits to the library, using -xregs=no%appl when - compiling with Sun cc, or -mno-app-regs when compiling - with gcc. Also removed -z redlocsym for Solaris, which - caused problems under some releases of ld. - homedir.c (after minor changes by mcs) - Under ksh, ~+ expands to the current value of the ksh - PWD environment variable, which contains the path of - the current working directory, including any symbolic - links that were traversed to get there. The special - username "+" is now treated equally by tecla, except - that it substitutes the return value of getcwd() if PWD - either isn't set, or if it points at a different - directory than that reported by getcwd(). - -08/03/2001 Version 1.2.3 released. - -08/03/2001 mcs@astro.caltech.edu - - getline.c - On compiling the library under HP-UX for the first time - I encountered and fixed a couple of bugs: - - 1. On all systems except Solaris, the callback function - required by tputs() takes an int argument for the - character that is to be printed. Under Solaris it - takes a char argument. The callback function was - passing this argument, regardless of type, to write(), - which wrote the first byte of the argument. This was - fine under Solaris and under little-endian systems, - because the first byte contained the character to be - written, but on big-endian systems, it always wrote - the zero byte at the other end of the word. As a - result, no control characters were being written to - the terminal. - 2. While attempting to start a newline after the user hit - enter, the library was outputting the control sequence - for moving the cursor down, instead of the newline - character. On many systems the control sequence for - moving the cursor down happends to be a newline - character, but under HP-UX it isn't. The result was - that no new line was being started under HP-UX. - -04/03/2001 mcs@astro.caltech.edu - - configure.in Makefile.in Makefile.stub configure config.guess - config.sub Makefile.rules install-sh PORTING README INSTALL - Configuration and compilation of the library is now - performed with the help of an autoconf configure - script. In addition to relieving the user of the need to - edit the Makefile, this also allows automatic compilation - of the reentrant version of the library on platforms that - can handle it, along with the creation of shared - libraries where configured. On systems that aren't known - to the configure script, just the static tecla library is - compiled. This is currently the case on all systems - except Linux, Solaris and HP-UX. In the hope that - installers will provide specific conigurations for other - systems, the configure.in script is heavily commented, - and instructions on how to use are included in a new - PORTING file. - -24/02/2001 Version 1.2b released. - -22/02/2001 mcs@astro.caltech.edu - - getline.c - It turns out that most terminals, but not all, on writing - a character in the rightmost column, don't wrap the - cursor onto the next line until the next character is - output. This library wasn't aware of this and thus if one - tried to reposition the cursor from the last column, - gl_get_line() thought that it was moving relative to a - point on the next line, and thus moved the cursor up a - line. The fix was to write one extra character when in - the last column to force the cursor onto the next line, - then backup the cursor to the start of the new line. - getline.c - On terminal initialization, the dynamic LINES and COLUMNS - environment variables were ignored unless - terminfo/termcap didn't return sensible dimensions. In - practice, when present they should override the static - versions in the terminfo/termcap databases. This is the - new behavior. In reality this probably won't have caused - many problems, because a SIGWINCH signal which informs of - terminal size changes is sent when the terminal is - opened, so the dimensions established during - initialization quickly get updated on most systems. - -18/02/2001 Version 1.2a released. - -18/02/2001 mcs@astro.caltech.edu - - getline.c - Three months ago I moved the point at which termios.h - was included in getline.c. Unfortunately, I didn't notice - that this moved it to after the test for TIOCGWINSZ being - defined. This resulted in SIGWINCH signals not being - trapped for, and thus terminal size changes went - unnoticed. I have now moved the test to after the - inclusion of termios.h. - -12/02/2001 Markus Gyger (described here by mcs) - - man3/pca_lookup_file.3 man3/gl_get_line.3 - man3/ef_expand_file.3 man3/cpl_complete_word.3 - In the 1.2 release of the library, all functions in the - library were given man pages. Most of these simply - include one of the above 4 man pages, which describe the - functions while describing the modules that they are in. - Markus added all of these function names to the lists in - the "NAME" headers of the respective man pages. - Previously only the primary function of each module was - named there. - -11/02/2001 mcs@astro.caltech.edu - - getline.c - On entering a line that wrapped over two or more - terminal, if the user pressed enter when the cursor - wasn't on the last of the wrapped lines, the text of the - wrapped lines that followed it got mixed up with the next - line written by the application, or the next input - line. Somehow this slipped through the cracks and wasn't - noticed until now. Anyway, it is fixed now. - -09/02/2001 Version 1.2 released. - -04/02/2001 mcs@astro.caltech.edu - - pcache.c libtecla.h - With all filesystems local, demo2 was very fast to start - up, but on a Sun system with one of the target - directories being on a remote nfs mounted filesystem, the - startup time was many seconds. This was due to the - executable selection callback being applied to all files - in the path at startup. To avoid this, all files are now - included in the cache, and the application specified - file-selection callback is only called on files as they - are matched. Whether the callback rejected or accepted - them is then cached so that the next time an already - checked file is looked at, the callback doesn't have to - be called. As a result, startup is now fast on all - systems, and since usually there are only a few matching - file completions at a time, the delay during completion - is also usually small. The only exception is if the user - tries to complete an empty string, at which point all - files have to be checked. Having done this once, however, - doing it again is fast. - man3/pca_lookup_file.3 - I added a man page documenting the new PathCache module. - man3/.3 - I have added man pages for all of the functions in each - of the modules. These 1-line pages use the .so directive - to redirect nroff to the man page of the parent module. - man Makefile update_html - I renamed man to man3 to make it easier to test man page - rediction, and updated Makefile and update_html - accordingly. I also instructed update_html to ignore - 1-line man pages when making html equivalents of the man - pages. - cplmatch.c - In cpl_list_completions() the size_t return value of - strlen() was being used as the length argument of a "%*s" - printf directive. This ought to be an int, so the return - value of strlen() is now cast to int. This would have - caused problems on architectures where the size of a - size_t is not equal to the size of an int. - -02/02/2001 mcs@astro.caltech.edu - - getline.c - Under UNIX, certain terminal bindings are set using the - stty command. This, for example, specifies which control - key generates a user-interrupt (usually ^C or ^Y). What I - hadn't realized was that ASCII NUL is used as the way to - specify that one of these bindings is unset. I have now - modified the code to skip unset bindings, leaving the - corresponding action bound to the built-in default, or a - user provided binding. - -28/01/2001 mcs@astro.caltech.edu - - pcache.c libtecla.h - A new module was added which supports searching for files - in any colon separated list of directories, such as the - unix execution PATH environment variable. Files in these - directories, after being individually okayed for - inclusion via an application provided callback, are - cached in a PathCache object. You can then look up the - full pathname of a given filename, or you can use the - provided completion callback to list possible completions - in the path-list. The contents of relative directories, - such as ".", obviously can't be cached, so these - directories are read on the fly during lookups and - completions. The obvious application of this facility is - to provide Tab-completion of commands, and thus a - callback to place executable files in the cache, is - provided. - demo2.c - This new program demonstrates the new PathCache - module. It reads and processes lines of input until the - word 'exit' is entered, or C-d is pressed. The default - tab-completion callback is replaced with one which at the - start of a line, looks up completions of commands in the - user's execution path, and when invoked in other parts of - the line, reverts to normal filename completion. Whenever - a new line is entered, it extracts the first word on the - line, looks it up in the user's execution path to see if - it corresponds to a known command file, and if so, - displays the full pathname of the file, along with the - remaining arguments. - cplfile.c - I added an optional pair of callback function/data - members to the new cpl_file_completions() configuration - structure. Where provided, this callback is asked - on a file-by-file basis, which files should be included - in the list of file completions. For example, a callback - is provided for listing only completions of executable - files. - cplmatch.c - When listing completions, the length of the type suffix - of each completion wasn't being taken into account - correctly when computing the column widths. Thus the - listing appeared ragged sometimes. This is now fixed. - pathutil.c - I added a function for prepending a string to a path, - and another for testing whether a pathname referred to - an executable file. - -28/01/2001 mcs@astro.caltech.edu - - libtecla.h cplmatch.c man/cpl_complete_word.3 - The use of a publically defined structure to configure - the cpl_file_completions() callback was flawed, so a new - approach has been designed, and the old method, albeit - still supported, is no longer documented in the man - pages. The definition of the CplFileArgs structure in - libtecla.h is now accompanied by comments warning people - not to modify it, since modifications could break - applications linked to shared versions of the tecla - library. The new method involves an opaque CplFileConf - object, instances of which are returned by a provided - constructor function, configured with provided accessor - functions, and when no longer needed, deleted with a - provided destructor function. This is documented in the - cpl_complete_word man page. The cpl_file_completions() - callback distinguishes what type of configuration - structure it has been sent by virtue of a code placed at - the beginning of the CplFileConf argument by its - constructor. - -04/01/2001 mcs@astro.caltech.edu (Release of version 1.1j) - - getline.c - I added upper-case bindings for the default meta-letter - keysequences such as M-b. They thus continue to work - when the user has caps-lock on. - Makefile - I re-implemented the "install" target in terms of new - install_lib, install_inc and install_man targets. When - distributing the library with other packages, these new - targets allows for finer grained control of the - installation process. - -30/12/2000 mcs@astro.caltech.edu - - getline.c man/gl_get_line.3 - I realized that the recall-history action that I - implemented wasn't what Markus had asked me for. What he - actually wanted was for down-history to continue going - forwards through a previous history recall session if no - history recall session had been started while entering - the current line. I have thus removed the recall-history - action and modified the down-history action function - accordingly. - -24/12/2000 mcs@astro.caltech.edu - - getline.c - I modified gl_get_line() to allow the previously returned - line to be passed in the start_line argument. - getline.c man/gl_get_line.3 - I added a recall-history action function, bound to M^P. - This recalls the last recalled history line, regardless - of whether it was from the current or previous line. - -13/12/2000 mcs@astro.caltech.edu (Release of version 1.1i) - - getline.c history.h history.c man/gl_get_line.3 - I implemented the equivalent of the ksh Operate action. I - have named the tecla equivalent "repeat-history". This - causes the line that is to be edited to returned, and - arranges for the next most recent history line to be - preloaded on the next call to gl_get_line(). Repeated - invocations of this action thus result in successive - history lines being repeated - hence the - name. Implementing the ksh Operate action was suggested - by Markus Gyger. In ksh it is bound to ^O, but since ^O - is traditionally bound by the default terminal settings, - to stop-output, I have bound the tecla equivalent to M-o. - -01/12/2000 mcs@astro.caltech.edu (Release of version 1.1h) - - getline.c keytab.c keytab.h man/gl_get_line.3 - I added a digit-argument action, to allow repeat - counts for actions to be entered. As in both tcsh - and readline, this is bound by default to each of - M-0, M-1 through to M-9, the number being appended - to the current repeat count. Once one of these has been - pressed, the subsequent digits of the repeat count can be - typed with or without the meta key pressed. It is also - possible to bind digit-argument to other keys, with or - without a numeric final keystroke. See man page for - details. - - getline.c man/gl_get_line.3 - Markus noted that my choice of M-< for the default - binding of read-from-file, could be confusing, since - readline binds this to beginning-of-history. I have - thus rebound it to ^X^F (ie. like find-file in emacs). - - getline.c history.c history.h man/gl_get_line.3 - I have now implemented equivalents of the readline - beginning-of-history and end-of-history actions. - These are bound to M-< and M-> respectively. - - history.c history.h - I Moved the definition of the GlHistory type, and - its subordinate types from history.h to history.c. - There is no good reason for any other module to - have access to the innards of this structure. - -27/11/2000 mcs@astro.caltech.edu (Release of version 1.1g) - - getline.c man/gl_get_line.3 - I added a "read-from-file" action function and bound it - by default to M-<. This causes gl_get_line() to - temporarily return input from the file who's name - precedes the cursor. - -26/11/2000 mcs@astro.caltech.edu - - getline.c keytab.c keytab.h man/gl_get_line.3 - I have reworked some of the keybinding code again. - - Now, within key binding strings, in addition to the - previously existing notation, you can now use M-a to - denote meta-a, and C-a to denote control-a. For example, - a key binding which triggers when the user presses the - meta key, the control key and the letter [ - simultaneously, can now be denoted by M-C-[, or M-^[ or - \EC-[ or \E^[. - - I also updated the man page to use M- instead of \E in - the list of default bindings, since this looks cleaner. - - getline.c man/gl_get_line.3 - I added a copy-region-as-kill action function and - gave it a default binding to M-w. - -22/11/2000 mcs@astro.caltech.edu - - *.c - Markus Gyger sent me a copy of a previous version of - the library, with const qualifiers added in appropriate - places. I have done the same for the latest version. - Among other things, this gets rid of the warnings - that are generated if one tells the compiler to - const qualify literal strings. - - getline.c getline.h glconf.c - I have moved the contents of glconf.c and the declaration - of the GetLine structure into getline.c. This is cleaner, - since now only functions in getline.c can mess with the - innards of GetLine objects. It also clears up some problems - with system header inclusion order under Solaris, and also - the possibility that this might result in inconsistent - system macro definitions, which in turn could cause different - declarations of the structure to be seen in different files. - - hash.c - I wrote a wrapper function to go around strcmp(), such that - when hash.c is compiled with a C++ compiler, the pointer - to the wrapper function is a C++ function pointer. - This makes it compatible with comparison function pointer - recorded in the hash table. - - cplmatch.c getline.c libtecla.h - Markus noted that the Sun C++ compiler wasn't able to - match up the declaration of cpl_complete_word() in - libtecla.h, where it is surrounded by a extern "C" {} - wrapper, with the definition of this function in - cplmatch.c. My suspicion is that the compiler looks not - only at the function name, but also at the function - arguments to see if two functions match, and that the - match_fn() argument, being a fully blown function pointer - declaration, got interpetted as that of a C function in - one case, and a C++ function in the other, thus - preventing a match. - - To fix this I now define a CplMatchFn typedef in libtecla.h, - and use this to declare the match_fn callback. - -20/11/2000 (Changes suggested by Markus Gyger to support C++ compilers): - expand.c - Renamed a variable called "explicit" to "xplicit", to - avoid conflicts when compiling with C++ compilers. - *.c - Added explicit casts when converting from (void *) to - other pointer types. This isn't needed in C but it is - in C++. - getline.c - tputs() has a strange declaration under Solaris. I was - enabling this declaration when the SPARC feature-test - macro was set. Markus changed the test to hinge on the - __sun and __SVR4 macros. - direader.c glconf.c stringrp.c - I had omitted to include string.h in these two files. - - Markus also suggested some other changes, which are still - under discussion. With the just above changes however, the - library compiles without complaint using g++. - -19/11/2000 mcs@astro.caltech.edu - getline.h getline.c keytab.c keytab.h glconf.c - man/gl_get_line.3 - I added support for backslash escapes (include \e - for the keyboard escape key) and literal binary - characters to the characters allowed within key sequences - of key bindings. - - getline.h getline.c keytab.c keytab.h glconf.c - man/gl_get_line.3 - I introduced symbolic names for the arrow keys, and - modified the library to use the cursor key sequences - reported by terminfo/termcap in addition to the default - ANSI ones. Anything bound to the symbolically named arrow - keys also gets bound to the default and terminfo/termcap - cursor key sequences. Note that under Solaris - terminfo/termcap report the properties of hardware X - terminals when TERM is xterm instead of the terminal - emulator properties, and the cursor keys on these two - systems generate different key sequences. This is an - example of why extra default sequences are needed. - - getline.h getline.c keytab.c - For some reason I was using \e to represent the escape - character. This is supported by gcc, which thus doesn't - emit a warning except with the -pedantic flag, but isn't - part of standard C. I now use a macro to define escape - as \033 in getline.h, and this is now used wherever the - escape character is needed. - -17/11/2000 mcs@astro.caltech.edu (Release of version 1.1d) - - getline.c, man/gl_get_line(3), html/gl_get_line.html - In tcsh ^D is bound to a function which does different - things depending on where the cursor is within the input - line. I have implemented its equivalent in the tecla - library. When invoked at the end of the line this action - function displays possible completions. When invoked on - an empty line it causes gl_get_line() to return NULL, - thus signalling end of input. When invoked within a line - it invokes forward-delete-char, as before. The new action - function is called del-char-or-list-or-eof. - - getline.c, man/gl_get_line(3), html/gl_get_line.html - I found that the complete-word and expand-file actions - had underscores in their names instead of hyphens. This - made them different from all other action functions, so I - have changed the underscores to hyphens. - - homedir.c - On SCO UnixWare while getpwuid_r() is available, the - associated _SC_GETPW_R_SIZE_MAX macro used by sysconf() - to find out how big to make the buffer to pass to this - function to cater for any password entry, doesn't - exist. I also hadn't catered for the case where sysconf() - reports that this limit is indeterminate. I have thus - change the code to substitute a default limit of 1024 if - either the above macro isn't defined or if sysconf() says - that the associated limit is indeterminate. - -17/11/2000 mcs@astro.caltech.edu (Release of version 1.1c) - - getline.c, getline.h, history.c, history.h - I have modified the way that the history recall functions - operate, to make them better emulate the behavior of - tcsh. Previously the history search bindings always - searched for the prefix that preceded the cursor, then - left the cursor at the same point in the line, so that a - following search would search using the same prefix. This - isn't how tcsh operates. On finding a matching line, tcsh - puts the cursor at the end of the line, but arranges for - the followup search to continue with the same prefix, - unless the user does any cursor motion or character - insertion operations in between, in which case it changes - the search prefix to the new set of characters that are - before the cursor. There are other complications as well, - which I have attempted to emulate. As far as I can - tell, the tecla history recall facilities now fully - emulate those of tcsh. - -16/11/2000 mcs@astro.caltech.edu (Release of version 1.1b) - - demo.c: - One can now quit from the demo by typing exit. - - keytab.c: - The first entry of the table was getting deleted - by _kt_clear_bindings() regardless of the source - of the binding. This deleted the up-arrow binding. - Symptoms noted by gazelle@yin.interaccess.com. - - getline.h: - Depending on which system include files were include - before the inclusion of getline.h, SIGWINCH and - TIOCGWINSZ might or might not be defined. This resulted - in different definitions of the GetLine object in - different files, and thus some very strange bugs! I have - now added #includes for the necessary system header files - in getline.h itself. The symptom was that on creating a - ~/.teclarc file, the demo program complained of a NULL - argument to kt_set_keybinding() for the first line of the - file. - -15/11/2000 mcs@astro.caltech.edu (Release of version 1.1a) - - demo.c: - I had neglected to check the return value of - new_GetLine() in the demo program. Oops. - - getline.c libtecla.h: - I wrote gl_change_terminal(). This allows one to change to - a different terminal or I/O stream, by specifying the - stdio streams to use for input and output, along with the - type of terminal that they are connected to. - - getline.c libtecla.h: - Renamed GetLine::isterm to GetLine::is_term. Standard - C reserves names that start with "is" followed by - alphanumeric characters, so this avoids potential - clashes in the future. - - keytab.c keytab.h - Each key-sequence can now have different binding - functions from different sources, with the user provided - binding having the highest precedence, followed by the - default binding, followed by any terminal specific - binding. This allows gl_change_terminal() to redefine the - terminal-specific bindings each time that - gl_change_terminal() is called, without overwriting the - user specified or default bindings. In the future, it will - also allow for reconfiguration of user specified - bindings after the call to new_GetLine(). Ie. deleting a - user specified binding should reinstate any default or - terminal specific binding. - - man/cpl_complete_word.3 html/cpl_complete_word.html - man/ef_expand_file.3 html/ef_expand_file.html - man/gl_get_line.3 html/gl_get_line.html - I added sections on thread safety to the man pages of the - individual modules. - - man/gl_get_line.3 html/gl_get_line.html - I documented the new gl_change_terminal() function. - - man/gl_get_line.3 html/gl_get_line.html - In the description of the ~/.teclarc configuration file, - I had omitted the 'bind' command word in the example - entry. I have now remedied this. diff --git a/libtecla-1.4.1/INSTALL b/libtecla-1.4.1/INSTALL deleted file mode 100644 index 1a1b036..0000000 --- a/libtecla-1.4.1/INSTALL +++ /dev/null @@ -1,168 +0,0 @@ -To compile and optionally install the library, it is first necessary -to create a makefile for your system, by typing: - - ./configure - -The Makefile that this generates is designed to install the files of -the library in subdirectories of /usr/local/. If you would prefer to -install them under a different directory, you can type: - - ./configure --prefix /wherever - -Where you would replace /wherever with your chosen directory. Other -command-line options are available, and can be listed by typing: - - ./configure --help - -Having run the configure script, you are then ready to make the -library. To do this, just type: - - make - -What 'make' does depends on whether the configure script knows about -your system. If the configure script doesn't know anything specific -about your system, it will arrange for 'make' to produce the static -tecla library, called libtecla.a, and if possible, the reentrant -version of this called libtecla_r.a. If it does know about your -system, it will also create shared libraries if possible. If you are -on a system that isn't known, and you would like shared libraries to -be compiled, please read the file called PORTING to see how this can -be achieved. - -To install the library, its include file and it manual pages, type: - - make install - -Note that this will also compile the library if you haven't already -done so. - -Having compiled the library, if you wish, you can test it by running -the demo programs. After building the library, you should find two -programs, called demo and demo2, in the current directory. - -The first of the demos programs reads input lines from the user, and -writes what was typed back to the screen. While typing a line of -input, you can experiment with line editing, tab completion, history -recall etc.. For details about these line editing features, see the -man page gl_get_line(3). If you haven't installed this yet, you can -see it anyway by typing: - - nroff -man man3/gl_get_line.3 | more - -The second demo program, called demo2, demonstrates command-completion -with the UNIX PATH. If you type in a partial command name, and press -TAB, the command name will be completed if possible, and possible -completions will be listed if it is ambiguous. When you then enter the -line, the demo program then prints out the full pathname of the -command that you typed. If you type anything after the command name, -filename completion with the tab key reverts to its default behavior -of completing filenames in the current directory. - -COMPILING IN A DIFFERENT DIRECTORY ----------------------------------- -If you unpack the distribution in a directory which is visible from -multiple hosts which have different architectures, you have the option -of compiling the library for the different architectures in different -directories. You might for example create a sub-directory for each -architecture, under the top level directory of the distribution. You -would then log in to a host of one of these architectures, cd to the -sub-directory that you created for it, and type: - - ../configure - -The configure script then creates a makefile in the current directory -which is designed to build the library, object files, demos etc for -the architecture of the current host, in the current directory, using -the original source code in ../. You then repeat this procedure on -hosts of other architectures. - -The compilation directories don't have to be sub-directories of the -top level directory of the distribution. That was just described as an -example. They can be anywhere you like. - -Every rule in the makefiles that are generated by the configure -script, cites the paths of the target and source files explicitly, so -this procedure should work on any system, without the need for vpath -makefile support. - -EMBEDDING IN OTHER PACKAGE DISTRIBUTIONS ----------------------------------------- - -If you distribute the library with another package, which has its own -heirarchy and configuration procedures, the following installation -options may be of interest to you. At first glance, the use of a GNU -configure script by the tecla library, may appear to reduce your -options for controlling what gets made, and where it gets installed, -but this isn't the case, because many of the parameters configured by -the configure script are assigned to make variables which can be -overriden when you run make. - -For example, lets say that you have your own configuration script in -the parent directory of the libtecla top-level directory. In your -configuration script, you would first need to have the following line: - - (cd libtecla; ./configure) - -Now, from your makefile or whatever script you use to build your -application, you would need to make the library. Assuming that your -makefile or build script is in the parent directory of the libtecla -distribution, the following line tells make to just make the -non-reentrant, static version of the tecla library, and to install it -and the tecla include file in sub-directories called lib and include -in your current directory. - - (cd libtecla; make LIBDIR=../lib INCDIR=../include TARGETS=normal TARGET_LIBS="static" install_lib install_inc) - -First, the LIBDIR=../lib means that on installing the library, it -should be placed in the directory libtecla/../lib. Similarly INCDIR -tells make where to place the include files. The install_lib and -install_inc targets tell make to install the libraries and the include -file. Because the install_man and install_bin targets have been -omitted in this example, the man pages and programs aren't installed. -If you were to include these additional targets then you could use the -MANDIR and BINDIR variables, respectively to control where they were -installed. - -The TARGETS variable is used to specify which of the normal and -reentrant versions of the library are compiled. This can contain one -or both of the words "normal" and "reentrant". If you don't specify -this when you invoke make, the default value generated by the -configure script will be used. Depending on whether reentrant POSIX -functions are available for compilation of the reentrant version, this -will be either "normal" or "normal reentrant". - -The TARGET_LIBS variable is used to specify which of the static and -shared libraries are to be built. This can contain one or both of the -words "static" and "shared". If you don't specify this when you invoke -make, the default value generated by the configure script will be -used. Depending on whether the configure script knows how to create -shared libraries on the target system, this will be either "static" or -"static shared". Beware that shared libraries aren't supported on many -systems, so requiring "shared" will limit which systems you can -compile your package on. Also note that unless your package installs -the tecla library in a directory which all users of your program will -have access to, you should only compile the static version. -Instructions for adding shared-library creation rules for new systems -are included in the PORTING file. - -The OPT variable can be used to change the default optimization from -the default of "-O" to something else. - -The DEMOS variable controls whether the demo programs are built. -Normally this has the value "demos", which tells the makefile to build -the demo programs. Setting it to an empty string stops the demos from -being built. - -The PROGRAMS variable is used to specify which programs are to be -built and subsequently installed. All available programs are built by -default. Currently there is only one such program, selected by -specifying the word "enhance". This program uses tecla-enhanced -pseudo-terminals to layer command line editing on top of third party -programs. - -The PROGRAMS_R variable serves the same purpose as the PROGRAMS -variable, except that programs listed here are linked with the -reentrant version of the library, and should be specified with a _r -suffix. Currently this variable is empty by default. - -Martin Shepherd (mcs@astro.caltech.edu) diff --git a/libtecla-1.4.1/LICENSE.TERMS b/libtecla-1.4.1/LICENSE.TERMS deleted file mode 100644 index 275eef5..0000000 --- a/libtecla-1.4.1/LICENSE.TERMS +++ /dev/null @@ -1,28 +0,0 @@ -Copyright (c) 2000, 2001 by Martin C. Shepherd. - -All rights reserved. - -Permission is hereby granted, free of charge, to any person obtaining a -copy of this software and associated documentation files (the -"Software"), to deal in the Software without restriction, including -without limitation the rights to use, copy, modify, merge, publish, -distribute, and/or sell copies of the Software, and to permit persons -to whom the Software is furnished to do so, provided that the above -copyright notice(s) and this permission notice appear in all copies of -the Software and that both the above copyright notice(s) and this -permission notice appear in supporting documentation. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS -OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT -OF THIRD PARTY RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR -HOLDERS INCLUDED IN THIS NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL -INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING -FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, -NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION -WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - -Except as contained in this notice, the name of a copyright holder -shall not be used in advertising or otherwise to promote the sale, use -or other dealings in this Software without prior written authorization -of the copyright holder. diff --git a/libtecla-1.4.1/Makefile b/libtecla-1.4.1/Makefile deleted file mode 100644 index 15116b5..0000000 --- a/libtecla-1.4.1/Makefile +++ /dev/null @@ -1,3 +0,0 @@ -default: - ./configure - make diff --git a/libtecla-1.4.1/Makefile.in b/libtecla-1.4.1/Makefile.in deleted file mode 100644 index f691375..0000000 --- a/libtecla-1.4.1/Makefile.in +++ /dev/null @@ -1,225 +0,0 @@ -#----------------------------------------------------------------------- -# This is the template that the libtecla configure script uses to create -# the libtecla Makefile. It does this by replacing all instances of -# @name@ with the value of the correspondingly named configuration -# variable. You should find another file in the same directory as this -# one, called "configure.in". The latter file contains extensive comments -# explaining how this all works. -#----------------------------------------------------------------------- - -# Where is the source code? - -srcdir = @srcdir@ - -# Where do you want to install the library, its header file, and the man pages? - -prefix=@prefix@ -exec_prefix=@exec_prefix@ -LIBDIR=@libdir@ -INCDIR=@includedir@ -MANDIR=@mandir@ -BINDIR=@bindir@ - -# Which C compiler do you want to use? - -CC = @CC@ - -# If 'make' doesn't define the MAKE variable, define it here. - -@SET_MAKE@ - -# To use RANLIB set the RANLIB variable to ranlib. Otherwise set it to -# :, which is the bourne shell do-nothing command. - -RANLIB = @RANLIB@ - -# The following optional defines change the characteristics of the library. -# -# USE_TERMINFO -# Use the terminfo terminal information database when looking up -# terminal characteristics. Most modern UNIX and UNIX-like operating -# systems support terminfo, so this define should normally be included. -# If in doubt leave it in, and see if the library compiles. -# USE_TERMCAP -# If you don't have terminfo but do have the termcap database, replace -# the -DUSE_TERMINFO with -DUSE_TERMCAP. If there is a termcap.h in -# /usr/include/, also add -DHAVE_TERMCAP_H. -# -# If neither USE_TERMINFO nor USE_TERMCAP are included, ANSI VT100 control -# sequences will be used to control all terminal types. -# -# For Solaris and Linux, use: -# -# DEFINES = -DUSE_TERMINFO -# - -DEFINES = @DEFS@ - -# -# The following defines are used in addition to the above when compiling -# the reentrant version of the library. Note that the definition of -# _POSIX_C_SOURCE to request reentrant functions, has the unfortunate -# side-effect on some systems of stopping the TIOCGWINSZ ioctl macro from -# getting defined. This in turn stops the library from being -# able to respond to terminal size changes. Under Solaris this can be -# remedied by adding -D__EXTENSIONS__. On linux this isn't necessary. -# If you don't get this right, the library will still work, but -# it will get confused if the terminal size gets changed and you try to -# edit a line that exceeds the terminal width. -# -# Thus on Solaris you should use: -# -# DEFINES_R = -D_POSIX_C_SOURCE=199506L -D__EXTENSIONS__ -# -# and on linux you should use: -# -# DEFINES_R = -D_POSIX_C_SOURCE=199506L -# - -DEFINES_R = -D_POSIX_C_SOURCE=199506L - -# -# The compiler optimization flags. I like to keep this separate so -# that I can set it to -g from the 'make' command line without having -# to edit this file when debugging the library. If you aren't working -# on modifying the library, leave this set to -O. -# - -OPT = -O - -# -# These are paranoid gcc warning flags to use when compiling new code. -# Simply invoke make with WARNING_FLAGS='$(PEDANTIC_FLAGS)'. -# -PEDANTIC_FLAGS=-Wall -Wshadow -Wpointer-arith -Wwrite-strings -Wstrict-prototypes -Wmissing-prototypes -Wmissing-declarations -Wredundant-decls - -# -# Specify any extra compiler warning options that you want to use. -# Leave this blank unless you are porting the library to a new system, -# or modifying the library. -# - -WARNING_FLAGS= - -# -# If you want to compile the demo program, specify any system -# libraries that are needed for the terminal I/O functions. -# -# If you are using terminfo, you will probably only need -lcurses. -# For termcap you may need -ltermcap or -ltermlib. -# -# For Solaris, use: -# -# LIBS = -lcurses -# -# For linux, use: -# -# LIBS = -lncurses -# - -LIBS = @LIBS@ - -# -# List the default target libraries. This should be one or -# both of the words "normal" and "reentrant". -# -TARGETS = @TARGETS@ - -# -# List which types of the above libraries are required. -# This should be one or both of the words "static" and "shared". -# -TARGET_LIBS = @TARGET_LIBS@ - -# -# If you want the demo programs to be built, the following variable -# should be assigned the single word: demos. If it isn't assigned -# anything, the demo programs won't be built. -# -DEMOS = demos - -# -# List the programs that are to be made by default. -# -PROGRAMS = enhance - -# -# List programs for which reentrant versions are to be built by default. -# -PROGRAMS_R = - -#----------------------------------------------------------------------- -# You shouldn't need to change anything below this line. -#----------------------------------------------------------------------- - -CFLAGS = $(OPT) $(WARNING_FLAGS) $(DEFINES) @CFLAGS@ @SHARED_CFLAGS@ - -default: $(TARGETS) - -normal: - @$(MAKE) -f $(srcdir)/Makefile.rules TARGETS="$(TARGET_LIBS)" SUFFIX="" CFLAGS="$(CFLAGS)" CC="$(CC)" OBJDIR=normal_obj LINK_SHARED='@LINK_SHARED@' SHARED_EXT='@SHARED_EXT@' SHARED_ALT='@SHARED_ALT@' LIBS='$(LIBS)' srcdir='$(srcdir)' LIBDIR='$(LIBDIR)' LN_S='@LN_S@' DEMOS="$(DEMOS)" PROGRAMS='$(PROGRAMS)' RANLIB='$(RANLIB)' - -reentrant: - @$(MAKE) -f $(srcdir)/Makefile.rules TARGETS="$(TARGET_LIBS)" SUFFIX="_r" CFLAGS="$(CFLAGS) $(DEFINES_R)" CC="$(CC)" OBJDIR=reentrant_obj LINK_SHARED='@LINK_SHARED@' SHARED_EXT='@SHARED_EXT@' SHARED_ALT='@SHARED_ALT@' LIBS='$(LIBS)' srcdir='$(srcdir)' LIBDIR='$(LIBDIR)' LN_S='@LN_S@' DEMOS="$(DEMOS)" PROGRAMS='$(PROGRAMS_R)' RANLIB='$(RANLIB)' - -demos: normal - -demos_r: reentrant - -clean: - rm -rf *.o normal_obj reentrant_obj libtecla*.a demo demo2 demo_r demo2_r enhance *~ man3/*~ html/*~ compile_reentrant compile_normal - @endings="@SHARED_EXT@ @SHARED_ALT@" ; \ - for alt in $$endings ; do \ - lib="libtecla*$$alt" ; \ - rm -f $$lib; echo rm -f $$lib ; \ - done - -distclean: clean - rm -f config.cache config.status config.log Makefile libtecla.map.opt - cp $(srcdir)/Makefile.stub Makefile - -install_lib: $(TARGETS) $(LIBDIR) - @for lib in libtecla.a libtecla_r.a ; do \ - if [ -f $$lib ] ; then \ - cp $$lib $(LIBDIR)/ ; chmod ugo+r $(LIBDIR)/$$lib; \ - echo "cp $$lib $(LIBDIR)/ ; chmod ugo+r $(LIBDIR)/$$lib"; \ - fi ; \ - done - @for lib in libtecla libtecla_r ; do \ - src="$$lib@SHARED_EXT@"; \ - if [ -f $$src ] ; then \ - dst="$(LIBDIR)/$$src"; \ - cp -f $$src $$dst; chmod a=rX $$dst; \ - echo "cp -f $$src $$dst ; chmod a=rX $$dst"; \ - endings="@SHARED_ALT@" ; \ - for alt in $$endings ; do \ - lnk="$$lib$$alt"; \ - (cd $(LIBDIR); rm -f $$lnk; @LN_S@ $$src $$lnk); \ - echo "(cd $(LIBDIR); rm -f $$lnk; @LN_S@ $$src $$lnk)"; \ - done ; \ - fi ; \ - done - -install_inc: $(INCDIR) - @if [ -f $(srcdir)/libtecla.h ]; then \ - cp $(srcdir)/libtecla.h $(INCDIR)/ ; chmod ugo+r $(INCDIR)/libtecla.h; \ - echo "cp $(srcdir)/libtecla.h $(INCDIR)/ ; chmod ugo+r $(INCDIR)/libtecla.h"; \ - fi - -install_man: $(MANDIR) $(MANDIR)/man3 - cd $(srcdir)/man3 && for page in *.3; do cp $$page $(MANDIR)/man3; chmod ugo+r $(MANDIR)/man3/$$page; done - -install_bin: $(BINDIR) $(PROGRAMS) $(PROGRAMS_R) - progs="$(PROGRAMS) $(PROGRAMS_R)"; \ - for prog in $$progs; do \ - cp $$prog $(BINDIR)/; \ - chmod ugo+rx $(BINDIR)/$$prog; \ - done - -install: install_lib install_inc install_man install_bin - -# Make any missing installation directories. - -$(MANDIR) $(MANDIR)/man3 $(LIBDIR) $(INCDIR) $(BINDIR): - $(srcdir)/install-sh -d $@ - chmod ugo+rx $@ diff --git a/libtecla-1.4.1/Makefile.rules b/libtecla-1.4.1/Makefile.rules deleted file mode 100644 index 6552057..0000000 --- a/libtecla-1.4.1/Makefile.rules +++ /dev/null @@ -1,142 +0,0 @@ -default: $(OBJDIR) $(TARGETS) $(DEMOS) $(PROGRAMS) - -#----------------------------------------------------------------------- -# You shouldn't need to change anything in this file. -#----------------------------------------------------------------------- - -# Create the directory in which the object files will be created. - -$(OBJDIR): - mkdir $(OBJDIR) - -# Construct the compilation command. - -COMPILE = $(CC) -c $(CFLAGS) -o $@ - -LIB_OBJECTS = $(OBJDIR)/getline.o $(OBJDIR)/keytab.o $(OBJDIR)/freelist.o \ - $(OBJDIR)/strngmem.o $(OBJDIR)/hash.o $(OBJDIR)/history.o \ - $(OBJDIR)/direader.o $(OBJDIR)/homedir.o $(OBJDIR)/pathutil.o \ - $(OBJDIR)/expand.o $(OBJDIR)/stringrp.o $(OBJDIR)/cplfile.o \ - $(OBJDIR)/cplmatch.o $(OBJDIR)/pcache.o $(OBJDIR)/version.o - -# List all of the programs that this makefile can build. - -PROGS = demo$(SUFFIX) demo2$(SUFFIX) enhance$(SUFFIX) - -static: libtecla$(SUFFIX).a - -libtecla$(SUFFIX).a: $(LIB_OBJECTS) - ar -ru $@ $(LIB_OBJECTS); \ - $(RANLIB) $@; \ - rm -f $(PROGS) - -shared: libtecla$(SUFFIX)$(SHARED_EXT) - -libtecla$(SUFFIX)$(SHARED_EXT): $(LIB_OBJECTS) $(srcdir)/libtecla.map \ - libtecla.map.opt - $(LINK_SHARED) - @endings="$(SHARED_ALT)" ; \ - for alt in $$endings ; do \ - lnk="libtecla$(SUFFIX)$$alt"; \ - echo "rm -f $$lnk; $(LN_S) $@ $$lnk"; \ - rm -f $$lnk; $(LN_S) $@ $$lnk; \ - done; \ - rm -f $(PROGS) - -libtecla.map.opt: $(srcdir)/libtecla.map - sed -n 's/^[ ]*\([_a-zA-Z0-9]*\)[ ]*;.*/+e \1/p' $? >$@ - -demos: demo$(SUFFIX) demo2$(SUFFIX) - -demo$(SUFFIX): $(OBJDIR)/demo.o - LD_RUN_PATH="$(LIBDIR):$$LD_RUN_PATH:`pwd`" $(CC) $(CFLAGS) -o $@ \ - $(OBJDIR)/demo.o -L. -ltecla$(SUFFIX) $(LIBS) - -demo2$(SUFFIX): $(OBJDIR)/demo2.o - LD_RUN_PATH="$(LIBDIR):$$LD_RUN_PATH:`pwd`" $(CC) $(CFLAGS) -o $@ \ - $(OBJDIR)/demo2.o -L. -ltecla$(SUFFIX) $(LIBS) - -enhance$(SUFFIX): $(OBJDIR)/enhance.o - LD_RUN_PATH="$(LIBDIR):$$LD_RUN_PATH:`pwd`" $(CC) $(CFLAGS) -o $@ \ - $(OBJDIR)/enhance.o -L. -ltecla$(SUFFIX) $(LIBS) - -#----------------------------------------------------------------------- -# Object file dependencies. -#----------------------------------------------------------------------- - -$(OBJDIR)/getline.o: $(srcdir)/getline.c $(srcdir)/pathutil.h \ - $(srcdir)/libtecla.h $(OBJDIR)/keytab.h $(srcdir)/history.h \ - $(srcdir)/freelist.h $(srcdir)/stringrp.h $(srcdir)/getline.h - $(COMPILE) $(srcdir)/getline.c - -$(OBJDIR)/keytab.o: $(srcdir)/keytab.c $(OBJDIR)/keytab.h \ - $(srcdir)/strngmem.h $(srcdir)/getline.h - $(COMPILE) $(srcdir)/keytab.c - -$(OBJDIR)/strngmem.o: $(srcdir)/strngmem.c $(srcdir)/strngmem.h \ - $(srcdir)/freelist.h - $(COMPILE) $(srcdir)/strngmem.c - -$(OBJDIR)/freelist.o: $(srcdir)/freelist.c $(srcdir)/freelist.h - $(COMPILE) $(srcdir)/freelist.c - -$(OBJDIR)/hash.o: $(srcdir)/hash.c $(srcdir)/hash.h $(srcdir)/strngmem.h \ - $(srcdir)/freelist.h - $(COMPILE) $(srcdir)/hash.c - -$(OBJDIR)/history.o: $(srcdir)/history.c $(srcdir)/history.h \ - $(srcdir)/freelist.h - $(COMPILE) $(srcdir)/history.c - -$(OBJDIR)/expand.o: $(srcdir)/expand.c $(srcdir)/freelist.h \ - $(srcdir)/direader.h $(srcdir)/pathutil.h $(srcdir)/homedir.h \ - $(srcdir)/stringrp.h $(srcdir)/libtecla.h - $(COMPILE) $(srcdir)/expand.c - -$(OBJDIR)/direader.o: $(srcdir)/direader.c $(srcdir)/direader.h - $(COMPILE) $(srcdir)/direader.c - -$(OBJDIR)/homedir.o: $(srcdir)/homedir.c $(srcdir)/pathutil.h \ - $(srcdir)/homedir.h - $(COMPILE) $(srcdir)/homedir.c - -$(OBJDIR)/pathutil.o: $(srcdir)/pathutil.c $(srcdir)/pathutil.h - $(COMPILE) $(srcdir)/pathutil.c - -$(OBJDIR)/stringrp.o: $(srcdir)/stringrp.c $(srcdir)/freelist.h \ - $(srcdir)/stringrp.h - $(COMPILE) $(srcdir)/stringrp.c - -$(OBJDIR)/cplfile.o: $(srcdir)/cplfile.c $(srcdir)/libtecla.h \ - $(srcdir)/direader.h $(srcdir)/homedir.h $(srcdir)/pathutil.h \ - $(srcdir)/cplfile.h - $(COMPILE) $(srcdir)/cplfile.c - -$(OBJDIR)/cplmatch.o: $(srcdir)/cplmatch.c $(srcdir)/libtecla.h \ - $(srcdir)/stringrp.h $(srcdir)/pathutil.h $(srcdir)/cplfile.h - $(COMPILE) $(srcdir)/cplmatch.c - -$(OBJDIR)/pcache.o: $(srcdir)/pcache.c $(srcdir)/libtecla.h \ - $(srcdir)/pathutil.h $(srcdir)/homedir.h $(srcdir)/freelist.h \ - $(srcdir)/direader.h $(srcdir)/stringrp.h - $(COMPILE) $(srcdir)/pcache.c - -$(OBJDIR)/demo.o: $(srcdir)/demo.c $(srcdir)/libtecla.h - $(COMPILE) $(srcdir)/demo.c - -$(OBJDIR)/demo2.o: $(srcdir)/demo2.c $(srcdir)/libtecla.h - $(COMPILE) $(srcdir)/demo2.c - -$(OBJDIR)/version.o: $(srcdir)/version.c $(srcdir)/libtecla.h - $(COMPILE) $(srcdir)/version.c - -$(OBJDIR)/enhance.o: $(srcdir)/enhance.c $(srcdir)/libtecla.h - $(COMPILE) $(srcdir)/enhance.c - -#----------------------------------------------------------------------- -# Include file dependencies. -#----------------------------------------------------------------------- - -$(OBJDIR)/keytab.h: $(srcdir)/keytab.h $(srcdir)/libtecla.h \ - $(srcdir)/hash.h $(srcdir)/strngmem.h - cp $(srcdir)/keytab.h $@ diff --git a/libtecla-1.4.1/Makefile.stub b/libtecla-1.4.1/Makefile.stub deleted file mode 100644 index 15116b5..0000000 --- a/libtecla-1.4.1/Makefile.stub +++ /dev/null @@ -1,3 +0,0 @@ -default: - ./configure - make diff --git a/libtecla-1.4.1/PORTING b/libtecla-1.4.1/PORTING deleted file mode 100644 index db39818..0000000 --- a/libtecla-1.4.1/PORTING +++ /dev/null @@ -1,38 +0,0 @@ -The Tecla library was written with portability in mind, so no -modifications to the source code should be needed on UNIX or LINUX -platforms. The default compilation and linking arrangements should -also work unchanged on these systems, but if no specific configuration -has been provided for your system, shared libraries won't be compiled. -Configuring these requires modifications to be made to the file: - - configure.in - -This file is heavily commented (comments start with the word dnl) and -is relatively simple, so the instructions and suggestions that you -find in this file should be sufficient to help you figure out how to -add a configuration for your system. This file is an input file to the -GNU autoconf program, which uses it as a template for generating the -distributed configure script. If autoconf is installed on your system, -creating a new configure script is a simple matter of typing. - - autoconf - -To avoid confusion with the leftovers of the previous configuration, -you should then do the following: - - rm -f config.cache - ./configure - make clean - ./configure - make - -The first ./configure creates a new makefile for your system, allowing -you to type 'make clean' to discard any files that were compiled with -the previous configuration. Since 'make clean' also deletes the new -makefile, a second invokation of the configure script is then -performed to re-create the makefile. The final make then creates the -tecla library from scratch. - -Once you have confirmed that the new configuration works, please send -the modified "configure.in" template to mcs@astro.caltech.edu, so that -your changes can be included in subsequent releases. diff --git a/libtecla-1.4.1/README b/libtecla-1.4.1/README deleted file mode 100644 index 894819d..0000000 --- a/libtecla-1.4.1/README +++ /dev/null @@ -1,53 +0,0 @@ -This is version 1.4.1 of the tecla command-line editing library. - -For the current official release, please direct your browser to: - - http://www.astro.caltech.edu/~mcs/tecla/index.html - -The tecla library provides UNIX and LINUX programs with interactive -command line editing facilities, similar to those of the unix tcsh -shell. In addition to simple command-line editing, it supports recall -of previously entered command lines, TAB completion of file names, and -in-line wild-card expansion of filenames. The internal functions -which perform file-name completion and wild-card expansion are also -available externally for optional use by programs, along with a module -for tab-completion and lookup of filenames in a list of directories. - -Note that special care has been taken to allow the use of this library -in threaded programs. The option to enable this is discussed in the -Makefile, and specific discussions of thread safety are presented in -the included man pages. - -For instructions on how to compile and install the library, please see -the INSTALL file, which should be in the same directory as this file. - -Copyright and Disclaimer ------------------------- -Copyright (c) 2000, 2001 by Martin C. Shepherd. - -All rights reserved. - -Permission is hereby granted, free of charge, to any person obtaining a -copy of this software and associated documentation files (the -"Software"), to deal in the Software without restriction, including -without limitation the rights to use, copy, modify, merge, publish, -distribute, and/or sell copies of the Software, and to permit persons -to whom the Software is furnished to do so, provided that the above -copyright notice(s) and this permission notice appear in all copies of -the Software and that both the above copyright notice(s) and this -permission notice appear in supporting documentation. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS -OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT -OF THIRD PARTY RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR -HOLDERS INCLUDED IN THIS NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL -INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING -FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, -NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION -WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - -Except as contained in this notice, the name of a copyright holder -shall not be used in advertising or otherwise to promote the sale, use -or other dealings in this Software without prior written authorization -of the copyright holder. diff --git a/libtecla-1.4.1/RELEASE.NOTES b/libtecla-1.4.1/RELEASE.NOTES deleted file mode 100644 index 1b18a4a..0000000 --- a/libtecla-1.4.1/RELEASE.NOTES +++ /dev/null @@ -1,357 +0,0 @@ -This file lists major changes which accompany each new release. - -Version 1.4.1: - - This is a maintenance release. It includes minor changes to support - Mac OS X (Darwin), the QNX real-time operating system, and Cygwin - under Windows. It also fixes an oversight that was preventing the - tab key from inserting tab characters when users unbound the - complete-word action from it. - -Version 1.4.0: - - The contents of the history list can now be saved and restored with - the new gl_save_history() and gl_load_history() functions. - - Event handlers can now be registered to watch for and respond to I/O - on arbitrary file descriptors while gl_get_line() is waiting for - terminal input from the user. See the gl_get_line(3) man page - for details on gl_watch_fd(). - - As an optional alternative to getting configuration information only - from ~/.teclarc, the new gl_configure_getline() function allows - configuration commands to be taken from any of, a string, a - specified application-specific file, and/or a specified - user-specific file. See the gl_get_line(3) man page for details. - - The version number of the library can now be queried using the - libtecla_version() function. See the libtecla(3) man page. - - The new gl_group_history() function allows applications to group - different types of input line in the history buffer, and arrange for - only members of the appropriate group to be recalled on a given call - to gl_get_line(). See the gl_get_line(3) man page. - - The new gl_show_history() function displays the current history list - to a given stdio output stream. See the gl_get_line(3) man page. - - new_GetLine() now allows you to specify a history buffer size of - zero, thus requesting that no history buffer be allocated. You can - subsequently resize or delete the history buffer at any time, by - calling gl_resize_history(), limit the number of lines that are - allowed in the buffer by calling gl_limit_history(), clear either - all history lines from the history list, or just the history lines - that are associated with the current history group, by calling - gl_clear_history, and toggle the history mechanism on and off by - calling gl_toggle_history(). - - The new gl_terminal_size() function can be used to query the - current terminal size. It can also be used to supply a default - terminal size on systems where no mechanism is available for - looking up the size. - - The contents and configuration of the history list can now be - obtained by the calling application, by calling the new - gl_lookup_history(), gl_state_of_history(), gl_range_of_history() - and gl_size_of_history() functions. See the gl_get_line(3) man page. - - Echoing of the input line as it is typed, can now be turned on and - off via the new gl_echo_mode() function. While echoing is disabled, - newly entered input lines are omitted from the history list. See - the gl_get_line(3) man page. - - While the default remains to display the prompt string literally, - the new gl_prompt_style() function can be used to enable text - attribute formatting directives in prompt strings, such as - underlining, bold font, and highlighting directives. - - Signal handling in gl_get_line() is now customizable. The default - signal handling behavior remains essentially the same, except that - the SIGTSTP, SIGTTIN and SIGTTOU are now forwarded to the - corresponding signal handler of the calling program, instead of - causing a SIGSTOP to be sent to the application. It is now possible - to remove signals from the list that are trapped by gl_get_line(), - as well as add new signals to this list. The signal and terminal - environments in which the signal handler of the calling program is - invoked, and what gl_get_line() does after the signal handler - returns, is now customizable on a per signal basis. You can now also - query the last signal that was caught by gl_get_line(). This is - useful when gl_get_line() aborts with errno=EINTR, and you need to - know which signal caused it to abort. - - Key-sequences bound to action functions can now start with printable - characters. Previously only keysequences starting with control or - meta characters were permitted. - - gl_get_line() is now 8-bit clean. If the calling program has - correctly called setlocale(LC_CTYPE,""), then the user can select an - alternate locale by setting the standard LC_CTYPE, LC_ALL, or LANG - environment variables, and international characters can then be - entered directly, either by using a non-US keyboard, or by using a - compose key on a standard US keyboard. Note that in locales in which - meta characters become printable, meta characters no longer match - M-c bindings, which then have to be entered using their escape-c - equivalents. Fortunately most modern terminal emulators either - output the escape-c version by default when the meta key is used, or - can be configured to do so (see the gl_get_line(3) man page), so in - most cases you can continue to use the meta key. - - Completion callback functions can now tell gl_get_line() to return - the input line immediately after a successful tab completion, simply - by setting the last character of the optional continuation suffix to - a newline character (ie. in the call to cpl_add_completion()). - - It is now safe to create and use multiple GetLine objects, albeit - still only from a single thread. In conjunction with the new - gl_configure_getline() function, this optionally allows multiple - GetLine objects with different bindings to be used to implement - different input modes. - - The edit-mode configuration command now accepts the argument, - none. This tells gl_get_line() to revert to using just the native - line editing facilities provided by the terminal driver. This could - be used if the termcap or terminfo entry of the host terminal were - badly corrupted. - - Application callback functions invoked by gl_get_line() can now - change the displayed prompt using the gl_replace_prompt() function. - - Their is now an optional program distributed with the library. This - is a beta release of a program which adds tecla command-line editing - to virtually any third party application without the application - needing to be linked to the library. See the enhance(3) man page for - further details. Although built and installed by default, the - INSTALL document explains how to prevent this. - - The INSTALL document now explains how you can stop the demo programs - from being built and installed. - - NetBSD/termcap fixes. Mike MacFaden reported two problems that he - saw when compiling libtecla under NetBSD. Both cases were related to - the use of termcap. Most systems use terminfo, so this problem has - gone unnoticed until now, and won't have affected the grand majority - of users. The configure script had a bug which prevented the check - for CPP working properly, and getline.c wouldn't compile due to an - undeclared variable when USE_TERMCAP was defined. Both problems have - now been fixed. Note that if you successfully compiled version - 1.3.3, this problem didn't affect you. - - An unfortunate and undocumented binding of the key-sequence M-O was - shadowing the arrow-key bindings on systems that use ^[OA etc. I - have removed this binding (the documented lower case M-o binding - remains bound). Under the KDE konsole terminal this was causing the - arrow keys to do something other than expected. - - There was a bug in the history list code which could result in - strange entries appearing at the start of the history list once - enough history lines had been added to the list to cause the - circular history buffer to wrap. This is now fixed. - -Version 1.3.3: - - Signal handling has been re-written, and documentation of its - behaviour has been added to the gl_get_line(3) man page. In addition - to eliminating race conditions, and appropriately setting errno for - those signals that abort gl_get_line(), many more signals are now - intercepted, making it less likely that the terminal will be left in - raw mode by a signal that isn't trapped by gl_get_line(). - - A bug was also fixed that was leaving the terminal in raw mode if - the editing mode was changed interactively between vi and emacs. - This was only noticeable when running programs from old shells that - don't reset terminal modes. - -Version 1.3.2: - - Tim Eliseo contributed a number of improvements to vi mode, - including a fuller set of vi key-bindings, implementation of the vi - constraint that the cursor can't backup past the point at which - input mode was entered, and restoration of overwritten characters - when backspacing in overwrite mode. There are also now new bindings - to allow users to toggle between vi and emacs modes interactively. - The terminal bell is now used in some circumstances, such as when an - unrecognized key sequence is entered. This can be turned off by the - new nobeep option in the tecla configuration file. - - Unrelated to the above, a problem under Linux which prevented ^Q - from being used to resume terminal output after the user had pressed - ^S, has been fixed. - -Version 1.3.1: - - In vi mode a bug was preventing the history-search-backward and - history-search-forward actions from doing anything when invoked on - empty lines. On empty lines they now act like up-history and - down-history respectively, as in emacs mode. - - When creating shared libraries under Linux, the -soname directive - was being used incorrectly. The result is that Linux binaries linked - with the 1.2.3, 1.2.4 and 1.3.0 versions of the tecla shared - libraries, will refuse to see other versions of the shared library - until relinked with version 1.3.1 or higher. - - The configure script can now handle the fact that under Solaris-2.6 - and earlier, the only curses library is a static one that hides in - /usr/ccs/lib. Under Linux it now also caters for old versions of GNU - ld which don't accept version scripts. - - The demos are now linked against the shared version of the library - if possible. Previously they were always linked with the static - version. - -Version 1.3.0: - - The major change in this release is the addition of an optional vi - command-line editing mode in gl_get_line(), along with lots of new - action functions to support its bindings. To enable this, first - create a ~/.teclarc file if you don't already have one, then add the - following line to it. - - edit-mode vi - - The default vi bindings, which are designed to mimic those of the vi - editor as closely as possible, are described in the gl_get_line(3) - man page. - - A new convenience function called ef_list_expansions() has been - added for listing filename expansions. See the ef_list_expansions(3) - man page for details. This is used in a new list-glob binding, bound - to ^Xg in emacs mode, and ^G in vi input mode. - - A bug has been fixed in the key-binding table expansion code. This - bug would have caused problems to anybody who defined more than - about 18 personalized key-bindings in their ~/.teclarc file. - -Version 1.2.4: - - Buffered I/O is now used for writing to terminals, and where - supported, cursor motion is done with move-n-positions terminfo - capabilities instead of doing lots of move-1-position requests. This - greatly improves how the library feels over slow links. - - You can now optionally compile different architectures in different - directories, without having to make multiple copies of the - distribution. This is documented in the INSTALL file. - - The ksh ~+ directive is now supported. - - Thanks to Markus Gyger for the above improvements. - - Documentation has been added to the INSTALL file describing features - designed to facilitate configuration and installation of the library - as part of larger packages. These features are intended to remove - the need to modify the tecla distribution's configuration and build - procedures when embedding the libtecla distribution in other package - distributions. - - A previous fix to stop the cursor from warping when the last - character of the input line was in the last column of the terminal, - was only being used for the first terminal line of the input line. - It is now used for all subsequent lines as well, as originally - intended. - -Version 1.2.3: - - The installation procedure has been better automated with the - addition of an autoconf configure script. This means that installers - can now compile and install the library by typing: - - ./configure - make - make install - - On all systems this makes at least the normal static version of the - tecla library. It also makes the reentrant version if reentrant - POSIX functions are detected. Under Solaris, Linux and HP-UX the - configuration script arranges for shared libraries to be compiled in - addition to the static libraries. It is hoped that installers will - return information about how to compile shared libraries on other - systems, for inclusion in future releases, and to this end, a new - PORTING guide has been provided. - - The versioning number scheme has been changed. This release would - have been 1.2c, but instead will be refered to as 1.2.3. The - versioning scheme, based on conventions used by Sun Microsystems, is - described in configure.in. - - The library was also tested under HP-UX, and this revealed two - serious bugs, both of which have now been fixed. - - The first bug prevented the library from writing control codes to - terminals on big-endian machines, with the exception of those - running under Solaris. This was due to an int variable being used - where a char was needed. - - The second bug had the symptom that on systems that don't use the - newline character as the control code for moving the cursor down a - line, a newline wasn't started when the user hit enter. - -Version 1.2b: - - Two more minor bug fixes: - - Many terminals don't wrap the cursor to the next line when a - character is written to the rightmost terminal column. Instead, they - delay starting a new line until one more character is written, at - which point they move the cursor two positions. gl_get_line() - wasn't aware of this, so cursor repositionings just after writing - the last character of a column, caused it to erroneously go up a - line. This has now been remedied, using a method that should work - regardless of whether a terminal exhibits this behavior or not. - - Some systems dynamically record the current terminal dimensions in - environment variables called LINES and COLUMNS. On such systems, - during the initial terminal setup, these values should override the - static values read from the terminal information databases, and now - do. Previously they were only used if the dimensions returned by - terminfo/termcap looked bogus. - -Version 1.2a: - - This minor release fixes the following two bugs: - - The initial terminal size and subsequent changes thereto, weren't - being noticed by gl_get_line(). This was because the test for the - existence of TIOCWINSZ was erroneously placed before the inclusion - of termios.h. One of the results was that on input lines that - spanned more than one terminal line, the cursor occasionally jumped - unexpectedly to the previous terminal line. - - On entering a line that wrapped over multiple terminal lines, - gl_get_line() simply output a carriage-return line-feed at the point - at which the user pressed return. Thus if one typed in such a line, - then moved back onto one of the earlier terminal lines before - hitting return, the cursor was left on a line containing part of the - line that had just been entered. This didn't do any harm, but it - looked a mess. - -Version 1.2: - - A new facility for looking up and completing filenames in UNIX-style - paths has now been added (eg. you can search for, or complete - commands using the UNIX PATH environment variable). See the - pca_lookup_file(3) man page. - - The already existing filename completion callback can now be made - selective in what types of files it lists. See the - cpl_complete_word(3) man page. - - Due to its potential to break applications when changed, the use of - the publically defined CplFileArgs structure to configure the - cpl_file_completions() callback is now deprecated. The definition - of this structure has been frozen, and its documentation has been - removed from the man pages. It will remain supported, but if you - have used it, you are recommended to switch to the new method, which - involves a new opaque configuration object, allocated via a provided - constructor function, configured via accessor functions, and - eventually deleted with a provided destructor function. The - cpl_file_completions() callback distinguishes which structure type - it has been sent by virtue of a code placed at the start of the new - structure by the constructor. It is assumed that no existing - applications set the boolean 'escaped' member of the CplFileArgs - structure to 4568. The new method is documented in the - cpl_complete_word(3) man page. - -Version 1.1j - - This was the initial public release on freshmeat.org. diff --git a/libtecla-1.4.1/config.guess b/libtecla-1.4.1/config.guess deleted file mode 100644 index 0ce538b..0000000 --- a/libtecla-1.4.1/config.guess +++ /dev/null @@ -1,1183 +0,0 @@ -#! /bin/sh -# Attempt to guess a canonical system name. -# Copyright (C) 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999, 2000 -# Free Software Foundation, Inc. -# -# This file 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. -# -# As a special exception to the GNU General Public License, if you -# distribute this file as part of a program that contains a -# configuration script generated by Autoconf, you may include it under -# the same distribution terms that you use for the rest of that program. - -# Written by Per Bothner . -# Please send patches to . -# -# This script attempts to guess a canonical system name similar to -# config.sub. If it succeeds, it prints the system name on stdout, and -# exits with 0. Otherwise, it exits with 1. -# -# The plan is that this can be called by configure scripts if you -# don't specify an explicit system type (host/target name). -# -# Only a few systems have been added to this list; please add others -# (but try to keep the structure clean). -# - -# Use $HOST_CC if defined. $CC may point to a cross-compiler -if test x"$CC_FOR_BUILD" = x; then - if test x"$HOST_CC" != x; then - CC_FOR_BUILD="$HOST_CC" - else - if test x"$CC" != x; then - CC_FOR_BUILD="$CC" - else - CC_FOR_BUILD=cc - fi - fi -fi - - -# This is needed to find uname on a Pyramid OSx when run in the BSD universe. -# (ghazi@noc.rutgers.edu 8/24/94.) -if (test -f /.attbin/uname) >/dev/null 2>&1 ; then - PATH=$PATH:/.attbin ; export PATH -fi - -UNAME_MACHINE=`(uname -m) 2>/dev/null` || UNAME_MACHINE=unknown -UNAME_RELEASE=`(uname -r) 2>/dev/null` || UNAME_RELEASE=unknown -UNAME_SYSTEM=`(uname -s) 2>/dev/null` || UNAME_SYSTEM=unknown -UNAME_VERSION=`(uname -v) 2>/dev/null` || UNAME_VERSION=unknown - -dummy=dummy-$$ -trap 'rm -f $dummy.c $dummy.o $dummy; exit 1' 1 2 15 - -# Note: order is significant - the case branches are not exclusive. - -case "${UNAME_MACHINE}:${UNAME_SYSTEM}:${UNAME_RELEASE}:${UNAME_VERSION}" in - *:NetBSD:*:*) - # Netbsd (nbsd) targets should (where applicable) match one or - # more of the tupples: *-*-netbsdelf*, *-*-netbsdaout*, - # *-*-netbsdecoff* and *-*-netbsd*. For targets that recently - # switched to ELF, *-*-netbsd* would select the old - # object file format. This provides both forward - # compatibility and a consistent mechanism for selecting the - # object file format. - # Determine the machine/vendor (is the vendor relevant). - case "${UNAME_MACHINE}" in - amiga) machine=m68k-cbm ;; - arm32) machine=arm-unknown ;; - atari*) machine=m68k-atari ;; - sun3*) machine=m68k-sun ;; - mac68k) machine=m68k-apple ;; - macppc) machine=powerpc-apple ;; - hp3[0-9][05]) machine=m68k-hp ;; - ibmrt|romp-ibm) machine=romp-ibm ;; - *) machine=${UNAME_MACHINE}-unknown ;; - esac - # The Operating System including object format. - if echo __ELF__ | $CC_FOR_BUILD -E - 2>/dev/null \ - | grep __ELF__ >/dev/null - then - # Once all utilities can be ECOFF (netbsdecoff) or a.out (netbsdaout). - # Return netbsd for either. FIX? - os=netbsd - else - os=netbsdelf - fi - # The OS release - release=`echo ${UNAME_RELEASE}|sed -e 's/[-_].*/\./'` - # Since CPU_TYPE-MANUFACTURER-KERNEL-OPERATING_SYSTEM: - # contains redundant information, the shorter form: - # CPU_TYPE-MANUFACTURER-OPERATING_SYSTEM is used. - echo "${machine}-${os}${release}" - exit 0 ;; - alpha:OSF1:*:*) - if test $UNAME_RELEASE = "V4.0"; then - UNAME_RELEASE=`/usr/sbin/sizer -v | awk '{print $3}'` - fi - # A Vn.n version is a released version. - # A Tn.n version is a released field test version. - # A Xn.n version is an unreleased experimental baselevel. - # 1.2 uses "1.2" for uname -r. - cat <$dummy.s - .data -\$Lformat: - .byte 37,100,45,37,120,10,0 # "%d-%x\n" - - .text - .globl main - .align 4 - .ent main -main: - .frame \$30,16,\$26,0 - ldgp \$29,0(\$27) - .prologue 1 - .long 0x47e03d80 # implver \$0 - lda \$2,-1 - .long 0x47e20c21 # amask \$2,\$1 - lda \$16,\$Lformat - mov \$0,\$17 - not \$1,\$18 - jsr \$26,printf - ldgp \$29,0(\$26) - mov 0,\$16 - jsr \$26,exit - .end main -EOF - $CC_FOR_BUILD $dummy.s -o $dummy 2>/dev/null - if test "$?" = 0 ; then - case `./$dummy` in - 0-0) - UNAME_MACHINE="alpha" - ;; - 1-0) - UNAME_MACHINE="alphaev5" - ;; - 1-1) - UNAME_MACHINE="alphaev56" - ;; - 1-101) - UNAME_MACHINE="alphapca56" - ;; - 2-303) - UNAME_MACHINE="alphaev6" - ;; - 2-307) - UNAME_MACHINE="alphaev67" - ;; - esac - fi - rm -f $dummy.s $dummy - echo ${UNAME_MACHINE}-dec-osf`echo ${UNAME_RELEASE} | sed -e 's/^[VTX]//' | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz'` - exit 0 ;; - Alpha\ *:Windows_NT*:*) - # How do we know it's Interix rather than the generic POSIX subsystem? - # Should we change UNAME_MACHINE based on the output of uname instead - # of the specific Alpha model? - echo alpha-pc-interix - exit 0 ;; - 21064:Windows_NT:50:3) - echo alpha-dec-winnt3.5 - exit 0 ;; - Amiga*:UNIX_System_V:4.0:*) - echo m68k-cbm-sysv4 - exit 0;; - amiga:OpenBSD:*:*) - echo m68k-unknown-openbsd${UNAME_RELEASE} - exit 0 ;; - *:[Aa]miga[Oo][Ss]:*:*) - echo ${UNAME_MACHINE}-unknown-amigaos - exit 0 ;; - arc64:OpenBSD:*:*) - echo mips64el-unknown-openbsd${UNAME_RELEASE} - exit 0 ;; - arc:OpenBSD:*:*) - echo mipsel-unknown-openbsd${UNAME_RELEASE} - exit 0 ;; - hkmips:OpenBSD:*:*) - echo mips-unknown-openbsd${UNAME_RELEASE} - exit 0 ;; - pmax:OpenBSD:*:*) - echo mipsel-unknown-openbsd${UNAME_RELEASE} - exit 0 ;; - sgi:OpenBSD:*:*) - echo mips-unknown-openbsd${UNAME_RELEASE} - exit 0 ;; - wgrisc:OpenBSD:*:*) - echo mipsel-unknown-openbsd${UNAME_RELEASE} - exit 0 ;; - *:OS/390:*:*) - echo i370-ibm-openedition - exit 0 ;; - arm:RISC*:1.[012]*:*|arm:riscix:1.[012]*:*) - echo arm-acorn-riscix${UNAME_RELEASE} - exit 0;; - SR2?01:HI-UX/MPP:*:*) - echo hppa1.1-hitachi-hiuxmpp - exit 0;; - Pyramid*:OSx*:*:* | MIS*:OSx*:*:* | MIS*:SMP_DC-OSx*:*:*) - # akee@wpdis03.wpafb.af.mil (Earle F. Ake) contributed MIS and NILE. - if test "`(/bin/universe) 2>/dev/null`" = att ; then - echo pyramid-pyramid-sysv3 - else - echo pyramid-pyramid-bsd - fi - exit 0 ;; - NILE*:*:*:dcosx) - echo pyramid-pyramid-svr4 - exit 0 ;; - sun4H:SunOS:5.*:*) - echo sparc-hal-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'` - exit 0 ;; - sun4*:SunOS:5.*:* | tadpole*:SunOS:5.*:*) - echo sparc-sun-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'` - exit 0 ;; - i86pc:SunOS:5.*:*) - echo i386-pc-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'` - exit 0 ;; - sun4*:SunOS:6*:*) - # According to config.sub, this is the proper way to canonicalize - # SunOS6. Hard to guess exactly what SunOS6 will be like, but - # it's likely to be more like Solaris than SunOS4. - echo sparc-sun-solaris3`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'` - exit 0 ;; - sun4*:SunOS:*:*) - case "`/usr/bin/arch -k`" in - Series*|S4*) - UNAME_RELEASE=`uname -v` - ;; - esac - # Japanese Language versions have a version number like `4.1.3-JL'. - echo sparc-sun-sunos`echo ${UNAME_RELEASE}|sed -e 's/-/_/'` - exit 0 ;; - sun3*:SunOS:*:*) - echo m68k-sun-sunos${UNAME_RELEASE} - exit 0 ;; - sun*:*:4.2BSD:*) - UNAME_RELEASE=`(head -1 /etc/motd | awk '{print substr($5,1,3)}') 2>/dev/null` - test "x${UNAME_RELEASE}" = "x" && UNAME_RELEASE=3 - case "`/bin/arch`" in - sun3) - echo m68k-sun-sunos${UNAME_RELEASE} - ;; - sun4) - echo sparc-sun-sunos${UNAME_RELEASE} - ;; - esac - exit 0 ;; - aushp:SunOS:*:*) - echo sparc-auspex-sunos${UNAME_RELEASE} - exit 0 ;; - atari*:OpenBSD:*:*) - echo m68k-unknown-openbsd${UNAME_RELEASE} - exit 0 ;; - # The situation for MiNT is a little confusing. The machine name - # can be virtually everything (everything which is not - # "atarist" or "atariste" at least should have a processor - # > m68000). The system name ranges from "MiNT" over "FreeMiNT" - # to the lowercase version "mint" (or "freemint"). Finally - # the system name "TOS" denotes a system which is actually not - # MiNT. But MiNT is downward compatible to TOS, so this should - # be no problem. - atarist[e]:*MiNT:*:* | atarist[e]:*mint:*:* | atarist[e]:*TOS:*:*) - echo m68k-atari-mint${UNAME_RELEASE} - exit 0 ;; - atari*:*MiNT:*:* | atari*:*mint:*:* | atarist[e]:*TOS:*:*) - echo m68k-atari-mint${UNAME_RELEASE} - exit 0 ;; - *falcon*:*MiNT:*:* | *falcon*:*mint:*:* | *falcon*:*TOS:*:*) - echo m68k-atari-mint${UNAME_RELEASE} - exit 0 ;; - milan*:*MiNT:*:* | milan*:*mint:*:* | *milan*:*TOS:*:*) - echo m68k-milan-mint${UNAME_RELEASE} - exit 0 ;; - hades*:*MiNT:*:* | hades*:*mint:*:* | *hades*:*TOS:*:*) - echo m68k-hades-mint${UNAME_RELEASE} - exit 0 ;; - *:*MiNT:*:* | *:*mint:*:* | *:*TOS:*:*) - echo m68k-unknown-mint${UNAME_RELEASE} - exit 0 ;; - sun3*:OpenBSD:*:*) - echo m68k-unknown-openbsd${UNAME_RELEASE} - exit 0 ;; - mac68k:OpenBSD:*:*) - echo m68k-unknown-openbsd${UNAME_RELEASE} - exit 0 ;; - mvme68k:OpenBSD:*:*) - echo m68k-unknown-openbsd${UNAME_RELEASE} - exit 0 ;; - mvme88k:OpenBSD:*:*) - echo m88k-unknown-openbsd${UNAME_RELEASE} - exit 0 ;; - powerpc:machten:*:*) - echo powerpc-apple-machten${UNAME_RELEASE} - exit 0 ;; - RISC*:Mach:*:*) - echo mips-dec-mach_bsd4.3 - exit 0 ;; - RISC*:ULTRIX:*:*) - echo mips-dec-ultrix${UNAME_RELEASE} - exit 0 ;; - VAX*:ULTRIX*:*:*) - echo vax-dec-ultrix${UNAME_RELEASE} - exit 0 ;; - 2020:CLIX:*:* | 2430:CLIX:*:*) - echo clipper-intergraph-clix${UNAME_RELEASE} - exit 0 ;; - mips:*:*:UMIPS | mips:*:*:RISCos) - sed 's/^ //' << EOF >$dummy.c -#ifdef __cplusplus -#include /* for printf() prototype */ - int main (int argc, char *argv[]) { -#else - int main (argc, argv) int argc; char *argv[]; { -#endif - #if defined (host_mips) && defined (MIPSEB) - #if defined (SYSTYPE_SYSV) - printf ("mips-mips-riscos%ssysv\n", argv[1]); exit (0); - #endif - #if defined (SYSTYPE_SVR4) - printf ("mips-mips-riscos%ssvr4\n", argv[1]); exit (0); - #endif - #if defined (SYSTYPE_BSD43) || defined(SYSTYPE_BSD) - printf ("mips-mips-riscos%sbsd\n", argv[1]); exit (0); - #endif - #endif - exit (-1); - } -EOF - $CC_FOR_BUILD $dummy.c -o $dummy \ - && ./$dummy `echo "${UNAME_RELEASE}" | sed -n 's/\([0-9]*\).*/\1/p'` \ - && rm $dummy.c $dummy && exit 0 - rm -f $dummy.c $dummy - echo mips-mips-riscos${UNAME_RELEASE} - exit 0 ;; - Night_Hawk:Power_UNIX:*:*) - echo powerpc-harris-powerunix - exit 0 ;; - m88k:CX/UX:7*:*) - echo m88k-harris-cxux7 - exit 0 ;; - m88k:*:4*:R4*) - echo m88k-motorola-sysv4 - exit 0 ;; - m88k:*:3*:R3*) - echo m88k-motorola-sysv3 - exit 0 ;; - AViiON:dgux:*:*) - # DG/UX returns AViiON for all architectures - UNAME_PROCESSOR=`/usr/bin/uname -p` - if [ $UNAME_PROCESSOR = mc88100 ] || [ $UNAME_PROCESSOR = mc88110 ] - then - if [ ${TARGET_BINARY_INTERFACE}x = m88kdguxelfx ] || \ - [ ${TARGET_BINARY_INTERFACE}x = x ] - then - echo m88k-dg-dgux${UNAME_RELEASE} - else - echo m88k-dg-dguxbcs${UNAME_RELEASE} - fi - else - echo i586-dg-dgux${UNAME_RELEASE} - fi - exit 0 ;; - M88*:DolphinOS:*:*) # DolphinOS (SVR3) - echo m88k-dolphin-sysv3 - exit 0 ;; - M88*:*:R3*:*) - # Delta 88k system running SVR3 - echo m88k-motorola-sysv3 - exit 0 ;; - XD88*:*:*:*) # Tektronix XD88 system running UTekV (SVR3) - echo m88k-tektronix-sysv3 - exit 0 ;; - Tek43[0-9][0-9]:UTek:*:*) # Tektronix 4300 system running UTek (BSD) - echo m68k-tektronix-bsd - exit 0 ;; - *:IRIX*:*:*) - echo mips-sgi-irix`echo ${UNAME_RELEASE}|sed -e 's/-/_/g'` - exit 0 ;; - ????????:AIX?:[12].1:2) # AIX 2.2.1 or AIX 2.1.1 is RT/PC AIX. - echo romp-ibm-aix # uname -m gives an 8 hex-code CPU id - exit 0 ;; # Note that: echo "'`uname -s`'" gives 'AIX ' - i?86:AIX:*:*) - echo i386-ibm-aix - exit 0 ;; - *:AIX:2:3) - if grep bos325 /usr/include/stdio.h >/dev/null 2>&1; then - sed 's/^ //' << EOF >$dummy.c - #include - - main() - { - if (!__power_pc()) - exit(1); - puts("powerpc-ibm-aix3.2.5"); - exit(0); - } -EOF - $CC_FOR_BUILD $dummy.c -o $dummy && ./$dummy && rm $dummy.c $dummy && exit 0 - rm -f $dummy.c $dummy - echo rs6000-ibm-aix3.2.5 - elif grep bos324 /usr/include/stdio.h >/dev/null 2>&1; then - echo rs6000-ibm-aix3.2.4 - else - echo rs6000-ibm-aix3.2 - fi - exit 0 ;; - *:AIX:*:4) - IBM_CPU_ID=`/usr/sbin/lsdev -C -c processor -S available | head -1 | awk '{ print $1 }'` - if /usr/sbin/lsattr -EHl ${IBM_CPU_ID} | grep POWER >/dev/null 2>&1; then - IBM_ARCH=rs6000 - else - IBM_ARCH=powerpc - fi - if [ -x /usr/bin/oslevel ] ; then - IBM_REV=`/usr/bin/oslevel` - else - IBM_REV=4.${UNAME_RELEASE} - fi - echo ${IBM_ARCH}-ibm-aix${IBM_REV} - exit 0 ;; - *:AIX:*:*) - echo rs6000-ibm-aix - exit 0 ;; - ibmrt:4.4BSD:*|romp-ibm:BSD:*) - echo romp-ibm-bsd4.4 - exit 0 ;; - ibmrt:*BSD:*|romp-ibm:BSD:*) # covers RT/PC BSD and - echo romp-ibm-bsd${UNAME_RELEASE} # 4.3 with uname added to - exit 0 ;; # report: romp-ibm BSD 4.3 - *:BOSX:*:*) - echo rs6000-bull-bosx - exit 0 ;; - DPX/2?00:B.O.S.:*:*) - echo m68k-bull-sysv3 - exit 0 ;; - 9000/[34]??:4.3bsd:1.*:*) - echo m68k-hp-bsd - exit 0 ;; - hp300:4.4BSD:*:* | 9000/[34]??:4.3bsd:2.*:*) - echo m68k-hp-bsd4.4 - exit 0 ;; - 9000/[34678]??:HP-UX:*:*) - case "${UNAME_MACHINE}" in - 9000/31? ) HP_ARCH=m68000 ;; - 9000/[34]?? ) HP_ARCH=m68k ;; - 9000/[678][0-9][0-9]) - sed 's/^ //' << EOF >$dummy.c - - #define _HPUX_SOURCE - #include - #include - - int main () - { - #if defined(_SC_KERNEL_BITS) - long bits = sysconf(_SC_KERNEL_BITS); - #endif - long cpu = sysconf (_SC_CPU_VERSION); - - switch (cpu) - { - case CPU_PA_RISC1_0: puts ("hppa1.0"); break; - case CPU_PA_RISC1_1: puts ("hppa1.1"); break; - case CPU_PA_RISC2_0: - #if defined(_SC_KERNEL_BITS) - switch (bits) - { - case 64: puts ("hppa2.0w"); break; - case 32: puts ("hppa2.0n"); break; - default: puts ("hppa2.0"); break; - } break; - #else /* !defined(_SC_KERNEL_BITS) */ - puts ("hppa2.0"); break; - #endif - default: puts ("hppa1.0"); break; - } - exit (0); - } -EOF - (CCOPTS= $CC_FOR_BUILD $dummy.c -o $dummy 2>/dev/null ) && HP_ARCH=`./$dummy` - rm -f $dummy.c $dummy - esac - HPUX_REV=`echo ${UNAME_RELEASE}|sed -e 's/[^.]*.[0B]*//'` - echo ${HP_ARCH}-hp-hpux${HPUX_REV} - exit 0 ;; - 3050*:HI-UX:*:*) - sed 's/^ //' << EOF >$dummy.c - #include - int - main () - { - long cpu = sysconf (_SC_CPU_VERSION); - /* The order matters, because CPU_IS_HP_MC68K erroneously returns - true for CPU_PA_RISC1_0. CPU_IS_PA_RISC returns correct - results, however. */ - if (CPU_IS_PA_RISC (cpu)) - { - switch (cpu) - { - case CPU_PA_RISC1_0: puts ("hppa1.0-hitachi-hiuxwe2"); break; - case CPU_PA_RISC1_1: puts ("hppa1.1-hitachi-hiuxwe2"); break; - case CPU_PA_RISC2_0: puts ("hppa2.0-hitachi-hiuxwe2"); break; - default: puts ("hppa-hitachi-hiuxwe2"); break; - } - } - else if (CPU_IS_HP_MC68K (cpu)) - puts ("m68k-hitachi-hiuxwe2"); - else puts ("unknown-hitachi-hiuxwe2"); - exit (0); - } -EOF - $CC_FOR_BUILD $dummy.c -o $dummy && ./$dummy && rm $dummy.c $dummy && exit 0 - rm -f $dummy.c $dummy - echo unknown-hitachi-hiuxwe2 - exit 0 ;; - 9000/7??:4.3bsd:*:* | 9000/8?[79]:4.3bsd:*:* ) - echo hppa1.1-hp-bsd - exit 0 ;; - 9000/8??:4.3bsd:*:*) - echo hppa1.0-hp-bsd - exit 0 ;; - *9??*:MPE/iX:*:*) - echo hppa1.0-hp-mpeix - exit 0 ;; - hp7??:OSF1:*:* | hp8?[79]:OSF1:*:* ) - echo hppa1.1-hp-osf - exit 0 ;; - hp8??:OSF1:*:*) - echo hppa1.0-hp-osf - exit 0 ;; - i?86:OSF1:*:*) - if [ -x /usr/sbin/sysversion ] ; then - echo ${UNAME_MACHINE}-unknown-osf1mk - else - echo ${UNAME_MACHINE}-unknown-osf1 - fi - exit 0 ;; - parisc*:Lites*:*:*) - echo hppa1.1-hp-lites - exit 0 ;; - hppa*:OpenBSD:*:*) - echo hppa-unknown-openbsd - exit 0 ;; - C1*:ConvexOS:*:* | convex:ConvexOS:C1*:*) - echo c1-convex-bsd - exit 0 ;; - C2*:ConvexOS:*:* | convex:ConvexOS:C2*:*) - if getsysinfo -f scalar_acc - then echo c32-convex-bsd - else echo c2-convex-bsd - fi - exit 0 ;; - C34*:ConvexOS:*:* | convex:ConvexOS:C34*:*) - echo c34-convex-bsd - exit 0 ;; - C38*:ConvexOS:*:* | convex:ConvexOS:C38*:*) - echo c38-convex-bsd - exit 0 ;; - C4*:ConvexOS:*:* | convex:ConvexOS:C4*:*) - echo c4-convex-bsd - exit 0 ;; - CRAY*X-MP:*:*:*) - echo xmp-cray-unicos - exit 0 ;; - CRAY*Y-MP:*:*:*) - echo ymp-cray-unicos${UNAME_RELEASE} - exit 0 ;; - CRAY*[A-Z]90:*:*:*) - echo ${UNAME_MACHINE}-cray-unicos${UNAME_RELEASE} \ - | sed -e 's/CRAY.*\([A-Z]90\)/\1/' \ - -e y/ABCDEFGHIJKLMNOPQRSTUVWXYZ/abcdefghijklmnopqrstuvwxyz/ - exit 0 ;; - CRAY*TS:*:*:*) - echo t90-cray-unicos${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/' - exit 0 ;; - CRAY*T3E:*:*:*) - echo alpha-cray-unicosmk${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/' - exit 0 ;; - CRAY*SV1:*:*:*) - echo sv1-cray-unicos${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/' - exit 0 ;; - CRAY-2:*:*:*) - echo cray2-cray-unicos - exit 0 ;; - F300:UNIX_System_V:*:*) - FUJITSU_SYS=`uname -p | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz' | sed -e 's/\///'` - FUJITSU_REL=`echo ${UNAME_RELEASE} | sed -e 's/ /_/'` - echo "f300-fujitsu-${FUJITSU_SYS}${FUJITSU_REL}" - exit 0 ;; - F301:UNIX_System_V:*:*) - echo f301-fujitsu-uxpv`echo $UNAME_RELEASE | sed 's/ .*//'` - exit 0 ;; - hp300:OpenBSD:*:*) - echo m68k-unknown-openbsd${UNAME_RELEASE} - exit 0 ;; - i?86:BSD/386:*:* | i?86:BSD/OS:*:* | *:Ascend\ Embedded/OS:*:*) - echo ${UNAME_MACHINE}-pc-bsdi${UNAME_RELEASE} - exit 0 ;; - sparc*:BSD/OS:*:*) - echo sparc-unknown-bsdi${UNAME_RELEASE} - exit 0 ;; - *:BSD/OS:*:*) - echo ${UNAME_MACHINE}-unknown-bsdi${UNAME_RELEASE} - exit 0 ;; - *:FreeBSD:*:*) - echo ${UNAME_MACHINE}-unknown-freebsd`echo ${UNAME_RELEASE}|sed -e 's/[-(].*//'` - exit 0 ;; - *:OpenBSD:*:*) - echo ${UNAME_MACHINE}-unknown-openbsd`echo ${UNAME_RELEASE}|sed -e 's/[-_].*/\./'` - exit 0 ;; - i*:CYGWIN*:*) - echo ${UNAME_MACHINE}-pc-cygwin - exit 0 ;; - i*:MINGW*:*) - echo ${UNAME_MACHINE}-pc-mingw32 - exit 0 ;; - i*:Windows_NT*:* | Pentium*:Windows_NT*:*) - # How do we know it's Interix rather than the generic POSIX subsystem? - # It also conflicts with pre-2.0 versions of AT&T UWIN. Should we - # UNAME_MACHINE based on the output of uname instead of i386? - echo i386-pc-interix - exit 0 ;; - i*:UWIN*:*) - echo ${UNAME_MACHINE}-pc-uwin - exit 0 ;; - p*:CYGWIN*:*) - echo powerpcle-unknown-cygwin - exit 0 ;; - prep*:SunOS:5.*:*) - echo powerpcle-unknown-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'` - exit 0 ;; - *:GNU:*:*) - echo `echo ${UNAME_MACHINE}|sed -e 's,[-/].*$,,'`-unknown-gnu`echo ${UNAME_RELEASE}|sed -e 's,/.*$,,'` - exit 0 ;; - *:Linux:*:*) - - # The BFD linker knows what the default object file format is, so - # first see if it will tell us. cd to the root directory to prevent - # problems with other programs or directories called `ld' in the path. - ld_help_string=`cd /; ld --help 2>&1` - ld_supported_emulations=`echo $ld_help_string \ - | sed -ne '/supported emulations:/!d - s/[ ][ ]*/ /g - s/.*supported emulations: *// - s/ .*// - p'` - case "$ld_supported_emulations" in - *ia64) - echo "${UNAME_MACHINE}-unknown-linux" - exit 0 - ;; - i?86linux) - echo "${UNAME_MACHINE}-pc-linux-gnuaout" - exit 0 - ;; - elf_i?86) - echo "${UNAME_MACHINE}-pc-linux" - exit 0 - ;; - i?86coff) - echo "${UNAME_MACHINE}-pc-linux-gnucoff" - exit 0 - ;; - sparclinux) - echo "${UNAME_MACHINE}-unknown-linux-gnuaout" - exit 0 - ;; - armlinux) - echo "${UNAME_MACHINE}-unknown-linux-gnuaout" - exit 0 - ;; - elf32arm*) - echo "${UNAME_MACHINE}-unknown-linux-gnuoldld" - exit 0 - ;; - armelf_linux*) - echo "${UNAME_MACHINE}-unknown-linux-gnu" - exit 0 - ;; - m68klinux) - echo "${UNAME_MACHINE}-unknown-linux-gnuaout" - exit 0 - ;; - elf32ppc | elf32ppclinux) - # Determine Lib Version - cat >$dummy.c < -#if defined(__GLIBC__) -extern char __libc_version[]; -extern char __libc_release[]; -#endif -main(argc, argv) - int argc; - char *argv[]; -{ -#if defined(__GLIBC__) - printf("%s %s\n", __libc_version, __libc_release); -#else - printf("unkown\n"); -#endif - return 0; -} -EOF - LIBC="" - $CC_FOR_BUILD $dummy.c -o $dummy 2>/dev/null - if test "$?" = 0 ; then - ./$dummy | grep 1\.99 > /dev/null - if test "$?" = 0 ; then - LIBC="libc1" - fi - fi - rm -f $dummy.c $dummy - echo powerpc-unknown-linux-gnu${LIBC} - exit 0 - ;; - esac - - if test "${UNAME_MACHINE}" = "alpha" ; then - cat <$dummy.s - .data - \$Lformat: - .byte 37,100,45,37,120,10,0 # "%d-%x\n" - - .text - .globl main - .align 4 - .ent main - main: - .frame \$30,16,\$26,0 - ldgp \$29,0(\$27) - .prologue 1 - .long 0x47e03d80 # implver \$0 - lda \$2,-1 - .long 0x47e20c21 # amask \$2,\$1 - lda \$16,\$Lformat - mov \$0,\$17 - not \$1,\$18 - jsr \$26,printf - ldgp \$29,0(\$26) - mov 0,\$16 - jsr \$26,exit - .end main -EOF - LIBC="" - $CC_FOR_BUILD $dummy.s -o $dummy 2>/dev/null - if test "$?" = 0 ; then - case `./$dummy` in - 0-0) - UNAME_MACHINE="alpha" - ;; - 1-0) - UNAME_MACHINE="alphaev5" - ;; - 1-1) - UNAME_MACHINE="alphaev56" - ;; - 1-101) - UNAME_MACHINE="alphapca56" - ;; - 2-303) - UNAME_MACHINE="alphaev6" - ;; - 2-307) - UNAME_MACHINE="alphaev67" - ;; - esac - - objdump --private-headers $dummy | \ - grep ld.so.1 > /dev/null - if test "$?" = 0 ; then - LIBC="libc1" - fi - fi - rm -f $dummy.s $dummy - echo ${UNAME_MACHINE}-unknown-linux-gnu${LIBC} ; exit 0 - elif test "${UNAME_MACHINE}" = "mips" ; then - cat >$dummy.c < /* for printf() prototype */ - int main (int argc, char *argv[]) { -#else - int main (argc, argv) int argc; char *argv[]; { -#endif -#ifdef __MIPSEB__ - printf ("%s-unknown-linux-gnu\n", argv[1]); -#endif -#ifdef __MIPSEL__ - printf ("%sel-unknown-linux-gnu\n", argv[1]); -#endif - return 0; -} -EOF - $CC_FOR_BUILD $dummy.c -o $dummy 2>/dev/null && ./$dummy "${UNAME_MACHINE}" && rm $dummy.c $dummy && exit 0 - rm -f $dummy.c $dummy - elif test "${UNAME_MACHINE}" = "s390"; then - echo s390-ibm-linux && exit 0 - else - # Either a pre-BFD a.out linker (linux-gnuoldld) - # or one that does not give us useful --help. - # GCC wants to distinguish between linux-gnuoldld and linux-gnuaout. - # If ld does not provide *any* "supported emulations:" - # that means it is gnuoldld. - echo "$ld_help_string" | grep >/dev/null 2>&1 "supported emulations:" - test $? != 0 && echo "${UNAME_MACHINE}-pc-linux-gnuoldld" && exit 0 - - case "${UNAME_MACHINE}" in - i?86) - VENDOR=pc; - ;; - *) - VENDOR=unknown; - ;; - esac - # Determine whether the default compiler is a.out or elf - cat >$dummy.c < -#ifdef __cplusplus -#include /* for printf() prototype */ - int main (int argc, char *argv[]) { -#else - int main (argc, argv) int argc; char *argv[]; { -#endif -#ifdef __ELF__ -# ifdef __GLIBC__ -# if __GLIBC__ >= 2 - printf ("%s-${VENDOR}-linux-gnu\n", argv[1]); -# else - printf ("%s-${VENDOR}-linux-gnulibc1\n", argv[1]); -# endif -# else - printf ("%s-${VENDOR}-linux-gnulibc1\n", argv[1]); -# endif -#else - printf ("%s-${VENDOR}-linux-gnuaout\n", argv[1]); -#endif - return 0; -} -EOF - $CC_FOR_BUILD $dummy.c -o $dummy 2>/dev/null && ./$dummy "${UNAME_MACHINE}" && rm $dummy.c $dummy && exit 0 - rm -f $dummy.c $dummy - fi ;; -# ptx 4.0 does uname -s correctly, with DYNIX/ptx in there. earlier versions -# are messed up and put the nodename in both sysname and nodename. - i?86:DYNIX/ptx:4*:*) - echo i386-sequent-sysv4 - exit 0 ;; - i?86:UNIX_SV:4.2MP:2.*) - # Unixware is an offshoot of SVR4, but it has its own version - # number series starting with 2... - # I am not positive that other SVR4 systems won't match this, - # I just have to hope. -- rms. - # Use sysv4.2uw... so that sysv4* matches it. - echo ${UNAME_MACHINE}-pc-sysv4.2uw${UNAME_VERSION} - exit 0 ;; - i?86:*:4.*:* | i?86:SYSTEM_V:4.*:*) - UNAME_REL=`echo ${UNAME_RELEASE} | sed 's/\/MP$//'` - if grep Novell /usr/include/link.h >/dev/null 2>/dev/null; then - echo ${UNAME_MACHINE}-univel-sysv${UNAME_REL} - else - echo ${UNAME_MACHINE}-pc-sysv${UNAME_REL} - fi - exit 0 ;; - i?86:*:5:7*) - # Fixed at (any) Pentium or better - UNAME_MACHINE=i586 - if [ ${UNAME_SYSTEM} = "UnixWare" ] ; then - echo ${UNAME_MACHINE}-sco-sysv${UNAME_RELEASE}uw${UNAME_VERSION} - else - echo ${UNAME_MACHINE}-pc-sysv${UNAME_RELEASE} - fi - exit 0 ;; - i?86:*:3.2:*) - if test -f /usr/options/cb.name; then - UNAME_REL=`sed -n 's/.*Version //p' /dev/null >/dev/null ; then - UNAME_REL=`(/bin/uname -X|egrep Release|sed -e 's/.*= //')` - (/bin/uname -X|egrep i80486 >/dev/null) && UNAME_MACHINE=i486 - (/bin/uname -X|egrep '^Machine.*Pentium' >/dev/null) \ - && UNAME_MACHINE=i586 - (/bin/uname -X|egrep '^Machine.*Pent ?II' >/dev/null) \ - && UNAME_MACHINE=i686 - (/bin/uname -X|egrep '^Machine.*Pentium Pro' >/dev/null) \ - && UNAME_MACHINE=i686 - echo ${UNAME_MACHINE}-pc-sco$UNAME_REL - else - echo ${UNAME_MACHINE}-pc-sysv32 - fi - exit 0 ;; - i?86:*DOS:*:*) - echo ${UNAME_MACHINE}-pc-msdosdjgpp - exit 0 ;; - pc:*:*:*) - # Left here for compatibility: - # uname -m prints for DJGPP always 'pc', but it prints nothing about - # the processor, so we play safe by assuming i386. - echo i386-pc-msdosdjgpp - exit 0 ;; - Intel:Mach:3*:*) - echo i386-pc-mach3 - exit 0 ;; - paragon:*:*:*) - echo i860-intel-osf1 - exit 0 ;; - i860:*:4.*:*) # i860-SVR4 - if grep Stardent /usr/include/sys/uadmin.h >/dev/null 2>&1 ; then - echo i860-stardent-sysv${UNAME_RELEASE} # Stardent Vistra i860-SVR4 - else # Add other i860-SVR4 vendors below as they are discovered. - echo i860-unknown-sysv${UNAME_RELEASE} # Unknown i860-SVR4 - fi - exit 0 ;; - mini*:CTIX:SYS*5:*) - # "miniframe" - echo m68010-convergent-sysv - exit 0 ;; - M68*:*:R3V[567]*:*) - test -r /sysV68 && echo 'm68k-motorola-sysv' && exit 0 ;; - 3[34]??:*:4.0:3.0 | 3[34]??,*:*:4.0:3.0 | 4850:*:4.0:3.0) - OS_REL='' - test -r /etc/.relid \ - && OS_REL=.`sed -n 's/[^ ]* [^ ]* \([0-9][0-9]\).*/\1/p' < /etc/.relid` - /bin/uname -p 2>/dev/null | grep 86 >/dev/null \ - && echo i486-ncr-sysv4.3${OS_REL} && exit 0 - /bin/uname -p 2>/dev/null | /bin/grep entium >/dev/null \ - && echo i586-ncr-sysv4.3${OS_REL} && exit 0 ;; - 3[34]??:*:4.0:* | 3[34]??,*:*:4.0:*) - /bin/uname -p 2>/dev/null | grep 86 >/dev/null \ - && echo i486-ncr-sysv4 && exit 0 ;; - m68*:LynxOS:2.*:*) - echo m68k-unknown-lynxos${UNAME_RELEASE} - exit 0 ;; - mc68030:UNIX_System_V:4.*:*) - echo m68k-atari-sysv4 - exit 0 ;; - i?86:LynxOS:2.*:* | i?86:LynxOS:3.[01]*:*) - echo i386-unknown-lynxos${UNAME_RELEASE} - exit 0 ;; - TSUNAMI:LynxOS:2.*:*) - echo sparc-unknown-lynxos${UNAME_RELEASE} - exit 0 ;; - rs6000:LynxOS:2.*:* | PowerPC:LynxOS:2.*:*) - echo rs6000-unknown-lynxos${UNAME_RELEASE} - exit 0 ;; - SM[BE]S:UNIX_SV:*:*) - echo mips-dde-sysv${UNAME_RELEASE} - exit 0 ;; - RM*:ReliantUNIX-*:*:*) - echo mips-sni-sysv4 - exit 0 ;; - RM*:SINIX-*:*:*) - echo mips-sni-sysv4 - exit 0 ;; - *:SINIX-*:*:*) - if uname -p 2>/dev/null >/dev/null ; then - UNAME_MACHINE=`(uname -p) 2>/dev/null` - echo ${UNAME_MACHINE}-sni-sysv4 - else - echo ns32k-sni-sysv - fi - exit 0 ;; - PENTIUM:CPunix:4.0*:*) # Unisys `ClearPath HMP IX 4000' SVR4/MP effort - # says - echo i586-unisys-sysv4 - exit 0 ;; - *:UNIX_System_V:4*:FTX*) - # From Gerald Hewes . - # How about differentiating between stratus architectures? -djm - echo hppa1.1-stratus-sysv4 - exit 0 ;; - *:*:*:FTX*) - # From seanf@swdc.stratus.com. - echo i860-stratus-sysv4 - exit 0 ;; - mc68*:A/UX:*:*) - echo m68k-apple-aux${UNAME_RELEASE} - exit 0 ;; - news*:NEWS-OS:*:6*) - echo mips-sony-newsos6 - exit 0 ;; - R[34]000:*System_V*:*:* | R4000:UNIX_SYSV:*:* | R*000:UNIX_SV:*:*) - if [ -d /usr/nec ]; then - echo mips-nec-sysv${UNAME_RELEASE} - else - echo mips-unknown-sysv${UNAME_RELEASE} - fi - exit 0 ;; - BeBox:BeOS:*:*) # BeOS running on hardware made by Be, PPC only. - echo powerpc-be-beos - exit 0 ;; - BeMac:BeOS:*:*) # BeOS running on Mac or Mac clone, PPC only. - echo powerpc-apple-beos - exit 0 ;; - BePC:BeOS:*:*) # BeOS running on Intel PC compatible. - echo i586-pc-beos - exit 0 ;; - SX-4:SUPER-UX:*:*) - echo sx4-nec-superux${UNAME_RELEASE} - exit 0 ;; - SX-5:SUPER-UX:*:*) - echo sx5-nec-superux${UNAME_RELEASE} - exit 0 ;; - Power*:Rhapsody:*:*) - echo powerpc-apple-rhapsody${UNAME_RELEASE} - exit 0 ;; - *:Rhapsody:*:*) - echo ${UNAME_MACHINE}-apple-rhapsody${UNAME_RELEASE} - exit 0 ;; - *:Darwin:*:*) - echo `uname -p`-apple-darwin${UNAME_RELEASE} - exit 0 ;; - *:procnto*:*:* | *:QNX:[0123456789]*:*) - if test "${UNAME_MACHINE}" = "x86pc"; then - UNAME_MACHINE=pc - fi - echo `uname -p`-${UNAME_MACHINE}-nto-qnx - exit 0 ;; - *:QNX:*:4*) - echo i386-pc-qnx - exit 0 ;; - NSR-W:NONSTOP_KERNEL:*:*) - echo nsr-tandem-nsk${UNAME_RELEASE} - exit 0 ;; - BS2000:POSIX*:*:*) - echo bs2000-siemens-sysv - exit 0 ;; - DS/*:UNIX_System_V:*:*) - echo ${UNAME_MACHINE}-${UNAME_SYSTEM}-${UNAME_RELEASE} - exit 0 ;; -esac - -#echo '(No uname command or uname output not recognized.)' 1>&2 -#echo "${UNAME_MACHINE}:${UNAME_SYSTEM}:${UNAME_RELEASE}:${UNAME_VERSION}" 1>&2 - -cat >$dummy.c < -# include -#endif -main () -{ -#if defined (sony) -#if defined (MIPSEB) - /* BFD wants "bsd" instead of "newsos". Perhaps BFD should be changed, - I don't know.... */ - printf ("mips-sony-bsd\n"); exit (0); -#else -#include - printf ("m68k-sony-newsos%s\n", -#ifdef NEWSOS4 - "4" -#else - "" -#endif - ); exit (0); -#endif -#endif - -#if defined (__arm) && defined (__acorn) && defined (__unix) - printf ("arm-acorn-riscix"); exit (0); -#endif - -#if defined (hp300) && !defined (hpux) - printf ("m68k-hp-bsd\n"); exit (0); -#endif - -#if defined (NeXT) -#if !defined (__ARCHITECTURE__) -#define __ARCHITECTURE__ "m68k" -#endif - int version; - version=`(hostinfo | sed -n 's/.*NeXT Mach \([0-9]*\).*/\1/p') 2>/dev/null`; - if (version < 4) - printf ("%s-next-nextstep%d\n", __ARCHITECTURE__, version); - else - printf ("%s-next-openstep%d\n", __ARCHITECTURE__, version); - exit (0); -#endif - -#if defined (MULTIMAX) || defined (n16) -#if defined (UMAXV) - printf ("ns32k-encore-sysv\n"); exit (0); -#else -#if defined (CMU) - printf ("ns32k-encore-mach\n"); exit (0); -#else - printf ("ns32k-encore-bsd\n"); exit (0); -#endif -#endif -#endif - -#if defined (__386BSD__) - printf ("i386-pc-bsd\n"); exit (0); -#endif - -#if defined (sequent) -#if defined (i386) - printf ("i386-sequent-dynix\n"); exit (0); -#endif -#if defined (ns32000) - printf ("ns32k-sequent-dynix\n"); exit (0); -#endif -#endif - -#if defined (_SEQUENT_) - struct utsname un; - - uname(&un); - - if (strncmp(un.version, "V2", 2) == 0) { - printf ("i386-sequent-ptx2\n"); exit (0); - } - if (strncmp(un.version, "V1", 2) == 0) { /* XXX is V1 correct? */ - printf ("i386-sequent-ptx1\n"); exit (0); - } - printf ("i386-sequent-ptx\n"); exit (0); - -#endif - -#if defined (vax) -#if !defined (ultrix) - printf ("vax-dec-bsd\n"); exit (0); -#else - printf ("vax-dec-ultrix\n"); exit (0); -#endif -#endif - -#if defined (alliant) && defined (i860) - printf ("i860-alliant-bsd\n"); exit (0); -#endif - - exit (1); -} -EOF - -$CC_FOR_BUILD $dummy.c -o $dummy 2>/dev/null && ./$dummy && rm $dummy.c $dummy && exit 0 -rm -f $dummy.c $dummy - -# Apollos put the system type in the environment. - -test -d /usr/apollo && { echo ${ISP}-apollo-${SYSTYPE}; exit 0; } - -# Convex versions that predate uname can use getsysinfo(1) - -if [ -x /usr/convex/getsysinfo ] -then - case `getsysinfo -f cpu_type` in - c1*) - echo c1-convex-bsd - exit 0 ;; - c2*) - if getsysinfo -f scalar_acc - then echo c32-convex-bsd - else echo c2-convex-bsd - fi - exit 0 ;; - c34*) - echo c34-convex-bsd - exit 0 ;; - c38*) - echo c38-convex-bsd - exit 0 ;; - c4*) - echo c4-convex-bsd - exit 0 ;; - esac -fi - -#echo '(Unable to guess system type)' 1>&2 - -exit 1 diff --git a/libtecla-1.4.1/config.sub b/libtecla-1.4.1/config.sub deleted file mode 100644 index c8e7785..0000000 --- a/libtecla-1.4.1/config.sub +++ /dev/null @@ -1,1268 +0,0 @@ -#! /bin/sh -# Configuration validation subroutine script, version 1.1. -# Copyright (C) 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999, 2000 -# Free Software Foundation, Inc. -# -# This file is (in principle) common to ALL GNU software. -# The presence of a machine in this file suggests that SOME GNU software -# can handle that machine. It does not imply ALL GNU software can. -# -# This file 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. - -# As a special exception to the GNU General Public License, if you -# distribute this file as part of a program that contains a -# configuration script generated by Autoconf, you may include it under -# the same distribution terms that you use for the rest of that program. - -# Written by Per Bothner . -# Please send patches to . -# -# Configuration subroutine to validate and canonicalize a configuration type. -# Supply the specified configuration type as an argument. -# If it is invalid, we print an error message on stderr and exit with code 1. -# Otherwise, we print the canonical config type on stdout and succeed. - -# This file is supposed to be the same for all GNU packages -# and recognize all the CPU types, system types and aliases -# that are meaningful with *any* GNU software. -# Each package is responsible for reporting which valid configurations -# it does not support. The user should be able to distinguish -# a failure to support a valid configuration from a meaningless -# configuration. - -# The goal of this file is to map all the various variations of a given -# machine specification into a single specification in the form: -# CPU_TYPE-MANUFACTURER-OPERATING_SYSTEM -# or in some cases, the newer four-part form: -# CPU_TYPE-MANUFACTURER-KERNEL-OPERATING_SYSTEM -# It is wrong to echo any other type of specification. - -if [ x$1 = x ] -then - echo Configuration name missing. 1>&2 - echo "Usage: $0 CPU-MFR-OPSYS" 1>&2 - echo "or $0 ALIAS" 1>&2 - echo where ALIAS is a recognized configuration type. 1>&2 - exit 1 -fi - -# First pass through any local machine types. -case $1 in - *local*) - echo $1 - exit 0 - ;; - *) - ;; -esac - -# Separate what the user gave into CPU-COMPANY and OS or KERNEL-OS (if any). -# Here we must recognize all the valid KERNEL-OS combinations. -maybe_os=`echo $1 | sed 's/^\(.*\)-\([^-]*-[^-]*\)$/\2/'` -case $maybe_os in - nto-qnx* | linux-gnu*) - os=-$maybe_os - basic_machine=`echo $1 | sed 's/^\(.*\)-\([^-]*-[^-]*\)$/\1/'` - ;; - *) - basic_machine=`echo $1 | sed 's/-[^-]*$//'` - if [ $basic_machine != $1 ] - then os=`echo $1 | sed 's/.*-/-/'` - else os=; fi - ;; -esac - -### Let's recognize common machines as not being operating systems so -### that things like config.sub decstation-3100 work. We also -### recognize some manufacturers as not being operating systems, so we -### can provide default operating systems below. -case $os in - -sun*os*) - # Prevent following clause from handling this invalid input. - ;; - -dec* | -mips* | -sequent* | -encore* | -pc532* | -sgi* | -sony* | \ - -att* | -7300* | -3300* | -delta* | -motorola* | -sun[234]* | \ - -unicom* | -ibm* | -next | -hp | -isi* | -apollo | -altos* | \ - -convergent* | -ncr* | -news | -32* | -3600* | -3100* | -hitachi* |\ - -c[123]* | -convex* | -sun | -crds | -omron* | -dg | -ultra | -tti* | \ - -harris | -dolphin | -highlevel | -gould | -cbm | -ns | -masscomp | \ - -apple) - os= - basic_machine=$1 - ;; - -sim | -cisco | -oki | -wec | -winbond) - os= - basic_machine=$1 - ;; - -scout) - ;; - -wrs) - os=-vxworks - basic_machine=$1 - ;; - -hiux*) - os=-hiuxwe2 - ;; - -sco5) - os=-sco3.2v5 - basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` - ;; - -sco4) - os=-sco3.2v4 - basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` - ;; - -sco3.2.[4-9]*) - os=`echo $os | sed -e 's/sco3.2./sco3.2v/'` - basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` - ;; - -sco3.2v[4-9]*) - # Don't forget version if it is 3.2v4 or newer. - basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` - ;; - -sco*) - os=-sco3.2v2 - basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` - ;; - -udk*) - basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` - ;; - -isc) - os=-isc2.2 - basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` - ;; - -clix*) - basic_machine=clipper-intergraph - ;; - -isc*) - basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` - ;; - -lynx*) - os=-lynxos - ;; - -ptx*) - basic_machine=`echo $1 | sed -e 's/86-.*/86-sequent/'` - ;; - -windowsnt*) - os=`echo $os | sed -e 's/windowsnt/winnt/'` - ;; - -psos*) - os=-psos - ;; - -mint | -mint[0-9]*) - basic_machine=m68k-atari - os=-mint - ;; -esac - -# Decode aliases for certain CPU-COMPANY combinations. -case $basic_machine in - # Recognize the basic CPU types without company name. - # Some are omitted here because they have special meanings below. - tahoe | i860 | ia64 | m32r | m68k | m68000 | m88k | ns32k | arc | arm \ - | arme[lb] | pyramid | mn10200 | mn10300 | tron | a29k \ - | 580 | i960 | h8300 \ - | x86 | ppcbe | mipsbe | mipsle | shbe | shle | armbe | armle \ - | hppa | hppa1.0 | hppa1.1 | hppa2.0 | hppa2.0w | hppa2.0n \ - | hppa64 \ - | alpha | alphaev[4-8] | alphaev56 | alphapca5[67] \ - | alphaev6[78] \ - | we32k | ns16k | clipper | i370 | sh | powerpc | powerpcle \ - | 1750a | dsp16xx | pdp11 | mips16 | mips64 | mipsel | mips64el \ - | mips64orion | mips64orionel | mipstx39 | mipstx39el \ - | mips64vr4300 | mips64vr4300el | mips64vr4100 | mips64vr4100el \ - | mips64vr5000 | miprs64vr5000el | mcore \ - | sparc | sparclet | sparclite | sparc64 | sparcv9 | v850 | c4x \ - | thumb | d10v | fr30 | avr) - basic_machine=$basic_machine-unknown - ;; - m88110 | m680[12346]0 | m683?2 | m68360 | m5200 | z8k | v70 | h8500 | w65 | pj | pjl) - ;; - - # We use `pc' rather than `unknown' - # because (1) that's what they normally are, and - # (2) the word "unknown" tends to confuse beginning users. - i[34567]86) - basic_machine=$basic_machine-pc - ;; - # Object if more than one company name word. - *-*-*) - echo Invalid configuration \`$1\': machine \`$basic_machine\' not recognized 1>&2 - exit 1 - ;; - # Recognize the basic CPU types with company name. - # FIXME: clean up the formatting here. - vax-* | tahoe-* | i[34567]86-* | i860-* | ia64-* | m32r-* | m68k-* | m68000-* \ - | m88k-* | sparc-* | ns32k-* | fx80-* | arc-* | arm-* | c[123]* \ - | mips-* | pyramid-* | tron-* | a29k-* | romp-* | rs6000-* \ - | power-* | none-* | 580-* | cray2-* | h8300-* | h8500-* | i960-* \ - | xmp-* | ymp-* \ - | x86-* | ppcbe-* | mipsbe-* | mipsle-* | shbe-* | shle-* | armbe-* | armle-* \ - | hppa-* | hppa1.0-* | hppa1.1-* | hppa2.0-* | hppa2.0w-* \ - | hppa2.0n-* | hppa64-* \ - | alpha-* | alphaev[4-8]-* | alphaev56-* | alphapca5[67]-* \ - | alphaev6[78]-* \ - | we32k-* | cydra-* | ns16k-* | pn-* | np1-* | xps100-* \ - | clipper-* | orion-* \ - | sparclite-* | pdp11-* | sh-* | powerpc-* | powerpcle-* \ - | sparc64-* | sparcv9-* | sparc86x-* | mips16-* | mips64-* | mipsel-* \ - | mips64el-* | mips64orion-* | mips64orionel-* \ - | mips64vr4100-* | mips64vr4100el-* | mips64vr4300-* | mips64vr4300el-* \ - | mipstx39-* | mipstx39el-* | mcore-* \ - | f301-* | armv*-* | s390-* | sv1-* | t3e-* \ - | m88110-* | m680[01234]0-* | m683?2-* | m68360-* | z8k-* | d10v-* \ - | thumb-* | v850-* | d30v-* | tic30-* | c30-* | fr30-* \ - | bs2000-*) - ;; - # Recognize the various machine names and aliases which stand - # for a CPU type and a company and sometimes even an OS. - 386bsd) - basic_machine=i386-unknown - os=-bsd - ;; - 3b1 | 7300 | 7300-att | att-7300 | pc7300 | safari | unixpc) - basic_machine=m68000-att - ;; - 3b*) - basic_machine=we32k-att - ;; - a29khif) - basic_machine=a29k-amd - os=-udi - ;; - adobe68k) - basic_machine=m68010-adobe - os=-scout - ;; - alliant | fx80) - basic_machine=fx80-alliant - ;; - altos | altos3068) - basic_machine=m68k-altos - ;; - am29k) - basic_machine=a29k-none - os=-bsd - ;; - amdahl) - basic_machine=580-amdahl - os=-sysv - ;; - amiga | amiga-*) - basic_machine=m68k-cbm - ;; - amigaos | amigados) - basic_machine=m68k-cbm - os=-amigaos - ;; - amigaunix | amix) - basic_machine=m68k-cbm - os=-sysv4 - ;; - apollo68) - basic_machine=m68k-apollo - os=-sysv - ;; - apollo68bsd) - basic_machine=m68k-apollo - os=-bsd - ;; - aux) - basic_machine=m68k-apple - os=-aux - ;; - balance) - basic_machine=ns32k-sequent - os=-dynix - ;; - convex-c1) - basic_machine=c1-convex - os=-bsd - ;; - convex-c2) - basic_machine=c2-convex - os=-bsd - ;; - convex-c32) - basic_machine=c32-convex - os=-bsd - ;; - convex-c34) - basic_machine=c34-convex - os=-bsd - ;; - convex-c38) - basic_machine=c38-convex - os=-bsd - ;; - cray | ymp) - basic_machine=ymp-cray - os=-unicos - ;; - cray2) - basic_machine=cray2-cray - os=-unicos - ;; - [ctj]90-cray) - basic_machine=c90-cray - os=-unicos - ;; - crds | unos) - basic_machine=m68k-crds - ;; - da30 | da30-*) - basic_machine=m68k-da30 - ;; - decstation | decstation-3100 | pmax | pmax-* | pmin | dec3100 | decstatn) - basic_machine=mips-dec - ;; - delta | 3300 | motorola-3300 | motorola-delta \ - | 3300-motorola | delta-motorola) - basic_machine=m68k-motorola - ;; - delta88) - basic_machine=m88k-motorola - os=-sysv3 - ;; - dpx20 | dpx20-*) - basic_machine=rs6000-bull - os=-bosx - ;; - dpx2* | dpx2*-bull) - basic_machine=m68k-bull - os=-sysv3 - ;; - ebmon29k) - basic_machine=a29k-amd - os=-ebmon - ;; - elxsi) - basic_machine=elxsi-elxsi - os=-bsd - ;; - encore | umax | mmax) - basic_machine=ns32k-encore - ;; - es1800 | OSE68k | ose68k | ose | OSE) - basic_machine=m68k-ericsson - os=-ose - ;; - fx2800) - basic_machine=i860-alliant - ;; - genix) - basic_machine=ns32k-ns - ;; - gmicro) - basic_machine=tron-gmicro - os=-sysv - ;; - h3050r* | hiux*) - basic_machine=hppa1.1-hitachi - os=-hiuxwe2 - ;; - h8300hms) - basic_machine=h8300-hitachi - os=-hms - ;; - h8300xray) - basic_machine=h8300-hitachi - os=-xray - ;; - h8500hms) - basic_machine=h8500-hitachi - os=-hms - ;; - harris) - basic_machine=m88k-harris - os=-sysv3 - ;; - hp300-*) - basic_machine=m68k-hp - ;; - hp300bsd) - basic_machine=m68k-hp - os=-bsd - ;; - hp300hpux) - basic_machine=m68k-hp - os=-hpux - ;; - hp3k9[0-9][0-9] | hp9[0-9][0-9]) - basic_machine=hppa1.0-hp - ;; - hp9k2[0-9][0-9] | hp9k31[0-9]) - basic_machine=m68000-hp - ;; - hp9k3[2-9][0-9]) - basic_machine=m68k-hp - ;; - hp9k6[0-9][0-9] | hp6[0-9][0-9]) - basic_machine=hppa1.0-hp - ;; - hp9k7[0-79][0-9] | hp7[0-79][0-9]) - basic_machine=hppa1.1-hp - ;; - hp9k78[0-9] | hp78[0-9]) - # FIXME: really hppa2.0-hp - basic_machine=hppa1.1-hp - ;; - hp9k8[67]1 | hp8[67]1 | hp9k80[24] | hp80[24] | hp9k8[78]9 | hp8[78]9 | hp9k893 | hp893) - # FIXME: really hppa2.0-hp - basic_machine=hppa1.1-hp - ;; - hp9k8[0-9][13679] | hp8[0-9][13679]) - basic_machine=hppa1.1-hp - ;; - hp9k8[0-9][0-9] | hp8[0-9][0-9]) - basic_machine=hppa1.0-hp - ;; - hppa-next) - os=-nextstep3 - ;; - hppaosf) - basic_machine=hppa1.1-hp - os=-osf - ;; - hppro) - basic_machine=hppa1.1-hp - os=-proelf - ;; - i370-ibm* | ibm*) - basic_machine=i370-ibm - ;; -# I'm not sure what "Sysv32" means. Should this be sysv3.2? - i[34567]86v32) - basic_machine=`echo $1 | sed -e 's/86.*/86-pc/'` - os=-sysv32 - ;; - i[34567]86v4*) - basic_machine=`echo $1 | sed -e 's/86.*/86-pc/'` - os=-sysv4 - ;; - i[34567]86v) - basic_machine=`echo $1 | sed -e 's/86.*/86-pc/'` - os=-sysv - ;; - i[34567]86sol2) - basic_machine=`echo $1 | sed -e 's/86.*/86-pc/'` - os=-solaris2 - ;; - i386mach) - basic_machine=i386-mach - os=-mach - ;; - i386-vsta | vsta) - basic_machine=i386-unknown - os=-vsta - ;; - i386-go32 | go32) - basic_machine=i386-unknown - os=-go32 - ;; - i386-mingw32 | mingw32) - basic_machine=i386-unknown - os=-mingw32 - ;; - iris | iris4d) - basic_machine=mips-sgi - case $os in - -irix*) - ;; - *) - os=-irix4 - ;; - esac - ;; - isi68 | isi) - basic_machine=m68k-isi - os=-sysv - ;; - m88k-omron*) - basic_machine=m88k-omron - ;; - magnum | m3230) - basic_machine=mips-mips - os=-sysv - ;; - merlin) - basic_machine=ns32k-utek - os=-sysv - ;; - miniframe) - basic_machine=m68000-convergent - ;; - *mint | -mint[0-9]* | *MiNT | *MiNT[0-9]*) - basic_machine=m68k-atari - os=-mint - ;; - mipsel*-linux*) - basic_machine=mipsel-unknown - os=-linux-gnu - ;; - mips*-linux*) - basic_machine=mips-unknown - os=-linux-gnu - ;; - mips3*-*) - basic_machine=`echo $basic_machine | sed -e 's/mips3/mips64/'` - ;; - mips3*) - basic_machine=`echo $basic_machine | sed -e 's/mips3/mips64/'`-unknown - ;; - mmix*) - basic_machine=mmix-knuth - os=-mmixware - ;; - monitor) - basic_machine=m68k-rom68k - os=-coff - ;; - msdos) - basic_machine=i386-unknown - os=-msdos - ;; - mvs) - basic_machine=i370-ibm - os=-mvs - ;; - ncr3000) - basic_machine=i486-ncr - os=-sysv4 - ;; - netbsd386) - basic_machine=i386-unknown - os=-netbsd - ;; - netwinder) - basic_machine=armv4l-rebel - os=-linux - ;; - news | news700 | news800 | news900) - basic_machine=m68k-sony - os=-newsos - ;; - news1000) - basic_machine=m68030-sony - os=-newsos - ;; - news-3600 | risc-news) - basic_machine=mips-sony - os=-newsos - ;; - necv70) - basic_machine=v70-nec - os=-sysv - ;; - next | m*-next ) - basic_machine=m68k-next - case $os in - -nextstep* ) - ;; - -ns2*) - os=-nextstep2 - ;; - *) - os=-nextstep3 - ;; - esac - ;; - nh3000) - basic_machine=m68k-harris - os=-cxux - ;; - nh[45]000) - basic_machine=m88k-harris - os=-cxux - ;; - nindy960) - basic_machine=i960-intel - os=-nindy - ;; - mon960) - basic_machine=i960-intel - os=-mon960 - ;; - np1) - basic_machine=np1-gould - ;; - nsr-tandem) - basic_machine=nsr-tandem - ;; - op50n-* | op60c-*) - basic_machine=hppa1.1-oki - os=-proelf - ;; - OSE68000 | ose68000) - basic_machine=m68000-ericsson - os=-ose - ;; - os68k) - basic_machine=m68k-none - os=-os68k - ;; - pa-hitachi) - basic_machine=hppa1.1-hitachi - os=-hiuxwe2 - ;; - paragon) - basic_machine=i860-intel - os=-osf - ;; - pbd) - basic_machine=sparc-tti - ;; - pbb) - basic_machine=m68k-tti - ;; - pc532 | pc532-*) - basic_machine=ns32k-pc532 - ;; - pentium | p5 | k5 | k6 | nexen) - basic_machine=i586-pc - ;; - pentiumpro | p6 | 6x86) - basic_machine=i686-pc - ;; - pentiumii | pentium2) - basic_machine=i786-pc - ;; - pentium-* | p5-* | k5-* | k6-* | nexen-*) - basic_machine=i586-`echo $basic_machine | sed 's/^[^-]*-//'` - ;; - pentiumpro-* | p6-* | 6x86-*) - basic_machine=i686-`echo $basic_machine | sed 's/^[^-]*-//'` - ;; - pentiumii-* | pentium2-*) - basic_machine=i786-`echo $basic_machine | sed 's/^[^-]*-//'` - ;; - pn) - basic_machine=pn-gould - ;; - power) basic_machine=rs6000-ibm - ;; - ppc) basic_machine=powerpc-unknown - ;; - ppc-*) basic_machine=powerpc-`echo $basic_machine | sed 's/^[^-]*-//'` - ;; - ppcle | powerpclittle | ppc-le | powerpc-little) - basic_machine=powerpcle-unknown - ;; - ppcle-* | powerpclittle-*) - basic_machine=powerpcle-`echo $basic_machine | sed 's/^[^-]*-//'` - ;; - ps2) - basic_machine=i386-ibm - ;; - rom68k) - basic_machine=m68k-rom68k - os=-coff - ;; - rm[46]00) - basic_machine=mips-siemens - ;; - rtpc | rtpc-*) - basic_machine=romp-ibm - ;; - sa29200) - basic_machine=a29k-amd - os=-udi - ;; - sequent) - basic_machine=i386-sequent - ;; - sh) - basic_machine=sh-hitachi - os=-hms - ;; - sparclite-wrs) - basic_machine=sparclite-wrs - os=-vxworks - ;; - sps7) - basic_machine=m68k-bull - os=-sysv2 - ;; - spur) - basic_machine=spur-unknown - ;; - st2000) - basic_machine=m68k-tandem - ;; - stratus) - basic_machine=i860-stratus - os=-sysv4 - ;; - sun2) - basic_machine=m68000-sun - ;; - sun2os3) - basic_machine=m68000-sun - os=-sunos3 - ;; - sun2os4) - basic_machine=m68000-sun - os=-sunos4 - ;; - sun3os3) - basic_machine=m68k-sun - os=-sunos3 - ;; - sun3os4) - basic_machine=m68k-sun - os=-sunos4 - ;; - sun4os3) - basic_machine=sparc-sun - os=-sunos3 - ;; - sun4os4) - basic_machine=sparc-sun - os=-sunos4 - ;; - sun4sol2) - basic_machine=sparc-sun - os=-solaris2 - ;; - sun3 | sun3-*) - basic_machine=m68k-sun - ;; - sun4) - basic_machine=sparc-sun - ;; - sun386 | sun386i | roadrunner) - basic_machine=i386-sun - ;; - sv1) - basic_machine=sv1-cray - os=-unicos - ;; - symmetry) - basic_machine=i386-sequent - os=-dynix - ;; - t3e) - basic_machine=t3e-cray - os=-unicos - ;; - tx39) - basic_machine=mipstx39-unknown - ;; - tx39el) - basic_machine=mipstx39el-unknown - ;; - tower | tower-32) - basic_machine=m68k-ncr - ;; - udi29k) - basic_machine=a29k-amd - os=-udi - ;; - ultra3) - basic_machine=a29k-nyu - os=-sym1 - ;; - v810 | necv810) - basic_machine=v810-nec - os=-none - ;; - vaxv) - basic_machine=vax-dec - os=-sysv - ;; - vms) - basic_machine=vax-dec - os=-vms - ;; - vpp*|vx|vx-*) - basic_machine=f301-fujitsu - ;; - vxworks960) - basic_machine=i960-wrs - os=-vxworks - ;; - vxworks68) - basic_machine=m68k-wrs - os=-vxworks - ;; - vxworks29k) - basic_machine=a29k-wrs - os=-vxworks - ;; - w65*) - basic_machine=w65-wdc - os=-none - ;; - w89k-*) - basic_machine=hppa1.1-winbond - os=-proelf - ;; - xmp) - basic_machine=xmp-cray - os=-unicos - ;; - xps | xps100) - basic_machine=xps100-honeywell - ;; - z8k-*-coff) - basic_machine=z8k-unknown - os=-sim - ;; - none) - basic_machine=none-none - os=-none - ;; - -# Here we handle the default manufacturer of certain CPU types. It is in -# some cases the only manufacturer, in others, it is the most popular. - w89k) - basic_machine=hppa1.1-winbond - ;; - op50n) - basic_machine=hppa1.1-oki - ;; - op60c) - basic_machine=hppa1.1-oki - ;; - mips) - if [ x$os = x-linux-gnu ]; then - basic_machine=mips-unknown - else - basic_machine=mips-mips - fi - ;; - romp) - basic_machine=romp-ibm - ;; - rs6000) - basic_machine=rs6000-ibm - ;; - vax) - basic_machine=vax-dec - ;; - pdp11) - basic_machine=pdp11-dec - ;; - we32k) - basic_machine=we32k-att - ;; - sparc | sparcv9) - basic_machine=sparc-sun - ;; - cydra) - basic_machine=cydra-cydrome - ;; - orion) - basic_machine=orion-highlevel - ;; - orion105) - basic_machine=clipper-highlevel - ;; - mac | mpw | mac-mpw) - basic_machine=m68k-apple - ;; - pmac | pmac-mpw) - basic_machine=powerpc-apple - ;; - c4x*) - basic_machine=c4x-none - os=-coff - ;; - *) - echo Invalid configuration \`$1\': machine \`$basic_machine\' not recognized 1>&2 - exit 1 - ;; -esac - -# Here we canonicalize certain aliases for manufacturers. -case $basic_machine in - *-digital*) - basic_machine=`echo $basic_machine | sed 's/digital.*/dec/'` - ;; - *-commodore*) - basic_machine=`echo $basic_machine | sed 's/commodore.*/cbm/'` - ;; - *) - ;; -esac - -# Decode manufacturer-specific aliases for certain operating systems. - -if [ x"$os" != x"" ] -then -case $os in - # First match some system type aliases - # that might get confused with valid system types. - # -solaris* is a basic system type, with this one exception. - -solaris1 | -solaris1.*) - os=`echo $os | sed -e 's|solaris1|sunos4|'` - ;; - -solaris) - os=-solaris2 - ;; - -svr4*) - os=-sysv4 - ;; - -unixware*) - os=-sysv4.2uw - ;; - -gnu/linux*) - os=`echo $os | sed -e 's|gnu/linux|linux-gnu|'` - ;; - # First accept the basic system types. - # The portable systems comes first. - # Each alternative MUST END IN A *, to match a version number. - # -sysv* is not here because it comes later, after sysvr4. - -gnu* | -bsd* | -mach* | -minix* | -genix* | -ultrix* | -irix* \ - | -*vms* | -sco* | -esix* | -isc* | -aix* | -sunos | -sunos[34]*\ - | -hpux* | -unos* | -osf* | -luna* | -dgux* | -solaris* | -sym* \ - | -amigaos* | -amigados* | -msdos* | -newsos* | -unicos* | -aof* \ - | -aos* \ - | -nindy* | -vxsim* | -vxworks* | -ebmon* | -hms* | -mvs* \ - | -clix* | -riscos* | -uniplus* | -iris* | -rtu* | -xenix* \ - | -hiux* | -386bsd* | -netbsd* | -openbsd* | -freebsd* | -riscix* \ - | -lynxos* | -bosx* | -nextstep* | -cxux* | -aout* | -elf* | -oabi* \ - | -ptx* | -coff* | -ecoff* | -winnt* | -domain* | -vsta* \ - | -udi* | -eabi* | -lites* | -ieee* | -go32* | -aux* \ - | -cygwin* | -pe* | -psos* | -moss* | -proelf* | -rtems* \ - | -mingw32* | -linux-gnu* | -uxpv* | -beos* | -mpeix* | -udk* \ - | -interix* | -uwin* | -rhapsody* | -darwin* | -opened* \ - | -openstep* | -oskit*) - # Remember, each alternative MUST END IN *, to match a version number. - ;; - -qnx*) - case $basic_machine in - x86-* | i[34567]86-*) - ;; - *) - os=-nto$os - ;; - esac - ;; - -nto*) - os=-nto-qnx - ;; - -sim | -es1800* | -hms* | -xray | -os68k* | -none* | -v88r* \ - | -windows* | -osx | -abug | -netware* | -os9* | -beos* \ - | -macos* | -mpw* | -magic* | -mmixware* | -mon960* | -lnews*) - ;; - -mac*) - os=`echo $os | sed -e 's|mac|macos|'` - ;; - -linux*) - os=`echo $os | sed -e 's|linux|linux-gnu|'` - ;; - -sunos5*) - os=`echo $os | sed -e 's|sunos5|solaris2|'` - ;; - -sunos6*) - os=`echo $os | sed -e 's|sunos6|solaris3|'` - ;; - -opened*) - os=-openedition - ;; - -wince*) - os=-wince - ;; - -osfrose*) - os=-osfrose - ;; - -osf*) - os=-osf - ;; - -utek*) - os=-bsd - ;; - -dynix*) - os=-bsd - ;; - -acis*) - os=-aos - ;; - -386bsd) - os=-bsd - ;; - -ctix* | -uts*) - os=-sysv - ;; - -ns2 ) - os=-nextstep2 - ;; - -nsk) - os=-nsk - ;; - # Preserve the version number of sinix5. - -sinix5.*) - os=`echo $os | sed -e 's|sinix|sysv|'` - ;; - -sinix*) - os=-sysv4 - ;; - -triton*) - os=-sysv3 - ;; - -oss*) - os=-sysv3 - ;; - -svr4) - os=-sysv4 - ;; - -svr3) - os=-sysv3 - ;; - -sysvr4) - os=-sysv4 - ;; - # This must come after -sysvr4. - -sysv*) - ;; - -ose*) - os=-ose - ;; - -es1800*) - os=-ose - ;; - -xenix) - os=-xenix - ;; - -*mint | -*MiNT) - os=-mint - ;; - -none) - ;; - *) - # Get rid of the `-' at the beginning of $os. - os=`echo $os | sed 's/[^-]*-//'` - echo Invalid configuration \`$1\': system \`$os\' not recognized 1>&2 - exit 1 - ;; -esac -else - -# Here we handle the default operating systems that come with various machines. -# The value should be what the vendor currently ships out the door with their -# machine or put another way, the most popular os provided with the machine. - -# Note that if you're going to try to match "-MANUFACTURER" here (say, -# "-sun"), then you have to tell the case statement up towards the top -# that MANUFACTURER isn't an operating system. Otherwise, code above -# will signal an error saying that MANUFACTURER isn't an operating -# system, and we'll never get to this point. - -case $basic_machine in - *-acorn) - os=-riscix1.2 - ;; - arm*-rebel) - os=-linux - ;; - arm*-semi) - os=-aout - ;; - pdp11-*) - os=-none - ;; - *-dec | vax-*) - os=-ultrix4.2 - ;; - m68*-apollo) - os=-domain - ;; - i386-sun) - os=-sunos4.0.2 - ;; - m68000-sun) - os=-sunos3 - # This also exists in the configure program, but was not the - # default. - # os=-sunos4 - ;; - m68*-cisco) - os=-aout - ;; - mips*-cisco) - os=-elf - ;; - mips*-*) - os=-elf - ;; - *-tti) # must be before sparc entry or we get the wrong os. - os=-sysv3 - ;; - sparc-* | *-sun) - os=-sunos4.1.1 - ;; - *-be) - os=-beos - ;; - *-ibm) - os=-aix - ;; - *-wec) - os=-proelf - ;; - *-winbond) - os=-proelf - ;; - *-oki) - os=-proelf - ;; - *-hp) - os=-hpux - ;; - *-hitachi) - os=-hiux - ;; - i860-* | *-att | *-ncr | *-altos | *-motorola | *-convergent) - os=-sysv - ;; - *-cbm) - os=-amigaos - ;; - *-dg) - os=-dgux - ;; - *-dolphin) - os=-sysv3 - ;; - m68k-ccur) - os=-rtu - ;; - m88k-omron*) - os=-luna - ;; - *-next ) - os=-nextstep - ;; - *-sequent) - os=-ptx - ;; - *-crds) - os=-unos - ;; - *-ns) - os=-genix - ;; - i370-*) - os=-mvs - ;; - *-next) - os=-nextstep3 - ;; - *-gould) - os=-sysv - ;; - *-highlevel) - os=-bsd - ;; - *-encore) - os=-bsd - ;; - *-sgi) - os=-irix - ;; - *-siemens) - os=-sysv4 - ;; - *-masscomp) - os=-rtu - ;; - f301-fujitsu) - os=-uxpv - ;; - *-rom68k) - os=-coff - ;; - *-*bug) - os=-coff - ;; - *-apple) - os=-macos - ;; - *-atari*) - os=-mint - ;; - *) - os=-none - ;; -esac -fi - -# Here we handle the case where we know the os, and the CPU type, but not the -# manufacturer. We pick the logical manufacturer. -vendor=unknown -case $basic_machine in - *-unknown) - case $os in - -riscix*) - vendor=acorn - ;; - -sunos*) - vendor=sun - ;; - -aix*) - vendor=ibm - ;; - -beos*) - vendor=be - ;; - -hpux*) - vendor=hp - ;; - -mpeix*) - vendor=hp - ;; - -hiux*) - vendor=hitachi - ;; - -unos*) - vendor=crds - ;; - -dgux*) - vendor=dg - ;; - -luna*) - vendor=omron - ;; - -genix*) - vendor=ns - ;; - -mvs* | -opened*) - vendor=ibm - ;; - -ptx*) - vendor=sequent - ;; - -vxsim* | -vxworks*) - vendor=wrs - ;; - -aux*) - vendor=apple - ;; - -hms*) - vendor=hitachi - ;; - -mpw* | -macos*) - vendor=apple - ;; - -*mint | -*MiNT) - vendor=atari - ;; - esac - basic_machine=`echo $basic_machine | sed "s/unknown/$vendor/"` - ;; -esac - -echo $basic_machine$os diff --git a/libtecla-1.4.1/configure b/libtecla-1.4.1/configure deleted file mode 100755 index feaa587..0000000 --- a/libtecla-1.4.1/configure +++ /dev/null @@ -1,1939 +0,0 @@ -#! /bin/sh - -# Guess values for system-dependent variables and create Makefiles. -# Generated automatically using autoconf version 2.13 -# Copyright (C) 1992, 93, 94, 95, 96 Free Software Foundation, Inc. -# -# This configure script is free software; the Free Software Foundation -# gives unlimited permission to copy, distribute and modify it. - -# Defaults: -ac_help= -ac_default_prefix=/usr/local -# Any additions from configure.in: - -# Initialize some variables set by options. -# The variables have the same names as the options, with -# dashes changed to underlines. -build=NONE -cache_file=./config.cache -exec_prefix=NONE -host=NONE -no_create= -nonopt=NONE -no_recursion= -prefix=NONE -program_prefix=NONE -program_suffix=NONE -program_transform_name=s,x,x, -silent= -site= -srcdir= -target=NONE -verbose= -x_includes=NONE -x_libraries=NONE -bindir='${exec_prefix}/bin' -sbindir='${exec_prefix}/sbin' -libexecdir='${exec_prefix}/libexec' -datadir='${prefix}/share' -sysconfdir='${prefix}/etc' -sharedstatedir='${prefix}/com' -localstatedir='${prefix}/var' -libdir='${exec_prefix}/lib' -includedir='${prefix}/include' -oldincludedir='/usr/include' -infodir='${prefix}/info' -mandir='${prefix}/man' - -# Initialize some other variables. -subdirs= -MFLAGS= MAKEFLAGS= -SHELL=${CONFIG_SHELL-/bin/sh} -# Maximum number of lines to put in a shell here document. -ac_max_here_lines=12 - -ac_prev= -for ac_option -do - - # If the previous option needs an argument, assign it. - if test -n "$ac_prev"; then - eval "$ac_prev=\$ac_option" - ac_prev= - continue - fi - - case "$ac_option" in - -*=*) ac_optarg=`echo "$ac_option" | sed 's/[-_a-zA-Z0-9]*=//'` ;; - *) ac_optarg= ;; - esac - - # Accept the important Cygnus configure options, so we can diagnose typos. - - case "$ac_option" in - - -bindir | --bindir | --bindi | --bind | --bin | --bi) - ac_prev=bindir ;; - -bindir=* | --bindir=* | --bindi=* | --bind=* | --bin=* | --bi=*) - bindir="$ac_optarg" ;; - - -build | --build | --buil | --bui | --bu) - ac_prev=build ;; - -build=* | --build=* | --buil=* | --bui=* | --bu=*) - build="$ac_optarg" ;; - - -cache-file | --cache-file | --cache-fil | --cache-fi \ - | --cache-f | --cache- | --cache | --cach | --cac | --ca | --c) - ac_prev=cache_file ;; - -cache-file=* | --cache-file=* | --cache-fil=* | --cache-fi=* \ - | --cache-f=* | --cache-=* | --cache=* | --cach=* | --cac=* | --ca=* | --c=*) - cache_file="$ac_optarg" ;; - - -datadir | --datadir | --datadi | --datad | --data | --dat | --da) - ac_prev=datadir ;; - -datadir=* | --datadir=* | --datadi=* | --datad=* | --data=* | --dat=* \ - | --da=*) - datadir="$ac_optarg" ;; - - -disable-* | --disable-*) - ac_feature=`echo $ac_option|sed -e 's/-*disable-//'` - # Reject names that are not valid shell variable names. - if test -n "`echo $ac_feature| sed 's/[-a-zA-Z0-9_]//g'`"; then - { echo "configure: error: $ac_feature: invalid feature name" 1>&2; exit 1; } - fi - ac_feature=`echo $ac_feature| sed 's/-/_/g'` - eval "enable_${ac_feature}=no" ;; - - -enable-* | --enable-*) - ac_feature=`echo $ac_option|sed -e 's/-*enable-//' -e 's/=.*//'` - # Reject names that are not valid shell variable names. - if test -n "`echo $ac_feature| sed 's/[-_a-zA-Z0-9]//g'`"; then - { echo "configure: error: $ac_feature: invalid feature name" 1>&2; exit 1; } - fi - ac_feature=`echo $ac_feature| sed 's/-/_/g'` - case "$ac_option" in - *=*) ;; - *) ac_optarg=yes ;; - esac - eval "enable_${ac_feature}='$ac_optarg'" ;; - - -exec-prefix | --exec_prefix | --exec-prefix | --exec-prefi \ - | --exec-pref | --exec-pre | --exec-pr | --exec-p | --exec- \ - | --exec | --exe | --ex) - ac_prev=exec_prefix ;; - -exec-prefix=* | --exec_prefix=* | --exec-prefix=* | --exec-prefi=* \ - | --exec-pref=* | --exec-pre=* | --exec-pr=* | --exec-p=* | --exec-=* \ - | --exec=* | --exe=* | --ex=*) - exec_prefix="$ac_optarg" ;; - - -gas | --gas | --ga | --g) - # Obsolete; use --with-gas. - with_gas=yes ;; - - -help | --help | --hel | --he) - # Omit some internal or obsolete options to make the list less imposing. - # This message is too long to be a string in the A/UX 3.1 sh. - cat << EOF -Usage: configure [options] [host] -Options: [defaults in brackets after descriptions] -Configuration: - --cache-file=FILE cache test results in FILE - --help print this message - --no-create do not create output files - --quiet, --silent do not print \`checking...' messages - --version print the version of autoconf that created configure -Directory and file names: - --prefix=PREFIX install architecture-independent files in PREFIX - [$ac_default_prefix] - --exec-prefix=EPREFIX install architecture-dependent files in EPREFIX - [same as prefix] - --bindir=DIR user executables in DIR [EPREFIX/bin] - --sbindir=DIR system admin executables in DIR [EPREFIX/sbin] - --libexecdir=DIR program executables in DIR [EPREFIX/libexec] - --datadir=DIR read-only architecture-independent data in DIR - [PREFIX/share] - --sysconfdir=DIR read-only single-machine data in DIR [PREFIX/etc] - --sharedstatedir=DIR modifiable architecture-independent data in DIR - [PREFIX/com] - --localstatedir=DIR modifiable single-machine data in DIR [PREFIX/var] - --libdir=DIR object code libraries in DIR [EPREFIX/lib] - --includedir=DIR C header files in DIR [PREFIX/include] - --oldincludedir=DIR C header files for non-gcc in DIR [/usr/include] - --infodir=DIR info documentation in DIR [PREFIX/info] - --mandir=DIR man documentation in DIR [PREFIX/man] - --srcdir=DIR find the sources in DIR [configure dir or ..] - --program-prefix=PREFIX prepend PREFIX to installed program names - --program-suffix=SUFFIX append SUFFIX to installed program names - --program-transform-name=PROGRAM - run sed PROGRAM on installed program names -EOF - cat << EOF -Host type: - --build=BUILD configure for building on BUILD [BUILD=HOST] - --host=HOST configure for HOST [guessed] - --target=TARGET configure for TARGET [TARGET=HOST] -Features and packages: - --disable-FEATURE do not include FEATURE (same as --enable-FEATURE=no) - --enable-FEATURE[=ARG] include FEATURE [ARG=yes] - --with-PACKAGE[=ARG] use PACKAGE [ARG=yes] - --without-PACKAGE do not use PACKAGE (same as --with-PACKAGE=no) - --x-includes=DIR X include files are in DIR - --x-libraries=DIR X library files are in DIR -EOF - if test -n "$ac_help"; then - echo "--enable and --with options recognized:$ac_help" - fi - exit 0 ;; - - -host | --host | --hos | --ho) - ac_prev=host ;; - -host=* | --host=* | --hos=* | --ho=*) - host="$ac_optarg" ;; - - -includedir | --includedir | --includedi | --included | --include \ - | --includ | --inclu | --incl | --inc) - ac_prev=includedir ;; - -includedir=* | --includedir=* | --includedi=* | --included=* | --include=* \ - | --includ=* | --inclu=* | --incl=* | --inc=*) - includedir="$ac_optarg" ;; - - -infodir | --infodir | --infodi | --infod | --info | --inf) - ac_prev=infodir ;; - -infodir=* | --infodir=* | --infodi=* | --infod=* | --info=* | --inf=*) - infodir="$ac_optarg" ;; - - -libdir | --libdir | --libdi | --libd) - ac_prev=libdir ;; - -libdir=* | --libdir=* | --libdi=* | --libd=*) - libdir="$ac_optarg" ;; - - -libexecdir | --libexecdir | --libexecdi | --libexecd | --libexec \ - | --libexe | --libex | --libe) - ac_prev=libexecdir ;; - -libexecdir=* | --libexecdir=* | --libexecdi=* | --libexecd=* | --libexec=* \ - | --libexe=* | --libex=* | --libe=*) - libexecdir="$ac_optarg" ;; - - -localstatedir | --localstatedir | --localstatedi | --localstated \ - | --localstate | --localstat | --localsta | --localst \ - | --locals | --local | --loca | --loc | --lo) - ac_prev=localstatedir ;; - -localstatedir=* | --localstatedir=* | --localstatedi=* | --localstated=* \ - | --localstate=* | --localstat=* | --localsta=* | --localst=* \ - | --locals=* | --local=* | --loca=* | --loc=* | --lo=*) - localstatedir="$ac_optarg" ;; - - -mandir | --mandir | --mandi | --mand | --man | --ma | --m) - ac_prev=mandir ;; - -mandir=* | --mandir=* | --mandi=* | --mand=* | --man=* | --ma=* | --m=*) - mandir="$ac_optarg" ;; - - -nfp | --nfp | --nf) - # Obsolete; use --without-fp. - with_fp=no ;; - - -no-create | --no-create | --no-creat | --no-crea | --no-cre \ - | --no-cr | --no-c) - no_create=yes ;; - - -no-recursion | --no-recursion | --no-recursio | --no-recursi \ - | --no-recurs | --no-recur | --no-recu | --no-rec | --no-re | --no-r) - no_recursion=yes ;; - - -oldincludedir | --oldincludedir | --oldincludedi | --oldincluded \ - | --oldinclude | --oldinclud | --oldinclu | --oldincl | --oldinc \ - | --oldin | --oldi | --old | --ol | --o) - ac_prev=oldincludedir ;; - -oldincludedir=* | --oldincludedir=* | --oldincludedi=* | --oldincluded=* \ - | --oldinclude=* | --oldinclud=* | --oldinclu=* | --oldincl=* | --oldinc=* \ - | --oldin=* | --oldi=* | --old=* | --ol=* | --o=*) - oldincludedir="$ac_optarg" ;; - - -prefix | --prefix | --prefi | --pref | --pre | --pr | --p) - ac_prev=prefix ;; - -prefix=* | --prefix=* | --prefi=* | --pref=* | --pre=* | --pr=* | --p=*) - prefix="$ac_optarg" ;; - - -program-prefix | --program-prefix | --program-prefi | --program-pref \ - | --program-pre | --program-pr | --program-p) - ac_prev=program_prefix ;; - -program-prefix=* | --program-prefix=* | --program-prefi=* \ - | --program-pref=* | --program-pre=* | --program-pr=* | --program-p=*) - program_prefix="$ac_optarg" ;; - - -program-suffix | --program-suffix | --program-suffi | --program-suff \ - | --program-suf | --program-su | --program-s) - ac_prev=program_suffix ;; - -program-suffix=* | --program-suffix=* | --program-suffi=* \ - | --program-suff=* | --program-suf=* | --program-su=* | --program-s=*) - program_suffix="$ac_optarg" ;; - - -program-transform-name | --program-transform-name \ - | --program-transform-nam | --program-transform-na \ - | --program-transform-n | --program-transform- \ - | --program-transform | --program-transfor \ - | --program-transfo | --program-transf \ - | --program-trans | --program-tran \ - | --progr-tra | --program-tr | --program-t) - ac_prev=program_transform_name ;; - -program-transform-name=* | --program-transform-name=* \ - | --program-transform-nam=* | --program-transform-na=* \ - | --program-transform-n=* | --program-transform-=* \ - | --program-transform=* | --program-transfor=* \ - | --program-transfo=* | --program-transf=* \ - | --program-trans=* | --program-tran=* \ - | --progr-tra=* | --program-tr=* | --program-t=*) - program_transform_name="$ac_optarg" ;; - - -q | -quiet | --quiet | --quie | --qui | --qu | --q \ - | -silent | --silent | --silen | --sile | --sil) - silent=yes ;; - - -sbindir | --sbindir | --sbindi | --sbind | --sbin | --sbi | --sb) - ac_prev=sbindir ;; - -sbindir=* | --sbindir=* | --sbindi=* | --sbind=* | --sbin=* \ - | --sbi=* | --sb=*) - sbindir="$ac_optarg" ;; - - -sharedstatedir | --sharedstatedir | --sharedstatedi \ - | --sharedstated | --sharedstate | --sharedstat | --sharedsta \ - | --sharedst | --shareds | --shared | --share | --shar \ - | --sha | --sh) - ac_prev=sharedstatedir ;; - -sharedstatedir=* | --sharedstatedir=* | --sharedstatedi=* \ - | --sharedstated=* | --sharedstate=* | --sharedstat=* | --sharedsta=* \ - | --sharedst=* | --shareds=* | --shared=* | --share=* | --shar=* \ - | --sha=* | --sh=*) - sharedstatedir="$ac_optarg" ;; - - -site | --site | --sit) - ac_prev=site ;; - -site=* | --site=* | --sit=*) - site="$ac_optarg" ;; - - -srcdir | --srcdir | --srcdi | --srcd | --src | --sr) - ac_prev=srcdir ;; - -srcdir=* | --srcdir=* | --srcdi=* | --srcd=* | --src=* | --sr=*) - srcdir="$ac_optarg" ;; - - -sysconfdir | --sysconfdir | --sysconfdi | --sysconfd | --sysconf \ - | --syscon | --sysco | --sysc | --sys | --sy) - ac_prev=sysconfdir ;; - -sysconfdir=* | --sysconfdir=* | --sysconfdi=* | --sysconfd=* | --sysconf=* \ - | --syscon=* | --sysco=* | --sysc=* | --sys=* | --sy=*) - sysconfdir="$ac_optarg" ;; - - -target | --target | --targe | --targ | --tar | --ta | --t) - ac_prev=target ;; - -target=* | --target=* | --targe=* | --targ=* | --tar=* | --ta=* | --t=*) - target="$ac_optarg" ;; - - -v | -verbose | --verbose | --verbos | --verbo | --verb) - verbose=yes ;; - - -version | --version | --versio | --versi | --vers) - echo "configure generated by autoconf version 2.13" - exit 0 ;; - - -with-* | --with-*) - ac_package=`echo $ac_option|sed -e 's/-*with-//' -e 's/=.*//'` - # Reject names that are not valid shell variable names. - if test -n "`echo $ac_package| sed 's/[-_a-zA-Z0-9]//g'`"; then - { echo "configure: error: $ac_package: invalid package name" 1>&2; exit 1; } - fi - ac_package=`echo $ac_package| sed 's/-/_/g'` - case "$ac_option" in - *=*) ;; - *) ac_optarg=yes ;; - esac - eval "with_${ac_package}='$ac_optarg'" ;; - - -without-* | --without-*) - ac_package=`echo $ac_option|sed -e 's/-*without-//'` - # Reject names that are not valid shell variable names. - if test -n "`echo $ac_package| sed 's/[-a-zA-Z0-9_]//g'`"; then - { echo "configure: error: $ac_package: invalid package name" 1>&2; exit 1; } - fi - ac_package=`echo $ac_package| sed 's/-/_/g'` - eval "with_${ac_package}=no" ;; - - --x) - # Obsolete; use --with-x. - with_x=yes ;; - - -x-includes | --x-includes | --x-include | --x-includ | --x-inclu \ - | --x-incl | --x-inc | --x-in | --x-i) - ac_prev=x_includes ;; - -x-includes=* | --x-includes=* | --x-include=* | --x-includ=* | --x-inclu=* \ - | --x-incl=* | --x-inc=* | --x-in=* | --x-i=*) - x_includes="$ac_optarg" ;; - - -x-libraries | --x-libraries | --x-librarie | --x-librari \ - | --x-librar | --x-libra | --x-libr | --x-lib | --x-li | --x-l) - ac_prev=x_libraries ;; - -x-libraries=* | --x-libraries=* | --x-librarie=* | --x-librari=* \ - | --x-librar=* | --x-libra=* | --x-libr=* | --x-lib=* | --x-li=* | --x-l=*) - x_libraries="$ac_optarg" ;; - - -*) { echo "configure: error: $ac_option: invalid option; use --help to show usage" 1>&2; exit 1; } - ;; - - *) - if test -n "`echo $ac_option| sed 's/[-a-z0-9.]//g'`"; then - echo "configure: warning: $ac_option: invalid host type" 1>&2 - fi - if test "x$nonopt" != xNONE; then - { echo "configure: error: can only configure for one host and one target at a time" 1>&2; exit 1; } - fi - nonopt="$ac_option" - ;; - - esac -done - -if test -n "$ac_prev"; then - { echo "configure: error: missing argument to --`echo $ac_prev | sed 's/_/-/g'`" 1>&2; exit 1; } -fi - -trap 'rm -fr conftest* confdefs* core core.* *.core $ac_clean_files; exit 1' 1 2 15 - -# File descriptor usage: -# 0 standard input -# 1 file creation -# 2 errors and warnings -# 3 some systems may open it to /dev/tty -# 4 used on the Kubota Titan -# 6 checking for... messages and results -# 5 compiler messages saved in config.log -if test "$silent" = yes; then - exec 6>/dev/null -else - exec 6>&1 -fi -exec 5>./config.log - -echo "\ -This file contains any messages produced by compilers while -running configure, to aid debugging if configure makes a mistake. -" 1>&5 - -# Strip out --no-create and --no-recursion so they do not pile up. -# Also quote any args containing shell metacharacters. -ac_configure_args= -for ac_arg -do - case "$ac_arg" in - -no-create | --no-create | --no-creat | --no-crea | --no-cre \ - | --no-cr | --no-c) ;; - -no-recursion | --no-recursion | --no-recursio | --no-recursi \ - | --no-recurs | --no-recur | --no-recu | --no-rec | --no-re | --no-r) ;; - *" "*|*" "*|*[\[\]\~\#\$\^\&\*\(\)\{\}\\\|\;\<\>\?]*) - ac_configure_args="$ac_configure_args '$ac_arg'" ;; - *) ac_configure_args="$ac_configure_args $ac_arg" ;; - esac -done - -# NLS nuisances. -# Only set these to C if already set. These must not be set unconditionally -# because not all systems understand e.g. LANG=C (notably SCO). -# Fixing LC_MESSAGES prevents Solaris sh from translating var values in `set'! -# Non-C LC_CTYPE values break the ctype check. -if test "${LANG+set}" = set; then LANG=C; export LANG; fi -if test "${LC_ALL+set}" = set; then LC_ALL=C; export LC_ALL; fi -if test "${LC_MESSAGES+set}" = set; then LC_MESSAGES=C; export LC_MESSAGES; fi -if test "${LC_CTYPE+set}" = set; then LC_CTYPE=C; export LC_CTYPE; fi - -# confdefs.h avoids OS command line length limits that DEFS can exceed. -rm -rf conftest* confdefs.h -# AIX cpp loses on an empty file, so make sure it contains at least a newline. -echo > confdefs.h - -# A filename unique to this package, relative to the directory that -# configure is in, which we can look for to find out if srcdir is correct. -ac_unique_file=getline.c - -# Find the source files, if location was not specified. -if test -z "$srcdir"; then - ac_srcdir_defaulted=yes - # Try the directory containing this script, then its parent. - ac_prog=$0 - ac_confdir=`echo $ac_prog|sed 's%/[^/][^/]*$%%'` - test "x$ac_confdir" = "x$ac_prog" && ac_confdir=. - srcdir=$ac_confdir - if test ! -r $srcdir/$ac_unique_file; then - srcdir=.. - fi -else - ac_srcdir_defaulted=no -fi -if test ! -r $srcdir/$ac_unique_file; then - if test "$ac_srcdir_defaulted" = yes; then - { echo "configure: error: can not find sources in $ac_confdir or .." 1>&2; exit 1; } - else - { echo "configure: error: can not find sources in $srcdir" 1>&2; exit 1; } - fi -fi -srcdir=`echo "${srcdir}" | sed 's%\([^/]\)/*$%\1%'` - -# Prefer explicitly selected file to automatically selected ones. -if test -z "$CONFIG_SITE"; then - if test "x$prefix" != xNONE; then - CONFIG_SITE="$prefix/share/config.site $prefix/etc/config.site" - else - CONFIG_SITE="$ac_default_prefix/share/config.site $ac_default_prefix/etc/config.site" - fi -fi -for ac_site_file in $CONFIG_SITE; do - if test -r "$ac_site_file"; then - echo "loading site script $ac_site_file" - . "$ac_site_file" - fi -done - -if test -r "$cache_file"; then - echo "loading cache $cache_file" - . $cache_file -else - echo "creating cache $cache_file" - > $cache_file -fi - -ac_ext=c -# CFLAGS is not in ac_cpp because -g, -O, etc. are not valid cpp options. -ac_cpp='$CPP $CPPFLAGS' -ac_compile='${CC-cc} -c $CFLAGS $CPPFLAGS conftest.$ac_ext 1>&5' -ac_link='${CC-cc} -o conftest${ac_exeext} $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS 1>&5' -cross_compiling=$ac_cv_prog_cc_cross - -ac_exeext= -ac_objext=o -if (echo "testing\c"; echo 1,2,3) | grep c >/dev/null; then - # Stardent Vistra SVR4 grep lacks -e, says ghazi@caip.rutgers.edu. - if (echo -n testing; echo 1,2,3) | sed s/-n/xn/ | grep xn >/dev/null; then - ac_n= ac_c=' -' ac_t=' ' - else - ac_n=-n ac_c= ac_t= - fi -else - ac_n= ac_c='\c' ac_t= -fi - - - - - -MAJOR_VER="1" - - - -MINOR_VER="4" - - - -MICRO_VER="1" - - -CFLAGS="$CFLAGS" -# Extract the first word of "gcc", so it can be a program name with args. -set dummy gcc; ac_word=$2 -echo $ac_n "checking for $ac_word""... $ac_c" 1>&6 -echo "configure:543: checking for $ac_word" >&5 -if eval "test \"`echo '$''{'ac_cv_prog_CC'+set}'`\" = set"; then - echo $ac_n "(cached) $ac_c" 1>&6 -else - if test -n "$CC"; then - ac_cv_prog_CC="$CC" # Let the user override the test. -else - IFS="${IFS= }"; ac_save_ifs="$IFS"; IFS=":" - ac_dummy="$PATH" - for ac_dir in $ac_dummy; do - test -z "$ac_dir" && ac_dir=. - if test -f $ac_dir/$ac_word; then - ac_cv_prog_CC="gcc" - break - fi - done - IFS="$ac_save_ifs" -fi -fi -CC="$ac_cv_prog_CC" -if test -n "$CC"; then - echo "$ac_t""$CC" 1>&6 -else - echo "$ac_t""no" 1>&6 -fi - -if test -z "$CC"; then - # Extract the first word of "cc", so it can be a program name with args. -set dummy cc; ac_word=$2 -echo $ac_n "checking for $ac_word""... $ac_c" 1>&6 -echo "configure:573: checking for $ac_word" >&5 -if eval "test \"`echo '$''{'ac_cv_prog_CC'+set}'`\" = set"; then - echo $ac_n "(cached) $ac_c" 1>&6 -else - if test -n "$CC"; then - ac_cv_prog_CC="$CC" # Let the user override the test. -else - IFS="${IFS= }"; ac_save_ifs="$IFS"; IFS=":" - ac_prog_rejected=no - ac_dummy="$PATH" - for ac_dir in $ac_dummy; do - test -z "$ac_dir" && ac_dir=. - if test -f $ac_dir/$ac_word; then - if test "$ac_dir/$ac_word" = "/usr/ucb/cc"; then - ac_prog_rejected=yes - continue - fi - ac_cv_prog_CC="cc" - break - fi - done - IFS="$ac_save_ifs" -if test $ac_prog_rejected = yes; then - # We found a bogon in the path, so make sure we never use it. - set dummy $ac_cv_prog_CC - shift - if test $# -gt 0; then - # We chose a different compiler from the bogus one. - # However, it has the same basename, so the bogon will be chosen - # first if we set CC to just the basename; use the full file name. - shift - set dummy "$ac_dir/$ac_word" "$@" - shift - ac_cv_prog_CC="$@" - fi -fi -fi -fi -CC="$ac_cv_prog_CC" -if test -n "$CC"; then - echo "$ac_t""$CC" 1>&6 -else - echo "$ac_t""no" 1>&6 -fi - - if test -z "$CC"; then - case "`uname -s`" in - *win32* | *WIN32*) - # Extract the first word of "cl", so it can be a program name with args. -set dummy cl; ac_word=$2 -echo $ac_n "checking for $ac_word""... $ac_c" 1>&6 -echo "configure:624: checking for $ac_word" >&5 -if eval "test \"`echo '$''{'ac_cv_prog_CC'+set}'`\" = set"; then - echo $ac_n "(cached) $ac_c" 1>&6 -else - if test -n "$CC"; then - ac_cv_prog_CC="$CC" # Let the user override the test. -else - IFS="${IFS= }"; ac_save_ifs="$IFS"; IFS=":" - ac_dummy="$PATH" - for ac_dir in $ac_dummy; do - test -z "$ac_dir" && ac_dir=. - if test -f $ac_dir/$ac_word; then - ac_cv_prog_CC="cl" - break - fi - done - IFS="$ac_save_ifs" -fi -fi -CC="$ac_cv_prog_CC" -if test -n "$CC"; then - echo "$ac_t""$CC" 1>&6 -else - echo "$ac_t""no" 1>&6 -fi - ;; - esac - fi - test -z "$CC" && { echo "configure: error: no acceptable cc found in \$PATH" 1>&2; exit 1; } -fi - -echo $ac_n "checking whether the C compiler ($CC $CFLAGS $LDFLAGS) works""... $ac_c" 1>&6 -echo "configure:656: checking whether the C compiler ($CC $CFLAGS $LDFLAGS) works" >&5 - -ac_ext=c -# CFLAGS is not in ac_cpp because -g, -O, etc. are not valid cpp options. -ac_cpp='$CPP $CPPFLAGS' -ac_compile='${CC-cc} -c $CFLAGS $CPPFLAGS conftest.$ac_ext 1>&5' -ac_link='${CC-cc} -o conftest${ac_exeext} $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS 1>&5' -cross_compiling=$ac_cv_prog_cc_cross - -cat > conftest.$ac_ext << EOF - -#line 667 "configure" -#include "confdefs.h" - -main(){return(0);} -EOF -if { (eval echo configure:672: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then - ac_cv_prog_cc_works=yes - # If we can't run a trivial program, we are probably using a cross compiler. - if (./conftest; exit) 2>/dev/null; then - ac_cv_prog_cc_cross=no - else - ac_cv_prog_cc_cross=yes - fi -else - echo "configure: failed program was:" >&5 - cat conftest.$ac_ext >&5 - ac_cv_prog_cc_works=no -fi -rm -fr conftest* -ac_ext=c -# CFLAGS is not in ac_cpp because -g, -O, etc. are not valid cpp options. -ac_cpp='$CPP $CPPFLAGS' -ac_compile='${CC-cc} -c $CFLAGS $CPPFLAGS conftest.$ac_ext 1>&5' -ac_link='${CC-cc} -o conftest${ac_exeext} $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS 1>&5' -cross_compiling=$ac_cv_prog_cc_cross - -echo "$ac_t""$ac_cv_prog_cc_works" 1>&6 -if test $ac_cv_prog_cc_works = no; then - { echo "configure: error: installation or configuration problem: C compiler cannot create executables." 1>&2; exit 1; } -fi -echo $ac_n "checking whether the C compiler ($CC $CFLAGS $LDFLAGS) is a cross-compiler""... $ac_c" 1>&6 -echo "configure:698: checking whether the C compiler ($CC $CFLAGS $LDFLAGS) is a cross-compiler" >&5 -echo "$ac_t""$ac_cv_prog_cc_cross" 1>&6 -cross_compiling=$ac_cv_prog_cc_cross - -echo $ac_n "checking whether we are using GNU C""... $ac_c" 1>&6 -echo "configure:703: checking whether we are using GNU C" >&5 -if eval "test \"`echo '$''{'ac_cv_prog_gcc'+set}'`\" = set"; then - echo $ac_n "(cached) $ac_c" 1>&6 -else - cat > conftest.c <&5; (eval $ac_try) 2>&5; }; } | egrep yes >/dev/null 2>&1; then - ac_cv_prog_gcc=yes -else - ac_cv_prog_gcc=no -fi -fi - -echo "$ac_t""$ac_cv_prog_gcc" 1>&6 - -if test $ac_cv_prog_gcc = yes; then - GCC=yes -else - GCC= -fi - -ac_test_CFLAGS="${CFLAGS+set}" -ac_save_CFLAGS="$CFLAGS" -CFLAGS= -echo $ac_n "checking whether ${CC-cc} accepts -g""... $ac_c" 1>&6 -echo "configure:731: checking whether ${CC-cc} accepts -g" >&5 -if eval "test \"`echo '$''{'ac_cv_prog_cc_g'+set}'`\" = set"; then - echo $ac_n "(cached) $ac_c" 1>&6 -else - echo 'void f(){}' > conftest.c -if test -z "`${CC-cc} -g -c conftest.c 2>&1`"; then - ac_cv_prog_cc_g=yes -else - ac_cv_prog_cc_g=no -fi -rm -f conftest* - -fi - -echo "$ac_t""$ac_cv_prog_cc_g" 1>&6 -if test "$ac_test_CFLAGS" = set; then - CFLAGS="$ac_save_CFLAGS" -elif test $ac_cv_prog_cc_g = yes; then - if test "$GCC" = yes; then - CFLAGS="-g -O2" - else - CFLAGS="-g" - fi -else - if test "$GCC" = yes; then - CFLAGS="-O2" - else - CFLAGS= - fi -fi - - - -echo $ac_n "checking whether ${MAKE-make} sets \${MAKE}""... $ac_c" 1>&6 -echo "configure:765: checking whether ${MAKE-make} sets \${MAKE}" >&5 -set dummy ${MAKE-make}; ac_make=`echo "$2" | sed 'y%./+-%__p_%'` -if eval "test \"`echo '$''{'ac_cv_prog_make_${ac_make}_set'+set}'`\" = set"; then - echo $ac_n "(cached) $ac_c" 1>&6 -else - cat > conftestmake <<\EOF -all: - @echo 'ac_maketemp="${MAKE}"' -EOF -# GNU make sometimes prints "make[1]: Entering...", which would confuse us. -eval `${MAKE-make} -f conftestmake 2>/dev/null | grep temp=` -if test -n "$ac_maketemp"; then - eval ac_cv_prog_make_${ac_make}_set=yes -else - eval ac_cv_prog_make_${ac_make}_set=no -fi -rm -f conftestmake -fi -if eval "test \"`echo '$ac_cv_prog_make_'${ac_make}_set`\" = yes"; then - echo "$ac_t""yes" 1>&6 - SET_MAKE= -else - echo "$ac_t""no" 1>&6 - SET_MAKE="MAKE=${MAKE-make}" -fi - - - -echo $ac_n "checking whether ln -s works""... $ac_c" 1>&6 -echo "configure:794: checking whether ln -s works" >&5 -if eval "test \"`echo '$''{'ac_cv_prog_LN_S'+set}'`\" = set"; then - echo $ac_n "(cached) $ac_c" 1>&6 -else - rm -f conftestdata -if ln -s X conftestdata 2>/dev/null -then - rm -f conftestdata - ac_cv_prog_LN_S="ln -s" -else - ac_cv_prog_LN_S=ln -fi -fi -LN_S="$ac_cv_prog_LN_S" -if test "$ac_cv_prog_LN_S" = "ln -s"; then - echo "$ac_t""yes" 1>&6 -else - echo "$ac_t""no" 1>&6 -fi - - - -for ac_prog in gawk mawk nawk awk -do -# Extract the first word of "$ac_prog", so it can be a program name with args. -set dummy $ac_prog; ac_word=$2 -echo $ac_n "checking for $ac_word""... $ac_c" 1>&6 -echo "configure:821: checking for $ac_word" >&5 -if eval "test \"`echo '$''{'ac_cv_prog_AWK'+set}'`\" = set"; then - echo $ac_n "(cached) $ac_c" 1>&6 -else - if test -n "$AWK"; then - ac_cv_prog_AWK="$AWK" # Let the user override the test. -else - IFS="${IFS= }"; ac_save_ifs="$IFS"; IFS=":" - ac_dummy="$PATH" - for ac_dir in $ac_dummy; do - test -z "$ac_dir" && ac_dir=. - if test -f $ac_dir/$ac_word; then - ac_cv_prog_AWK="$ac_prog" - break - fi - done - IFS="$ac_save_ifs" -fi -fi -AWK="$ac_cv_prog_AWK" -if test -n "$AWK"; then - echo "$ac_t""$AWK" 1>&6 -else - echo "$ac_t""no" 1>&6 -fi - -test -n "$AWK" && break -done - - - -# Extract the first word of "ranlib", so it can be a program name with args. -set dummy ranlib; ac_word=$2 -echo $ac_n "checking for $ac_word""... $ac_c" 1>&6 -echo "configure:855: checking for $ac_word" >&5 -if eval "test \"`echo '$''{'ac_cv_prog_RANLIB'+set}'`\" = set"; then - echo $ac_n "(cached) $ac_c" 1>&6 -else - if test -n "$RANLIB"; then - ac_cv_prog_RANLIB="$RANLIB" # Let the user override the test. -else - IFS="${IFS= }"; ac_save_ifs="$IFS"; IFS=":" - ac_dummy="$PATH" - for ac_dir in $ac_dummy; do - test -z "$ac_dir" && ac_dir=. - if test -f $ac_dir/$ac_word; then - ac_cv_prog_RANLIB="ranlib" - break - fi - done - IFS="$ac_save_ifs" - test -z "$ac_cv_prog_RANLIB" && ac_cv_prog_RANLIB=":" -fi -fi -RANLIB="$ac_cv_prog_RANLIB" -if test -n "$RANLIB"; then - echo "$ac_t""$RANLIB" 1>&6 -else - echo "$ac_t""no" 1>&6 -fi - - - -ac_aux_dir= -for ac_dir in $srcdir $srcdir/.. $srcdir/../..; do - if test -f $ac_dir/install-sh; then - ac_aux_dir=$ac_dir - ac_install_sh="$ac_aux_dir/install-sh -c" - break - elif test -f $ac_dir/install.sh; then - ac_aux_dir=$ac_dir - ac_install_sh="$ac_aux_dir/install.sh -c" - break - fi -done -if test -z "$ac_aux_dir"; then - { echo "configure: error: can not find install-sh or install.sh in $srcdir $srcdir/.. $srcdir/../.." 1>&2; exit 1; } -fi -ac_config_guess=$ac_aux_dir/config.guess -ac_config_sub=$ac_aux_dir/config.sub -ac_configure=$ac_aux_dir/configure # This should be Cygnus configure. - - -# Do some error checking and defaulting for the host and target type. -# The inputs are: -# configure --host=HOST --target=TARGET --build=BUILD NONOPT -# -# The rules are: -# 1. You are not allowed to specify --host, --target, and nonopt at the -# same time. -# 2. Host defaults to nonopt. -# 3. If nonopt is not specified, then host defaults to the current host, -# as determined by config.guess. -# 4. Target and build default to nonopt. -# 5. If nonopt is not specified, then target and build default to host. - -# The aliases save the names the user supplied, while $host etc. -# will get canonicalized. -case $host---$target---$nonopt in -NONE---*---* | *---NONE---* | *---*---NONE) ;; -*) { echo "configure: error: can only configure for one host and one target at a time" 1>&2; exit 1; } ;; -esac - - -# Make sure we can run config.sub. -if ${CONFIG_SHELL-/bin/sh} $ac_config_sub sun4 >/dev/null 2>&1; then : -else { echo "configure: error: can not run $ac_config_sub" 1>&2; exit 1; } -fi - -echo $ac_n "checking host system type""... $ac_c" 1>&6 -echo "configure:931: checking host system type" >&5 - -host_alias=$host -case "$host_alias" in -NONE) - case $nonopt in - NONE) - if host_alias=`${CONFIG_SHELL-/bin/sh} $ac_config_guess`; then : - else { echo "configure: error: can not guess host type; you must specify one" 1>&2; exit 1; } - fi ;; - *) host_alias=$nonopt ;; - esac ;; -esac - -host=`${CONFIG_SHELL-/bin/sh} $ac_config_sub $host_alias` -host_cpu=`echo $host | sed 's/^\([^-]*\)-\([^-]*\)-\(.*\)$/\1/'` -host_vendor=`echo $host | sed 's/^\([^-]*\)-\([^-]*\)-\(.*\)$/\2/'` -host_os=`echo $host | sed 's/^\([^-]*\)-\([^-]*\)-\(.*\)$/\3/'` -echo "$ac_t""$host" 1>&6 - -echo $ac_n "checking target system type""... $ac_c" 1>&6 -echo "configure:952: checking target system type" >&5 - -target_alias=$target -case "$target_alias" in -NONE) - case $nonopt in - NONE) target_alias=$host_alias ;; - *) target_alias=$nonopt ;; - esac ;; -esac - -target=`${CONFIG_SHELL-/bin/sh} $ac_config_sub $target_alias` -target_cpu=`echo $target | sed 's/^\([^-]*\)-\([^-]*\)-\(.*\)$/\1/'` -target_vendor=`echo $target | sed 's/^\([^-]*\)-\([^-]*\)-\(.*\)$/\2/'` -target_os=`echo $target | sed 's/^\([^-]*\)-\([^-]*\)-\(.*\)$/\3/'` -echo "$ac_t""$target" 1>&6 - -echo $ac_n "checking build system type""... $ac_c" 1>&6 -echo "configure:970: checking build system type" >&5 - -build_alias=$build -case "$build_alias" in -NONE) - case $nonopt in - NONE) build_alias=$host_alias ;; - *) build_alias=$nonopt ;; - esac ;; -esac - -build=`${CONFIG_SHELL-/bin/sh} $ac_config_sub $build_alias` -build_cpu=`echo $build | sed 's/^\([^-]*\)-\([^-]*\)-\(.*\)$/\1/'` -build_vendor=`echo $build | sed 's/^\([^-]*\)-\([^-]*\)-\(.*\)$/\2/'` -build_os=`echo $build | sed 's/^\([^-]*\)-\([^-]*\)-\(.*\)$/\3/'` -echo "$ac_t""$build" 1>&6 - -test "$host_alias" != "$target_alias" && - test "$program_prefix$program_suffix$program_transform_name" = \ - NONENONEs,x,x, && - program_prefix=${target_alias}- - - - -case $target in -*-sun-solaris2.[0-6]|*-sun-solaris2.[0-6].*) - LIBS="$LIBS -L/usr/ccs/lib" - ;; -esac - - -echo $ac_n "checking how to run the C preprocessor""... $ac_c" 1>&6 -echo "configure:1002: checking how to run the C preprocessor" >&5 -# On Suns, sometimes $CPP names a directory. -if test -n "$CPP" && test -d "$CPP"; then - CPP= -fi -if test -z "$CPP"; then -if eval "test \"`echo '$''{'ac_cv_prog_CPP'+set}'`\" = set"; then - echo $ac_n "(cached) $ac_c" 1>&6 -else - # This must be in double quotes, not single quotes, because CPP may get - # substituted into the Makefile and "${CC-cc}" will confuse make. - CPP="${CC-cc} -E" - # On the NeXT, cc -E runs the code through the compiler's parser, - # not just through cpp. - cat > conftest.$ac_ext < -Syntax Error -EOF -ac_try="$ac_cpp conftest.$ac_ext >/dev/null 2>conftest.out" -{ (eval echo configure:1023: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; } -ac_err=`grep -v '^ *+' conftest.out | grep -v "^conftest.${ac_ext}\$"` -if test -z "$ac_err"; then - : -else - echo "$ac_err" >&5 - echo "configure: failed program was:" >&5 - cat conftest.$ac_ext >&5 - rm -rf conftest* - CPP="${CC-cc} -E -traditional-cpp" - cat > conftest.$ac_ext < -Syntax Error -EOF -ac_try="$ac_cpp conftest.$ac_ext >/dev/null 2>conftest.out" -{ (eval echo configure:1040: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; } -ac_err=`grep -v '^ *+' conftest.out | grep -v "^conftest.${ac_ext}\$"` -if test -z "$ac_err"; then - : -else - echo "$ac_err" >&5 - echo "configure: failed program was:" >&5 - cat conftest.$ac_ext >&5 - rm -rf conftest* - CPP="${CC-cc} -nologo -E" - cat > conftest.$ac_ext < -Syntax Error -EOF -ac_try="$ac_cpp conftest.$ac_ext >/dev/null 2>conftest.out" -{ (eval echo configure:1057: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; } -ac_err=`grep -v '^ *+' conftest.out | grep -v "^conftest.${ac_ext}\$"` -if test -z "$ac_err"; then - : -else - echo "$ac_err" >&5 - echo "configure: failed program was:" >&5 - cat conftest.$ac_ext >&5 - rm -rf conftest* - CPP=/lib/cpp -fi -rm -f conftest* -fi -rm -f conftest* -fi -rm -f conftest* - ac_cv_prog_CPP="$CPP" -fi - CPP="$ac_cv_prog_CPP" -else - ac_cv_prog_CPP="$CPP" -fi -echo "$ac_t""$CPP" 1>&6 - -echo $ac_n "checking for tigetstr in -lcurses""... $ac_c" 1>&6 -echo "configure:1082: checking for tigetstr in -lcurses" >&5 -ac_lib_var=`echo curses'_'tigetstr | sed 'y%./+-%__p_%'` -if eval "test \"`echo '$''{'ac_cv_lib_$ac_lib_var'+set}'`\" = set"; then - echo $ac_n "(cached) $ac_c" 1>&6 -else - ac_save_LIBS="$LIBS" -LIBS="-lcurses $LIBS" -cat > conftest.$ac_ext <&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then - rm -rf conftest* - eval "ac_cv_lib_$ac_lib_var=yes" -else - echo "configure: failed program was:" >&5 - cat conftest.$ac_ext >&5 - rm -rf conftest* - eval "ac_cv_lib_$ac_lib_var=no" -fi -rm -f conftest* -LIBS="$ac_save_LIBS" - -fi -if eval "test \"`echo '$ac_cv_lib_'$ac_lib_var`\" = yes"; then - echo "$ac_t""yes" 1>&6 - - cat >> confdefs.h <<\EOF -#define USE_TERMINFO 1 -EOF - - LIBS="$LIBS -lcurses" - -else - echo "$ac_t""no" 1>&6 -echo $ac_n "checking for tigetstr in -lncurses""... $ac_c" 1>&6 -echo "configure:1126: checking for tigetstr in -lncurses" >&5 -ac_lib_var=`echo ncurses'_'tigetstr | sed 'y%./+-%__p_%'` -if eval "test \"`echo '$''{'ac_cv_lib_$ac_lib_var'+set}'`\" = set"; then - echo $ac_n "(cached) $ac_c" 1>&6 -else - ac_save_LIBS="$LIBS" -LIBS="-lncurses $LIBS" -cat > conftest.$ac_ext <&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then - rm -rf conftest* - eval "ac_cv_lib_$ac_lib_var=yes" -else - echo "configure: failed program was:" >&5 - cat conftest.$ac_ext >&5 - rm -rf conftest* - eval "ac_cv_lib_$ac_lib_var=no" -fi -rm -f conftest* -LIBS="$ac_save_LIBS" - -fi -if eval "test \"`echo '$ac_cv_lib_'$ac_lib_var`\" = yes"; then - echo "$ac_t""yes" 1>&6 - - cat >> confdefs.h <<\EOF -#define USE_TERMINFO 1 -EOF - - LIBS="$LIBS -lncurses" - -else - echo "$ac_t""no" 1>&6 -echo $ac_n "checking for tgetstr in -lcurses""... $ac_c" 1>&6 -echo "configure:1170: checking for tgetstr in -lcurses" >&5 -ac_lib_var=`echo curses'_'tgetstr | sed 'y%./+-%__p_%'` -if eval "test \"`echo '$''{'ac_cv_lib_$ac_lib_var'+set}'`\" = set"; then - echo $ac_n "(cached) $ac_c" 1>&6 -else - ac_save_LIBS="$LIBS" -LIBS="-lcurses $LIBS" -cat > conftest.$ac_ext <&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then - rm -rf conftest* - eval "ac_cv_lib_$ac_lib_var=yes" -else - echo "configure: failed program was:" >&5 - cat conftest.$ac_ext >&5 - rm -rf conftest* - eval "ac_cv_lib_$ac_lib_var=no" -fi -rm -f conftest* -LIBS="$ac_save_LIBS" - -fi -if eval "test \"`echo '$ac_cv_lib_'$ac_lib_var`\" = yes"; then - echo "$ac_t""yes" 1>&6 - - cat >> confdefs.h <<\EOF -#define USE_TERMCAP 1 -EOF - - LIBS="$LIBS -lcurses" - ac_safe=`echo "termcap.h" | sed 'y%./+-%__p_%'` -echo $ac_n "checking for termcap.h""... $ac_c" 1>&6 -echo "configure:1212: checking for termcap.h" >&5 -if eval "test \"`echo '$''{'ac_cv_header_$ac_safe'+set}'`\" = set"; then - echo $ac_n "(cached) $ac_c" 1>&6 -else - cat > conftest.$ac_ext < -EOF -ac_try="$ac_cpp conftest.$ac_ext >/dev/null 2>conftest.out" -{ (eval echo configure:1222: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; } -ac_err=`grep -v '^ *+' conftest.out | grep -v "^conftest.${ac_ext}\$"` -if test -z "$ac_err"; then - rm -rf conftest* - eval "ac_cv_header_$ac_safe=yes" -else - echo "$ac_err" >&5 - echo "configure: failed program was:" >&5 - cat conftest.$ac_ext >&5 - rm -rf conftest* - eval "ac_cv_header_$ac_safe=no" -fi -rm -f conftest* -fi -if eval "test \"`echo '$ac_cv_header_'$ac_safe`\" = yes"; then - echo "$ac_t""yes" 1>&6 - cat >> confdefs.h <<\EOF -#define HAVE_TERMCAP_H 1 -EOF - -else - echo "$ac_t""no" 1>&6 -fi - - -else - echo "$ac_t""no" 1>&6 -fi - -fi - -fi - - - -ac_safe=`echo "term.h" | sed 'y%./+-%__p_%'` -echo $ac_n "checking for term.h""... $ac_c" 1>&6 -echo "configure:1259: checking for term.h" >&5 -if eval "test \"`echo '$''{'ac_cv_header_$ac_safe'+set}'`\" = set"; then - echo $ac_n "(cached) $ac_c" 1>&6 -else - cat > conftest.$ac_ext < -EOF -ac_try="$ac_cpp conftest.$ac_ext >/dev/null 2>conftest.out" -{ (eval echo configure:1269: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; } -ac_err=`grep -v '^ *+' conftest.out | grep -v "^conftest.${ac_ext}\$"` -if test -z "$ac_err"; then - rm -rf conftest* - eval "ac_cv_header_$ac_safe=yes" -else - echo "$ac_err" >&5 - echo "configure: failed program was:" >&5 - cat conftest.$ac_ext >&5 - rm -rf conftest* - eval "ac_cv_header_$ac_safe=no" -fi -rm -f conftest* -fi -if eval "test \"`echo '$ac_cv_header_'$ac_safe`\" = yes"; then - echo "$ac_t""yes" 1>&6 - cat >> confdefs.h <<\EOF -#define HAVE_TERM_H 1 -EOF - -else - echo "$ac_t""no" 1>&6 - - ac_safe=`echo "ncurses/term.h" | sed 'y%./+-%__p_%'` -echo $ac_n "checking for ncurses/term.h""... $ac_c" 1>&6 -echo "configure:1294: checking for ncurses/term.h" >&5 -if eval "test \"`echo '$''{'ac_cv_header_$ac_safe'+set}'`\" = set"; then - echo $ac_n "(cached) $ac_c" 1>&6 -else - cat > conftest.$ac_ext < -EOF -ac_try="$ac_cpp conftest.$ac_ext >/dev/null 2>conftest.out" -{ (eval echo configure:1304: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; } -ac_err=`grep -v '^ *+' conftest.out | grep -v "^conftest.${ac_ext}\$"` -if test -z "$ac_err"; then - rm -rf conftest* - eval "ac_cv_header_$ac_safe=yes" -else - echo "$ac_err" >&5 - echo "configure: failed program was:" >&5 - cat conftest.$ac_ext >&5 - rm -rf conftest* - eval "ac_cv_header_$ac_safe=no" -fi -rm -f conftest* -fi -if eval "test \"`echo '$ac_cv_header_'$ac_safe`\" = yes"; then - echo "$ac_t""yes" 1>&6 - cat >> confdefs.h <<\EOF -#define HAVE_NCURSES_TERM_H 1 -EOF - -else - echo "$ac_t""no" 1>&6 -fi - - -fi - - - -ac_safe=`echo "curses.h" | sed 'y%./+-%__p_%'` -echo $ac_n "checking for curses.h""... $ac_c" 1>&6 -echo "configure:1335: checking for curses.h" >&5 -if eval "test \"`echo '$''{'ac_cv_header_$ac_safe'+set}'`\" = set"; then - echo $ac_n "(cached) $ac_c" 1>&6 -else - cat > conftest.$ac_ext < -EOF -ac_try="$ac_cpp conftest.$ac_ext >/dev/null 2>conftest.out" -{ (eval echo configure:1345: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; } -ac_err=`grep -v '^ *+' conftest.out | grep -v "^conftest.${ac_ext}\$"` -if test -z "$ac_err"; then - rm -rf conftest* - eval "ac_cv_header_$ac_safe=yes" -else - echo "$ac_err" >&5 - echo "configure: failed program was:" >&5 - cat conftest.$ac_ext >&5 - rm -rf conftest* - eval "ac_cv_header_$ac_safe=no" -fi -rm -f conftest* -fi -if eval "test \"`echo '$ac_cv_header_'$ac_safe`\" = yes"; then - echo "$ac_t""yes" 1>&6 - cat >> confdefs.h <<\EOF -#define HAVE_CURSES_H 1 -EOF - -else - echo "$ac_t""no" 1>&6 - - ac_safe=`echo "ncurses/curses.h" | sed 'y%./+-%__p_%'` -echo $ac_n "checking for ncurses/curses.h""... $ac_c" 1>&6 -echo "configure:1370: checking for ncurses/curses.h" >&5 -if eval "test \"`echo '$''{'ac_cv_header_$ac_safe'+set}'`\" = set"; then - echo $ac_n "(cached) $ac_c" 1>&6 -else - cat > conftest.$ac_ext < -EOF -ac_try="$ac_cpp conftest.$ac_ext >/dev/null 2>conftest.out" -{ (eval echo configure:1380: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; } -ac_err=`grep -v '^ *+' conftest.out | grep -v "^conftest.${ac_ext}\$"` -if test -z "$ac_err"; then - rm -rf conftest* - eval "ac_cv_header_$ac_safe=yes" -else - echo "$ac_err" >&5 - echo "configure: failed program was:" >&5 - cat conftest.$ac_ext >&5 - rm -rf conftest* - eval "ac_cv_header_$ac_safe=no" -fi -rm -f conftest* -fi -if eval "test \"`echo '$ac_cv_header_'$ac_safe`\" = yes"; then - echo "$ac_t""yes" 1>&6 - cat >> confdefs.h <<\EOF -#define HAVE_NCURSES_CURSES_H 1 -EOF - -else - echo "$ac_t""no" 1>&6 -fi - - -fi - - - - -TARGETS="normal reentrant" - - -echo $ac_n "checking for reentrant functions""... $ac_c" 1>&6 -echo "configure:1414: checking for reentrant functions" >&5 -if eval "test \"`echo '$''{'tecla_cv_reentrant'+set}'`\" = set"; then - echo $ac_n "(cached) $ac_c" 1>&6 -else - - KEPT_CFLAGS="$CFLAGS" - CFLAGS="$CFLAGS -D_POSIX_C_SOURCE=199506L" - cat > conftest.$ac_ext < -#include -#include -#include -#include - -int main() { - - (void) readdir_r(NULL, NULL, NULL); - (void) getpwuid_r(geteuid(), NULL, NULL, 0, NULL); - (void) getpwnam_r(NULL, NULL, NULL, 0, NULL); - -; return 0; } -EOF -if { (eval echo configure:1439: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then - rm -rf conftest* - tecla_cv_reentrant=yes -else - echo "configure: failed program was:" >&5 - cat conftest.$ac_ext >&5 - rm -rf conftest* - tecla_cv_reentrant=no -fi -rm -f conftest* - CFLAGS="$KEPT_CFLAGS" - -fi - -echo "$ac_t""$tecla_cv_reentrant" 1>&6 - - -if test $tecla_cv_reentrant = no; then - TARGETS="normal" -fi - - -echo $ac_n "checking for select system call""... $ac_c" 1>&6 -echo "configure:1462: checking for select system call" >&5 -if eval "test \"`echo '$''{'tecla_cv_select'+set}'`\" = set"; then - echo $ac_n "(cached) $ac_c" 1>&6 -else - - cat > conftest.$ac_ext < -#endif -#include -#include -#include - -int main() { - - fd_set fds; - int nready; - FD_ZERO(&fds); - FD_SET(1, &fds); - nready = select(2, &fds, &fds, &fds, NULL); - -; return 0; } -EOF -if { (eval echo configure:1488: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then - rm -rf conftest* - tecla_cv_select=yes -else - echo "configure: failed program was:" >&5 - cat conftest.$ac_ext >&5 - rm -rf conftest* - tecla_cv_select=no -fi -rm -f conftest* - -fi - -echo "$ac_t""$tecla_cv_select" 1>&6 - - -if test $tecla_cv_select = yes; then - cat >> confdefs.h <<\EOF -#define HAVE_SELECT 1 -EOF - -fi - - -echo $ac_n "checking for SysV pseudo-terminals""... $ac_c" 1>&6 -echo "configure:1513: checking for SysV pseudo-terminals" >&5 -if eval "test \"`echo '$''{'tecla_cv_sysv_pty'+set}'`\" = set"; then - echo $ac_n "(cached) $ac_c" 1>&6 -else - - cat > conftest.$ac_ext < -#include -#include - -int main() { - - char *name = ptsname(0); - int i1 = grantpt(0); - int i2 = unlockpt(0); - int i3 = ioctl(0, I_PUSH, "ptem"); - return 0; - -; return 0; } -EOF -if { (eval echo configure:1536: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then - rm -rf conftest* - tecla_cv_sysv_pty=yes -else - echo "configure: failed program was:" >&5 - cat conftest.$ac_ext >&5 - rm -rf conftest* - tecla_cv_sysv_pty=no -fi -rm -f conftest* - -fi - -echo "$ac_t""$tecla_cv_sysv_pty" 1>&6 - - -if test $tecla_cv_sysv_pty = yes; then - cat >> confdefs.h <<\EOF -#define HAVE_SYSV_PTY 1 -EOF - -fi - - - -SHARED_EXT="" - - - -SHARED_ALT="" - - - -SHARED_CFLAGS="" - - - -LINK_SHARED="" - - -case $target in -*solaris*) - cat >> confdefs.h <<\EOF -#define __EXTENSIONS__ 1 -EOF - - SHARED_EXT=".so.${MAJOR_VER}" - SHARED_ALT=".so" - LINK_SHARED='ld -G -M $$(srcdir)/libtecla.map -o $$@ -h $$(@F) -z defs -i $$(LIB_OBJECTS) $$(LIBS) -lc' - SHARED_CFLAGS="-Kpic" - case $CC in - */cc|cc) SHARED_CFLAGS="$SHARED_CFLAGS -xstrconst" ;; - gcc) CFLAGS="-I/usr/include $CFLAGS" ;; - esac - case $target in - *sparc*) SHARED_CFLAGS="$SHARED_CFLAGS -xregs=no%appl" - esac - ;; -*linux*) - SHARED_EXT=".so.${MAJOR_VER}.${MINOR_VER}.${MICRO_VER}" - SHARED_ALT=".so .so.${MAJOR_VER}" - - - echo $ac_n "checking for --version-script in GNU ld""... $ac_c" 1>&6 -echo "configure:1600: checking for --version-script in GNU ld" >&5 -if eval "test \"`echo '$''{'tecla_cv_gnu_ld_script'+set}'`\" = set"; then - echo $ac_n "(cached) $ac_c" 1>&6 -else - - if (echo 'void dummy(void) {return;}' > dummy.c; $CC -c -fpic dummy.c; \ - ld -o dummy.so dummy.o -shared --version-script=$srcdir/libtecla.map) 1>&2 2>/dev/null; then - tecla_cv_gnu_ld_script=yes - else - tecla_cv_gnu_ld_script=no - fi - rm -f dummy.c dummy.o dummy.so - -fi - -echo "$ac_t""$tecla_cv_gnu_ld_script" 1>&6 - if test $tecla_cv_gnu_ld_script = yes; then - VERSION_OPT='--version-script=$$(srcdir)/libtecla.map' - else - VERSION_OPT='' - fi - - LINK_SHARED='ld -o $$@ -soname libtecla$$(SUFFIX).so.'${MAJOR_VER}' -shared '$VERSION_OPT' $$(LIB_OBJECTS) $$(LIBS) -lc' - SHARED_CFLAGS="-fpic" - ;; -*hpux*) - SHARED_EXT=".${MAJOR_VER}" - SHARED_ALT=".sl" - LINK_SHARED='ld -b +h $$(@F) +k +vshlibunsats -o $$@ -c libtecla.map.opt $$(LIB_OBJECTS) $$(LIBS) -lc' - SHARED_CFLAGS="+z" - ;; -*dec-osf*) - cat >> confdefs.h <<\EOF -#define _OSF_SOURCE 1 -EOF - - ;; -esac - - -if test "$GCC"_ = "yes"_ && test "$LINK_SHARED"_ != "_" ; then - SHARED_CFLAGS="-fpic" - case $target_os in - sparc*solaris*) SHARED_CFLAGS="$SHARED_CFLAGS -mno-app-regs" - esac - LINK_SHARED="$LINK_SHARED `gcc -print-libgcc-file-name`" -fi - - - - - -if test "$LINK_SHARED"_ != "_"; then - TARGET_LIBS="static shared" -else - TARGET_LIBS="static" - LINK_SHARED="@:" -fi - - -trap '' 1 2 15 -cat > confcache <<\EOF -# This file is a shell script that caches the results of configure -# tests run on this system so they can be shared between configure -# scripts and configure runs. It is not useful on other systems. -# If it contains results you don't want to keep, you may remove or edit it. -# -# By default, configure uses ./config.cache as the cache file, -# creating it if it does not exist already. You can give configure -# the --cache-file=FILE option to use a different cache file; that is -# what configure does when it calls configure scripts in -# subdirectories, so they share the cache. -# Giving --cache-file=/dev/null disables caching, for debugging configure. -# config.status only pays attention to the cache file if you give it the -# --recheck option to rerun configure. -# -EOF -# The following way of writing the cache mishandles newlines in values, -# but we know of no workaround that is simple, portable, and efficient. -# So, don't put newlines in cache variables' values. -# Ultrix sh set writes to stderr and can't be redirected directly, -# and sets the high bit in the cache file unless we assign to the vars. -(set) 2>&1 | - case `(ac_space=' '; set | grep ac_space) 2>&1` in - *ac_space=\ *) - # `set' does not quote correctly, so add quotes (double-quote substitution - # turns \\\\ into \\, and sed turns \\ into \). - sed -n \ - -e "s/'/'\\\\''/g" \ - -e "s/^\\([a-zA-Z0-9_]*_cv_[a-zA-Z0-9_]*\\)=\\(.*\\)/\\1=\${\\1='\\2'}/p" - ;; - *) - # `set' quotes correctly as required by POSIX, so do not add quotes. - sed -n -e 's/^\([a-zA-Z0-9_]*_cv_[a-zA-Z0-9_]*\)=\(.*\)/\1=${\1=\2}/p' - ;; - esac >> confcache -if cmp -s $cache_file confcache; then - : -else - if test -w $cache_file; then - echo "updating cache $cache_file" - cat confcache > $cache_file - else - echo "not updating unwritable cache $cache_file" - fi -fi -rm -f confcache - -trap 'rm -fr conftest* confdefs* core core.* *.core $ac_clean_files; exit 1' 1 2 15 - -test "x$prefix" = xNONE && prefix=$ac_default_prefix -# Let make expand exec_prefix. -test "x$exec_prefix" = xNONE && exec_prefix='${prefix}' - -# Any assignment to VPATH causes Sun make to only execute -# the first set of double-colon rules, so remove it if not needed. -# If there is a colon in the path, we need to keep it. -if test "x$srcdir" = x.; then - ac_vpsub='/^[ ]*VPATH[ ]*=[^:]*$/d' -fi - -trap 'rm -f $CONFIG_STATUS conftest*; exit 1' 1 2 15 - -# Transform confdefs.h into DEFS. -# Protect against shell expansion while executing Makefile rules. -# Protect against Makefile macro expansion. -cat > conftest.defs <<\EOF -s%#define \([A-Za-z_][A-Za-z0-9_]*\) *\(.*\)%-D\1=\2%g -s%[ `~#$^&*(){}\\|;'"<>?]%\\&%g -s%\[%\\&%g -s%\]%\\&%g -s%\$%$$%g -EOF -DEFS=`sed -f conftest.defs confdefs.h | tr '\012' ' '` -rm -f conftest.defs - - -# Without the "./", some shells look in PATH for config.status. -: ${CONFIG_STATUS=./config.status} - -echo creating $CONFIG_STATUS -rm -f $CONFIG_STATUS -cat > $CONFIG_STATUS </dev/null | sed 1q`: -# -# $0 $ac_configure_args -# -# Compiler output produced by configure, useful for debugging -# configure, is in ./config.log if it exists. - -ac_cs_usage="Usage: $CONFIG_STATUS [--recheck] [--version] [--help]" -for ac_option -do - case "\$ac_option" in - -recheck | --recheck | --rechec | --reche | --rech | --rec | --re | --r) - echo "running \${CONFIG_SHELL-/bin/sh} $0 $ac_configure_args --no-create --no-recursion" - exec \${CONFIG_SHELL-/bin/sh} $0 $ac_configure_args --no-create --no-recursion ;; - -version | --version | --versio | --versi | --vers | --ver | --ve | --v) - echo "$CONFIG_STATUS generated by autoconf version 2.13" - exit 0 ;; - -help | --help | --hel | --he | --h) - echo "\$ac_cs_usage"; exit 0 ;; - *) echo "\$ac_cs_usage"; exit 1 ;; - esac -done - -ac_given_srcdir=$srcdir - -trap 'rm -fr `echo "Makefile" | sed "s/:[^ ]*//g"` conftest*; exit 1' 1 2 15 -EOF -cat >> $CONFIG_STATUS < conftest.subs <<\\CEOF -$ac_vpsub -$extrasub -s%@SHELL@%$SHELL%g -s%@CFLAGS@%$CFLAGS%g -s%@CPPFLAGS@%$CPPFLAGS%g -s%@CXXFLAGS@%$CXXFLAGS%g -s%@FFLAGS@%$FFLAGS%g -s%@DEFS@%$DEFS%g -s%@LDFLAGS@%$LDFLAGS%g -s%@LIBS@%$LIBS%g -s%@exec_prefix@%$exec_prefix%g -s%@prefix@%$prefix%g -s%@program_transform_name@%$program_transform_name%g -s%@bindir@%$bindir%g -s%@sbindir@%$sbindir%g -s%@libexecdir@%$libexecdir%g -s%@datadir@%$datadir%g -s%@sysconfdir@%$sysconfdir%g -s%@sharedstatedir@%$sharedstatedir%g -s%@localstatedir@%$localstatedir%g -s%@libdir@%$libdir%g -s%@includedir@%$includedir%g -s%@oldincludedir@%$oldincludedir%g -s%@infodir@%$infodir%g -s%@mandir@%$mandir%g -s%@MAJOR_VER@%$MAJOR_VER%g -s%@MINOR_VER@%$MINOR_VER%g -s%@MICRO_VER@%$MICRO_VER%g -s%@CC@%$CC%g -s%@SET_MAKE@%$SET_MAKE%g -s%@LN_S@%$LN_S%g -s%@AWK@%$AWK%g -s%@RANLIB@%$RANLIB%g -s%@host@%$host%g -s%@host_alias@%$host_alias%g -s%@host_cpu@%$host_cpu%g -s%@host_vendor@%$host_vendor%g -s%@host_os@%$host_os%g -s%@target@%$target%g -s%@target_alias@%$target_alias%g -s%@target_cpu@%$target_cpu%g -s%@target_vendor@%$target_vendor%g -s%@target_os@%$target_os%g -s%@build@%$build%g -s%@build_alias@%$build_alias%g -s%@build_cpu@%$build_cpu%g -s%@build_vendor@%$build_vendor%g -s%@build_os@%$build_os%g -s%@CPP@%$CPP%g -s%@TARGETS@%$TARGETS%g -s%@SHARED_EXT@%$SHARED_EXT%g -s%@SHARED_ALT@%$SHARED_ALT%g -s%@SHARED_CFLAGS@%$SHARED_CFLAGS%g -s%@LINK_SHARED@%$LINK_SHARED%g -s%@TARGET_LIBS@%$TARGET_LIBS%g - -CEOF -EOF - -cat >> $CONFIG_STATUS <<\EOF - -# Split the substitutions into bite-sized pieces for seds with -# small command number limits, like on Digital OSF/1 and HP-UX. -ac_max_sed_cmds=90 # Maximum number of lines to put in a sed script. -ac_file=1 # Number of current file. -ac_beg=1 # First line for current file. -ac_end=$ac_max_sed_cmds # Line after last line for current file. -ac_more_lines=: -ac_sed_cmds="" -while $ac_more_lines; do - if test $ac_beg -gt 1; then - sed "1,${ac_beg}d; ${ac_end}q" conftest.subs > conftest.s$ac_file - else - sed "${ac_end}q" conftest.subs > conftest.s$ac_file - fi - if test ! -s conftest.s$ac_file; then - ac_more_lines=false - rm -f conftest.s$ac_file - else - if test -z "$ac_sed_cmds"; then - ac_sed_cmds="sed -f conftest.s$ac_file" - else - ac_sed_cmds="$ac_sed_cmds | sed -f conftest.s$ac_file" - fi - ac_file=`expr $ac_file + 1` - ac_beg=$ac_end - ac_end=`expr $ac_end + $ac_max_sed_cmds` - fi -done -if test -z "$ac_sed_cmds"; then - ac_sed_cmds=cat -fi -EOF - -cat >> $CONFIG_STATUS <> $CONFIG_STATUS <<\EOF -for ac_file in .. $CONFIG_FILES; do if test "x$ac_file" != x..; then - # Support "outfile[:infile[:infile...]]", defaulting infile="outfile.in". - case "$ac_file" in - *:*) ac_file_in=`echo "$ac_file"|sed 's%[^:]*:%%'` - ac_file=`echo "$ac_file"|sed 's%:.*%%'` ;; - *) ac_file_in="${ac_file}.in" ;; - esac - - # Adjust a relative srcdir, top_srcdir, and INSTALL for subdirectories. - - # Remove last slash and all that follows it. Not all systems have dirname. - ac_dir=`echo $ac_file|sed 's%/[^/][^/]*$%%'` - if test "$ac_dir" != "$ac_file" && test "$ac_dir" != .; then - # The file is in a subdirectory. - test ! -d "$ac_dir" && mkdir "$ac_dir" - ac_dir_suffix="/`echo $ac_dir|sed 's%^\./%%'`" - # A "../" for each directory in $ac_dir_suffix. - ac_dots=`echo $ac_dir_suffix|sed 's%/[^/]*%../%g'` - else - ac_dir_suffix= ac_dots= - fi - - case "$ac_given_srcdir" in - .) srcdir=. - if test -z "$ac_dots"; then top_srcdir=. - else top_srcdir=`echo $ac_dots|sed 's%/$%%'`; fi ;; - /*) srcdir="$ac_given_srcdir$ac_dir_suffix"; top_srcdir="$ac_given_srcdir" ;; - *) # Relative path. - srcdir="$ac_dots$ac_given_srcdir$ac_dir_suffix" - top_srcdir="$ac_dots$ac_given_srcdir" ;; - esac - - - echo creating "$ac_file" - rm -f "$ac_file" - configure_input="Generated automatically from `echo $ac_file_in|sed 's%.*/%%'` by configure." - case "$ac_file" in - *Makefile*) ac_comsub="1i\\ -# $configure_input" ;; - *) ac_comsub= ;; - esac - - ac_file_inputs=`echo $ac_file_in|sed -e "s%^%$ac_given_srcdir/%" -e "s%:% $ac_given_srcdir/%g"` - sed -e "$ac_comsub -s%@configure_input@%$configure_input%g -s%@srcdir@%$srcdir%g -s%@top_srcdir@%$top_srcdir%g -" $ac_file_inputs | (eval "$ac_sed_cmds") > $ac_file -fi; done -rm -f conftest.s* - -EOF -cat >> $CONFIG_STATUS <> $CONFIG_STATUS <<\EOF - -exit 0 -EOF -chmod +x $CONFIG_STATUS -rm -fr confdefs* $ac_clean_files -test "$no_create" = yes || ${CONFIG_SHELL-/bin/sh} $CONFIG_STATUS || exit 1 - diff --git a/libtecla-1.4.1/configure.in b/libtecla-1.4.1/configure.in deleted file mode 100644 index 3b083b9..0000000 --- a/libtecla-1.4.1/configure.in +++ /dev/null @@ -1,412 +0,0 @@ -dnl This is the input file which autoconf uses to construct a -dnl "configure" script for the tecla library. It is a bourne shell -dnl script which autoconf pre-processes with the m4 preprocessor to -dnl expand autoconf-defined m4 macros such as AC_INIT(). The -dnl following line just initializes autoconf. Autoconf interprets the -dnl single argument as the name of an arbitrary file, which it uses to -dnl ensure that it is being run correctly from the directory which -dnl contains the libtecla source code. - -AC_INIT(getline.c) - -dnl Here we set the major version number of the tecla library. -dnl Incrementing this number implies that a change has been made to -dnl the library's public interface, which makes it binary incompatible -dnl with programs that were linked with previous shared versions of -dnl the tecla library. Incompatible changes of this type should be -dnl avoided at all costs, so it is hoped that the major version number -dnl won't ever have to change. The major version number must be a -dnl small integer number, preferably a single numeric digit. - -AC_SUBST(MAJOR_VER) -MAJOR_VER="1" - -dnl Set the minor version number of the tecla library. This number -dnl should be incremented by one whenever additional functionality, -dnl such as new functions or modules, are added to the library. The -dnl idea is that a program that was linked with a shared library of -dnl the same major version number, but a lower minor version number, -dnl will continue to function when the run-time loader links it -dnl against the updated version. The minor version number must be a -dnl small integer number, which should be reset to 0 whenever the -dnl major version number is incremented. - -AC_SUBST(MINOR_VER) -MINOR_VER="4" - -dnl Set the micro version number of the tecla library. This is -dnl incremented whenever modifications to the library are made which -dnl make no changes to the public interface, but which fix bugs and/or -dnl improve the behind-the-scenes implementation. The micro version -dnl number should be reset to 0 whenever the minor version number is -dnl incremented. The micro version number must be a small integer -dnl number. - -AC_SUBST(MICRO_VER) -MICRO_VER="1" - -dnl The AC_PROG_CC line looks for a C compiler, and if gcc is chosen, -dnl sets the $GCC shell variable to "yes". Make sure that CFLAGS is -dnl set to something first, to prevent AC_PROG_CC from substituting -g -dnl for the optimization level. - -CFLAGS="$CFLAGS" -AC_PROG_CC - -dnl Apparently not all implementations of the 'make' command define -dnl the MAKE variable. The following directive creates a variable -dnl called SET_MAKE which when expanded in a makefile is either empty -dnl if the local 'make' command was found to define the MAKE variable, -dnl or contains an assignment which will give the MAKE variable the -dnl value 'make'. - -AC_PROG_MAKE_SET - -dnl The following directive causes autoconf to see if symbolic links -dnl are supported on the current filesystem. If so, it sets the -dnl variable LN_S to "ln -s". Otherwise it sets LN_S to just "ln". -dnl This allows us to create symbolic links where possible, but falls -dnl back to creating hard links where symbolic links aren't available. - -AC_PROG_LN_S - -dnl The following macro searches for the best implementation of awk -dnl on the host system, and records it in the AWK shell variable. - -AC_PROG_AWK - -dnl If ranlib is needed on the target system, the RANLIB make variable -dnl is set to ranlib. Otherwise it is set to :, which is the do-nothing -dnl command of the bourne shell. - -AC_PROG_RANLIB - -dnl The following directive tells autoconf to figure out the target -dnl system type and assign a canonical name for this to the $target -dnl shell variable. This is used below in the target-specific case -dnl statement. - -AC_CANONICAL_SYSTEM - -dnl In early versions of Solaris, some libraries are in /usr/ccs/lib, -dnl where gcc doesn't look. The tests below for the curses library -dnl would thus fail without this directory being added to the search -dnl path. We thus add it here before the tests. Note that in the -dnl following, since [ and ] are m4 quotes, and m4 will remove the -dnl outermost quotes when it processes this file, we have to double -dnl them up here to get [0-6] to appear in the output configure -dnl script. - -case $target in -*-sun-solaris2.[[0-6]]|*-sun-solaris2.[[0-6]].*) - LIBS="$LIBS -L/usr/ccs/lib" - ;; -esac - -dnl The following lines look for terminfo functions in the normal -dnl curses library. If not found, they are searched for in the GNU -dnl ncurses library. If the terminfo functions still aren't found, -dnl then termcap functions are searched for in the curses library. If -dnl either set of functions is found, the corresponding variable -dnl USE_TERMINFO or USE_TERMCAP is arranged to be defined in CFLAGS, -dnl via the exported DEFINES shell variable, and the library in which -dnl they were found is appended to the LIBS shell variable. - -AC_CHECK_LIB(curses, tigetstr, [ - AC_DEFINE(USE_TERMINFO) - LIBS="$LIBS -lcurses" -], [AC_CHECK_LIB(ncurses, tigetstr, [ - AC_DEFINE(USE_TERMINFO) - LIBS="$LIBS -lncurses" -], [AC_CHECK_LIB(curses, tgetstr, [ - AC_DEFINE(USE_TERMCAP) - LIBS="$LIBS -lcurses" - AC_CHECK_HEADER(termcap.h, AC_DEFINE(HAVE_TERMCAP_H)) -])])]) - -dnl Some systems don't have term.h, some systems squirrel it away -dnl in an ncurses sub-directory of the system include directory. -dnl If term.h exists in the normal location, arrange for HAVE_TERM_H -dnl to be added to CFLAGS in the Makefile, by appending it to the -dnl DEFINES shell variable. Otherwise, if it exists under an ncurses -dnl sub-directory, arrange for HAVE_NCURSES_TERM_H to be set instead. -dnl If it isn't found in either of these places, neither of these -dnl variables is set, so term.h just doesn't get included. - -AC_CHECK_HEADER(term.h, AC_DEFINE(HAVE_TERM_H), [ - AC_CHECK_HEADER(ncurses/term.h, AC_DEFINE(HAVE_NCURSES_TERM_H)) -]) - -dnl Do the same search for curses.h. - -AC_CHECK_HEADER(curses.h, AC_DEFINE(HAVE_CURSES_H), [ - AC_CHECK_HEADER(ncurses/curses.h, AC_DEFINE(HAVE_NCURSES_CURSES_H)) -]) - -dnl The following variable lists the targets that will be created if -dnl the user runs make without any arguments. Initially we assume -dnl that we can create both the normal and the reentrant versions -dnl of the library. - -AC_SUBST(TARGETS) -TARGETS="normal reentrant" - -dnl Check for reentrant functions by attempting to compile and link a -dnl temporary program which calls them, being sure to include the -dnl appropriate headers and define _POSIX_C_SOURCE, just in case any -dnl of the functions are defined as macros. In the following, -dnl AC_CACHE_CHECK outputs the message "checking for reentrant -dnl functions". If this check has been done before, it assigns the -dnl cached yes/no value to tecla_cv_reentrant. Otherwise it uses -dnl AC_TRY_LINK() to attempt to compile and link the specified dummy -dnl program, and sets tecla_cv_reentrant to yes or no, depending on -dnl whether this succeeds. Finally it caches the value of -dnl tecla_cv_reentrant in the file config.cache, and writes "yes" or -dnl "no" to the terminal. - -AC_CACHE_CHECK(for reentrant functions, tecla_cv_reentrant, [ - KEPT_CFLAGS="$CFLAGS" - CFLAGS="$CFLAGS -D_POSIX_C_SOURCE=199506L" - AC_TRY_LINK([ -#include -#include -#include -#include -#include - ], [ - (void) readdir_r(NULL, NULL, NULL); - (void) getpwuid_r(geteuid(), NULL, NULL, 0, NULL); - (void) getpwnam_r(NULL, NULL, NULL, 0, NULL); - ], tecla_cv_reentrant=yes, tecla_cv_reentrant=no) - CFLAGS="$KEPT_CFLAGS" -]) - -dnl If the necessary reentrant functions weren't found to be -dnl available, default to only compiling the non-reentrant version of -dnl the library. - -if test $tecla_cv_reentrant = no; then - TARGETS="normal" -fi - -dnl Check for the select system call with the normal arguments, -dnl by attempting to compile and link a temporary program which -dnl calls it, being sure to include the appropriate headers. -dnl In the following, AC_CACHE_CHECK outputs the message -dnl "checking for select system call". If this check has been done -dnl before, it assigns the cached yes/no value to tecla_cv_select. -dnl Otherwise it uses AC_TRY_LINK() to attempt to compile and link -dnl the specified dummy program, and sets tecla_cv_select to yes -dnl or no, depending on whether this succeeds. Finally it caches -dnl the value of tecla_cv_select in the file config.cache, and -dnl writes "yes" or "no" to the terminal. - -AC_CACHE_CHECK(for select system call, tecla_cv_select, [ - AC_TRY_LINK([ -#ifdef HAVE_SELECT_H -#include -#endif -#include -#include -#include - ], [ - fd_set fds; - int nready; - FD_ZERO(&fds); - FD_SET(1, &fds); - nready = select(2, &fds, &fds, &fds, NULL); - ], tecla_cv_select=yes, tecla_cv_select=no) -]) - -dnl If the select function was available, arrange for HAVE_SELECT to -dnl be defined by CFLAGS. - -if test $tecla_cv_select = yes; then - AC_DEFINE(HAVE_SELECT) -fi - -dnl Check if this system supports the system V pseudo terminal interface. - -AC_CACHE_CHECK(for SysV pseudo-terminals, tecla_cv_sysv_pty, [ - AC_TRY_LINK([ -#include -#include -#include - ], [ - char *name = ptsname(0); - int i1 = grantpt(0); - int i2 = unlockpt(0); - int i3 = ioctl(0, I_PUSH, "ptem"); - return 0; - ], tecla_cv_sysv_pty=yes, tecla_cv_sysv_pty=no) -]) - -dnl If the system-V pseudo-terminal interface is available, arrange -dnl for HAVE_SYSV_PTY to be defined by CFLAGS. - -if test $tecla_cv_sysv_pty = yes; then - AC_DEFINE(HAVE_SYSV_PTY) -fi - -dnl The following variable contains the extension to append to -dnl "libtecla" and "libtecla_r" when creating shared libraries on the -dnl target platform. This is system dependent and is ignored if -dnl LINK_SHARED remains an empty string. On most platforms that -dnl support shared libaries, this will be .so.$MAJOR_VER, where -dnl MAJOR_VER is the major version number described above, which on -dnl some systems, tells the run-time loader if the program being -dnl loaded is binary compatible with a given version of the library -dnl (see the discussion of MAJOR_VER near the top of this file). -dnl The following empty default can be overriden on a system by system -dnl basis later in this file. - -AC_SUBST(SHARED_EXT) -SHARED_EXT="" - -dnl When a shared library is installed with the extension $SHARED_EXT, -dnl you can optionally produce other copies of this library with -dnl different extensions. This is done using symbolic or hard links, -dnl depending on what is available on the current filesystem, and the -dnl extensions to use for these links are listed in the following -dnl variable, separated by spaces. The following empty default can be -dnl overriden on a system by system basis later in this file. - -AC_SUBST(SHARED_ALT) -SHARED_ALT="" - -dnl The following variable lists extra compilation flags needed to -dnl create object files that can be included in shared libraries. -dnl Normally one would include a flag to tell the C compiler to -dnl compile position-independent code. This option commonly includes -dnl the acronym 'pic'. - -AC_SUBST(SHARED_CFLAGS) -SHARED_CFLAGS="" - -dnl On systems that support shared libraries, the following variable -dnl provides the command needed to make a shared library. In this -dnl variable, $$@ will be replaced with the name of the shared -dnl library, $$(LIB_OBJECTS) will be replaced with a space separated -dnl list of the object files that are to be included in the library, -dnl and libtecla$$(SUFFIX) will be the name of the library being -dnl built, minus the system-specific extension (eg. libtecla or -dnl libtecla_r). If LINK_SHARED is left as an empty string, shared -dnl library creation will not attempted. If your system supports -dnl shared library creation, you should override the default value of -dnl this variable in the target-specific case statement later in this -dnl file. - -AC_SUBST(LINK_SHARED) -LINK_SHARED="" - -dnl The following bourne shell case statement is where system -dnl dependencies can be added. In particular, if your system supports -dnl shared library creation, the following switch is the place to -dnl configure it. To do so you will first need to find out what target -dnl type was just assigned by the AC_CANONICAL_SYSTEM macro executed -dnl previously. The target type of your current system can be -dnl determined by cd'ing to the top level directory of the tecla -dnl distribution, and typing the command "sh config.guess". This will -dnl report what autoconf thinks the system type is. Note that this -dnl will be very specific, so if you know that the configuration -dnl parameters that you are about to provide apply to different -dnl versions of the current system type, you can express this in the -dnl case statement by using a wild-card in place of the version -dnl number, or by using an | alternation to list one or more version -dnl names. Beware that autoconf uses [] as quote characters, so if you -dnl want to use a regexp character range like [a-z], you should write -dnl this as [[a-z]]. - -case $target in -*solaris*) - AC_DEFINE(__EXTENSIONS__) - SHARED_EXT=".so.${MAJOR_VER}" - SHARED_ALT=".so" - LINK_SHARED='ld -G -M $$(srcdir)/libtecla.map -o $$@ -h $$(@F) -z defs -i $$(LIB_OBJECTS) $$(LIBS) -lc' - SHARED_CFLAGS="-Kpic" - case $CC in - */cc|cc) SHARED_CFLAGS="$SHARED_CFLAGS -xstrconst" ;; - gcc) CFLAGS="-I/usr/include $CFLAGS" ;; - esac - case $target in - *sparc*) SHARED_CFLAGS="$SHARED_CFLAGS -xregs=no%appl" - esac - ;; -*linux*) - SHARED_EXT=".so.${MAJOR_VER}.${MINOR_VER}.${MICRO_VER}" - SHARED_ALT=".so .so.${MAJOR_VER}" - -dnl See if the installed version of Gnu ld accepts version scripts. - - AC_CACHE_CHECK([for --version-script in GNU ld], tecla_cv_gnu_ld_script, [ - if (echo 'void dummy(void) {return;}' > dummy.c; $CC -c -fpic dummy.c; \ - ld -o dummy.so dummy.o -shared --version-script=$srcdir/libtecla.map) 1>&2 2>/dev/null; then - tecla_cv_gnu_ld_script=yes - else - tecla_cv_gnu_ld_script=no - fi - rm -f dummy.c dummy.o dummy.so - ]) - if test $tecla_cv_gnu_ld_script = yes; then - VERSION_OPT='--version-script=$$(srcdir)/libtecla.map' - else - VERSION_OPT='' - fi - - LINK_SHARED='ld -o $$@ -soname libtecla$$(SUFFIX).so.'${MAJOR_VER}' -shared '$VERSION_OPT' $$(LIB_OBJECTS) $$(LIBS) -lc' - SHARED_CFLAGS="-fpic" - ;; -*hpux*) - SHARED_EXT=".${MAJOR_VER}" - SHARED_ALT=".sl" - LINK_SHARED='ld -b +h $$(@F) +k +vshlibunsats -o $$@ -c libtecla.map.opt $$(LIB_OBJECTS) $$(LIBS) -lc' - SHARED_CFLAGS="+z" - ;; -*dec-osf*) - AC_DEFINE(_OSF_SOURCE) - ;; -esac - -dnl The following statement checks to see if the GNU C compiler has -dnl been chosen instead of the normal compiler of the host operating -dnl system. If it has, and shared library creation has been -dnl configured, it replaces the shared-library-specific C compilation -dnl flags with those supported by gcc. Also append the gcc run-time -dnl library to the shared library link line. - -if test "$GCC"_ = "yes"_ && test "$LINK_SHARED"_ != "_" ; then - SHARED_CFLAGS="-fpic" - case $target_os in - sparc*solaris*) SHARED_CFLAGS="$SHARED_CFLAGS -mno-app-regs" - esac - LINK_SHARED="$LINK_SHARED `gcc -print-libgcc-file-name`" -fi - -dnl The following variable will list which types of libraries, -dnl "static", and possibly "shared", are to be created and installed. - -AC_SUBST(TARGET_LIBS) - -dnl If shared library creation has been configured, add shared -dnl libraries to the list of libraries to be built. - -if test "$LINK_SHARED"_ != "_"; then - TARGET_LIBS="static shared" -else - TARGET_LIBS="static" - LINK_SHARED="@:" -fi - -dnl The following directive must always be the last line of any -dnl autoconf script. It causes autoconf to create the configure -dnl script, which for each argument of AC_OUTPUT, will look for a -dnl filename formed by appending ".in" to the argument, preprocess -dnl that file, replacing @VAR@ directives with the corresponding value -dnl of the specified shell variable VAR, as set above in this file, -dnl and write the resulting output to the filename given in the -dnl argument. Note that only shell variables that were exported above -dnl with the AC_SUBST() directive will be substituted in @VAR@ -dnl directives (some macros like AC_PROG_CC also call AC_SUBST for you -dnl for the variables that they output). - -AC_OUTPUT(Makefile) diff --git a/libtecla-1.4.1/cplfile.c b/libtecla-1.4.1/cplfile.c deleted file mode 100644 index 73eef1d..0000000 --- a/libtecla-1.4.1/cplfile.c +++ /dev/null @@ -1,874 +0,0 @@ -/* - * Copyright (c) 2000, 2001 by Martin C. Shepherd. - * - * All rights reserved. - * - * Permission is hereby granted, free of charge, to any person obtaining a - * copy of this software and associated documentation files (the - * "Software"), to deal in the Software without restriction, including - * without limitation the rights to use, copy, modify, merge, publish, - * distribute, and/or sell copies of the Software, and to permit persons - * to whom the Software is furnished to do so, provided that the above - * copyright notice(s) and this permission notice appear in all copies of - * the Software and that both the above copyright notice(s) and this - * permission notice appear in supporting documentation. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS - * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT - * OF THIRD PARTY RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR - * HOLDERS INCLUDED IN THIS NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL - * INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING - * FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, - * NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION - * WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - * - * Except as contained in this notice, the name of a copyright holder - * shall not be used in advertising or otherwise to promote the sale, use - * or other dealings in this Software without prior written authorization - * of the copyright holder. - */ - -/* - * Standard includes. - */ -#include -#include -#include -#include -#include -#include - -/* - * Local includes. - */ -#include "libtecla.h" -#include "direader.h" -#include "homedir.h" -#include "pathutil.h" -#include "cplfile.h" - -/* - * Set the maximum length allowed for usernames. - * names. - */ -#define USR_LEN 100 - -/* - * Set the maximum length allowed for environment variable names. - */ -#define ENV_LEN 100 - -/* - * Set the max length of the error-reporting string. There is no point - * in this being longer than the width of a typical terminal window. - * In composing error messages, I have assumed that this number is - * at least 80, so you don't decrease it below this number. - */ -#define ERRLEN 200 - -/* - * The resources needed to complete a filename are maintained in objects - * of the following type. - */ -struct CompleteFile { - DirReader *dr; /* A directory reader */ - HomeDir *home; /* A home directory expander */ - PathName *path; /* The buffer in which to accumulate the path */ - PathName *buff; /* A pathname work buffer */ - char usrnam[USR_LEN+1]; /* The buffer used when reading the names of */ - /* users. */ - char envnam[ENV_LEN+1]; /* The buffer used when reading the names of */ - /* environment variables. */ - char errmsg[ERRLEN+1]; /* The error-report buffer */ -}; - -static int cf_expand_home_dir(CompleteFile *cf, const char *user); -static int cf_complete_username(CompleteFile *cf, WordCompletion *cpl, - const char *prefix, const char *line, - int word_start, int word_end, int escaped); -static HOME_DIR_FN(cf_homedir_callback); -static int cf_complete_entry(CompleteFile *cf, WordCompletion *cpl, - const char *line, int word_start, int word_end, - int escaped, CplCheckFn *check_fn, - void *check_data); -static char *cf_read_name(CompleteFile *cf, const char *type, - const char *string, int slen, - char *nambuf, int nammax); -static int cf_prepare_suffix(CompleteFile *cf, const char *suffix, - int add_escapes); - -/* - * A stack based object of the following type is used to pass data to the - * cf_homedir_callback() function. - */ -typedef struct { - CompleteFile *cf; /* The file-completion resource object */ - WordCompletion *cpl; /* The string-completion rsource object */ - const char *prefix; /* The username prefix to be completed */ - const char *line; /* The line from which the prefix was extracted */ - int word_start; /* The index in line[] of the start of the username */ - int word_end; /* The index in line[] following the end of the prefix */ - int escaped; /* If true, add escapes to the completion suffixes */ -} CfHomeArgs; - -/*....................................................................... - * Create a new file-completion object. - * - * Output: - * return CompleteFile * The new object, or NULL on error. - */ -CompleteFile *_new_CompleteFile(void) -{ - CompleteFile *cf; /* The object to be returned */ -/* - * Allocate the container. - */ - cf = (CompleteFile *) malloc(sizeof(CompleteFile)); - if(!cf) { - fprintf(stderr, "_new_CompleteFile: Insufficient memory.\n"); - return NULL; - }; -/* - * Before attempting any operation that might fail, initialize the - * container at least up to the point at which it can safely be passed - * to _del_CompleteFile(). - */ - cf->dr = NULL; - cf->home = NULL; - cf->path = NULL; - cf->buff = NULL; - cf->usrnam[0] = '\0'; - cf->envnam[0] = '\0'; - cf->errmsg[0] = '\0'; -/* - * Create the object that is used for reading directories. - */ - cf->dr = _new_DirReader(); - if(!cf->dr) - return _del_CompleteFile(cf); -/* - * Create the object that is used to lookup home directories. - */ - cf->home = _new_HomeDir(); - if(!cf->home) - return _del_CompleteFile(cf); -/* - * Create the buffer in which the completed pathname is accumulated. - */ - cf->path = _new_PathName(); - if(!cf->path) - return _del_CompleteFile(cf); -/* - * Create a pathname work buffer. - */ - cf->buff = _new_PathName(); - if(!cf->buff) - return _del_CompleteFile(cf); - return cf; -} - -/*....................................................................... - * Delete a file-completion object. - * - * Input: - * cf CompleteFile * The object to be deleted. - * Output: - * return CompleteFile * The deleted object (always NULL). - */ -CompleteFile *_del_CompleteFile(CompleteFile *cf) -{ - if(cf) { - cf->dr = _del_DirReader(cf->dr); - cf->home = _del_HomeDir(cf->home); - cf->path = _del_PathName(cf->path); - cf->buff = _del_PathName(cf->buff); - free(cf); - }; - return NULL; -} - -/*....................................................................... - * Look up the possible completions of the incomplete filename that - * lies between specified indexes of a given command-line string. - * - * Input: - * cpl WordCompletion * The object in which to record the completions. - * cf CompleteFile * The filename-completion resource object. - * line const char * The string containing the incomplete filename. - * word_start int The index of the first character in line[] - * of the incomplete filename. - * word_end int The index of the character in line[] that - * follows the last character of the incomplete - * filename. - * escaped int If true, backslashes in line[] are - * interpreted as escaping the characters - * that follow them, and any spaces, tabs, - * backslashes, or wildcard characters in the - * returned suffixes will be similarly escaped. - * If false, backslashes will be interpreted as - * literal parts of the file name, and no - * backslashes will be added to the returned - * suffixes. - * check_fn CplCheckFn * If not zero, this argument specifies a - * function to call to ask whether a given - * file should be included in the list - * of completions. - * check_data void * Anonymous data to be passed to check_fn(). - * Output: - * return int 0 - OK. - * 1 - Error. A description of the error can be - * acquired by calling _cf_last_error(cf). - */ -int _cf_complete_file(WordCompletion *cpl, CompleteFile *cf, - const char *line, int word_start, int word_end, - int escaped, CplCheckFn *check_fn, void *check_data) -{ - const char *lptr; /* A pointer into line[] */ - int nleft; /* The number of characters still to be processed */ - /* in line[]. */ -/* - * Check the arguments. - */ - if(!cpl || !cf || !line || word_end < word_start) { - if(cf) - strcpy(cf->errmsg, "_cf_complete_file: Invalid arguments"); - return 1; - }; -/* - * Clear the buffer in which the filename will be constructed. - */ - _pn_clear_path(cf->path); -/* - * How many characters are to be processed? - */ - nleft = word_end - word_start; -/* - * Get a pointer to the start of the incomplete filename. - */ - lptr = line + word_start; -/* - * If the first character is a tilde, then perform home-directory - * interpolation. - */ - if(nleft > 0 && *lptr == '~') { - int slen; - if(!cf_read_name(cf, "User", ++lptr, --nleft, cf->usrnam, USR_LEN)) - return 1; -/* - * Advance over the username in the input line. - */ - slen = strlen(cf->usrnam); - lptr += slen; - nleft -= slen; -/* - * If we haven't hit the end of the input string then we have a complete - * username to translate to the corresponding home directory. - */ - if(nleft > 0) { - if(cf_expand_home_dir(cf, cf->usrnam)) - return 1; -/* - * ~user and ~ are usually followed by a directory separator to - * separate them from the file contained in the home directory. - * If the home directory is the root directory, then we don't want - * to follow the home directory by a directory separator, so we should - * skip over it so that it doesn't get copied into the filename. - */ - if(strcmp(cf->path->name, FS_ROOT_DIR) == 0 && - strncmp(lptr, FS_DIR_SEP, FS_DIR_SEP_LEN) == 0) { - lptr += FS_DIR_SEP_LEN; - nleft -= FS_DIR_SEP_LEN; - }; -/* - * If we have reached the end of the input string, then the username - * may be incomplete, and we should attempt to complete it. - */ - } else { -/* - * Look up the possible completions of the username. - */ - return cf_complete_username(cf, cpl, cf->usrnam, line, word_start+1, - word_end, escaped); - }; - }; -/* - * Copy the rest of the path, stopping to expand $envvar expressions - * where encountered. - */ - while(nleft > 0) { - int seglen; /* The length of the next segment to be copied */ -/* - * Find the length of the next segment to be copied, stopping if an - * unescaped '$' is seen, or the end of the path is reached. - */ - for(seglen=0; seglen < nleft; seglen++) { - int c = lptr[seglen]; - if(escaped && c == '\\') - seglen++; - else if(c == '$') - break; -/* - * We will be completing the last component of the file name, - * so whenever a directory separator is seen, assume that it - * might be the start of the last component, and mark the character - * that follows it as the start of the name that is to be completed. - */ - if(nleft >= FS_DIR_SEP_LEN && - strncmp(lptr + seglen, FS_DIR_SEP, FS_DIR_SEP_LEN)==0) { - word_start = (lptr + seglen) - line + FS_DIR_SEP_LEN; - }; - }; -/* - * We have reached either the end of the filename or the start of - * $environment_variable expression. Record the newly checked - * segment of the filename in the output filename, removing - * backslash-escapes where needed. - */ - if(_pn_append_to_path(cf->path, lptr, seglen, escaped) == NULL) { - strcpy(cf->errmsg, "Insufficient memory to complete filename"); - return 1; - }; - lptr += seglen; - nleft -= seglen; -/* - * If the above loop finished before we hit the end of the filename, - * then this was because an unescaped $ was seen. In this case, interpolate - * the value of the environment variable that follows it into the output - * filename. - */ - if(nleft > 0) { - char *value; /* The value of the environment variable */ - int vlen; /* The length of the value string */ - int nlen; /* The length of the environment variable name */ -/* - * Read the name of the environment variable. - */ - if(!cf_read_name(cf, "Environment", ++lptr, --nleft, cf->envnam, ENV_LEN)) - return 1; -/* - * Advance over the environment variable name in the input line. - */ - nlen = strlen(cf->envnam); - lptr += nlen; - nleft -= nlen; -/* - * Get the value of the environment variable. - */ - value = getenv(cf->envnam); - if(!value) { - const char *fmt = "Unknown environment variable: %.*s"; - sprintf(cf->errmsg, fmt, ERRLEN - strlen(fmt), cf->envnam); - return 1; - }; - vlen = strlen(value); -/* - * If we are at the start of the filename and the first character of the - * environment variable value is a '~', attempt home-directory - * interpolation. - */ - if(cf->path->name[0] == '\0' && value[0] == '~') { - if(!cf_read_name(cf, "User", value+1, vlen-1, cf->usrnam, USR_LEN) || - cf_expand_home_dir(cf, cf->usrnam)) - return 1; -/* - * If the home directory is the root directory, and the ~usrname expression - * was followed by a directory separator, prevent the directory separator - * from being appended to the root directory by skipping it in the - * input line. - */ - if(strcmp(cf->path->name, FS_ROOT_DIR) == 0 && - strncmp(lptr, FS_DIR_SEP, FS_DIR_SEP_LEN) == 0) { - lptr += FS_DIR_SEP_LEN; - nleft -= FS_DIR_SEP_LEN; - }; - } else { -/* - * Append the value of the environment variable to the output path. - */ - if(_pn_append_to_path(cf->path, value, strlen(value), escaped)==NULL) { - strcpy(cf->errmsg, "Insufficient memory to complete filename"); - return 1; - }; -/* - * Prevent extra directory separators from being added. - */ - if(nleft >= FS_DIR_SEP_LEN && - strcmp(cf->path->name, FS_ROOT_DIR) == 0 && - strncmp(lptr, FS_DIR_SEP, FS_DIR_SEP_LEN) == 0) { - lptr += FS_DIR_SEP_LEN; - nleft -= FS_DIR_SEP_LEN; - } else if(vlen > FS_DIR_SEP_LEN && - strcmp(value + vlen - FS_DIR_SEP_LEN, FS_DIR_SEP)==0) { - cf->path->name[vlen-FS_DIR_SEP_LEN] = '\0'; - }; - }; -/* - * If adding the environment variable didn't form a valid directory, - * we can't complete the line, since there is no way to separate append - * a partial filename to an environment variable reference without - * that appended part of the name being seen later as part of the - * environment variable name. Thus if the currently constructed path - * isn't a directory, quite now with no completions having been - * registered. - */ - if(!_pu_path_is_dir(cf->path->name)) - return 0; -/* - * For the reasons given above, if we have reached the end of the filename - * with the expansion of an environment variable, the only allowed - * completion involves the addition of a directory separator. - */ - if(nleft == 0) { - if(cpl_add_completion(cpl, line, lptr-line, word_end, FS_DIR_SEP, - "", "")) { - strncpy(cf->errmsg, cpl_last_error(cpl), ERRLEN); - cf->errmsg[ERRLEN] = '\0'; - return 1; - }; - return 0; - }; - }; - }; -/* - * Complete the filename if possible. - */ - return cf_complete_entry(cf, cpl, line, word_start, word_end, escaped, - check_fn, check_data); -} - -/*....................................................................... - * Return a description of the last path-completion error that occurred. - * - * Input: - * cf CompleteFile * The path-completion resource object. - * Output: - * return const char * The description of the last error. - */ -const char *_cf_last_error(CompleteFile *cf) -{ - return cf ? cf->errmsg : "NULL CompleteFile argument"; -} - -/*....................................................................... - * Lookup the home directory of the specified user, or the current user - * if no name is specified, appending it to output pathname. - * - * Input: - * cf CompleteFile * The pathname completion resource object. - * user const char * The username to lookup, or "" to lookup the - * current user. - * Output: - * return int 0 - OK. - * 1 - Error. - */ -static int cf_expand_home_dir(CompleteFile *cf, const char *user) -{ -/* - * Attempt to lookup the home directory. - */ - const char *home_dir = _hd_lookup_home_dir(cf->home, user); -/* - * Failed? - */ - if(!home_dir) { - strncpy(cf->errmsg, _hd_last_home_dir_error(cf->home), ERRLEN); - cf->errmsg[ERRLEN] = '\0'; - return 1; - }; -/* - * Append the home directory to the pathname string. - */ - if(_pn_append_to_path(cf->path, home_dir, -1, 0) == NULL) { - strcpy(cf->errmsg, "Insufficient memory for home directory expansion"); - return 1; - }; - return 0; -} - -/*....................................................................... - * Lookup and report all completions of a given username prefix. - * - * Input: - * cf CompleteFile * The filename-completion resource object. - * cpl WordCompletion * The object in which to record the completions. - * prefix const char * The prefix of the usernames to lookup. - * line const char * The command-line in which the username appears. - * word_start int The index within line[] of the start of the - * username that is being completed. - * word_end int The index within line[] of the character which - * follows the incomplete username. - * escaped int True if the completions need to have special - * characters escaped. - * Output: - * return int 0 - OK. - * 1 - Error. - */ -static int cf_complete_username(CompleteFile *cf, WordCompletion *cpl, - const char *prefix, const char *line, - int word_start, int word_end, int escaped) -{ -/* - * Set up a container of anonymous arguments to be sent to the - * username-lookup iterator. - */ - CfHomeArgs args; - args.cf = cf; - args.cpl = cpl; - args.prefix = prefix; - args.line = line; - args.word_start = word_start; - args.word_end = word_end; - args.escaped = escaped; -/* - * Iterate through the list of users, recording those which start - * with the specified prefix. - */ - if(_hd_scan_user_home_dirs(cf->home, &args, cf_homedir_callback)) { - strncpy(cf->errmsg, _hd_last_home_dir_error(cf->home), ERRLEN); - cf->errmsg[ERRLEN] = '\0'; - return 1; - }; - return 0; -} - -/*....................................................................... - * The user/home-directory scanner callback function (see homedir.h) - * used by cf_complete_username(). - */ -static HOME_DIR_FN(cf_homedir_callback) -{ -/* - * Get the file-completion resources from the anonymous data argument. - */ - CfHomeArgs *args = (CfHomeArgs *) data; - WordCompletion *cpl = args->cpl; - CompleteFile *cf = args->cf; -/* - * Get the length of the username prefix. - */ - int prefix_len = strlen(args->prefix); -/* - * Get the length of the latest user name that is to be compared to - * the prefix. - */ - int name_len = strlen(usrnam); -/* - * See if the latest username starts with the prefix that we are - * searching for, and record its suffix in the array of matches if so. - */ - if(name_len >= prefix_len && strncmp(args->prefix, usrnam, prefix_len)==0) { -/* - * Copy the username into the pathname work buffer, adding backslash - * escapes where needed. - */ - if(cf_prepare_suffix(cf, usrnam+prefix_len, args->escaped)) { - strncpy(errmsg, cf->errmsg, maxerr); - errmsg[maxerr] = '\0'; - return 1; - }; -/* - * Report the completion suffix that was copied above. - */ - if(cpl_add_completion(cpl, args->line, args->word_start, args->word_end, - cf->buff->name, FS_DIR_SEP, FS_DIR_SEP)) { - strncpy(errmsg, cpl_last_error(cpl), maxerr); - errmsg[maxerr] = '\0'; - return 1; - }; - }; - return 0; -} - -/*....................................................................... - * Report possible completions of the filename in cf->path->name[]. - * - * Input: - * cf CompleteFile * The file-completion resource object. - * cpl WordCompletion * The object in which to record the completions. - * line const char * The input line, as received by the callback - * function. - * word_start int The index within line[] of the start of the - * last component of the filename that is being - * completed. - * word_end int The index within line[] of the character which - * follows the incomplete filename. - * escaped int If true, escape special characters in the - * completion suffixes. - * check_fn CplCheckFn * If not zero, this argument specifies a - * function to call to ask whether a given - * file should be included in the list - * of completions. - * check_data void * Anonymous data to be passed to check_fn(). - * Output: - * return int 0 - OK. - * 1 - Error. - */ -static int cf_complete_entry(CompleteFile *cf, WordCompletion *cpl, - const char *line, int word_start, int word_end, - int escaped, CplCheckFn *check_fn, - void *check_data) -{ - const char *dirpath; /* The name of the parent directory */ - int start; /* The index of the start of the last filename */ - /* component in the transcribed filename. */ - const char *prefix; /* The filename prefix to be completed */ - int prefix_len; /* The length of the filename prefix */ - const char *file_name; /* The lastest filename being compared */ - int waserr = 0; /* True after errors */ - int terminated=0; /* True if the directory part had to be terminated */ -/* - * Get the pathname string and its current length. - */ - char *pathname = cf->path->name; - int pathlen = strlen(pathname); -/* - * Locate the start of the final component of the pathname. - */ - for(start=pathlen - 1; start >= 0 && - strncmp(pathname + start, FS_DIR_SEP, FS_DIR_SEP_LEN) != 0; start--) - ; -/* - * Is the parent directory the root directory? - */ - if(start==0 || - (start < 0 && strncmp(pathname, FS_ROOT_DIR, FS_ROOT_DIR_LEN) == 0)) { - dirpath = FS_ROOT_DIR; - start += FS_ROOT_DIR_LEN; -/* - * If we found a directory separator then the part which precedes the - * last component is the name of the directory to be opened. - */ - } else if(start > 0) { -/* - * The _dr_open_dir() function requires the directory name to be '\0' - * terminated, so temporarily do this by overwriting the first character - * of the directory separator. - */ - pathname[start] = '\0'; - dirpath = pathname; - terminated = 1; -/* - * We reached the start of the pathname before finding a directory - * separator, so arrange to open the current working directory. - */ - } else { - start = 0; - dirpath = FS_PWD; - }; -/* - * Attempt to open the directory. - */ - if(_dr_open_dir(cf->dr, dirpath, NULL)) { - const char *fmt = "Can't open directory: %.*s"; - sprintf(cf->errmsg, fmt, ERRLEN - strlen(fmt), dirpath); - return 1; - }; -/* - * If removed above, restore the directory separator and skip over it - * to the start of the filename. - */ - if(terminated) { - memcpy(pathname + start, FS_DIR_SEP, FS_DIR_SEP_LEN); - start += FS_DIR_SEP_LEN; - }; -/* - * Get the filename prefix and its length. - */ - prefix = pathname + start; - prefix_len = strlen(prefix); -/* - * Traverse the directory, looking for files who's prefixes match the - * last component of the pathname. - */ - while((file_name = _dr_next_file(cf->dr)) != NULL && !waserr) { - int name_len = strlen(file_name); -/* - * Is the latest filename a possible completion of the filename prefix? - */ - if(name_len >= prefix_len && strncmp(prefix, file_name, prefix_len)==0) { -/* - * When listing all files in a directory, don't list files that start - * with '.'. This is how hidden files are denoted in UNIX. - */ - if(prefix_len > 0 || file_name[0] != '.') { -/* - * Copy the completion suffix into the work pathname cf->buff->name, - * adding backslash escapes if needed. - */ - if(cf_prepare_suffix(cf, file_name + prefix_len, escaped)) { - waserr = 1; - } else { -/* - * We want directories to be displayed with directory suffixes, - * and other fully completed filenames to be followed by spaces. - * To check the type of the file, append the current suffix - * to the path being completed, check the filetype, then restore - * the path to its original form. - */ - const char *cont_suffix = ""; /* The suffix to add if fully */ - /* completed. */ - const char *type_suffix = ""; /* The suffix to add when listing */ - if(_pn_append_to_path(cf->path, file_name + prefix_len, - -1, escaped) == NULL) { - strcpy(cf->errmsg, "Insufficient memory to complete filename."); - return 1; - }; -/* - * Specify suffixes according to the file type. - */ - if(_pu_path_is_dir(cf->path->name)) { - cont_suffix = FS_DIR_SEP; - type_suffix = FS_DIR_SEP; - } else if(!check_fn || check_fn(check_data, cf->path->name)) { - cont_suffix = " "; - } else { - cf->path->name[pathlen] = '\0'; - continue; - }; -/* - * Remove the temporarily added suffix. - */ - cf->path->name[pathlen] = '\0'; -/* - * Record the latest completion. - */ - if(cpl_add_completion(cpl, line, word_start, word_end, cf->buff->name, - type_suffix, cont_suffix)) - waserr = 1; - }; - }; - }; - }; -/* - * Close the directory. - */ - _dr_close_dir(cf->dr); - return waserr; -} - -/*....................................................................... - * Read a username or environment variable name, stopping when a directory - * separator is seen, when the end of the string is reached, or the - * output buffer overflows. - * - * Input: - * cf CompleteFile * The file-completion resource object. - * type char * The capitalized name of the type of name being read. - * string char * The string who's prefix contains the name. - * slen int The number of characters in string[]. - * nambuf char * The output name buffer. - * nammax int The longest string that will fit in nambuf[], excluding - * the '\0' terminator. - * Output: - * return char * A pointer to nambuf on success. On error NULL is - * returned and a description of the error is recorded - * in cf->errmsg[]. - */ -static char *cf_read_name(CompleteFile *cf, const char *type, - const char *string, int slen, - char *nambuf, int nammax) -{ - int namlen; /* The number of characters in nambuf[] */ - const char *sptr; /* A pointer into string[] */ -/* - * Work out the max number of characters that should be copied. - */ - int nmax = nammax < slen ? nammax : slen; -/* - * Get the environment variable name that follows the dollar. - */ - for(sptr=string,namlen=0; - namlen < nmax && (slen-namlen < FS_DIR_SEP_LEN || - strncmp(sptr, FS_DIR_SEP, FS_DIR_SEP_LEN) != 0); - namlen++) { - nambuf[namlen] = *sptr++; - }; -/* - * Did the name overflow the buffer? - */ - if(namlen >= nammax) { - const char *fmt = "%.*s name too long"; - sprintf(cf->errmsg, fmt, ERRLEN - strlen(fmt), type); - return NULL; - }; -/* - * Terminate the string. - */ - nambuf[namlen] = '\0'; - return nambuf; -} - -/*....................................................................... - * Using the work buffer cf->buff, make a suitably escaped copy of a - * given completion suffix, ready to be passed to cpl_add_completion(). - * - * Input: - * cf CompleteFile * The file-completion resource object. - * suffix char * The suffix to be copied. - * add_escapes int If true, escape special characters. - * Output: - * return int 0 - OK. - * 1 - Error. - */ -static int cf_prepare_suffix(CompleteFile *cf, const char *suffix, - int add_escapes) -{ - const char *sptr; /* A pointer into suffix[] */ - int nbsl; /* The number of backslashes to add to the suffix */ - int i; -/* - * How long is the suffix? - */ - int suffix_len = strlen(suffix); -/* - * Clear the work buffer. - */ - _pn_clear_path(cf->buff); -/* - * Count the number of backslashes that will have to be added to - * escape spaces, tabs, backslashes and wildcard characters. - */ - nbsl = 0; - if(add_escapes) { - for(sptr = suffix; *sptr; sptr++) { - switch(*sptr) { - case ' ': case '\t': case '\\': case '*': case '?': case '[': - nbsl++; - break; - }; - }; - }; -/* - * Arrange for the output path buffer to have sufficient room for the - * both the suffix and any backslashes that have to be inserted. - */ - if(_pn_resize_path(cf->buff, suffix_len + nbsl) == NULL) { - strcpy(cf->errmsg, "Insufficient memory to complete filename"); - return 1; - }; -/* - * If the suffix doesn't need any escapes, copy it directly into the - * work buffer. - */ - if(nbsl==0) { - strcpy(cf->buff->name, suffix); - } else { -/* - * Make a copy with special characters escaped? - */ - if(nbsl > 0) { - const char *src = suffix; - char *dst = cf->buff->name; - for(i=0; i -#include -#include - -/* - * Local includes. - */ -#include "libtecla.h" -#include "stringrp.h" -#include "pathutil.h" -#include "cplfile.h" - -/* - * Specify the number of strings to allocate when the string free-list - * is exhausted. This also sets the number of elements to expand the - * matches[] array by whenever it is found to be too small. - */ -#define STR_BLK_FACT 100 - -/* - * Set the max length of the error-reporting string. There is no point - * in this being longer than the width of a typical terminal window. - * In composing error messages, I have assumed that this number is - * at least 80, so don't decrease it below 80. - */ -#define ERRLEN 200 - -/* - * Completion matches are recorded in containers of the following - * type. - */ -struct WordCompletion { - StringGroup *sg; /* Memory for a group of strings */ - int matches_dim; /* The allocated size of result.matches[] */ - char errmsg[ERRLEN+1]; /* The error-reporting buffer */ - CplMatches result; /* Completions to be returned to the caller */ - CompleteFile *cf; /* The resources used for filename completion */ -}; - -static void cpl_sort_matches(WordCompletion *cpl); -static void cpl_zap_duplicates(WordCompletion *cpl); -static void cpl_clear_completions(WordCompletion *cpl); -static int cpl_cmp_matches(const void *v1, const void *v2); -static int cpl_cmp_suffixes(const void *v1, const void *v2); - -/* - * The new_CplFileConf() constructor sets the integer first member of - * the returned object to the following magic number. On seeing this, - * cpl_file_completions() knows when it is passed a valid CplFileConf - * object. - */ -#define CFC_ID_CODE 4568 - -/* - * A pointer to a structure of the following type can be passed to - * the builtin file-completion callback function to modify its behavior. - */ -struct CplFileConf { - int id; /* new_CplFileConf() sets this to CFC_ID_CODE */ - int escaped; /* If none-zero, backslashes in the input line are */ - /* interpreted as escaping special characters and */ - /* spaces, and any special characters and spaces in */ - /* the listed completions will also be escaped with */ - /* added backslashes. This is the default behaviour. */ - /* If zero, backslashes are interpreted as being */ - /* literal parts of the filename, and none are added */ - /* to the completion suffixes. */ - int file_start; /* The index in the input line of the first character */ - /* of the filename. If you specify -1 here, */ - /* cpl_file_completions() identifies the */ - /* the start of the filename by looking backwards for */ - /* an unescaped space, or the beginning of the line. */ - CplCheckFn *chk_fn; /* If not zero, this argument specifies a */ - /* function to call to ask whether a given */ - /* file should be included in the list */ - /* of completions. */ - void *chk_data; /* Anonymous data to be passed to check_fn(). */ -}; - -static void cpl_init_FileConf(CplFileConf *cfc); - -/*....................................................................... - * Create a new string-completion object. - * - * Output: - * return WordCompletion * The new object, or NULL on error. - */ -WordCompletion *new_WordCompletion(void) -{ - WordCompletion *cpl; /* The object to be returned */ -/* - * Allocate the container. - */ - cpl = (WordCompletion *) malloc(sizeof(WordCompletion)); - if(!cpl) { - fprintf(stderr, "new_WordCompletion: Insufficient memory.\n"); - return NULL; - }; -/* - * Before attempting any operation that might fail, initialize the - * container at least up to the point at which it can safely be passed - * to del_WordCompletion(). - */ - cpl->sg = NULL; - cpl->matches_dim = 0; - cpl->result.suffix = NULL; - cpl->result.cont_suffix = NULL; - cpl->result.matches = NULL; - cpl->result.nmatch = 0; - cpl->cf = NULL; -/* - * Allocate an object that allows a group of strings to be allocated - * efficiently by placing many of them in contiguous string segments. - */ - cpl->sg = _new_StringGroup(_pu_pathname_dim()); - if(!cpl->sg) - return del_WordCompletion(cpl); -/* - * Allocate an array for matching completions. This will be extended later - * if needed. - */ - cpl->matches_dim = STR_BLK_FACT; - cpl->result.matches = (CplMatch *) malloc(sizeof(cpl->result.matches[0]) * - cpl->matches_dim); - if(!cpl->result.matches) { - fprintf(stderr, - "new_WordCompletion: Insufficient memory to allocate array of matches.\n"); - return del_WordCompletion(cpl); - }; -/* - * Allocate a filename-completion resource object. - */ - cpl->cf = _new_CompleteFile(); - if(!cpl->cf) - return del_WordCompletion(cpl); - return cpl; -} - -/*....................................................................... - * Delete a string-completion object. - * - * Input: - * cpl WordCompletion * The object to be deleted. - * Output: - * return WordCompletion * The deleted object (always NULL). - */ -WordCompletion *del_WordCompletion(WordCompletion *cpl) -{ - if(cpl) { - cpl->sg = _del_StringGroup(cpl->sg); - if(cpl->result.matches) { - free(cpl->result.matches); - cpl->result.matches = NULL; - cpl->cf = _del_CompleteFile(cpl->cf); - }; - free(cpl); - }; - return NULL; -} - -/*....................................................................... - * This function is designed to be called by CplMatchFn callback - * functions. It adds one possible completion of the token that is being - * completed to an array of completions. If the completion needs any - * special quoting to be valid when displayed in the input line, this - * quoting must be included in the string. - * - * Input: - * cpl WordCompletion * The argument of the same name that was passed - * to the calling CplMatchFn callback function. - * line const char * The input line, as received by the callback - * function. - * word_start int The index within line[] of the start of the - * word that is being completed. - * word_end int The index within line[] of the character which - * follows the incomplete word, as received by the - * calling callback function. - * suffix const char * The appropriately quoted string that could - * be appended to the incomplete token to complete - * it. A copy of this string will be allocated - * internally. - * type_suffix const char * When listing multiple completions, gl_get_line() - * appends this string to the completion to indicate - * its type to the user. If not pertinent pass "". - * Otherwise pass a literal or static string. - * cont_suffix const char * If this turns out to be the only completion, - * gl_get_line() will append this string as - * a continuation. For example, the builtin - * file-completion callback registers a directory - * separator here for directory matches, and a - * space otherwise. If the match were a function - * name you might want to append an open - * parenthesis, etc.. If not relevant pass "". - * Otherwise pass a literal or static string. - * Output: - * return int 0 - OK. - * 1 - Error. - */ -int cpl_add_completion(WordCompletion *cpl, const char *line, - int word_start, int word_end, const char *suffix, - const char *type_suffix, const char *cont_suffix) -{ - CplMatch *match; /* The container of the new match */ - char *string; /* A newly allocated copy of the completion string */ -/* - * Check the arguments. - */ - if(!cpl) - return 1; - if(!suffix) - return 0; - if(!type_suffix) - type_suffix = ""; - if(!cont_suffix) - cont_suffix = ""; -/* - * Do we need to extend the array of matches[]? - */ - if(cpl->result.nmatch+1 > cpl->matches_dim) { - int needed = cpl->matches_dim + STR_BLK_FACT; - CplMatch *matches = (CplMatch *) realloc(cpl->result.matches, - sizeof(cpl->result.matches[0]) * needed); - if(!matches) { - strcpy(cpl->errmsg, "Insufficient memory to extend array of matches."); - return 1; - }; - cpl->result.matches = matches; - cpl->matches_dim = needed; - }; -/* - * Allocate memory to store the combined completion prefix and the - * new suffix. - */ - string = _sg_alloc_string(cpl->sg, word_end-word_start + strlen(suffix)); - if(!string) { - strcpy(cpl->errmsg, "Insufficient memory to extend array of matches."); - return 1; - }; -/* - * Compose the string. - */ - strncpy(string, line + word_start, word_end - word_start); - strcpy(string + word_end - word_start, suffix); -/* - * Record the new match. - */ - match = cpl->result.matches + cpl->result.nmatch++; - match->completion = string; - match->suffix = string + word_end - word_start; - match->type_suffix = type_suffix; -/* - * Record the continuation suffix. - */ - cpl->result.cont_suffix = cont_suffix; - return 0; -} - -/*....................................................................... - * Sort the array of matches. - * - * Input: - * cpl WordCompletion * The completion resource object. - */ -static void cpl_sort_matches(WordCompletion *cpl) -{ - qsort(cpl->result.matches, cpl->result.nmatch, - sizeof(cpl->result.matches[0]), cpl_cmp_matches); -} - -/*....................................................................... - * This is a qsort() comparison function used to sort matches. - * - * Input: - * v1, v2 void * Pointers to the two matches to be compared. - * Output: - * return int -1 -> v1 < v2. - * 0 -> v1 == v2 - * 1 -> v1 > v2 - */ -static int cpl_cmp_matches(const void *v1, const void *v2) -{ - const CplMatch *m1 = (const CplMatch *) v1; - const CplMatch *m2 = (const CplMatch *) v2; - return strcmp(m1->completion, m2->completion); -} - -/*....................................................................... - * Sort the array of matches in order of their suffixes. - * - * Input: - * cpl WordCompletion * The completion resource object. - */ -static void cpl_sort_suffixes(WordCompletion *cpl) -{ - qsort(cpl->result.matches, cpl->result.nmatch, - sizeof(cpl->result.matches[0]), cpl_cmp_suffixes); -} - -/*....................................................................... - * This is a qsort() comparison function used to sort matches in order of - * their suffixes. - * - * Input: - * v1, v2 void * Pointers to the two matches to be compared. - * Output: - * return int -1 -> v1 < v2. - * 0 -> v1 == v2 - * 1 -> v1 > v2 - */ -static int cpl_cmp_suffixes(const void *v1, const void *v2) -{ - const CplMatch *m1 = (const CplMatch *) v1; - const CplMatch *m2 = (const CplMatch *) v2; - return strcmp(m1->suffix, m2->suffix); -} - -/*....................................................................... - * Find the common prefix of all of the matching completion matches, - * and record a pointer to it in cpl->result.suffix. Note that this has - * the side effect of sorting the matches into suffix order. - * - * Input: - * cpl WordCompletion * The completion resource object. - * Output: - * return int 0 - OK. - * 1 - Error. - */ -static int cpl_common_suffix(WordCompletion *cpl) -{ - CplMatches *result; /* The result container */ - const char *first, *last; /* The first and last matching suffixes */ - int length; /* The length of the common suffix */ -/* - * Get the container of the array of matching files. - */ - result = &cpl->result; -/* - * No matching completions? - */ - if(result->nmatch < 1) - return 0; -/* - * Sort th matches into suffix order. - */ - cpl_sort_suffixes(cpl); -/* - * Given that the array of matches is sorted, the first and last - * suffixes are those that differ most in their prefixes, so the common - * prefix of these strings is the longest common prefix of all of the - * suffixes. - */ - first = result->matches[0].suffix; - last = result->matches[result->nmatch - 1].suffix; -/* - * Find the point at which the first and last matching strings - * first difffer. - */ - while(*first && *first == *last) { - first++; - last++; - }; -/* - * How long is the common suffix? - */ - length = first - result->matches[0].suffix; -/* - * Allocate memory to record the common suffix. - */ - result->suffix = _sg_alloc_string(cpl->sg, length); - if(!result->suffix) { - strcpy(cpl->errmsg, - "Insufficient memory to record common completion suffix."); - return 1; - }; -/* - * Record the common suffix. - */ - strncpy(result->suffix, result->matches[0].suffix, length); - result->suffix[length] = '\0'; - return 0; -} - -/*....................................................................... - * Discard the contents of the array of possible completion matches. - * - * Input: - * cpl WordCompletion * The word-completion resource object. - */ -static void cpl_clear_completions(WordCompletion *cpl) -{ -/* - * Discard all of the strings. - */ - _clr_StringGroup(cpl->sg); -/* - * Record the fact that the array is now empty. - */ - cpl->result.nmatch = 0; - cpl->result.suffix = NULL; - cpl->result.cont_suffix = ""; -/* - * Also clear the error message. - */ - cpl->errmsg[0] = '\0'; - return; -} - -/*....................................................................... - * Given an input line and the point at which it completion is to be - * attempted, return an array of possible completions. - * - * Input: - * cpl WordCompletion * The completion resource object. - * line char * The current input line. - * word_end int The index of the character in line[] which - * follows the end of the token that is being - * completed. - * data void * Anonymous 'data' to be passed to match_fn(). - * match_fn CplMatchFn * The function that will identify the prefix - * to be completed from the input line, and - * record completion matches. - * Output: - * return CplMatches * The container of the array of possible - * completions. The returned pointer refers - * to a container owned by the parent WordCompletion - * object, and its contents thus potentially - * change on every call to cpl_matches(). - * On error, NULL is returned, and a description - * of the error can be acquired by calling - * cpl_last_error(cpl). - */ -CplMatches *cpl_complete_word(WordCompletion *cpl, const char *line, - int word_end, void *data, - CplMatchFn *match_fn) -{ - int line_len; /* The total length of the input line */ -/* - * How long is the input line? - */ - line_len = strlen(line); -/* - * Check the arguments. - */ - if(!cpl || !line || !match_fn || word_end < 0 || word_end > line_len) { - if(cpl) - strcpy(cpl->errmsg, "cpl_complete_word: Invalid arguments."); - return NULL; - }; -/* - * Clear the return container. - */ - cpl_clear_completions(cpl); -/* - * Have the matching function record possible completion matches in - * cpl->result.matches. - */ - if(match_fn(cpl, data, line, word_end)) { - if(cpl->errmsg[0] == '\0') - strcpy(cpl->errmsg, "Error completing word."); - return NULL; - }; -/* - * Record a copy of the common initial part of all of the prefixes - * in cpl->result.common. - */ - if(cpl_common_suffix(cpl)) - return NULL; -/* - * Sort the matches into lexicographic order. - */ - cpl_sort_matches(cpl); -/* - * Discard any duplicate matches. - */ - cpl_zap_duplicates(cpl); -/* - * If there is more than one match, discard the continuation suffix. - */ - if(cpl->result.nmatch > 1) - cpl->result.cont_suffix = ""; -/* - * Return the array of matches. - */ - return &cpl->result; -} - -/*....................................................................... - * Print out an array of matching completions. - * - * Input: - * result CplMatches * The container of the sorted array of - * completions. - * fp FILE * The output stream to write to. - * term_width int The width of the terminal. - * Output: - * return int 0 - OK. - * 1 - Error. - */ -int cpl_list_completions(CplMatches *result, FILE *fp, int term_width) -{ - int maxlen; /* The length of the longest matching string */ - int width; /* The width of a column */ - int ncol; /* The number of columns to list */ - int nrow; /* The number of rows needed to list all of the matches */ - int row,col; /* The row and column being written to */ - int i; -/* - * Check the arguments. - */ - if(!result || !fp) { - fprintf(stderr, "cpl_list_completions: NULL argument(s).\n"); - return 1; - }; -/* - * Not enough space to list anything? - */ - if(term_width < 1) - return 0; -/* - * Work out the maximum length of the matching strings. - */ - maxlen = 0; - for(i=0; inmatch; i++) { - CplMatch *match = result->matches + i; - int len = strlen(match->completion) + - strlen(match->type_suffix); - if(len > maxlen) - maxlen = len; - }; -/* - * Nothing to list? - */ - if(maxlen == 0) - return 0; -/* - * Split the available terminal width into columns of maxlen + 2 characters. - */ - width = maxlen + 2; - ncol = term_width / width; -/* - * If the column width is greater than the terminal width, the matches will - * just have to overlap onto the next line. - */ - if(ncol < 1) - ncol = 1; -/* - * How many rows will be needed? - */ - nrow = (result->nmatch + ncol - 1) / ncol; -/* - * Print the matches out in ncol columns, sorted in row order within each - * column. - */ - for(row=0; row < nrow; row++) { - for(col=0; col < ncol; col++) { - int m = col*nrow + row; - if(m < result->nmatch) { - CplMatch *match = result->matches + m; - if(fprintf(fp, "%s%-*s%s", match->completion, - (int) (ncol > 1 ? maxlen - strlen(match->completion):0), - match->type_suffix, colerrmsg : "NULL WordCompletion argument"; -} - -/*....................................................................... - * When an error occurs while performing a completion, you registerf a - * terse description of the error by calling cpl_record_error(). This - * message will then be returned on the next call to cpl_last_error(). - * - * Input: - * cpl WordCompletion * The string-completion resource object that was - * originally passed to the callback. - * errmsg const char * The description of the error. - */ -void cpl_record_error(WordCompletion *cpl, const char *errmsg) -{ - if(cpl && errmsg) { - strncpy(cpl->errmsg, errmsg, ERRLEN); - cpl->errmsg[ERRLEN] = '\0'; - }; -} - -/*....................................................................... - * This is the builtin completion callback function which performs file - * completion. - * - * Input: - * cpl WordCompletion * An opaque pointer to the object that will - * contain the matches. This should be filled - * via zero or more calls to cpl_add_completion(). - * data void * Either NULL to request the default - * file-completion behavior, or a pointer to a - * CplFileConf structure, whose members specify - * a different behavior. - * line char * The current input line. - * word_end int The index of the character in line[] which - * follows the end of the token that is being - * completed. - * Output - * return int 0 - OK. - * 1 - Error. - */ -CPL_MATCH_FN(cpl_file_completions) -{ - const char *start_path; /* The pointer to the start of the pathname */ - /* in line[]. */ - CplFileConf *conf; /* The new-style configuration object. */ -/* - * The following configuration object will be used if the caller didn't - * provide one. - */ - CplFileConf default_conf; -/* - * This function can be called externally, so check its arguments. - */ - if(!cpl) - return 1; - if(!line || word_end < 0) { - strcpy(cpl->errmsg, "cpl_file_completions: Invalid arguments."); - return 1; - }; -/* - * The 'data' argument is either a CplFileConf pointer, identifiable - * by having an integer id code as its first member, or the deprecated - * CplFileArgs pointer, or can be NULL to request the default - * configuration. - */ - if(data && *(int *)data == CFC_ID_CODE) { - conf = (CplFileConf *) data; - } else { -/* - * Select the defaults. - */ - conf = &default_conf; - cpl_init_FileConf(&default_conf); -/* - * If we have been passed an instance of the deprecated CplFileArgs - * structure, copy its configuration parameters over the defaults. - */ - if(data) { - CplFileArgs *args = (CplFileArgs *) data; - conf->escaped = args->escaped; - conf->file_start = args->file_start; - }; - }; -/* - * Get the start of the filename. If not specified by the caller - * identify it by searching backwards in the input line for an - * unescaped space or the start of the line. - */ - if(conf->file_start < 0) { - start_path = _pu_start_of_path(line, word_end); - if(!start_path) { - strcpy(cpl->errmsg, "Unable to find the start of the filename."); - return 1; - }; - } else { - start_path = line + conf->file_start; - }; -/* - * Perform the completion. - */ - if(_cf_complete_file(cpl, cpl->cf, line, start_path - line, word_end, - conf->escaped, conf->chk_fn, conf->chk_data)) { - cpl_record_error(cpl, _cf_last_error(cpl->cf)); - return 1; - }; - return 0; -} - -/*....................................................................... - * Initialize a CplFileArgs structure with default configuration - * parameters. Note that the CplFileArgs configuration type is - * deprecated. The opaque CplFileConf object should be used in future - * applications. - * - * Input: - * cfa CplFileArgs * The configuration object of the - * cpl_file_completions() callback. - */ -void cpl_init_FileArgs(CplFileArgs *cfa) -{ - if(cfa) { - cfa->escaped = 1; - cfa->file_start = -1; - }; -} - -/*....................................................................... - * Initialize a CplFileConf structure with default configuration - * parameters. - * - * Input: - * cfc CplFileConf * The configuration object of the - * cpl_file_completions() callback. - */ -static void cpl_init_FileConf(CplFileConf *cfc) -{ - if(cfc) { - cfc->id = CFC_ID_CODE; - cfc->escaped = 1; - cfc->file_start = -1; - cfc->chk_fn = 0; - cfc->chk_data = NULL; - }; -} - -/*....................................................................... - * Create a new CplFileConf object and initialize it with defaults. - * - * Output: - * return CplFileConf * The new object, or NULL on error. - */ -CplFileConf *new_CplFileConf(void) -{ - CplFileConf *cfc; /* The object to be returned */ -/* - * Allocate the container. - */ - cfc = (CplFileConf *)malloc(sizeof(CplFileConf)); - if(!cfc) - return NULL; -/* - * Before attempting any operation that might fail, initialize the - * container at least up to the point at which it can safely be passed - * to del_CplFileConf(). - */ - cpl_init_FileConf(cfc); - return cfc; -} - -/*....................................................................... - * Delete a CplFileConf object. - * - * Input: - * cfc CplFileConf * The object to be deleted. - * Output: - * return CplFileConf * The deleted object (always NULL). - */ -CplFileConf *del_CplFileConf(CplFileConf *cfc) -{ - if(cfc) { -/* - * Delete the container. - */ - free(cfc); - }; - return NULL; -} - -/*....................................................................... - * If backslashes in the filename should be treated as literal - * characters, call the following function with literal=1. Otherwise - * the default is to treat them as escape characters, used for escaping - * spaces etc.. - * - * Input: - * cfc CplFileConf * The cpl_file_completions() configuration object - * to be configured. - * literal int Pass non-zero here to enable literal interpretation - * of backslashes. Pass 0 to turn off literal - * interpretation. - */ -void cfc_literal_escapes(CplFileConf *cfc, int literal) -{ - if(cfc) - cfc->escaped = !literal; -} - -/*....................................................................... - * Call this function if you know where the index at which the - * filename prefix starts in the input line. Otherwise by default, - * or if you specify start_index to be -1, the filename is taken - * to start after the first unescaped space preceding the cursor, - * or the start of the line, which ever comes first. - * - * Input: - * cfc CplFileConf * The cpl_file_completions() configuration object - * to be configured. - * start_index int The index of the start of the filename in - * the input line, or -1 to select the default. - */ -void cfc_file_start(CplFileConf *cfc, int start_index) -{ - if(cfc) - cfc->file_start = start_index; -} - -/*....................................................................... - * If you only want certain types of files to be included in the - * list of completions, you use the following function to specify a - * callback function which will be called to ask whether a given file - * should be included. - * - * Input: - * cfc CplFileConf * The cpl_file_completions() configuration object - * to be configured. - * chk_fn CplCheckFn * Zero to disable filtering, or a pointer to a - * function that returns 1 if a given file should - * be included in the list of completions. - * chk_data void * Anonymous data to be passed to chk_fn() - * every time that it is called. - */ -void cfc_set_check_fn(CplFileConf *cfc, CplCheckFn *chk_fn, void *chk_data) -{ - if(cfc) { - cfc->chk_fn = chk_fn; - cfc->chk_data = chk_data; - }; -} - -/*....................................................................... - * The following CplCheckFn callback returns non-zero if the specified - * filename is that of an executable. - */ -CPL_CHECK_FN(cpl_check_exe) -{ - return _pu_path_is_exe(pathname); -} - -/*....................................................................... - * Remove duplicates from a sorted array of matches. - * - * Input: - * cpl WordCompletion * The completion resource object. - */ -static void cpl_zap_duplicates(WordCompletion *cpl) -{ - CplMatch *matches; /* The array of matches */ - int nmatch; /* The number of elements in matches[] */ - const char *completion; /* The completion string of the last unique match */ - const char *type_suffix; /* The type of the last unique match */ - int src; /* The index of the match being considered */ - int dst; /* The index at which to record the next */ - /* unique match. */ -/* - * Get the array of matches and the number of matches that it - * contains. - */ - matches = cpl->result.matches; - nmatch = cpl->result.nmatch; -/* - * No matches? - */ - if(nmatch < 1) - return; -/* - * Initialize the comparison strings with the first match. - */ - completion = matches[0].completion; - type_suffix = matches[0].type_suffix; -/* - * Go through the array of matches, copying each new unrecorded - * match at the head of the array, while discarding duplicates. - */ - for(src=dst=1; srccompletion) != 0 || - strcmp(type_suffix, match->type_suffix) != 0) { - if(src != dst) - matches[dst] = *match; - dst++; - completion = match->completion; - type_suffix = match->type_suffix; - }; - }; -/* - * Record the number of unique matches that remain. - */ - cpl->result.nmatch = dst; - return; -} diff --git a/libtecla-1.4.1/demo.c b/libtecla-1.4.1/demo.c deleted file mode 100644 index 8bee92d..0000000 --- a/libtecla-1.4.1/demo.c +++ /dev/null @@ -1,109 +0,0 @@ -/* - * Copyright (c) 2000, 2001 by the California Institute of Technology. - * - * All rights reserved. - * - * Permission is hereby granted, free of charge, to any person obtaining a - * copy of this software and associated documentation files (the - * "Software"), to deal in the Software without restriction, including - * without limitation the rights to use, copy, modify, merge, publish, - * distribute, and/or sell copies of the Software, and to permit persons - * to whom the Software is furnished to do so, provided that the above - * copyright notice(s) and this permission notice appear in all copies of - * the Software and that both the above copyright notice(s) and this - * permission notice appear in supporting documentation. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS - * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT - * OF THIRD PARTY RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR - * HOLDERS INCLUDED IN THIS NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL - * INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING - * FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, - * NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION - * WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - * - * Except as contained in this notice, the name of a copyright holder - * shall not be used in advertising or otherwise to promote the sale, use - * or other dealings in this Software without prior written authorization - * of the copyright holder. - */ - -#include -#include -#include -#include -#include -#include - -#include -#include - -#include "libtecla.h" - -int main(int argc, char *argv[]) -{ - char *line; /* A line of input */ - GetLine *gl; /* The line editor */ - int major,minor,micro; /* The version number of the library */ -/* - * Create the line editor, specifying a max line length of 500 bytes, - * and 10000 bytes to allocate to storage of historical input lines. - */ - gl = new_GetLine(500, 5000); - if(!gl) - return 1; -/* - * If the user has the LC_CTYPE or LC_ALL environment variables set, - * enable display of characters corresponding to the specified locale. - */ - (void) setlocale(LC_CTYPE, ""); -/* - * Lookup and display the version number of the library. - */ - libtecla_version(&major, &minor, µ); - printf("Welcome to the demo program of libtecla version %d.%d.%d\n", - major, minor, micro); -/* - * Load history. - */ - (void) gl_load_history(gl, "~/.demo_history", "#"); -/* - * Read lines of input from the user and print them to stdout. - */ - do { -/* - * Get a new line from the user. - */ - line = gl_get_line(gl, "$ ", NULL, 0); - if(!line) - break; -/* - * Display what was entered. - */ - if(printf("You entered: %s", line) < 0 || fflush(stdout)) - break; -/* - * If the user types "exit", quit the program. - */ - if(strcmp(line, "exit\n")==0) - break; - else if(strcmp(line, "history\n")==0) - gl_show_history(gl, stdout, "%N %T %H\n", 0, -1); - else if(strcmp(line, "size\n")==0) { - GlTerminalSize size = gl_terminal_size(gl, 80, 24); - printf("Terminal size = %d columns x %d lines.\n", size.ncolumn, - size.nline); - }; - } while(1); -/* - * Save historical command lines. - */ - (void) gl_save_history(gl, "~/.demo_history", "#", -1); -/* - * Clean up. - */ - gl = del_GetLine(gl); - return 0; -} - diff --git a/libtecla-1.4.1/demo2.c b/libtecla-1.4.1/demo2.c deleted file mode 100644 index e1e80c6..0000000 --- a/libtecla-1.4.1/demo2.c +++ /dev/null @@ -1,352 +0,0 @@ -/* - * Copyright (c) 2000, 2001 by Martin C. Shepherd. - * - * All rights reserved. - * - * Permission is hereby granted, free of charge, to any person obtaining a - * copy of this software and associated documentation files (the - * "Software"), to deal in the Software without restriction, including - * without limitation the rights to use, copy, modify, merge, publish, - * distribute, and/or sell copies of the Software, and to permit persons - * to whom the Software is furnished to do so, provided that the above - * copyright notice(s) and this permission notice appear in all copies of - * the Software and that both the above copyright notice(s) and this - * permission notice appear in supporting documentation. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS - * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT - * OF THIRD PARTY RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR - * HOLDERS INCLUDED IN THIS NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL - * INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING - * FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, - * NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION - * WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - * - * Except as contained in this notice, the name of a copyright holder - * shall not be used in advertising or otherwise to promote the sale, use - * or other dealings in this Software without prior written authorization - * of the copyright holder. - */ - -#include -#include -#include -#include -#include -#include - -#include -#include - -#include "libtecla.h" - -/* - * Encapsulate the resources needed by this demo. - */ -typedef struct { - GetLine *gl; /* The line editor */ - PathCache *pc; /* A cache of executables in the user's path */ - PcaPathConf *ppc; /* The configuration argument of pca_path_completions() */ -} DemoRes; - -/* - * The following functions allocate and free instances of the above - * structure. - */ -static DemoRes *new_DemoRes(void); -static DemoRes *del_DemoRes(DemoRes *res); - -/* - * Search backwards for the start of a pathname. - */ -static char *start_of_path(const char *string, int back_from); - -/* - * Find the array indexes of the first character of the first - * space-delimited word in the specified string, and of the character - * that follows it. - */ -static int get_word_limits(const char *string, int *wa, int *wb); - -/* - * This is the demonstration completion callback function (defined below). - */ -static CPL_MATCH_FN(demo_cpl_fn); - -/*....................................................................... - * This demo takes no arguments. It reads lines of input until the - * word 'exit' is entered, or C-d is pressed. It replaces the default - * tab-completion callback with one which when invoked at the start of - * a line, looks up completions of commands in the user's execution - * path, and when invoked in other parts of the line, reverts to - * normal filename completion. Whenever a new line is entered, it - * extracts the first word on the line, looks it up in the user's - * execution path to see if it corresponds to a known executable file, - * and if so, displays the full pathname of the file, along with the - * remaining arguments. - */ -int main(int argc, char *argv[]) -{ - char *line; /* A line of input */ - DemoRes *res; /* The resources of the demo */ - int wa,wb; /* The delimiting indexes of a word in line[] */ - int major,minor,micro; /* The version number of the library */ -/* - * Allocate the resources needed by this demo. - */ - res = new_DemoRes(); - if(!res) - return 1; -/* - * If the user has the LC_CTYPE or LC_ALL environment variables set, - * enable display of characters corresponding to the specified locale. - */ - (void) setlocale(LC_CTYPE, ""); -/* - * Lookup and display the version number of the library. - */ - libtecla_version(&major, &minor, µ); - printf("Welcome to the demo2 program of libtecla version %d.%d.%d\n", - major, minor, micro); -/* - * Read lines of input from the user and print them to stdout. - */ - do { -/* - * Get a new line from the user. - */ - line = gl_get_line(res->gl, "$ ", NULL, 0); - if(!line) - break; -/* - * Work out the extent of the first word in the input line, and - * try to identify this as a command in the path, displaying the - * full pathname of the match if found. - */ - if(get_word_limits(line, &wa, &wb) == 0) { - char *cmd = pca_lookup_file(res->pc, line + wa, wb-wa, 0); - if(cmd) { - printf("Command=%s\n", cmd); - printf("Arguments=%s", line+wb); - } else { - printf("Command not found\n"); - }; - }; -/* - * If the user types "exit", quit the program. - */ - if(strcmp(line, "exit\n")==0) - break; - } while(1); -/* - * Clean up. - */ - res = del_DemoRes(res); - return 0; -} - -/*....................................................................... - * This completion callback searches for completions of executables in - * the user's path when invoked on a word at the start of the path, and - * performs normal filename completion elsewhere. - */ -static CPL_MATCH_FN(demo_cpl_fn) -{ -/* - * Get the resource object that was passed to gl_customize_completion(). - */ - DemoRes *res = (DemoRes *) data; -/* - * Find the start of the filename prefix to be completed, searching - * backwards for the first unescaped space, or the start of the line. - */ - char *start = start_of_path(line, word_end); -/* - * Skip spaces preceding the start of the prefix. - */ - while(start > line && isspace((int)(unsigned char) start[-1])) - start--; -/* - * If the filename prefix is at the start of the line, attempt - * to complete the filename as a command in the path. Otherwise - * perform normal filename completion. - */ - return (start == line) ? - pca_path_completions(cpl, res->ppc, line, word_end) : - cpl_file_completions(cpl, NULL, line, word_end); -} - -/*....................................................................... - * Search backwards for the potential start of a filename. This - * looks backwards from the specified index in a given string, - * stopping at the first unescaped space or the start of the line. - * - * Input: - * string const char * The string to search backwards in. - * back_from int The index of the first character in string[] - * that follows the pathname. - * Output: - * return char * The pointer to the first character of - * the potential pathname, or NULL on error. - */ -static char *start_of_path(const char *string, int back_from) -{ - int i, j; -/* - * Search backwards from the specified index. - */ - for(i=back_from-1; i>=0; i--) { - int c = string[i]; -/* - * Stop on unescaped spaces. - */ - if(isspace((int)(unsigned char)c)) { -/* - * The space can't be escaped if we are at the start of the line. - */ - if(i==0) - break; -/* - * Find the extent of the escape characters which precedes the space. - */ - for(j=i-1; j>=0 && string[j]=='\\'; j--) - ; -/* - * If there isn't an odd number of escape characters before the space, - * then the space isn't escaped. - */ - if((i - 1 - j) % 2 == 0) - break; - }; - }; - return (char *)string + i + 1; -} - -/*....................................................................... - * Create a new DemoRes object containing the resources needed by the - * demo. - * - * Output: - * return DemoRes * The new object, or NULL on error. - */ -static DemoRes *new_DemoRes(void) -{ - DemoRes *res; /* The object to be returned */ -/* - * Allocate the container. - */ - res = (DemoRes *)malloc(sizeof(DemoRes)); - if(!res) { - fprintf(stderr, "new_DemoRes: Insufficient memory.\n"); - return NULL; - }; -/* - * Before attempting any operation that might fail, initialize the - * container at least up to the point at which it can safely be passed - * to del_DemoRes(). - */ - res->gl = NULL; - res->pc = NULL; - res->ppc = NULL; -/* - * Create the line editor, specifying a max line length of 500 bytes, - * and 10000 bytes to allocate to storage of historical input lines. - */ - res->gl = new_GetLine(500, 10000); - if(!res->gl) - return del_DemoRes(res); -/* - * Enable text attribute formatting directives in prompt strings. - */ - gl_prompt_style(res->gl, GL_FORMAT_PROMPT); -/* - * Allocate a cache of the executable files found in the user's path. - */ - res->pc = new_PathCache(); - if(!res->pc) - return del_DemoRes(res); -/* - * Populate the cache with the contents of the user's path. - */ - if(pca_scan_path(res->pc, getenv("PATH"))) - return del_DemoRes(res); -/* - * Arrange for susequent calls to pca_lookup_file() and pca_path_completions() - * to only report files that are executable by the user. - */ - pca_set_check_fn(res->pc, cpl_check_exe, NULL); -/* - * Allocate a configuration object for use with pca_path_completions(). - */ - res->ppc = new_PcaPathConf(res->pc); - if(!res->ppc) - return del_DemoRes(res); -/* - * Replace the builtin filename completion callback with one which - * searches for completions of executables in the user's path when - * invoked on a word at the start of the path, and completes files - * elsewhere. - */ - if(gl_customize_completion(res->gl, res, demo_cpl_fn)) - return del_DemoRes(res); - return res; -} - -/*....................................................................... - * Delete a DemoRes object. - * - * Input: - * res DemoRes * The object to be deleted. - * Output: - * return DemoRes * The deleted object (always NULL). - */ -static DemoRes *del_DemoRes(DemoRes *res) -{ - if(res) { - res->gl = del_GetLine(res->gl); - res->pc = del_PathCache(res->pc); - res->ppc = del_PcaPathConf(res->ppc); - free(res); - }; - return NULL; -} - -/*....................................................................... - * Return the limits of the word at the start of a given string, ignoring - * leading white-space, and interpretting the first unescaped space, tab or - * the end of the line, as the end of the word. - * - * Input: - * string const char * The string to tokenize. - * Input/Output: - * wa,wb int * The indexes of the first character of the word, - * and the character which follows the last - * character of the word, will be assigned to - * *wa and *wb, respectively. - * Output: - * return int 0 - A word was found. - * 1 - No word was found before the end of the - * string. - */ -static int get_word_limits(const char *string, int *wa, int *wb) -{ - int escaped = 0; /* True if the next character is escaped */ -/* - * Skip leading white-space. - */ - for(*wa=0; isspace((int)(unsigned char)string[*wa]); (*wa)++) - ; -/* - * Find the first unescaped space, stopping early if the end of the - * string is reached. - */ - for(*wb = *wa; ; (*wb)++) { - int c = string[*wb]; - if(c=='\\') - escaped = !escaped; - else if((!escaped && isspace((int)(unsigned char)c)) || c=='\0') - break; - }; - return *wa == *wb; -} diff --git a/libtecla-1.4.1/direader.c b/libtecla-1.4.1/direader.c deleted file mode 100644 index 8a81fbf..0000000 --- a/libtecla-1.4.1/direader.c +++ /dev/null @@ -1,299 +0,0 @@ -/* - * Copyright (c) 2000, 2001 by Martin C. Shepherd. - * - * All rights reserved. - * - * Permission is hereby granted, free of charge, to any person obtaining a - * copy of this software and associated documentation files (the - * "Software"), to deal in the Software without restriction, including - * without limitation the rights to use, copy, modify, merge, publish, - * distribute, and/or sell copies of the Software, and to permit persons - * to whom the Software is furnished to do so, provided that the above - * copyright notice(s) and this permission notice appear in all copies of - * the Software and that both the above copyright notice(s) and this - * permission notice appear in supporting documentation. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS - * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT - * OF THIRD PARTY RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR - * HOLDERS INCLUDED IN THIS NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL - * INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING - * FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, - * NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION - * WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - * - * Except as contained in this notice, the name of a copyright holder - * shall not be used in advertising or otherwise to promote the sale, use - * or other dealings in this Software without prior written authorization - * of the copyright holder. - */ - -/* - * Standard includes. - */ -#include -#include -#include -#include - -/* - * Operating system includes. - */ -#include -#include -#include -#include - -#include "direader.h" - -/* - * Use the reentrant POSIX threads version of readdir()? - */ -#if defined(_POSIX_C_SOURCE) && _POSIX_C_SOURCE >= 199506L -#define USE_READDIR_R 1 -#endif - -/* - * Set the max length of the error-reporting string. There is no point - * in this being longer than the width of a typical terminal window. - * In composing error messages, I have assumed that this number is - * at least 80, so you don't decrease it below this number. - */ -#define ERRLEN 200 - -/* - * Objects of the following type are used to maintain the resources - * needed to read directories. - */ -struct DirReader { - DIR *dir; /* The directory stream (if open, NULL otherwise) */ - struct dirent *file; /* The latest directory entry */ - char errmsg[ERRLEN+1]; /* Error-report buffer */ -#ifdef USE_READDIR_R - struct dirent *buffer; /* A buffer used by the threaded version of readdir */ - int buffer_dim; /* The allocated size of buffer[] */ -#endif -}; - -static int _dr_path_is_dir(const char *pathname); - -/*....................................................................... - * Create a new DirReader object. - * - * Output: - * return DirReader * The new object, or NULL on error. - */ -DirReader *_new_DirReader(void) -{ - DirReader *dr; /* The object to be returned */ -/* - * Allocate the container. - */ - dr = (DirReader *) malloc(sizeof(DirReader)); - if(!dr) { - fprintf(stderr, "_new_DirReader: Insufficient memory.\n"); - return NULL; - }; -/* - * Before attempting any operation that might fail, initialize the - * container at least up to the point at which it can safely be passed - * to _del_DirReader(). - */ - dr->dir = NULL; - dr->file = NULL; - dr->errmsg[0] = '\0'; -#ifdef USE_READDIR_R - dr->buffer = NULL; - dr->buffer_dim = 0; -#endif - return dr; -} - -/*....................................................................... - * Delete a DirReader object. - * - * Input: - * dr DirReader * The object to be deleted. - * Output: - * return DirReader * The deleted object (always NULL). - */ -DirReader *_del_DirReader(DirReader *dr) -{ - if(dr) { - _dr_close_dir(dr); -#ifdef USE_READDIR_R - free(dr->buffer); -#endif - free(dr); - }; - return NULL; -} - -/*....................................................................... - * Open a new directory. - * - * Input: - * dr DirReader * The directory reader resource object. - * path const char * The directory to be opened. - * Input/Output: - * errmsg char ** If an error occurs and errmsg isn't NULL, a - * pointer to an error description will be assigned - * to *errmsg. - * Output: - * return int 0 - OK. - * 1 - Error (see *errmsg for a description). - */ -int _dr_open_dir(DirReader *dr, const char *path, char **errmsg) -{ - DIR *dir = NULL; /* The directory stream */ -/* - * If a directory is already open, close it first. - */ - (void) _dr_close_dir(dr); -/* - * Is the path a directory? - */ - if(!_dr_path_is_dir(path)) { - if(errmsg) { - const char *fmt = "Can't open directory: %.*s\n"; - sprintf(dr->errmsg, fmt, ERRLEN - strlen(fmt), path); - *errmsg = dr->errmsg; - }; - return 1; - }; -/* - * Attempt to open the directory. - */ - dir = opendir(path); - if(!dir) { - if(errmsg) { - const char *fmt = "Can't open directory: %.*s\n"; - sprintf(dr->errmsg, fmt, ERRLEN - strlen(fmt), path); - *errmsg = dr->errmsg; - }; - return 1; - }; -/* - * If using POSIX threads, allocate a buffer for readdir_r(). - */ -#ifdef USE_READDIR_R - { - size_t size; - int name_max = pathconf(path, _PC_NAME_MAX); -#ifdef NAME_MAX - if(name_max < 0) - name_max = NAME_MAX; -#endif - if(name_max < 0) { - if(errmsg) { - strcpy(dr->errmsg, "Unable to deduce readdir() buffer size."); - *errmsg = dr->errmsg; - }; - closedir(dir); - return 1; - }; -/* - * How big a buffer do we need to allocate? - */ - size = sizeof(struct dirent) + name_max; -/* - * Extend the buffer? - */ - if(size > dr->buffer_dim || !dr->buffer) { - struct dirent *buffer = (struct dirent *) (dr->buffer ? - realloc(dr->buffer, size) : - malloc(size)); - if(!buffer) { - if(errmsg) { - strcpy(dr->errmsg, "Insufficient memory for readdir() buffer."); - *errmsg = dr->errmsg; - }; - closedir(dir); - return 1; - }; - dr->buffer = buffer; - dr->buffer_dim = size; - }; - }; -#endif -/* - * Record the successfully opened directory. - */ - dr->dir = dir; - return 0; -} - -/*....................................................................... - * If the DirReader object is currently contains an open directory, - * close it. - * - * Input: - * dr DirReader * The directory reader resource object. - */ -void _dr_close_dir(DirReader *dr) -{ - if(dr && dr->dir) { - closedir(dr->dir); - dr->dir = NULL; - dr->file = NULL; - dr->errmsg[0] = '\0'; - }; -} - -/*....................................................................... - * Read the next file from the directory opened with _dr_open_dir(). - * - * Input: - * dr DirReader * The directory reader resource object. - * Output: - * return char * The name of the new file, or NULL if we reached - * the end of the directory. - */ -char *_dr_next_file(DirReader *dr) -{ -/* - * Are we currently reading a directory? - */ - if(dr->dir) { -/* - * Read the next directory entry. - */ -#ifdef USE_READDIR_R - if(readdir_r(dr->dir, dr->buffer, &dr->file) == 0 && dr->file) - return dr->file->d_name; -#else - dr->file = readdir(dr->dir); - if(dr->file) - return dr->file->d_name; -#endif - }; -/* - * When the end of a directory is reached, close it. - */ - _dr_close_dir(dr); - return NULL; -} - -/*....................................................................... - * Return 1 if the specified pathname refers to a directory. - * - * Input: - * pathname const char * The path to test. - * Output: - * return int 0 - Not a directory. - * 1 - pathname[] refers to a directory. - */ -static int _dr_path_is_dir(const char *pathname) -{ - struct stat statbuf; /* The file-statistics return buffer */ -/* - * Look up the file attributes. - */ - if(stat(pathname, &statbuf) < 0) - return 0; -/* - * Is the file a directory? - */ - return S_ISDIR(statbuf.st_mode) != 0; -} diff --git a/libtecla-1.4.1/direader.h b/libtecla-1.4.1/direader.h deleted file mode 100644 index 2cf178e..0000000 --- a/libtecla-1.4.1/direader.h +++ /dev/null @@ -1,44 +0,0 @@ -#ifndef dirreader_h -#define dirreader_h - -/* - * Copyright (c) 2000, 2001 by Martin C. Shepherd. - * - * All rights reserved. - * - * Permission is hereby granted, free of charge, to any person obtaining a - * copy of this software and associated documentation files (the - * "Software"), to deal in the Software without restriction, including - * without limitation the rights to use, copy, modify, merge, publish, - * distribute, and/or sell copies of the Software, and to permit persons - * to whom the Software is furnished to do so, provided that the above - * copyright notice(s) and this permission notice appear in all copies of - * the Software and that both the above copyright notice(s) and this - * permission notice appear in supporting documentation. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS - * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT - * OF THIRD PARTY RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR - * HOLDERS INCLUDED IN THIS NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL - * INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING - * FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, - * NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION - * WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - * - * Except as contained in this notice, the name of a copyright holder - * shall not be used in advertising or otherwise to promote the sale, use - * or other dealings in this Software without prior written authorization - * of the copyright holder. - */ - -typedef struct DirReader DirReader; - -DirReader *_new_DirReader(void); -DirReader *_del_DirReader(DirReader *dr); - -int _dr_open_dir(DirReader *dr, const char *pathname, char **errmsg); -char *_dr_next_file(DirReader *dr); -void _dr_close_dir(DirReader *dr); - -#endif diff --git a/libtecla-1.4.1/enhance.c b/libtecla-1.4.1/enhance.c deleted file mode 100644 index 72f5061..0000000 --- a/libtecla-1.4.1/enhance.c +++ /dev/null @@ -1,689 +0,0 @@ -#include -#include -#include -#include -#include -#include - -#include -#include - -#include -#include -#include -#include -#include - -#if HAVE_SYSV_PTY -#include /* System-V stream I/O */ -char *ptsname(int fd); -int grantpt(int fd); -int unlockpt(int fd); -#endif - -#include "libtecla.h" - -/* - * Pseudo-terminal devices are found in the following directory. - */ -#define PTY_DEV_DIR "/dev/" - -/* - * Pseudo-terminal controller device file names start with the following - * prefix. - */ -#define PTY_CNTRL "pty" - -/* - * Pseudo-terminal slave device file names start with the following - * prefix. - */ -#define PTY_SLAVE "tty" - -/* - * Specify the maximum suffix length for the control and slave device - * names. - */ -#define PTY_MAX_SUFFIX 10 - -/* - * Set the maximum length of the master and slave terminal device filenames, - * including space for a terminating '\0'. - */ -#define PTY_MAX_NAME (sizeof(PTY_DEV_DIR)-1 + \ - (sizeof(PTY_SLAVE) > sizeof(PTY_CNTRL) ? \ - sizeof(PTY_SLAVE) : sizeof(PTY_CNTRL))-1 \ - + PTY_MAX_SUFFIX + 1) -/* - * Set the maximum length of an input line. - */ -#define PTY_MAX_LINE 4096 - -/* - * Set the size of the buffer used for accumulating bytes written by the - * user's terminal to its stdout. - */ -#define PTY_MAX_READ 1000 - -/* - * Set the amount of memory used to record history. - */ -#define PTY_HIST_SIZE 10000 - -/* - * Set the timeout delay used to check for quickly arriving - * sequential output from the application. - */ -#define PTY_READ_TIMEOUT 100000 /* micro-seconds */ - -static int pty_open_master(const char *prog, int *cntrl, char *slave_name); -static int pty_open_slave(const char *prog, char *slave_name); -static int pty_child(const char *prog, int slave, char *argv[]); -static int pty_parent(const char *prog, int cntrl); -static int pty_stop_parent(int waserr, int cntrl, GetLine *gl, char *rbuff); -static GL_FD_EVENT_FN(pty_read_from_program); -static int pty_write_to_fd(int fd, const char *string, int n); -static void pty_child_exited(int sig); -static int pty_master_readable(int fd, long usec); - -/*....................................................................... - * Run a program with enhanced terminal editing facilities. - * - * Usage: - * enhance program [args...] - */ -int main(int argc, char *argv[]) -{ - int cntrl = -1; /* The fd of the pseudo-terminal controller device */ - int slave = -1; /* The fd of the pseudo-terminal slave device */ - pid_t pid; /* The return value of fork() */ - int status; /* The return statuses of the parent and child functions */ - char slave_name[PTY_MAX_NAME]; /* The filename of the slave end of the */ - /* pseudo-terminal. */ - char *prog; /* The name of the program (ie. argv[0]) */ -/* - * Check the arguments. - */ - if(argc < 2) { - fprintf(stderr, "Usage: %s [arguments...]\n", argv[0]); - return 1; - }; -/* - * Get the name of the program. - */ - prog = argv[0]; -/* - * If the user has the LC_CTYPE or LC_ALL environment variables set, - * enable display of characters corresponding to the specified locale. - */ - (void) setlocale(LC_CTYPE, ""); -/* - * If the program is taking its input from a pipe or a file, or - * sending its output to something other than a terminal, run the - * program without tecla. - */ - if(!isatty(STDIN_FILENO) || !isatty(STDOUT_FILENO)) { - if(execvp(argv[1], argv + 1) < 0) { - fprintf(stderr, "%s: Unable to execute %s (%s).\n", prog, argv[1], - strerror(errno)); - fflush(stderr); - _exit(1); - }; - }; -/* - * Open the master side of a pseudo-terminal pair, and return - * the corresponding file descriptor and the filename of the - * slave end of the pseudo-terminal. - */ - if(pty_open_master(prog, &cntrl, slave_name)) - return 1; -/* - * Set up a signal handler to watch for the child process exiting. - */ - signal(SIGCHLD, pty_child_exited); -/* - * The above signal handler sends the parent process a SIGINT signal. - * This signal is caught by gl_get_line(), which resets the terminal - * settings, and if the application signal handler for this signal - * doesn't abort the process, gl_get_line() returns NULL with errno - * set to EINTR. Arrange to ignore the signal, so that gl_get_line() - * returns and we have a chance to cleanup. - */ - signal(SIGINT, SIG_IGN); -/* - * We will read user input in one process, and run the user's program - * in a child process. - */ - pid = fork(); - if(pid < 0) { - fprintf(stderr, "%s: Unable to fork child process (%s).\n", prog, - strerror(errno)); - return 1; - }; -/* - * Are we the parent? - */ - if(pid!=0) { - status = pty_parent(prog, cntrl); - close(cntrl); - } else { - close(cntrl); /* The child doesn't use the slave device */ - signal(SIGCHLD, pty_child_exited); - if((slave = pty_open_slave(prog, slave_name)) >= 0) { - status = pty_child(prog, slave, argv + 1); - close(slave); - } else { - status = 1; - }; - }; - return status; -} - -/*....................................................................... - * Open the master side of a pseudo-terminal pair, and return - * the corresponding file descriptor and the filename of the - * slave end of the pseudo-terminal. - * - * Input/Output: - * prog const char * The name of this program. - * cntrl int * The file descriptor of the pseudo-terminal - * controller device will be assigned tp *cntrl. - * slave_name char * The file-name of the pseudo-terminal slave device - * will be recorded in slave_name[], which must have - * at least PTY_MAX_NAME elements. - * Output: - * return int 0 - OK. - * 1 - Error. - */ -static int pty_open_master(const char *prog, int *cntrl, char *slave_name) -{ - char master_name[PTY_MAX_NAME]; /* The filename of the master device */ - DIR *dir; /* The directory iterator */ - struct dirent *file; /* A file in "/dev" */ -/* - * Mark the controller device as not opened yet. - */ - *cntrl = -1; -/* - * On systems with the Sys-V pseudo-terminal interface, we don't - * have to search for a free master terminal. We just open /dev/ptmx, - * and if there is a free master terminal device, we are given a file - * descriptor connected to it. - */ -#if HAVE_SYSV_PTY - *cntrl = open("/dev/ptmx", O_RDWR); - if(*cntrl >= 0) { -/* - * Get the filename of the slave side of the pseudo-terminal. - */ - char *name = ptsname(*cntrl); - if(name) { - if(strlen(name)+1 > PTY_MAX_NAME) { - fprintf(stderr, "%s: Slave pty filename too long.\n", prog); - return 1; - }; - strcpy(slave_name, name); -/* - * If unable to get the slave name, discard the controller file descriptor, - * ready to try a search instead. - */ - } else { - close(*cntrl); - *cntrl = -1; - }; - } else { -#endif -/* - * On systems without /dev/ptmx, or if opening /dev/ptmx failed, - * we open one master terminal after another, until one that isn't - * in use by another program is found. - * - * Open the devices directory. - */ - dir = opendir(PTY_DEV_DIR); - if(!dir) { - fprintf(stderr, "%s: Couldn't open %s (%s)\n", prog, PTY_DEV_DIR, - strerror(errno)); - return 1; - }; -/* - * Look for pseudo-terminal controller device files in the devices - * directory. - */ - while(*cntrl < 0 && (file = readdir(dir))) { - if(strncmp(file->d_name, PTY_CNTRL, sizeof(PTY_CNTRL)-1) == 0) { -/* - * Get the common extension of the control and slave filenames. - */ - const char *ext = file->d_name + sizeof(PTY_CNTRL)-1; - if(strlen(ext) > PTY_MAX_SUFFIX) - continue; -/* - * Attempt to open the control file. - */ - strcpy(master_name, PTY_DEV_DIR); - strcat(master_name, PTY_CNTRL); - strcat(master_name, ext); - *cntrl = open(master_name, O_RDWR); - if(*cntrl < 0) - continue; -/* - * Attempt to open the matching slave file. - */ - strcpy(slave_name, PTY_DEV_DIR); - strcat(slave_name, PTY_SLAVE); - strcat(slave_name, ext); - }; - }; - closedir(dir); -#if HAVE_SYSV_PTY - }; -#endif -/* - * Did we fail to find a pseudo-terminal pair that we could open? - */ - if(*cntrl < 0) { - fprintf(stderr, "%s: Unable to find a free pseudo-terminal.\n", prog); - return 1; - }; -/* - * System V systems require the program that opens the master to - * grant access to the slave side of the pseudo-terminal. - */ -#ifdef HAVE_SYSV_PTY - if(grantpt(*cntrl) < 0 || - unlockpt(*cntrl) < 0) { - fprintf(stderr, "%s: Unable to unlock terminal (%s).\n", prog, - strerror(errno)); - return 1; - }; -#endif -/* - * Success. - */ - return 0; -} - -/*....................................................................... - * Open the slave end of a pseudo-terminal. - * - * Input: - * prog const char * The name of this program. - * slave_name char * The filename of the slave device. - * Output: - * return int The file descriptor of the successfully opened - * slave device, or < 0 on error. - */ -static int pty_open_slave(const char *prog, char *slave_name) -{ - int fd; /* The file descriptor of the slave device */ -/* - * Place the process in its own process group. In system-V based - * OS's, this ensures that when the pseudo-terminal is opened, it - * becomes the controlling terminal of the process. - */ - if(setsid() < 0) { - fprintf(stderr, "%s: Unable to form new process group (%s).\n", prog, - strerror(errno)); - return -1; - }; -/* - * Attempt to open the specified device. - */ - fd = open(slave_name, O_RDWR); - if(fd < 0) { - fprintf(stderr, "%s: Unable to open pseudo-terminal slave device (%s).\n", - prog, strerror(errno)); - return -1; - }; -/* - * On system-V streams based systems, we need to push the stream modules - * that implement pseudo-terminal and termio interfaces. At least on - * Solaris, which pushes these automatically when a slave is opened, - * this is redundant, so ignore errors when pushing the modules. - */ -#if HAVE_SYSV_PTY - (void) ioctl(fd, I_PUSH, "ptem"); - (void) ioctl(fd, I_PUSH, "ldterm"); -/* - * On BSD based systems other than SunOS 4.x, the following makes the - * pseudo-terminal the controlling terminal of the child process. - * According to the pseudo-terminal example code in Steven's - * Advanced programming in the unix environment, the !defined(CIBAUD) - * part of the clause prevents this from being used under SunOS. Since - * I only have his code with me, and won't have access to the book, - * I don't know why this is necessary. - */ -#elif defined(TIOCSCTTY) && !defined(CIBAUD) - if(ioctl(fd, TIOCSCTTY, (char *) 0) < 0) { - fprintf(stderr, "%s: Unable to establish controlling terminal (%s).\n", - prog, strerror(errno)); - close(fd); - return -1; - }; -#endif - return fd; -} - -/*....................................................................... - * Read input from the controlling terminal of the program, using - * gl_get_line(), and feed it to the user's program running in a child - * process, via the controller side of the pseudo-terminal. Also pass - * data received from the user's program via the conroller end of - * the pseudo-terminal, to stdout. - * - * Input: - * prog const char * The name of this program. - * cntrl int The file descriptor of the controller end of the - * pseudo-terminal. - * Output: - * return int 0 - OK. - * 1 - Error. - */ -static int pty_parent(const char *prog, int cntrl) -{ - GetLine *gl = NULL; /* The gl_get_line() resource object */ - char *line; /* An input line read from the user */ - char *rbuff=NULL; /* A buffer for reading from the pseudo terminal */ -/* - * Allocate the gl_get_line() resource object. - */ - gl = new_GetLine(PTY_MAX_LINE, PTY_HIST_SIZE); - if(!gl) - return pty_stop_parent(1, cntrl, gl, rbuff); -/* - * Allocate a buffer to use to accumulate bytes read from the - * pseudo-terminal. - */ - rbuff = (char *) malloc(PTY_MAX_READ+1); - if(!rbuff) - return pty_stop_parent(1, cntrl, gl, rbuff); - rbuff[0] = '\0'; -/* - * Register an event handler to watch for data appearing from the - * user's program on the controller end of the pseudo terminal. - */ - if(gl_watch_fd(gl, cntrl, GLFD_READ, pty_read_from_program, rbuff)) - return pty_stop_parent(1, cntrl, gl, rbuff); -/* - * Read input lines from the user and pass them on to the user's program, - * by writing to the controller end of the pseudo-terminal. - */ - while((line=gl_get_line(gl, rbuff, NULL, 0))) { - if(pty_write_to_fd(cntrl, line, strlen(line))) - return pty_stop_parent(1, cntrl, gl, rbuff); - rbuff[0] = '\0'; - }; - return pty_stop_parent(0, cntrl, gl, rbuff); -} - -/*....................................................................... - * This is a private return function of pty_parent(), used to release - * dynamically allocated resources, close the controller end of the - * pseudo-terminal, and wait for the child to exit. It returns the - * exit status of the child process, unless the caller reports an - * error itself, in which case the caller's error status is returned. - * - * Input: - * waserr int True if the caller is calling this function because - * an error occured. - * cntrl int The file descriptor of the controller end of the - * pseudo-terminal. - * gl GetLine * The resource object of gl_get_line(). - * rbuff char * The buffer used to accumulate bytes read from - * the pseudo-terminal. - * Output: - * return int The desired exit status of the program. - */ -static int pty_stop_parent(int waserr, int cntrl, GetLine *gl, char *rbuff) -{ - int status; /* The return status of the child process */ -/* - * Close the controller end of the terminal. - */ - close(cntrl); -/* - * Delete the resource object. - */ - gl = del_GetLine(gl); -/* - * Delete the read buffer. - */ - if(rbuff) - free(rbuff); -/* - * Wait for the user's program to end. - */ - (void) wait(&status); -/* - * Return either our error status, or the return status of the child - * program. - */ - return waserr ? 1 : status; -} - -/*....................................................................... - * Run the user's program, with its stdin and stdout connected to the - * slave end of the psuedo-terminal. - * - * Input: - * prog const char * The name of this program. - * slave int The file descriptor of the slave end of the - * pseudo terminal. - * argv char *[] The argument vector to pass to the user's program, - * where argv[0] is the name of the user's program, - * and the last argument is followed by a pointer - * to NULL. - * Output: - * return int If this function returns at all, an error must - * have occured when trying to overlay the process - * with the user's program. In this case 1 is - * returned. - */ -static int pty_child(const char *prog, int slave, char *argv[]) -{ - struct termios attr; /* The terminal attributes */ -/* - * We need to stop the pseudo-terminal from echoing everything that we send it. - */ - if(tcgetattr(slave, &attr)) { - fprintf(stderr, "%s: Can't get pseudo-terminal attributes (%s).\n", prog, - strerror(errno)); - return 1; - }; - attr.c_lflag &= ~(ECHO); - while(tcsetattr(slave, TCSADRAIN, &attr)) { - if(errno != EINTR) { - fprintf(stderr, "%s: tcsetattr error: %s\n", prog, strerror(errno)); - return 1; - }; - }; -/* - * Arrange for stdin, stdout and stderr to be connected to the slave device, - * ignoring errors that imply that either stdin or stdout is closed. - */ - while(dup2(slave, STDIN_FILENO) < 0 && errno==EINTR) - ; - while(dup2(slave, STDOUT_FILENO) < 0 && errno==EINTR) - ; - while(dup2(slave, STDERR_FILENO) < 0 && errno==EINTR) - ; -/* - * Run the user's program. - */ - if(execvp(argv[0], argv) < 0) { - fprintf(stderr, "%s: Unable to execute %s (%s).\n", prog, argv[0], - strerror(errno)); - fflush(stderr); - _exit(1); - }; - return 0; /* This should never be reached */ -} - -/*....................................................................... - * This is the event-handler that is called by gl_get_line() whenever - * there is tet waiting to be read from the user's program, via the - * controller end of the pseudo-terminal. See libtecla.h for details - * about its arguments. - */ -static GL_FD_EVENT_FN(pty_read_from_program) -{ - char *nlptr; /* A pointer to the last newline in the accumulated string */ - char *crptr; /* A pointer to the last '\r' in the accumulated string */ - char *nextp; /* A pointer to the next unprocessed character */ -/* - * Get the read buffer in which we are accumulating a line to be - * forwarded to stdout. - */ - char *rbuff = (char *) data; -/* - * New data may arrive while we are processing the current read, and - * it is more efficient to display this here than to keep returning to - * gl_get_line() and have it display the latest prefix as a prompt, - * followed by the current input line, so we loop, delaying a bit at - * the end of each iteration to check for more data arriving from - * the application, before finally returning to gl_get_line() when - * no more input is available. - */ - do { -/* - * Get the current length of the output string. - */ - int len = strlen(rbuff); -/* - * Read the text from the program. - */ - int nnew = read(fd, rbuff + len, PTY_MAX_READ - len); - if(nnew < 0) - return GLFD_ABORT; - len += nnew; -/* - * Nul terminate the accumulated string. - */ - rbuff[len] = '\0'; -/* - * Find the last newline and last carriage return in the buffer, if any. - */ - nlptr = strrchr(rbuff, '\n'); - crptr = strrchr(rbuff, '\r'); -/* - * We want to output up to just before the last newline or carriage - * return. If there are no newlines of carriage returns in the line, - * and the buffer is full, then we should output the whole line. In - * all cases a new output line will be started after the latest text - * has been output. The intention is to leave any incomplete line - * in the buffer, for (perhaps temporary) use as the current prompt. - */ - if(nlptr) { - nextp = crptr && crptr < nlptr ? crptr : nlptr; - } else if(crptr) { - nextp = crptr; - } else if(len >= PTY_MAX_READ) { - nextp = rbuff + len; - } else { - nextp = NULL; - }; -/* - * Do we have any text to output yet? - */ - if(nextp) { -/* - * If there was already some text in rbuff before this function - * was called, then it will have been used as a prompt. Arrange - * to rewrite this prefix, plus the new suffix, by moving back to - * the start of the line. - */ - if(len > 0) - (void) pty_write_to_fd(STDOUT_FILENO, "\r", 1); -/* - * Write everything up to the last newline to stdout. - */ - (void) pty_write_to_fd(STDOUT_FILENO, rbuff, nextp - rbuff); -/* - * Start a new line. - */ - (void) pty_write_to_fd(STDOUT_FILENO, "\r\n", 2); -/* - * Skip trailing carriage returns and newlines. - */ - while(*nextp=='\n' || *nextp=='\r') - nextp++; -/* - * Move any unwritten text following the newline, to the start of the - * buffer. - */ - memmove(rbuff, nextp, len - (nextp - rbuff) + 1); - }; - } while(pty_master_readable(fd, PTY_READ_TIMEOUT)); -/* - * Make the incomplete line in the output buffer the current prompt. - */ - gl_replace_prompt(gl, rbuff); - return GLFD_REFRESH; -} - -/*....................................................................... - * Write a given string to a specified file descriptor. - * - * Input: - * fd int The file descriptor to write to. - * string const char * The string to write (of at least 'n' characters). - * n int The number of characters to write. - * Output: - * return int 0 - OK. - * 1 - Error. - */ -static int pty_write_to_fd(int fd, const char *string, int n) -{ - int ndone = 0; /* The number of characters written so far */ -/* - * Do as many writes as are needed to write the whole string. - */ - while(ndone < n) { - int nnew = write(fd, string + ndone, n - ndone); - if(nnew > 0) - ndone += nnew; - else if(errno != EINTR) - return 1; - }; - return 0; -} - -/*....................................................................... - * This is the signal handler that is called when the child process - * that is running the user's program exits for any reason. It closes - * the slave end of the terminal, so that gl_get_line() in the parent - * process sees an end of file. - */ -static void pty_child_exited(int sig) -{ - raise(SIGINT); -} - -/*....................................................................... - * Return non-zero after a given amount of time if there is data waiting - * to be read from a given file descriptor. - * - * Input: - * fd int The descriptor to watch. - * usec long The number of micro-seconds to wait for input to - * arrive before giving up. - * Output: - * return int 0 - No data is waiting to be read (or select isn't - * available). - * 1 - Data is waiting to be read. - */ -static int pty_master_readable(int fd, long usec) -{ -#if HAVE_SELECT - fd_set rfds; /* The set of file descriptors to check */ - struct timeval timeout; /* The timeout */ - FD_ZERO(&rfds); - FD_SET(fd, &rfds); - timeout.tv_sec = 0; - timeout.tv_usec = usec; - return select(fd+1, &rfds, NULL, NULL, &timeout) == 1; -#else - return 0; -#endif -} diff --git a/libtecla-1.4.1/expand.c b/libtecla-1.4.1/expand.c deleted file mode 100644 index c1600ab..0000000 --- a/libtecla-1.4.1/expand.c +++ /dev/null @@ -1,1265 +0,0 @@ -/* - * Copyright (c) 2000, 2001 by Martin C. Shepherd. - * - * All rights reserved. - * - * Permission is hereby granted, free of charge, to any person obtaining a - * copy of this software and associated documentation files (the - * "Software"), to deal in the Software without restriction, including - * without limitation the rights to use, copy, modify, merge, publish, - * distribute, and/or sell copies of the Software, and to permit persons - * to whom the Software is furnished to do so, provided that the above - * copyright notice(s) and this permission notice appear in all copies of - * the Software and that both the above copyright notice(s) and this - * permission notice appear in supporting documentation. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS - * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT - * OF THIRD PARTY RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR - * HOLDERS INCLUDED IN THIS NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL - * INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING - * FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, - * NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION - * WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - * - * Except as contained in this notice, the name of a copyright holder - * shall not be used in advertising or otherwise to promote the sale, use - * or other dealings in this Software without prior written authorization - * of the copyright holder. - */ - -#include -#include -#include -#include - -#include "freelist.h" -#include "direader.h" -#include "pathutil.h" -#include "homedir.h" -#include "stringrp.h" -#include "libtecla.h" - -/* - * Specify the number of elements to extend the files[] array by - * when it proves to be too small. This also sets the initial size - * of the array. - */ -#define MATCH_BLK_FACT 256 - -/* - * A list of directory iterators is maintained using nodes of the - * following form. - */ -typedef struct DirNode DirNode; -struct DirNode { - DirNode *next; /* The next directory in the list */ - DirNode *prev; /* The node that precedes this node in the list */ - DirReader *dr; /* The directory reader object */ -}; - -typedef struct { - FreeList *mem; /* Memory for DirNode list nodes */ - DirNode *head; /* The head of the list of used and unused cache nodes */ - DirNode *next; /* The next unused node between head and tail */ - DirNode *tail; /* The tail of the list of unused cache nodes */ -} DirCache; - -/* - * Specify how many directory cache nodes to allocate at a time. - */ -#define DIR_CACHE_BLK 20 - -/* - * Set the maximum length allowed for usernames. - */ -#define USR_LEN 100 - -/* - * Set the maximum length allowed for environment variable names. - */ -#define ENV_LEN 100 - -/* - * Set the max length of the error-reporting string. There is no point - * in this being longer than the width of a typical terminal window. - * In composing error messages, I have assumed that this number is - * at least 80, so you don't decrease it below this number. - */ -#define ERRLEN 200 - -struct ExpandFile { - StringGroup *sg; /* A list of string segments in which */ - /* matching filenames are stored. */ - DirCache cache; /* The cache of directory reader objects */ - PathName *path; /* The pathname being matched */ - HomeDir *home; /* Home-directory lookup object */ - int files_dim; /* The allocated dimension of result.files[] */ - char usrnam[USR_LEN+1]; /* A user name */ - char envnam[ENV_LEN+1]; /* An environment variable name */ - char errmsg[ERRLEN+1]; /* Error-report buffer */ - FileExpansion result; /* The container used to return the results of */ - /* expanding a path. */ -}; - -static int ef_record_pathname(ExpandFile *ef, const char *pathname, - int remove_escapes); -static char *ef_cache_pathname(ExpandFile *ef, const char *pathname, - int remove_escapes); -static void ef_clear_files(ExpandFile *ef); - -static DirNode *ef_open_dir(ExpandFile *ef, const char *pathname); -static DirNode *ef_close_dir(ExpandFile *ef, DirNode *node); -static char *ef_expand_special(ExpandFile *ef, const char *path, int pathlen); -static int ef_match_relative_pathname(ExpandFile *ef, DirReader *dr, - const char *pattern, int separate); -static int ef_matches_range(int c, const char *pattern, const char **endp); -static int ef_string_matches_pattern(const char *file, const char *pattern, - int xplicit, const char *nextp); -static int ef_cmp_strings(const void *v1, const void *v2); - -/*....................................................................... - * Create the resources needed to expand filenames. - * - * Output: - * return ExpandFile * The new object, or NULL on error. - */ -ExpandFile *new_ExpandFile(void) -{ - ExpandFile *ef; /* The object to be returned */ -/* - * Allocate the container. - */ - ef = (ExpandFile *) malloc(sizeof(ExpandFile)); - if(!ef) { - fprintf(stderr, "new_ExpandFile: Insufficient memory.\n"); - return NULL; - }; -/* - * Before attempting any operation that might fail, initialize the - * container at least up to the point at which it can safely be passed - * to del_ExpandFile(). - */ - ef->sg = NULL; - ef->cache.mem = NULL; - ef->cache.head = NULL; - ef->cache.next = NULL; - ef->cache.tail = NULL; - ef->path = NULL; - ef->home = NULL; - ef->result.files = NULL; - ef->result.nfile = 0; - ef->usrnam[0] = '\0'; - ef->envnam[0] = '\0'; - ef->errmsg[0] = '\0'; -/* - * Allocate a list of string segments for storing filenames. - */ - ef->sg = _new_StringGroup(_pu_pathname_dim()); - if(!ef->sg) - return del_ExpandFile(ef); -/* - * Allocate a freelist for allocating directory cache nodes. - */ - ef->cache.mem = _new_FreeList("new_ExpandFile", sizeof(DirNode), DIR_CACHE_BLK); - if(!ef->cache.mem) - return del_ExpandFile(ef); -/* - * Allocate a pathname buffer. - */ - ef->path = _new_PathName(); - if(!ef->path) - return del_ExpandFile(ef); -/* - * Allocate an object for looking up home-directories. - */ - ef->home = _new_HomeDir(); - if(!ef->home) - return del_ExpandFile(ef); -/* - * Allocate an array for files. This will be extended later if needed. - */ - ef->files_dim = MATCH_BLK_FACT; - ef->result.files = (char **) malloc(sizeof(ef->result.files[0]) * - ef->files_dim); - if(!ef->result.files) { - fprintf(stderr, - "new_ExpandFile: Insufficient memory to allocate array of files.\n"); - return del_ExpandFile(ef); - }; - return ef; -} - -/*....................................................................... - * Delete a ExpandFile object. - * - * Input: - * ef ExpandFile * The object to be deleted. - * Output: - * return ExpandFile * The deleted object (always NULL). - */ -ExpandFile *del_ExpandFile(ExpandFile *ef) -{ - if(ef) { - DirNode *dnode; -/* - * Delete the string segments. - */ - ef->sg = _del_StringGroup(ef->sg); -/* - * Delete the cached directory readers. - */ - for(dnode=ef->cache.head; dnode; dnode=dnode->next) - dnode->dr = _del_DirReader(dnode->dr); -/* - * Delete the memory from which the DirNode list was allocated, thus - * deleting the list at the same time. - */ - ef->cache.mem = _del_FreeList("del_ExpandFile", ef->cache.mem, 1); - ef->cache.head = ef->cache.tail = ef->cache.next = NULL; -/* - * Delete the pathname buffer. - */ - ef->path = _del_PathName(ef->path); -/* - * Delete the home-directory lookup object. - */ - ef->home = _del_HomeDir(ef->home); -/* - * Delete the array of pointers to files. - */ - if(ef->result.files) { - free(ef->result.files); - ef->result.files = NULL; - }; -/* - * Delete the container. - */ - free(ef); - }; - return NULL; -} - -/*....................................................................... - * Expand a pathname, converting ~user/ and ~/ patterns at the start - * of the pathname to the corresponding home directories, replacing - * $envvar with the value of the corresponding environment variable, - * and then, if there are any wildcards, matching these against existing - * filenames. - * - * If no errors occur, a container is returned containing the array of - * files that resulted from the expansion. If there were no wildcards - * in the input pathname, this will contain just the original pathname - * after expansion of ~ and $ expressions. If there were any wildcards, - * then the array will contain the files that matched them. Note that - * if there were any wildcards but no existing files match them, this - * is counted as an error and NULL is returned. - * - * The supported wildcards and their meanings are: - * * - Match any sequence of zero or more characters. - * ? - Match any single character. - * [chars] - Match any single character that appears in 'chars'. - * If 'chars' contains an expression of the form a-b, - * then any character between a and b, including a and b, - * matches. The '-' character looses its special meaning - * as a range specifier when it appears at the start - * of the sequence of characters. - * [^chars] - The same as [chars] except that it matches any single - * character that doesn't appear in 'chars'. - * - * Wildcard expressions are applied to individual filename components. - * They don't match across directory separators. A '.' character at - * the beginning of a filename component must also be matched - * explicitly by a '.' character in the input pathname, since these - * are UNIX's hidden files. - * - * Input: - * ef ExpandFile * The pathname expansion resource object. - * path char * The path name to be expanded. - * pathlen int The length of the suffix of path[] that - * constitutes the filename to be expanded, - * or -1 to specify that the whole of the - * path string should be used. Note that - * regardless of the value of this argument, - * path[] must contain a '\0' terminated - * string, since this function checks that - * pathlen isn't mistakenly too long. - * Output: - * return FileExpansion * A pointer to a container within the given - * ExpandFile object. This contains an array - * of the pathnames that resulted from expanding - * ~ and $ expressions and from matching any - * wildcards, sorted into lexical order. - * This container and its contents will be - * recycled on subsequent calls, so if you need - * to keep the results of two successive runs, - * you will either have to allocate a private - * copy of the array, or use two ExpandFile - * objects. - * - * On error NULL is returned. A description - * of the error can be acquired by calling the - * ef_last_error() function. - */ -FileExpansion *ef_expand_file(ExpandFile *ef, const char *path, int pathlen) -{ - DirNode *dnode; /* A directory-reader cache node */ - const char *dirname; /* The name of the top level directory of the search */ - const char *pptr; /* A pointer into path[] */ - int wild; /* True if the path contains any wildcards */ -/* - * Check the arguments. - */ - if(!ef || !path) { - if(ef) - strcpy(ef->errmsg, "ef_expand_file: NULL path argument"); - else - fprintf(stderr, "ef_expand_file: NULL argument(s).\n"); - return NULL; - }; -/* - * If the caller specified that the whole of path[] be matched, - * work out the corresponding length. - */ - if(pathlen < 0 || pathlen > strlen(path)) - pathlen = strlen(path); -/* - * Discard previous expansion results. - */ - ef_clear_files(ef); -/* - * Preprocess the path, expanding ~/, ~user/ and $envvar references, - * using ef->path as a work directory and returning a pointer to - * a copy of the resulting pattern in the cache. - */ - path = ef_expand_special(ef, path, pathlen); - if(!path) - return NULL; -/* - * Clear the pathname buffer. - */ - _pn_clear_path(ef->path); -/* - * Does the pathname contain any wildcards? - */ - for(wild=0,pptr=path; !wild && *pptr; pptr++) { - switch(*pptr) { - case '\\': /* Skip escaped characters */ - if(pptr[1]) - pptr++; - break; - case '*': case '?': case '[': /* A wildcard character? */ - wild = 1; - break; - }; - }; -/* - * If there are no wildcards to match, copy the current expanded - * path into the output array, removing backslash escapes while doing so. - */ - if(!wild) { - if(ef_record_pathname(ef, path, 1)) - return NULL; -/* - * Does the filename exist? - */ - ef->result.exists = _pu_file_exists(ef->result.files[0]); -/* - * Match wildcards against existing files. - */ - } else { -/* - * Only existing files that match the pattern will be returned in the - * cache. - */ - ef->result.exists = 1; -/* - * Treat matching of the root-directory as a special case since it - * isn't contained in a directory. - */ - if(strcmp(path, FS_ROOT_DIR) == 0) { - if(ef_record_pathname(ef, FS_ROOT_DIR, 0)) - return NULL; - } else { -/* - * What should the top level directory of the search be? - */ - if(strncmp(path, FS_ROOT_DIR, FS_ROOT_DIR_LEN) == 0) { - dirname = FS_ROOT_DIR; - if(!_pn_append_to_path(ef->path, FS_ROOT_DIR, -1, 0)) { - strcpy(ef->errmsg, "Insufficient memory to record path"); - return NULL; - }; - path += FS_ROOT_DIR_LEN; - } else { - dirname = FS_PWD; - }; -/* - * Open the top-level directory of the search. - */ - dnode = ef_open_dir(ef, dirname); - if(!dnode) - return NULL; -/* - * Recursively match successive directory components of the path. - */ - if(ef_match_relative_pathname(ef, dnode->dr, path, 0)) { - dnode = ef_close_dir(ef, dnode); - return NULL; - }; -/* - * Cleanup. - */ - dnode = ef_close_dir(ef, dnode); - }; -/* - * No files matched? - */ - if(ef->result.nfile < 1) { - strcpy(ef->errmsg, "No files match"); - return NULL; - }; -/* - * Sort the pathnames that matched. - */ - qsort(ef->result.files, ef->result.nfile, sizeof(ef->result.files[0]), - ef_cmp_strings); - }; -/* - * Return the result container. - */ - return &ef->result; -} - -/*....................................................................... - * Attempt to recursively match the given pattern with the contents of - * the current directory, descending sub-directories as needed. - * - * Input: - * ef ExpandFile * The pathname expansion resource object. - * dr DirReader * The directory reader object of the directory - * to be searched. - * pattern const char * The pattern to match with files in the current - * directory. - * separate int When appending a filename from the specified - * directory to ef->pathname, insert a directory - * separator between the existing pathname and - * the filename, unless separate is zero. - * Output: - * return int 0 - OK. - * 1 - Error. - */ -static int ef_match_relative_pathname(ExpandFile *ef, DirReader *dr, - const char *pattern, int separate) -{ - const char *nextp; /* The pointer to the character that follows the part */ - /* of the pattern that is to be matched with files */ - /* in the current directory. */ - char *file; /* The name of the file being matched */ - int pathlen; /* The length of ef->pathname[] on entry to this */ - /* function */ -/* - * Record the current length of the pathname string recorded in - * ef->pathname[]. - */ - pathlen = strlen(ef->path->name); -/* - * Get a pointer to the character that follows the end of the part of - * the pattern that should be matched to files within the current directory. - * This will either point to a directory separator, or to the '\0' terminator - * of the pattern string. - */ - for(nextp=pattern; *nextp && strncmp(nextp, FS_DIR_SEP, FS_DIR_SEP_LEN) != 0; - nextp++) - ; -/* - * Read each file from the directory, attempting to match it to the - * current pattern. - */ - while((file=_dr_next_file(dr)) != NULL) { -/* - * Does the latest file match the pattern up to nextp? - */ - if(ef_string_matches_pattern(file, pattern, file[0]=='.', nextp)) { -/* - * Append the new directory entry to the current matching pathname. - */ - if((separate && _pn_append_to_path(ef->path, FS_DIR_SEP, -1, 0)==NULL) || - _pn_append_to_path(ef->path, file, -1, 0)==NULL) { - strcpy(ef->errmsg, "Insufficient memory to record path"); - return 1; - }; -/* - * If we have reached the end of the pattern, record the accumulated - * pathname in the list of matching files. - */ - if(*nextp == '\0') { - if(ef_record_pathname(ef, ef->path->name, 0)) - return 1; -/* - * If the matching directory entry is a subdirectory, and the - * next character of the pattern is a directory separator, - * recursively call the current function to scan the sub-directory - * for matches. - */ - } else if(_pu_path_is_dir(ef->path->name) && - strncmp(nextp, FS_DIR_SEP, FS_DIR_SEP_LEN) == 0) { -/* - * If the pattern finishes with the directory separator, then - * record the pathame as matching. - */ - if(nextp[FS_DIR_SEP_LEN] == '\0') { - if(ef_record_pathname(ef, ef->path->name, 0)) - return 1; -/* - * Match files within the directory. - */ - } else { - DirNode *subdnode = ef_open_dir(ef, ef->path->name); - if(subdnode) { - if(ef_match_relative_pathname(ef, subdnode->dr, - nextp+FS_DIR_SEP_LEN, 1)) { - subdnode = ef_close_dir(ef, subdnode); - return 1; - }; - subdnode = ef_close_dir(ef, subdnode); - }; - }; - }; -/* - * Remove the latest filename from the pathname string, so that - * another matching file can be appended. - */ - ef->path->name[pathlen] = '\0'; - }; - }; - return 0; -} - -/*....................................................................... - * Record a new matching filename. - * - * Input: - * ef ExpandFile * The filename-match resource object. - * pathname const char * The pathname to record. - * remove_escapes int If true, remove backslash escapes in the - * recorded copy of the pathname. - * Output: - * return int 0 - OK. - * 1 - Error (ef->errmsg will contain a - * description of the error). - */ -static int ef_record_pathname(ExpandFile *ef, const char *pathname, - int remove_escapes) -{ - char *copy; /* The recorded copy of pathname[] */ -/* - * Attempt to make a copy of the pathname in the cache. - */ - copy = ef_cache_pathname(ef, pathname, remove_escapes); - if(!copy) - return 1; -/* - * If there isn't room to record a pointer to the recorded pathname in the - * array of files, attempt to extend the array. - */ - if(ef->result.nfile + 1 > ef->files_dim) { - int files_dim = ef->files_dim + MATCH_BLK_FACT; - char **files = (char **) realloc(ef->result.files, - files_dim * sizeof(files[0])); - if(!files) { - sprintf(ef->errmsg, - "Insufficient memory to record all of the matching filenames"); - return 1; - }; - ef->result.files = files; - ef->files_dim = files_dim; - }; -/* - * Record a pointer to the new match. - */ - ef->result.files[ef->result.nfile++] = copy; - return 0; -} - -/*....................................................................... - * Record a pathname in the cache. - * - * Input: - * ef ExpandFile * The filename-match resource object. - * pathname char * The pathname to record. - * remove_escapes int If true, remove backslash escapes in the - * copy of the pathname. - * Output: - * return char * The pointer to the copy of the pathname. - * On error NULL is returned and a description - * of the error is left in ef->errmsg[]. - */ -static char *ef_cache_pathname(ExpandFile *ef, const char *pathname, - int remove_escapes) -{ - char *copy = _sg_store_string(ef->sg, pathname, remove_escapes); - if(!copy) - strcpy(ef->errmsg, "Insufficient memory to store pathname"); - return copy; -} - -/*....................................................................... - * Clear the results of the previous expansion operation, ready for the - * next. - * - * Input: - * ef ExpandFile * The pathname expansion resource object. - */ -static void ef_clear_files(ExpandFile *ef) -{ - _clr_StringGroup(ef->sg); - _pn_clear_path(ef->path); - ef->result.exists = 0; - ef->result.nfile = 0; - ef->errmsg[0] = '\0'; - return; -} - -/*....................................................................... - * Get a new directory reader object from the cache. - * - * Input: - * ef ExpandFile * The pathname expansion resource object. - * pathname const char * The pathname of the directory. - * Output: - * return DirNode * The cache entry of the new directory reader, - * or NULL on error. On error, ef->errmsg will - * contain a description of the error. - */ -static DirNode *ef_open_dir(ExpandFile *ef, const char *pathname) -{ - char *errmsg = NULL; /* An error message from a called function */ - DirNode *node; /* The cache node used */ -/* - * Get the directory reader cache. - */ - DirCache *cache = &ef->cache; -/* - * Extend the cache if there are no free cache nodes. - */ - if(!cache->next) { - node = (DirNode *) _new_FreeListNode(cache->mem); - if(!node) { - sprintf(ef->errmsg, "Insufficient memory to open a new directory"); - return NULL; - }; -/* - * Initialize the cache node. - */ - node->next = NULL; - node->prev = NULL; - node->dr = NULL; -/* - * Allocate a directory reader object. - */ - node->dr = _new_DirReader(); - if(!node->dr) { - sprintf(ef->errmsg, "Insufficient memory to open a new directory"); - node = (DirNode *) _del_FreeListNode(cache->mem, node); - return NULL; - }; -/* - * Append the node to the cache list. - */ - node->prev = cache->tail; - if(cache->tail) - cache->tail->next = node; - else - cache->head = node; - cache->next = cache->tail = node; - }; -/* - * Get the first unused node, but don't remove it from the list yet. - */ - node = cache->next; -/* - * Attempt to open the specified directory. - */ - if(_dr_open_dir(node->dr, pathname, &errmsg)) { - strncpy(ef->errmsg, errmsg, ERRLEN); - ef->errmsg[ERRLEN] = '\0'; - return NULL; - }; -/* - * Now that we have successfully opened the specified directory, - * remove the cache node from the list, and relink the list around it. - */ - cache->next = node->next; - if(node->prev) - node->prev->next = node->next; - else - cache->head = node->next; - if(node->next) - node->next->prev = node->prev; - else - cache->tail = node->prev; - node->next = node->prev = NULL; -/* - * Return the successfully initialized cache node to the caller. - */ - return node; -} - -/*....................................................................... - * Return a directory reader object to the cache, after first closing - * the directory that it was managing. - * - * Input: - * ef ExpandFile * The pathname expansion resource object. - * node DirNode * The cache entry of the directory reader, as returned - * by ef_open_dir(). - * Output: - * return DirNode * The deleted DirNode (ie. allways NULL). - */ -static DirNode *ef_close_dir(ExpandFile *ef, DirNode *node) -{ -/* - * Get the directory reader cache. - */ - DirCache *cache = &ef->cache; -/* - * Close the directory. - */ - _dr_close_dir(node->dr); -/* - * Return the node to the tail of the cache list. - */ - node->next = NULL; - node->prev = cache->tail; - if(cache->tail) - cache->tail->next = node; - else - cache->head = cache->tail = node; - if(!cache->next) - cache->next = node; - return NULL; -} - -/*....................................................................... - * Return non-zero if the specified file name matches a given glob - * pattern. - * - * Input: - * file const char * The file-name component to be matched to the pattern. - * pattern const char * The start of the pattern to match against file[]. - * xplicit int If non-zero, the first character must be matched - * explicitly (ie. not with a wildcard). - * nextp const char * The pointer to the the character following the - * end of the pattern in pattern[]. - * Output: - * return int 0 - Doesn't match. - * 1 - The file-name string matches the pattern. - */ -static int ef_string_matches_pattern(const char *file, const char *pattern, - int xplicit, const char *nextp) -{ - const char *pptr = pattern; /* The pointer used to scan the pattern */ - const char *fptr = file; /* The pointer used to scan the filename string */ -/* - * Match each character of the pattern in turn. - */ - while(pptr < nextp) { -/* - * Handle the next character of the pattern. - */ - switch(*pptr) { -/* - * A match zero-or-more characters wildcard operator. - */ - case '*': -/* - * Skip the '*' character in the pattern. - */ - pptr++; -/* - * If wildcards aren't allowed, the pattern doesn't match. - */ - if(xplicit) - return 0; -/* - * If the pattern ends with a the '*' wildcard, then the - * rest of the filename matches this. - */ - if(pptr >= nextp) - return 1; -/* - * Using the wildcard to match successively longer sections of - * the remaining characters of the filename, attempt to match - * the tail of the filename against the tail of the pattern. - */ - for( ; *fptr; fptr++) { - if(ef_string_matches_pattern(fptr, pptr, 0, nextp)) - return 1; - }; - return 0; /* The pattern following the '*' didn't match */ - break; -/* - * A match-one-character wildcard operator. - */ - case '?': -/* - * If there is a character to be matched, skip it and advance the - * pattern pointer. - */ - if(!xplicit && *fptr) { - fptr++; - pptr++; -/* - * If we hit the end of the filename string, there is no character - * matching the operator, so the string doesn't match. - */ - } else { - return 0; - }; - break; -/* - * A character range operator, with the character ranges enclosed - * in matching square brackets. - */ - case '[': - if(xplicit || !ef_matches_range(*fptr++, ++pptr, &pptr)) - return 0; - break; -/* - * A backslash in the pattern prevents the following character as - * being seen as a special character. - */ - case '\\': - pptr++; - /* Note fallthrough to default */ -/* - * A normal character to be matched explicitly. - */ - default: - if(*fptr == *pptr) { - fptr++; - pptr++; - } else { - return 0; - }; - break; - }; -/* - * After passing the first character, turn off the explicit match - * requirement. - */ - xplicit = 0; - }; -/* - * To get here the pattern must have been exhausted. If the filename - * string matched, then the filename string must also have been - * exhausted. - */ - return *fptr == '\0'; -} - -/*....................................................................... - * Match a character range expression terminated by an unescaped close - * square bracket. - * - * Input: - * c int The character to be matched with the range - * pattern. - * pattern const char * The range pattern to be matched (ie. after the - * initiating '[' character). - * endp const char ** On output a pointer to the character following the - * range expression will be assigned to *endp. - * Output: - * return int 0 - Doesn't match. - * 1 - The character matched. - */ -static int ef_matches_range(int c, const char *pattern, const char **endp) -{ - const char *pptr = pattern; /* The pointer used to scan the pattern */ - int invert = 0; /* True to invert the sense of the match */ - int matched = 0; /* True if the character matched the pattern */ -/* - * If the first character is a caret, the sense of the match is - * inverted and only if the character isn't one of those in the - * range, do we say that it matches. - */ - if(*pptr == '^') { - pptr++; - invert = 1; - }; -/* - * The hyphen is only a special character when it follows the first - * character of the range (not including the caret). - */ - if(*pptr == '-') { - pptr++; - if(c == '-') { - *endp = pptr; - matched = 1; - }; -/* - * Skip other leading '-' characters since they make no sense. - */ - while(*pptr == '-') - pptr++; - }; -/* - * The hyphen is only a special character when it follows the first - * character of the range (not including the caret or a hyphen). - */ - if(*pptr == ']') { - pptr++; - if(c == ']') { - *endp = pptr; - matched = 1; - }; - }; -/* - * Having dealt with the characters that have special meanings at - * the beginning of a character range expression, see if the - * character matches any of the remaining characters of the range, - * up until a terminating ']' character is seen. - */ - while(!matched && *pptr && *pptr != ']') { -/* - * Is this a range of characters signaled by the two end characters - * separated by a hyphen? - */ - if(*pptr == '-') { - if(pptr[1] != ']') { - if(c >= pptr[-1] && c <= pptr[1]) - matched = 1; - pptr += 2; - }; -/* - * A normal character to be compared directly. - */ - } else if(*pptr++ == c) { - matched = 1; - }; - }; -/* - * Find the terminating ']'. - */ - while(*pptr && *pptr != ']') - pptr++; -/* - * Did we find a terminating ']'? - */ - if(*pptr == ']') { - *endp = pptr + 1; - return matched ? !invert : invert; - }; -/* - * If the pattern didn't end with a ']' then it doesn't match, regardless - * of the value of the required sense of the match. - */ - *endp = pptr; - return 0; -} - -/*....................................................................... - * This is a qsort() comparison function used to sort strings. - * - * Input: - * v1, v2 void * Pointers to the two strings to be compared. - * Output: - * return int -1 -> v1 < v2. - * 0 -> v1 == v2 - * 1 -> v1 > v2 - */ -static int ef_cmp_strings(const void *v1, const void *v2) -{ - char * const *s1 = (char * const *) v1; - char * const *s2 = (char * const *) v2; - return strcmp(*s1, *s2); -} - -/*....................................................................... - * Preprocess a path, expanding ~/, ~user/ and $envvar references, using - * ef->path as a work buffer, then copy the result into a cache entry, - * and return a pointer to this copy. - * - * Input: - * ef ExpandFile * The resource object of the file matcher. - * pathlen int The length of the prefix of path[] to be expanded. - * Output: - * return char * A pointer to a copy of the output path in the - * cache. On error NULL is returned, and a description - * of the error is left in ef->errmsg[]. - */ -static char *ef_expand_special(ExpandFile *ef, const char *path, int pathlen) -{ - int spos; /* The index of the start of the path segment that needs */ - /* to be copied from path[] to the output pathname. */ - int ppos; /* The index of a character in path[] */ - char *pptr; /* A pointer into the output path */ - int escaped; /* True if the previous character was a '\' */ - int i; -/* - * Clear the pathname buffer. - */ - _pn_clear_path(ef->path); -/* - * We need to perform two passes, one to expand environment variables - * and a second to do tilde expansion. This caters for the case - * where an initial dollar expansion yields a tilde expression. - */ - escaped = 0; - for(spos=ppos=0; ppos < pathlen; ppos++) { - int c = path[ppos]; - if(escaped) { - escaped = 0; - } else if(c == '\\') { - escaped = 1; - } else if(c == '$') { - int envlen; /* The length of the environment variable */ - char *value; /* The value of the environment variable */ -/* - * Record the preceding unrecorded part of the pathname. - */ - if(spos < ppos && _pn_append_to_path(ef->path, path + spos, ppos-spos, 0) - == NULL) { - strcpy(ef->errmsg, "Insufficient memory to expand path"); - return NULL; - }; -/* - * Skip the dollar. - */ - ppos++; -/* - * Copy the environment variable name that follows the dollar into - * ef->envnam[], stopping if a directory separator or end of string - * is seen. - */ - for(envlen=0; envlenenvnam[envlen] = path[ppos++]; -/* - * If the username overflowed the buffer, treat it as invalid (note that - * on most unix systems only 8 characters are allowed in a username, - * whereas our ENV_LEN is much bigger than that. - */ - if(envlen >= ENV_LEN) { - strcpy(ef->errmsg, "Environment variable name too long"); - return NULL; - }; -/* - * Terminate the environment variable name. - */ - ef->envnam[envlen] = '\0'; -/* - * Lookup the value of the environment variable. - */ - value = getenv(ef->envnam); - if(!value) { - const char *fmt = "No expansion found for: $%.*s"; - sprintf(ef->errmsg, fmt, ERRLEN - strlen(fmt), ef->envnam); - return NULL; - }; -/* - * Copy the value of the environment variable into the output pathname. - */ - if(_pn_append_to_path(ef->path, value, -1, 0) == NULL) { - strcpy(ef->errmsg, "Insufficient memory to expand path"); - return NULL; - }; -/* - * Record the start of the uncopied tail of the input pathname. - */ - spos = ppos; - }; - }; -/* - * Record the uncopied tail of the pathname. - */ - if(spos < ppos && _pn_append_to_path(ef->path, path + spos, ppos-spos, 0) - == NULL) { - strcpy(ef->errmsg, "Insufficient memory to expand path"); - return NULL; - }; -/* - * If the first character of the resulting pathname is a tilde, - * then attempt to substitute the home directory of the specified user. - */ - pptr = ef->path->name; - if(*pptr == '~' && path[0] != '\\') { - int usrlen; /* The length of the username following the tilde */ - const char *homedir; /* The home directory of the user */ - int homelen; /* The length of the home directory string */ - int plen; /* The current length of the path */ - int skip=0; /* The number of characters to skip after the ~user */ -/* - * Get the current length of the output path. - */ - plen = strlen(ef->path->name); -/* - * Skip the tilde. - */ - pptr++; -/* - * Copy the optional username that follows the tilde into ef->usrnam[]. - */ - for(usrlen=0; usrlenusrnam[usrlen] = *pptr++; -/* - * If the username overflowed the buffer, treat it as invalid (note that - * on most unix systems only 8 characters are allowed in a username, - * whereas our USR_LEN is much bigger than that. - */ - if(usrlen >= USR_LEN) { - strcpy(ef->errmsg, "Username too long"); - return NULL; - }; -/* - * Terminate the username string. - */ - ef->usrnam[usrlen] = '\0'; -/* - * Lookup the home directory of the user. - */ - homedir = _hd_lookup_home_dir(ef->home, ef->usrnam); - if(!homedir) { - strncpy(ef->errmsg, _hd_last_home_dir_error(ef->home), ERRLEN); - ef->errmsg[ERRLEN] = '\0'; - return NULL; - }; - homelen = strlen(homedir); -/* - * ~user and ~ are usually followed by a directory separator to - * separate them from the file contained in the home directory. - * If the home directory is the root directory, then we don't want - * to follow the home directory by a directory separator, so we must - * erase it. - */ - if(strcmp(homedir, FS_ROOT_DIR) == 0 && - strncmp(pptr, FS_DIR_SEP, FS_DIR_SEP_LEN) == 0) { - skip = FS_DIR_SEP_LEN; - }; -/* - * If needed, increase the size of the pathname buffer to allow it - * to accomodate the home directory instead of the tilde expression. - * Note that pptr may not be valid after this call. - */ - if(_pn_resize_path(ef->path, plen - usrlen - 1 - skip + homelen)==NULL) { - strcpy(ef->errmsg, "Insufficient memory to expand filename"); - return NULL; - }; -/* - * Move the part of the pathname that follows the tilde expression to - * the end of where the home directory will need to be inserted. - */ - memmove(ef->path->name + homelen, - ef->path->name + 1 + usrlen + skip, plen - usrlen - 1 - skip+1); -/* - * Write the home directory at the beginning of the string. - */ - for(i=0; ipath->name[i] = homedir[i]; - }; -/* - * Copy the result into the cache, and return a pointer to the copy. - */ - return ef_cache_pathname(ef, ef->path->name, 0); -} - -/*....................................................................... - * Return a description of the last path-expansion error that occurred. - * - * Input: - * ef ExpandFile * The path-expansion resource object. - * Output: - * return char * The description of the last error. - */ -const char *ef_last_error(ExpandFile *ef) -{ - return ef ? ef->errmsg : "NULL ExpandFile argument"; -} - -/*....................................................................... - * Print out an array of matching files. - * - * Input: - * result FileExpansion * The container of the sorted array of - * expansions. - * fp FILE * The output stream to write to. - * term_width int The width of the terminal. - * Output: - * return int 0 - OK. - * 1 - Error. - */ -int ef_list_expansions(FileExpansion *result, FILE *fp, int term_width) -{ - int maxlen; /* The length of the longest matching string */ - int width; /* The width of a column */ - int ncol; /* The number of columns to list */ - int nrow; /* The number of rows needed to list all of the expansions */ - int row,col; /* The row and column being written to */ - int i; -/* - * Check the arguments. - */ - if(!result || !fp) { - fprintf(stderr, "ef_list_expansions: NULL argument(s).\n"); - return 1; - }; -/* - * Not enough space to list anything? - */ - if(term_width < 1) - return 0; -/* - * Work out the maximum length of the matching filenames. - */ - maxlen = 0; - for(i=0; infile; i++) { - int len = strlen(result->files[i]); - if(len > maxlen) - maxlen = len; - }; -/* - * Nothing to list? - */ - if(maxlen == 0) - return 0; -/* - * Split the available terminal width into columns of maxlen + 2 characters. - */ - width = maxlen + 2; - ncol = term_width / width; -/* - * If the column width is greater than the terminal width, the matches will - * just have to overlap onto the next line. - */ - if(ncol < 1) - ncol = 1; -/* - * How many rows will be needed? - */ - nrow = (result->nfile + ncol - 1) / ncol; -/* - * Print the expansions out in ncol columns, sorted in row order within each - * column. - */ - for(row=0; row < nrow; row++) { - for(col=0; col < ncol; col++) { - int m = col*nrow + row; - if(m < result->nfile) { - const char *filename = result->files[m]; - if(fprintf(fp, "%s%-*s%s", filename, - (int) (ncol > 1 ? maxlen - strlen(filename):0), "", - col -#include - -#include "freelist.h" - -typedef struct FreeListBlock FreeListBlock; -struct FreeListBlock { - FreeListBlock *next; /* The next block in the list */ - char *nodes; /* The array of free-list nodes */ -}; - -struct FreeList { - size_t node_size; /* The size of a free-list node */ - unsigned blocking_factor; /* The number of nodes per block */ - long nbusy; /* The number of nodes that are in use */ - FreeListBlock *block; /* The head of the list of free-list blocks */ - void *free_list; /* The free-list of nodes */ -}; - -static FreeListBlock *_new_FreeListBlock(FreeList *fl); -static FreeListBlock *_del_FreeListBlock(FreeListBlock *fl); -static void _thread_FreeListBlock(FreeList *fl, FreeListBlock *block); - -/*....................................................................... - * Allocate a new free-list from blocks of 'blocking_factor' objects of size - * node_size. - * - * Input: - * caller const char * The name of the calling function, for use in - * error messages, or NULL to not report errors - * to stderr. - * node_size size_t The size of the free-list nodes to be returned - * by _new_FreeListNode(). Use sizeof() to - * determine this. - * blocking_factor unsigned The number of objects of size 'object_size' - * to allocate per block. - * Output: - * return FreeList * The new freelist, or NULL on error. - */ -FreeList *_new_FreeList(const char *caller, size_t node_size, - unsigned blocking_factor) -{ - FreeList *fl; /* The new free-list container */ -/* - * When a free-list node is on the free-list, it is used as a (void *) - * link field. Roundup node_size to a mulitple of the size of a void - * pointer. This, plus the fact that the array of nodes is obtained via - * malloc, which returns memory suitably aligned for any object, will - * ensure that the first sizeof(void *) bytes of each node will be - * suitably aligned to use as a (void *) link pointer. - */ - node_size = sizeof(void *) * - ((node_size + sizeof(void *) - 1) / sizeof(void *)); -/* - * Enfore a minimum block size. - */ - if(blocking_factor < 1) - blocking_factor = 1; -/* - * Allocate the container of the free list. - */ - fl = (FreeList *) malloc(sizeof(FreeList)); - if(!fl) { - if(caller) - fprintf(stderr, "_new_FreeList (%s): Insufficient memory.\n", caller); - return NULL; - }; -/* - * Before attempting any operation that might fail, initialize the - * container at least up to the point at which it can safely be passed - * to _del_FreeList(). - */ - fl->node_size = node_size; - fl->blocking_factor = blocking_factor; - fl->nbusy = 0; - fl->block = NULL; - fl->free_list = NULL; -/* - * Allocate the first block of memory. - */ - fl->block = _new_FreeListBlock(fl); - if(!fl->block) { - if(caller) - fprintf(stderr, "_new_FreeList (%s): Insufficient memory.\n", caller); - return _del_FreeList(caller, fl, 1); - }; -/* - * Add the new list of nodes to the free-list. - */ - fl->free_list = fl->block->nodes; -/* - * Return the free-list for use. - */ - return fl; -} - -/*....................................................................... - * Re-thread a freelist to reclaim all allocated nodes. - * This function should not be called unless if it is known that none - * of the currently allocated nodes are still being used. - * - * Input: - * fl FreeList * The free-list to be reset, or NULL. - */ -void _rst_FreeList(FreeList *fl) -{ - if(fl) { - FreeListBlock *block; -/* - * Re-thread the nodes of each block into individual free-lists. - */ - for(block=fl->block; block; block=block->next) - _thread_FreeListBlock(fl, block); -/* - * Link all of the block freelists into one large freelist. - */ - fl->free_list = NULL; - for(block=fl->block; block; block=block->next) { -/* - * Locate the last node of the current block. - */ - char *last_node = block->nodes + fl->node_size * - (fl->blocking_factor - 1); -/* - * Make the link-field of the last node point to the first - * node of the current freelist, then make the first node of the - * new block the start of the freelist. - */ - *(void **)last_node = fl->free_list; - fl->free_list = block->nodes; - }; -/* - * All allocated nodes have now been returned to the freelist. - */ - fl->nbusy = 0; - }; -} - -/*....................................................................... - * Delete a free-list. - * - * Input: - * caller const char * The name of the calling function, for use in - * error messages, or NULL if error messages - * shouldn't be reported to stderr. - * fl FreeList * The free-list to be deleted, or NULL. - * force int If force==0 then _del_FreeList() will complain - * and refuse to delete the free-list if any - * of nodes have not been returned to the free-list. - * If force!=0 then _del_FreeList() will not check - * whether any nodes are still in use and will - * always delete the list. - * Output: - * return FreeList * Always NULL (even if the list couldn't be - * deleted). - */ -FreeList *_del_FreeList(const char *caller, FreeList *fl, int force) -{ - if(fl) { -/* - * Check whether any nodes are in use. - */ - if(!force && _busy_FreeListNodes(fl) != 0) { - if(caller) - fprintf(stderr, "_del_FreeList (%s): %ld nodes are still in use.\n", - caller, _busy_FreeListNodes(fl)); - return NULL; - }; -/* - * Delete the list blocks. - */ - { - FreeListBlock *next = fl->block; - while(next) { - FreeListBlock *block = next; - next = block->next; - block = _del_FreeListBlock(block); - }; - }; - fl->block = NULL; - fl->free_list = NULL; -/* - * Discard the container. - */ - free(fl); - }; - return NULL; -} - -/*....................................................................... - * Allocate a new object from a free-list. - * - * Input: - * fl FreeList * The free-list to return an object from. - * Output: - * return void * A new object of the size that was specified via - * the node_size argument of _new_FreeList() when - * the free-list was created, or NULL if there - * is insufficient memory, or 'fl' is NULL. - */ -void *_new_FreeListNode(FreeList *fl) -{ - void *node; /* The node to be returned */ -/* - * Check arguments. - */ - if(!fl) - return NULL; -/* - * If the free-list has been exhausted extend it by allocating - * another block of nodes. - */ - if(!fl->free_list) { - FreeListBlock *block = _new_FreeListBlock(fl); - if(!block) - return NULL; -/* - * Prepend the new block to the list of free-list blocks. - */ - block->next = fl->block; - fl->block = block; -/* - * Add the new list of nodes to the free-list. - */ - fl->free_list = fl->block->nodes; - }; -/* - * Remove and return a node from the front of the free list. - */ - node = fl->free_list; - fl->free_list = *(void **)node; -/* - * Record the loss of a node from the free-list. - */ - fl->nbusy++; -/* - * Return the node. - */ - return node; -} - -/*....................................................................... - * Return an object to the free-list that it was allocated from. - * - * Input: - * caller const char * The name of the calling function, for use in - * error messages, or NULL to not report errors - * to stderr. - * fl FreeList * The free-list from which the object was taken. - * object void * The node to be returned. - * Output: - * return void * Always NULL. - */ -void *_del_FreeListNode(FreeList *fl, void *object) -{ -/* - * Check arguments. - */ - if(!fl) - return NULL; -/* - * Return the node to the head of the free list. - */ - if(object) { - *(void **)object = fl->free_list; - fl->free_list = object; -/* - * Record the return of the node to the free-list. - */ - fl->nbusy--; - }; - return NULL; -} - -/*....................................................................... - * Return a count of the number of nodes that are currently allocated. - * - * Input: - * fl FreeList * The list to count wrt, or NULL. - * Output: - * return long The number of nodes (or 0 if fl==NULL). - */ -long _busy_FreeListNodes(FreeList *fl) -{ - return fl ? fl->nbusy : 0; -} - -/*....................................................................... - * Allocate a new list of free-list nodes. On return the nodes will - * be linked together as a list starting with the node at the lowest - * address and ending with a NULL next pointer. - * - * Input: - * fl FreeList * The free-list to allocate the list for. - * Output: - * return FreeListBlock * The new linked block of free-list nodes, - * or NULL on error. - */ -static FreeListBlock *_new_FreeListBlock(FreeList *fl) -{ - FreeListBlock *block; /* The new block to be returned */ -/* - * Allocate the container. - */ - block = (FreeListBlock *) malloc(sizeof(FreeListBlock)); - if(!block) - return NULL; -/* - * Before attempting any operation that might fail, initialize the - * container at least up to the point at which it can safely be passed - * to _del_FreeListBlock(). - */ - block->next = NULL; - block->nodes = NULL; -/* - * Allocate the block of nodes. - */ - block->nodes = (char *) malloc(fl->node_size * fl->blocking_factor); - if(!block->nodes) - return _del_FreeListBlock(block); -/* - * Initialize the block as a linked list of FreeListNode's. - */ - _thread_FreeListBlock(fl, block); - return block; -} - -/*....................................................................... - * Link each node of a freelist block to the node that follows it. - * - * Input: - * fl FreeList * The freelist that contains the block. - * block FreeListBlock * The block to be threaded. - */ -static void _thread_FreeListBlock(FreeList *fl, FreeListBlock *block) -{ - char *mem = block->nodes; - int i; - for(i=0; iblocking_factor - 1; i++, mem += fl->node_size) - *(void **)mem = mem + fl->node_size; /* Link to the next node */ - *(void **)mem = NULL; /* Terminate the list */ -} - -/*....................................................................... - * Delete a free-list block. - * - * Input: - * fl FreeListBlock * The block to be deleted, or NULL. - * Output: - * return FreeListBlock * Always NULL. - */ -static FreeListBlock *_del_FreeListBlock(FreeListBlock *fl) -{ - if(fl) { - fl->next = NULL; - if(fl->nodes) - free(fl->nodes); - fl->nodes = NULL; - free(fl); - }; - return NULL; -} diff --git a/libtecla-1.4.1/freelist.h b/libtecla-1.4.1/freelist.h deleted file mode 100644 index 84e6aef..0000000 --- a/libtecla-1.4.1/freelist.h +++ /dev/null @@ -1,82 +0,0 @@ -#ifndef freelist_h -#define freelist_h - -/* - * Copyright (c) 2000, 2001 by Martin C. Shepherd. - * - * All rights reserved. - * - * Permission is hereby granted, free of charge, to any person obtaining a - * copy of this software and associated documentation files (the - * "Software"), to deal in the Software without restriction, including - * without limitation the rights to use, copy, modify, merge, publish, - * distribute, and/or sell copies of the Software, and to permit persons - * to whom the Software is furnished to do so, provided that the above - * copyright notice(s) and this permission notice appear in all copies of - * the Software and that both the above copyright notice(s) and this - * permission notice appear in supporting documentation. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS - * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT - * OF THIRD PARTY RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR - * HOLDERS INCLUDED IN THIS NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL - * INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING - * FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, - * NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION - * WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - * - * Except as contained in this notice, the name of a copyright holder - * shall not be used in advertising or otherwise to promote the sale, use - * or other dealings in this Software without prior written authorization - * of the copyright holder. - */ - -/* - * This module provides a memory allocation scheme that helps to - * prevent memory fragmentation by allocating large blocks of - * fixed sized objects and forming them into a free-list for - * subsequent allocations. The free-list is expanded as needed. - */ -typedef struct FreeList FreeList; - -/* - * Allocate a new free-list from blocks of 'blocking_factor' objects of size - * node_size. The node_size argument should be determined by applying - * the sizeof() operator to the object type that you intend to allocate from - * the freelist. - */ -FreeList *_new_FreeList(const char *caller, size_t node_size, - unsigned blocking_factor); - -/* - * If it is known that none of the nodes currently allocated from - * a freelist are still in use, the following function can be called - * to return all nodes to the freelist without the overhead of - * having to call del_FreeListNode() for every allocated node. The - * nodes of the freelist can then be reused by future callers to - * new_FreeListNode(). - */ -void _rst_FreeList(FreeList *fl); - -/* - * Delete a free-list. - */ -FreeList *_del_FreeList(const char *caller, FreeList *fl, int force); - -/* - * Determine the number of nodes that are currently allocated. - */ -long _busy_FreeListNodes(FreeList *fl); - -/* - * Allocate a new object from a free-list. - */ -void *_new_FreeListNode(FreeList *fl); - -/* - * Return an object to the free-list that it was allocated from. - */ -void *_del_FreeListNode(FreeList *fl, void *object); - -#endif diff --git a/libtecla-1.4.1/getline.c b/libtecla-1.4.1/getline.c deleted file mode 100644 index 242fae3..0000000 --- a/libtecla-1.4.1/getline.c +++ /dev/null @@ -1,8346 +0,0 @@ -/* - * Copyright (c) 2000, 2001 by Martin C. Shepherd. - * - * All rights reserved. - * - * Permission is hereby granted, free of charge, to any person obtaining a - * copy of this software and associated documentation files (the - * "Software"), to deal in the Software without restriction, including - * without limitation the rights to use, copy, modify, merge, publish, - * distribute, and/or sell copies of the Software, and to permit persons - * to whom the Software is furnished to do so, provided that the above - * copyright notice(s) and this permission notice appear in all copies of - * the Software and that both the above copyright notice(s) and this - * permission notice appear in supporting documentation. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS - * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT - * OF THIRD PARTY RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR - * HOLDERS INCLUDED IN THIS NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL - * INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING - * FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, - * NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION - * WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - * - * Except as contained in this notice, the name of a copyright holder - * shall not be used in advertising or otherwise to promote the sale, use - * or other dealings in this Software without prior written authorization - * of the copyright holder. - */ - -/* - * Standard headers. - */ -#include -#include -#include -#include -#include -#include -#include - -/* - * UNIX headers. - */ -#include -#ifdef HAVE_SELECT -#include -#include -#endif - -/* - * Handle the different sources of terminal control string and size - * information. Note that if no terminal information database is available, - * ANSI VT100 control sequences are used. - */ -#if defined(USE_TERMINFO) || defined(USE_TERMCAP) -/* - * Include curses.h or ncurses/curses.h depending on which is available. - */ -#ifdef HAVE_CURSES_H -#include -#elif defined(HAVE_NCURSES_CURSES_H) -#include -#endif -/* - * Include term.h where available. - */ -#if defined(HAVE_TERM_H) -#include -#elif defined(HAVE_NCURSES_TERM_H) -#include -#endif -/* - * When using termcap, include termcap.h on systems that have it. - * Otherwise assume that all prototypes are provided by curses.h. - */ -#if defined(USE_TERMCAP) && defined(HAVE_TERMCAP_H) -#include -#endif -/* - * Unfortunately both terminfo and termcap require one to use the tputs() - * function to output terminal control characters, and this function - * doesn't allow one to specify a file stream. As a result, the following - * file-scope variable is used to pass the current output file stream. - * This is bad, but there doesn't seem to be any alternative. - */ -static FILE *tputs_fp = NULL; -/* - * Under Solaris default Curses the output function that tputs takes is - * declared to have a char argument. On all other systems and on Solaris - * X/Open Curses (Issue 4, Version 2) it expects an int argument (using - * c89 or options -I /usr/xpg4/include -L /usr/xpg4/lib -R /usr/xpg4/lib - * selects XPG4v2 Curses on Solaris 2.6 and later). - * - * Similarly, under Mac OS X, the return value of the tputs output - * function is declared as void, whereas it is declared as int on - * other systems. - */ -#if defined __sun && defined __SVR4 && !defined _XOPEN_CURSES -static int gl_tputs_putchar(char c) {return putc(c, tputs_fp);} -#elif defined(__APPLE__) && defined(__MACH__) -static void gl_tputs_putchar(int c) {(void) putc(c, tputs_fp);} -#else -static int gl_tputs_putchar(int c) {return putc(c, tputs_fp);} -#endif -#endif - -/* - * POSIX headers. - */ -#include -#include - -/* - * Does the system provide the signal and ioctl query facility used - * to inform the process of terminal window size changes? - */ -#if defined(SIGWINCH) && defined(TIOCGWINSZ) -#define USE_SIGWINCH 1 -#endif - -/* - * Provide typedefs for standard POSIX structures. - */ -typedef struct sigaction SigAction; -typedef struct termios Termios; - -/* - * Local headers. - */ -#include "pathutil.h" -#include "libtecla.h" -#include "keytab.h" -#include "history.h" -#include "freelist.h" -#include "stringrp.h" -#include "getline.h" - -/* - * Enumerate the available editing styles. - */ -typedef enum { - GL_EMACS_MODE, /* Emacs style editing */ - GL_VI_MODE, /* Vi style editing */ - GL_NO_EDITOR /* Fall back to the basic OS-provided editing */ -} GlEditor; - -/* - * In vi mode, the following datatype is used to implement the - * undo command. It records a copy of the input line from before - * the command-mode action which edited the input line. - */ -typedef struct { - char *line; /* A historical copy of the input line */ - int buff_curpos; /* The historical location of the cursor in */ - /* line[] when the line was modified. */ - int ntotal; /* The number of characters in line[] */ - int saved; /* True once a line has been saved after the */ - /* last call to gl_interpret_char(). */ -} ViUndo; - -/* - * In vi mode, the following datatype is used to record information - * needed by the vi-repeat-change command. - */ -typedef struct { - KtKeyFn *fn; /* The last action function that made a */ - /* change to the line. */ - int count; /* The repeat count that was passed to the */ - /* above command. */ - int input_curpos; /* Whenever vi command mode is entered, the */ - /* the position at which it was first left */ - /* is recorded here. */ - int command_curpos; /* Whenever vi command mode is entered, the */ - /* the location of the cursor is recorded */ - /* here. */ - char input_char; /* Commands that call gl_read_character() */ - /* record the character here, so that it can */ - /* used on repeating the function. */ - int saved; /* True if a function has been saved since the */ - /* last call to gl_interpret_char(). */ - int active; /* True while a function is being repeated. */ -} ViRepeat; - -/* - * The following datatype is used to encapsulate information specific - * to vi mode. - */ -typedef struct { - ViUndo undo; /* Information needed to implement the vi */ - /* undo command. */ - ViRepeat repeat; /* Information needed to implement the vi */ - /* repeat command. */ - int command; /* True in vi command-mode */ - int find_forward; /* True if the last character search was in the */ - /* forward direction. */ - int find_onto; /* True if the last character search left the */ - /* on top of the located character, as opposed */ - /* to just before or after it. */ - char find_char; /* The last character sought, or '\0' if no */ - /* searches have been performed yet. */ -} ViMode; - -#ifdef HAVE_SELECT -/* - * Define a type for recording a file-descriptor callback and its associated - * data. - */ -typedef struct { - GlFdEventFn *fn; /* The callback function */ - void *data; /* Anonymous data to pass to the callback function */ -} GlFdHandler; - -/* - * A list of nodes of the following type is used to record file-activity - * event handlers, but only on systems that have the select() system call. - */ -typedef struct GlFdNode GlFdNode; -struct GlFdNode { - GlFdNode *next; /* The next in the list of nodes */ - int fd; /* The file descriptor being watched */ - GlFdHandler rd; /* The callback to call when fd is readable */ - GlFdHandler wr; /* The callback to call when fd is writable */ - GlFdHandler ur; /* The callback to call when fd has urgent data */ -}; - -/* - * Set the number of the above structures to allocate every time that - * the freelist of GlFdNode's becomes exhausted. - */ -#define GLFD_FREELIST_BLOCKING 10 - -/* - * Listen for and handle file-descriptor events. - */ -static int gl_event_handler(GetLine *gl); - -static int gl_call_fd_handler(GetLine *gl, GlFdHandler *gfh, int fd, - GlFdEvent event); -#endif - -/* - * Each signal that gl_get_line() traps is described by a list node - * of the following type. - */ -typedef struct GlSignalNode GlSignalNode; -struct GlSignalNode { - GlSignalNode *next; /* The next signal in the list */ - int signo; /* The number of the signal */ - sigset_t proc_mask; /* A process mask which only includes signo */ - SigAction original; /* The signal disposition of the calling program */ - /* for this signal. */ - unsigned flags; /* A bitwise union of GlSignalFlags enumerators */ - GlAfterSignal after; /* What to do after the signal has been handled */ - int errno_value; /* What to set errno to */ -}; - -/* - * Set the number of the above structures to allocate every time that - * the freelist of GlSignalNode's becomes exhausted. - */ -#define GLS_FREELIST_BLOCKING 30 - -/* - * Define the contents of the GetLine object. - * Note that the typedef for this object can be found in libtecla.h. - */ -struct GetLine { - GlHistory *glh; /* The line-history buffer */ - WordCompletion *cpl; /* String completion resource object */ - CplMatchFn(*cpl_fn); /* The tab completion callback function */ - void *cpl_data; /* Callback data to pass to cpl_fn() */ - ExpandFile *ef; /* ~user/, $envvar and wildcard expansion */ - /* resource object. */ - StringGroup *capmem; /* Memory for recording terminal capability */ - /* strings. */ - int input_fd; /* The file descriptor to read on */ - int output_fd; /* The file descriptor to write to */ - FILE *input_fp; /* A stream wrapper around input_fd */ - FILE *output_fp; /* A stream wrapper around output_fd */ - FILE *file_fp; /* When input is being temporarily taken from */ - /* a file, this is its file-pointer. Otherwise */ - /* it is NULL. */ - char *term; /* The terminal type specified on the last call */ - /* to gl_change_terminal(). */ - int is_term; /* True if stdin is a terminal */ - size_t linelen; /* The max number of characters per line */ - char *line; /* A line-input buffer of allocated size */ - /* linelen+2. The extra 2 characters are */ - /* reserved for "\n\0". */ - char *cutbuf; /* A cut-buffer of the same size as line[] */ - const char *prompt; /* The current prompt string */ - int prompt_len; /* The length of the prompt string */ - int prompt_changed; /* True after a callback changes the prompt */ - int prompt_style; /* How the prompt string is displayed */ - FreeList *sig_mem; /* Memory for nodes of the signal list */ - GlSignalNode *sigs; /* The head of the list of signals */ - sigset_t old_signal_set; /* The signal set on entry to gl_get_line() */ - sigset_t new_signal_set; /* The set of signals that we are trapping */ - Termios oldattr; /* Saved terminal attributes. */ - KeyTab *bindings; /* A table of key-bindings */ - int ntotal; /* The number of characters in gl->line[] */ - int buff_curpos; /* The cursor position within gl->line[] */ - int term_curpos; /* The cursor position on the terminal */ - int buff_mark; /* A marker location in the buffer */ - int insert_curpos; /* The cursor position at start of insert */ - int insert; /* True in insert mode */ - int number; /* If >= 0, a numeric argument is being read */ - int endline; /* True to tell gl_get_input_line() to return */ - /* the current contents of gl->line[] */ - KtKeyFn *current_fn; /* The action function that is currently being */ - /* invoked. */ - int current_count; /* The repeat count passed to current_fn() */ - GlhLineID preload_id; /* When not zero, this should be the ID of a */ - /* line in the history buffer for potential */ - /* recall. */ - int preload_history; /* If true, preload the above history line when */ - /* gl_get_input_line() is next called. */ - long keyseq_count; /* The number of key sequences entered by the */ - /* the user since new_GetLine() was called. */ - long last_search; /* The value of oper_count during the last */ - /* history search operation. */ - GlEditor editor; /* The style of editing, (eg. vi or emacs) */ - int silence_bell; /* True if gl_ring_bell() should do nothing. */ - ViMode vi; /* Parameters used when editing in vi mode */ - const char *left; /* The string that moves the cursor 1 character */ - /* left. */ - const char *right; /* The string that moves the cursor 1 character */ - /* right. */ - const char *up; /* The string that moves the cursor 1 character */ - /* up. */ - const char *down; /* The string that moves the cursor 1 character */ - /* down. */ - const char *home; /* The string that moves the cursor home */ - const char *bol; /* Move cursor to beginning of line */ - const char *clear_eol; /* The string that clears from the cursor to */ - /* the end of the line. */ - const char *clear_eod; /* The string that clears from the cursor to */ - /* the end of the display. */ - const char *u_arrow; /* The string returned by the up-arrow key */ - const char *d_arrow; /* The string returned by the down-arrow key */ - const char *l_arrow; /* The string returned by the left-arrow key */ - const char *r_arrow; /* The string returned by the right-arrow key */ - const char *sound_bell; /* The string needed to ring the terminal bell */ - const char *bold; /* Switch to the bold font */ - const char *underline; /* Underline subsequent characters */ - const char *standout; /* Turn on standout mode */ - const char *dim; /* Switch to a dim font */ - const char *reverse; /* Turn on reverse video */ - const char *blink; /* Switch to a blinking font */ - const char *text_attr_off; /* Turn off all text attributes */ - int nline; /* The height of the terminal in lines */ - int ncolumn; /* The width of the terminal in columns */ -#ifdef USE_TERMCAP - char *tgetent_buf; /* The buffer that is used by tgetent() to */ - /* store a terminal description. */ - char *tgetstr_buf; /* The buffer that is used by tgetstr() to */ - /* store terminal capabilities. */ -#endif -#ifdef USE_TERMINFO - const char *left_n; /* The parameter string that moves the cursor */ - /* n characters left. */ - const char *right_n; /* The parameter string that moves the cursor */ - /* n characters right. */ -#endif - char *app_file; /* The pathname of the application-specific */ - /* .teclarc configuration file, or NULL. */ - char *user_file; /* The pathname of the user-specific */ - /* .teclarc configuration file, or NULL. */ - int configured; /* True as soon as any teclarc configuration */ - /* file has been read. */ - int echo; /* True to display the line as it is being */ - /* entered. If 0, only the prompt will be */ - /* displayed, and the line will not be */ - /* archived in the history list. */ - int last_signal; /* The last signal that was caught by */ - /* the last call to gl_get_line(), or -1 */ - /* if no signal has been caught yet. */ -#ifdef HAVE_SELECT - FreeList *fd_node_mem; /* A freelist of GlFdNode structures */ - GlFdNode *fd_nodes; /* The list of fd event descriptions */ - fd_set rfds; /* The set of fds to watch for readability */ - fd_set wfds; /* The set of fds to watch for writability */ - fd_set ufds; /* The set of fds to watch for urgent data */ - int max_fd; /* The maximum file-descriptor being watched */ -#endif -}; - -/* - * Define the max amount of space needed to store a termcap terminal - * description. Unfortunately this has to be done by guesswork, so - * there is the potential for buffer overflows if we guess too small. - * Fortunately termcap has been replaced by terminfo on most - * platforms, and with terminfo this isn't an issue. The value that I - * am using here is the conventional value, as recommended by certain - * web references. - */ -#ifdef USE_TERMCAP -#define TERMCAP_BUF_SIZE 2048 -#endif - -/* - * Set the size of the string segments used to store terminal capability - * strings. - */ -#define CAPMEM_SEGMENT_SIZE 512 - -/* - * If no terminal size information is available, substitute the - * following vt100 default sizes. - */ -#define GL_DEF_NLINE 24 -#define GL_DEF_NCOLUMN 80 - -/* - * List the signals that we need to catch. In general these are - * those that by default terminate or suspend the process, since - * in such cases we need to restore terminal settings. - */ -static const struct GlDefSignal { - int signo; /* The number of the signal */ - unsigned flags; /* A bitwise union of GlSignalFlags enumerators */ - GlAfterSignal after; /* What to do after the signal has been delivered */ - int errno_value; /* What to set errno to */ -} gl_signal_list[] = { - {SIGABRT, GLS_SUSPEND_INPUT, GLS_ABORT, EINTR}, - {SIGINT, GLS_SUSPEND_INPUT, GLS_ABORT, EINTR}, - {SIGTERM, GLS_SUSPEND_INPUT, GLS_ABORT, EINTR}, - {SIGALRM, GLS_RESTORE_ENV, GLS_CONTINUE, 0}, - {SIGCONT, GLS_RESTORE_ENV, GLS_CONTINUE, 0}, -#if defined(SIGHUP) -#ifdef ENOTTY - {SIGHUP, GLS_SUSPEND_INPUT, GLS_ABORT, ENOTTY}, -#else - {SIGHUP, GLS_SUSPEND_INPUT, GLS_ABORT, EINTR}, -#endif -#endif -#if defined(SIGPIPE) -#ifdef EPIPE - {SIGPIPE, GLS_SUSPEND_INPUT, GLS_ABORT, EPIPE}, -#else - {SIGPIPE, GLS_SUSPEND_INPUT, GLS_ABORT, EINTR}, -#endif -#endif -#ifdef SIGPWR - {SIGPWR, GLS_RESTORE_ENV, GLS_CONTINUE, 0}, -#endif -#ifdef SIGQUIT - {SIGQUIT, GLS_SUSPEND_INPUT, GLS_ABORT, EINTR}, -#endif -#ifdef SIGTSTP - {SIGTSTP, GLS_SUSPEND_INPUT, GLS_CONTINUE, 0}, -#endif -#ifdef SIGTTIN - {SIGTTIN, GLS_SUSPEND_INPUT, GLS_CONTINUE, 0}, -#endif -#ifdef SIGTTOU - {SIGTTOU, GLS_SUSPEND_INPUT, GLS_CONTINUE, 0}, -#endif -#ifdef SIGUSR1 - {SIGUSR1, GLS_RESTORE_ENV, GLS_CONTINUE, 0}, -#endif -#ifdef SIGUSR2 - {SIGUSR2, GLS_RESTORE_ENV, GLS_CONTINUE, 0}, -#endif -#ifdef SIGVTALRM - {SIGVTALRM, GLS_RESTORE_ENV, GLS_CONTINUE, 0}, -#endif -#ifdef SIGWINCH - {SIGWINCH, GLS_RESTORE_ENV, GLS_CONTINUE, 0}, -#endif -#ifdef SIGXCPU - {SIGXCPU, GLS_RESTORE_ENV, GLS_CONTINUE, 0}, -#endif -}; - -/* - * Define file-scope variables for use in signal handlers. - */ -static volatile sig_atomic_t gl_pending_signal = -1; -static sigjmp_buf gl_setjmp_buffer; - -static void gl_signal_handler(int signo); - -static int gl_check_caught_signal(GetLine *gl); - -/* - * Define a tab to be a string of 8 spaces. - */ -#define TAB_WIDTH 8 - -/* - * Does the system send us SIGWINCH signals when the terminal size - * changes? - */ -#ifdef USE_SIGWINCH -static int gl_resize_terminal(GetLine *gl, int redisplay); -#endif - -/* - * Getline calls this to temporarily override certain signal handlers - * of the calling program. - */ -static int gl_override_signal_handlers(GetLine *gl); - -/* - * Getline calls this to restore the signal handlers of the calling - * program. - */ -static int gl_restore_signal_handlers(GetLine *gl); - -/* - * Put the terminal into raw input mode, after saving the original - * terminal attributes in gl->oldattr. - */ -static int gl_raw_terminal_mode(GetLine *gl); - -/* - * Restore the terminal attributes from gl->oldattr. - */ -static int gl_restore_terminal_attributes(GetLine *gl); - -/* - * Read a line from the user in raw mode. - */ -static int gl_get_input_line(GetLine *gl, const char *start_line, - int start_pos); - -/* - * Set the largest key-sequence that can be handled. - */ -#define GL_KEY_MAX 64 - -/* - * Handle the receipt of the potential start of a new key-sequence from - * the user. - */ -static int gl_interpret_char(GetLine *gl, char c); - -/* - * Bind a single control or meta character to an action. - */ -static int gl_bind_control_char(GetLine *gl, KtBinder binder, - char c, const char *action); - -/* - * Set up terminal-specific key bindings. - */ -static int gl_bind_terminal_keys(GetLine *gl); - -/* - * Lookup terminal control string and size information. - */ -static int gl_control_strings(GetLine *gl, const char *term); - -/* - * Wrappers around the terminfo and termcap functions that lookup - * strings in the terminal information databases. - */ -#ifdef USE_TERMINFO -static const char *gl_tigetstr(GetLine *gl, const char *name); -#elif defined(USE_TERMCAP) -static const char *gl_tgetstr(GetLine *gl, const char *name, char **bufptr); -#endif - -/* - * Output a binary string directly to the terminal. - */ -static int gl_output_raw_string(GetLine *gl, const char *string); - -/* - * Output a terminal control sequence. - */ -static int gl_output_control_sequence(GetLine *gl, int nline, - const char *string); - -/* - * Output a character or string to the terminal after converting tabs - * to spaces and control characters to a caret followed by the modified - * character. - */ -static int gl_output_char(GetLine *gl, char c, char pad); -static int gl_output_string(GetLine *gl, const char *string, char pad); - -/* - * Delete nc characters starting from the one under the cursor. - * Optionally copy the deleted characters to the cut buffer. - */ -static int gl_delete_chars(GetLine *gl, int nc, int cut); - -/* - * Add a character to the line buffer at the current cursor position, - * inserting or overwriting according the current mode. - */ -static int gl_add_char_to_line(GetLine *gl, char c); - -/* - * Insert/append a string to the line buffer and terminal at the current - * cursor position. - */ -static int gl_add_string_to_line(GetLine *gl, const char *s); - -/* - * Read a single character from the terminal. - */ -static int gl_read_character(GetLine *gl, char *c); - - -/* - * Move the terminal cursor n positions to the left or right. - */ -static int gl_terminal_move_cursor(GetLine *gl, int n); - -/* - * Move the terminal cursor to a given position. - */ -static int gl_set_term_curpos(GetLine *gl, int term_curpos); - -/* - * Set the position of the cursor both in the line input buffer and on the - * terminal. - */ -static int gl_place_cursor(GetLine *gl, int buff_curpos); - -/* - * Return the terminal cursor position that corresponds to a given - * line buffer cursor position. - */ -static int gl_buff_curpos_to_term_curpos(GetLine *gl, int buff_curpos); - -/* - * Return the number of terminal characters needed to display a - * given raw character. - */ -static int gl_displayed_char_width(GetLine *gl, char c, int term_curpos); - -/* - * Return the number of terminal characters needed to display a - * given substring. - */ -static int gl_displayed_string_width(GetLine *gl, const char *string, int nc, - int term_curpos); -/* - * Return non-zero if 'c' is to be considered part of a word. - */ -static int gl_is_word_char(int c); - -/* - * Read a tecla configuration file. - */ -static int _gl_read_config_file(GetLine *gl, const char *filename, KtBinder who); - -/* - * Read a tecla configuration string. - */ -static int _gl_read_config_string(GetLine *gl, const char *buffer, KtBinder who); - -/* - * Define the callback function used by _gl_parse_config_line() to - * read the next character of a configuration stream. - */ -#define GLC_GETC_FN(fn) int (fn)(void *stream) -typedef GLC_GETC_FN(GlcGetcFn); - -static GLC_GETC_FN(glc_file_getc); /* Read from a file */ -static GLC_GETC_FN(glc_buff_getc); /* Read from a string */ - -/* - * Parse a single configuration command line. - */ -static int _gl_parse_config_line(GetLine *gl, void *stream, GlcGetcFn *getc_fn, - const char *origin, KtBinder who, int *lineno); - -/* - * Bind the actual arrow key bindings to match those of the symbolic - * arrow-key bindings. - */ -static int _gl_bind_arrow_keys(GetLine *gl); - -/* - * Copy the binding of the specified symbolic arrow-key binding to - * the terminal specific, and default arrow-key key-sequences. - */ -static int _gl_rebind_arrow_key(KeyTab *bindings, const char *name, - const char *term_seq, - const char *def_seq1, - const char *def_seq2); - -/* - * After the gl_read_from_file() action has been used to tell gl_get_line() - * to temporarily read input from a file, gl_revert_input() arranges - * for input to be reverted to the input stream last registered with - * gl_change_terminal(). - */ -static void gl_revert_input(GetLine *gl); - -/* - * Flush unwritten characters to the terminal. - */ -static int gl_flush_output(GetLine *gl); - -/* - * Change the editor style being emulated. - */ -static int gl_change_editor(GetLine *gl, GlEditor editor); - -/* - * Searching in a given direction, return the index of a given (or - * read) character in the input line, or the character that precedes - * it in the specified search direction. Return -1 if not found. - */ -static int gl_find_char(GetLine *gl, int count, int forward, int onto, char c); - -/* - * Return the buffer index of the nth word ending after the cursor. - */ -static int gl_nth_word_end_forward(GetLine *gl, int n); - -/* - * Return the buffer index of the nth word start after the cursor. - */ -static int gl_nth_word_start_forward(GetLine *gl, int n); - -/* - * Return the buffer index of the nth word start before the cursor. - */ -static int gl_nth_word_start_backward(GetLine *gl, int n); - -/* - * When called when vi command mode is enabled, this function saves the - * current line and cursor position for potential restoration later - * by the vi undo command. - */ -static void gl_save_for_undo(GetLine *gl); - -/* - * If in vi mode, switch to vi command mode. - */ -static void gl_vi_command_mode(GetLine *gl); - -/* - * In vi mode this is used to delete up to or onto a given or read - * character in the input line. Also switch to insert mode if requested - * after the deletion. - */ -static int gl_delete_find(GetLine *gl, int count, char c, int forward, - int onto, int change); - -/* - * Copy the characters between the cursor and the count'th instance of - * a specified (or read) character in the input line, into the cut buffer. - */ -static int gl_copy_find(GetLine *gl, int count, char c, int forward, int onto); - -/* - * Return the line index of the parenthesis that either matches the one under - * the cursor, or not over a parenthesis character, the index of the next - * close parenthesis. Return -1 if not found. - */ -static int gl_index_of_matching_paren(GetLine *gl); - -/* - * Replace a malloc'd string (or NULL), with another malloc'd copy of - * a string (or NULL). - */ -static int gl_record_string(char **sptr, const char *string); - -/* - * Enumerate text display attributes as powers of two, suitable for - * use in a bit-mask. - */ -typedef enum { - GL_TXT_STANDOUT=1, /* Display text highlighted */ - GL_TXT_UNDERLINE=2, /* Display text underlined */ - GL_TXT_REVERSE=4, /* Display text with reverse video */ - GL_TXT_BLINK=8, /* Display blinking text */ - GL_TXT_DIM=16, /* Display text in a dim font */ - GL_TXT_BOLD=32 /* Display text using a bold font */ -} GlTextAttr; - -/* - * Display the prompt regardless of the current visibility mode. - */ -static int gl_display_prompt(GetLine *gl); - -/* - * Return the number of characters used by the prompt on the terminal. - */ -static int gl_displayed_prompt_width(GetLine *gl); - -/* - * Prepare the return the current input line to the caller of gl_get_line(). - */ -static int gl_line_ended(GetLine *gl, int newline_char, int archive); - -/* - * Set the maximum length of a line in a user's tecla configuration - * file (not counting comments). - */ -#define GL_CONF_BUFLEN 100 - -/* - * Set the maximum number of arguments supported by individual commands - * in tecla configuration files. - */ -#define GL_CONF_MAXARG 10 - -/* - * Prototype the available action functions. - */ -static KT_KEY_FN(gl_user_interrupt); -static KT_KEY_FN(gl_abort); -static KT_KEY_FN(gl_suspend); -static KT_KEY_FN(gl_stop_output); -static KT_KEY_FN(gl_start_output); -static KT_KEY_FN(gl_literal_next); -static KT_KEY_FN(gl_cursor_left); -static KT_KEY_FN(gl_cursor_right); -static KT_KEY_FN(gl_insert_mode); -static KT_KEY_FN(gl_beginning_of_line); -static KT_KEY_FN(gl_end_of_line); -static KT_KEY_FN(gl_delete_line); -static KT_KEY_FN(gl_kill_line); -static KT_KEY_FN(gl_forward_word); -static KT_KEY_FN(gl_backward_word); -static KT_KEY_FN(gl_forward_delete_char); -static KT_KEY_FN(gl_backward_delete_char); -static KT_KEY_FN(gl_forward_delete_word); -static KT_KEY_FN(gl_backward_delete_word); -static KT_KEY_FN(gl_delete_refind); -static KT_KEY_FN(gl_delete_invert_refind); -static KT_KEY_FN(gl_delete_to_column); -static KT_KEY_FN(gl_delete_to_parenthesis); -static KT_KEY_FN(gl_forward_delete_find); -static KT_KEY_FN(gl_backward_delete_find); -static KT_KEY_FN(gl_forward_delete_to); -static KT_KEY_FN(gl_backward_delete_to); -static KT_KEY_FN(gl_upcase_word); -static KT_KEY_FN(gl_downcase_word); -static KT_KEY_FN(gl_capitalize_word); -static KT_KEY_FN(gl_redisplay); -static KT_KEY_FN(gl_clear_screen); -static KT_KEY_FN(gl_transpose_chars); -static KT_KEY_FN(gl_set_mark); -static KT_KEY_FN(gl_exchange_point_and_mark); -static KT_KEY_FN(gl_kill_region); -static KT_KEY_FN(gl_copy_region_as_kill); -static KT_KEY_FN(gl_yank); -static KT_KEY_FN(gl_up_history); -static KT_KEY_FN(gl_down_history); -static KT_KEY_FN(gl_history_search_backward); -static KT_KEY_FN(gl_history_re_search_backward); -static KT_KEY_FN(gl_history_search_forward); -static KT_KEY_FN(gl_history_re_search_forward); -static KT_KEY_FN(gl_complete_word); -static KT_KEY_FN(gl_expand_filename); -static KT_KEY_FN(gl_del_char_or_list_or_eof); -static KT_KEY_FN(gl_list_or_eof); -static KT_KEY_FN(gl_read_from_file); -static KT_KEY_FN(gl_beginning_of_history); -static KT_KEY_FN(gl_end_of_history); -static KT_KEY_FN(gl_digit_argument); -static KT_KEY_FN(gl_newline); -static KT_KEY_FN(gl_repeat_history); -static KT_KEY_FN(gl_vi_insert); -static KT_KEY_FN(gl_vi_overwrite); -static KT_KEY_FN(gl_change_case); -static KT_KEY_FN(gl_vi_insert_at_bol); -static KT_KEY_FN(gl_vi_append_at_eol); -static KT_KEY_FN(gl_vi_append); -static KT_KEY_FN(gl_list_glob); -static KT_KEY_FN(gl_backward_kill_line); -static KT_KEY_FN(gl_goto_column); -static KT_KEY_FN(gl_forward_to_word); -static KT_KEY_FN(gl_vi_replace_char); -static KT_KEY_FN(gl_vi_change_rest_of_line); -static KT_KEY_FN(gl_vi_change_line); -static KT_KEY_FN(gl_vi_change_to_bol); -static KT_KEY_FN(gl_vi_change_refind); -static KT_KEY_FN(gl_vi_change_invert_refind); -static KT_KEY_FN(gl_vi_change_to_column); -static KT_KEY_FN(gl_vi_change_to_parenthesis); -static KT_KEY_FN(gl_vi_forward_change_word); -static KT_KEY_FN(gl_vi_backward_change_word); -static KT_KEY_FN(gl_vi_forward_change_find); -static KT_KEY_FN(gl_vi_backward_change_find); -static KT_KEY_FN(gl_vi_forward_change_to); -static KT_KEY_FN(gl_vi_backward_change_to); -static KT_KEY_FN(gl_vi_forward_change_char); -static KT_KEY_FN(gl_vi_backward_change_char); -static KT_KEY_FN(gl_forward_copy_char); -static KT_KEY_FN(gl_backward_copy_char); -static KT_KEY_FN(gl_forward_find_char); -static KT_KEY_FN(gl_backward_find_char); -static KT_KEY_FN(gl_forward_to_char); -static KT_KEY_FN(gl_backward_to_char); -static KT_KEY_FN(gl_repeat_find_char); -static KT_KEY_FN(gl_invert_refind_char); -static KT_KEY_FN(gl_append_yank); -static KT_KEY_FN(gl_backward_copy_word); -static KT_KEY_FN(gl_forward_copy_word); -static KT_KEY_FN(gl_copy_to_bol); -static KT_KEY_FN(gl_copy_refind); -static KT_KEY_FN(gl_copy_invert_refind); -static KT_KEY_FN(gl_copy_to_column); -static KT_KEY_FN(gl_copy_to_parenthesis); -static KT_KEY_FN(gl_copy_rest_of_line); -static KT_KEY_FN(gl_copy_line); -static KT_KEY_FN(gl_backward_copy_find); -static KT_KEY_FN(gl_forward_copy_find); -static KT_KEY_FN(gl_backward_copy_to); -static KT_KEY_FN(gl_forward_copy_to); -static KT_KEY_FN(gl_vi_undo); -static KT_KEY_FN(gl_emacs_editing_mode); -static KT_KEY_FN(gl_vi_editing_mode); -static KT_KEY_FN(gl_ring_bell); -static KT_KEY_FN(gl_vi_repeat_change); -static KT_KEY_FN(gl_find_parenthesis); -static KT_KEY_FN(gl_read_init_files); -static KT_KEY_FN(gl_list_history); - -/* - * Name the available action functions. - */ -static const struct {const char *name; KT_KEY_FN(*fn);} gl_actions[] = { - {"user-interrupt", gl_user_interrupt}, - {"abort", gl_abort}, - {"suspend", gl_suspend}, - {"stop-output", gl_stop_output}, - {"start-output", gl_start_output}, - {"literal-next", gl_literal_next}, - {"cursor-right", gl_cursor_right}, - {"cursor-left", gl_cursor_left}, - {"insert-mode", gl_insert_mode}, - {"beginning-of-line", gl_beginning_of_line}, - {"end-of-line", gl_end_of_line}, - {"delete-line", gl_delete_line}, - {"kill-line", gl_kill_line}, - {"forward-word", gl_forward_word}, - {"backward-word", gl_backward_word}, - {"forward-delete-char", gl_forward_delete_char}, - {"backward-delete-char", gl_backward_delete_char}, - {"forward-delete-word", gl_forward_delete_word}, - {"backward-delete-word", gl_backward_delete_word}, - {"delete-refind", gl_delete_refind}, - {"delete-invert-refind", gl_delete_invert_refind}, - {"delete-to-column", gl_delete_to_column}, - {"delete-to-parenthesis", gl_delete_to_parenthesis}, - {"forward-delete-find", gl_forward_delete_find}, - {"backward-delete-find", gl_backward_delete_find}, - {"forward-delete-to", gl_forward_delete_to}, - {"backward-delete-to", gl_backward_delete_to}, - {"upcase-word", gl_upcase_word}, - {"downcase-word", gl_downcase_word}, - {"capitalize-word", gl_capitalize_word}, - {"redisplay", gl_redisplay}, - {"clear-screen", gl_clear_screen}, - {"transpose-chars", gl_transpose_chars}, - {"set-mark", gl_set_mark}, - {"exchange-point-and-mark", gl_exchange_point_and_mark}, - {"kill-region", gl_kill_region}, - {"copy-region-as-kill", gl_copy_region_as_kill}, - {"yank", gl_yank}, - {"up-history", gl_up_history}, - {"down-history", gl_down_history}, - {"history-search-backward", gl_history_search_backward}, - {"history-re-search-backward", gl_history_re_search_backward}, - {"history-search-forward", gl_history_search_forward}, - {"history-re-search-forward", gl_history_re_search_forward}, - {"complete-word", gl_complete_word}, - {"expand-filename", gl_expand_filename}, - {"del-char-or-list-or-eof", gl_del_char_or_list_or_eof}, - {"read-from-file", gl_read_from_file}, - {"beginning-of-history", gl_beginning_of_history}, - {"end-of-history", gl_end_of_history}, - {"digit-argument", gl_digit_argument}, - {"newline", gl_newline}, - {"repeat-history", gl_repeat_history}, - {"vi-insert", gl_vi_insert}, - {"vi-overwrite", gl_vi_overwrite}, - {"vi-insert-at-bol", gl_vi_insert_at_bol}, - {"vi-append-at-eol", gl_vi_append_at_eol}, - {"vi-append", gl_vi_append}, - {"change-case", gl_change_case}, - {"list-glob", gl_list_glob}, - {"backward-kill-line", gl_backward_kill_line}, - {"goto-column", gl_goto_column}, - {"forward-to-word", gl_forward_to_word}, - {"vi-replace-char", gl_vi_replace_char}, - {"vi-change-rest-of-line", gl_vi_change_rest_of_line}, - {"vi-change-line", gl_vi_change_line}, - {"vi-change-to-bol", gl_vi_change_to_bol}, - {"vi-change-refind", gl_vi_change_refind}, - {"vi-change-invert-refind", gl_vi_change_invert_refind}, - {"vi-change-to-column", gl_vi_change_to_column}, - {"vi-change-to-parenthesis", gl_vi_change_to_parenthesis}, - {"forward-copy-char", gl_forward_copy_char}, - {"backward-copy-char", gl_backward_copy_char}, - {"forward-find-char", gl_forward_find_char}, - {"backward-find-char", gl_backward_find_char}, - {"forward-to-char", gl_forward_to_char}, - {"backward-to-char", gl_backward_to_char}, - {"repeat-find-char", gl_repeat_find_char}, - {"invert-refind-char", gl_invert_refind_char}, - {"append-yank", gl_append_yank}, - {"backward-copy-word", gl_backward_copy_word}, - {"forward-copy-word", gl_forward_copy_word}, - {"copy-to-bol", gl_copy_to_bol}, - {"copy-refind", gl_copy_refind}, - {"copy-invert-refind", gl_copy_invert_refind}, - {"copy-to-column", gl_copy_to_column}, - {"copy-to-parenthesis", gl_copy_to_parenthesis}, - {"copy-rest-of-line", gl_copy_rest_of_line}, - {"copy-line", gl_copy_line}, - {"backward-copy-find", gl_backward_copy_find}, - {"forward-copy-find", gl_forward_copy_find}, - {"backward-copy-to", gl_backward_copy_to}, - {"forward-copy-to", gl_forward_copy_to}, - {"list-or-eof", gl_list_or_eof}, - {"vi-undo", gl_vi_undo}, - {"vi-backward-change-word", gl_vi_backward_change_word}, - {"vi-forward-change-word", gl_vi_forward_change_word}, - {"vi-backward-change-find", gl_vi_backward_change_find}, - {"vi-forward-change-find", gl_vi_forward_change_find}, - {"vi-backward-change-to", gl_vi_backward_change_to}, - {"vi-forward-change-to", gl_vi_forward_change_to}, - {"vi-backward-change-char", gl_vi_backward_change_char}, - {"vi-forward-change-char", gl_vi_forward_change_char}, - {"emacs-mode", gl_emacs_editing_mode}, - {"vi-mode", gl_vi_editing_mode}, - {"ring-bell", gl_ring_bell}, - {"vi-repeat-change", gl_vi_repeat_change}, - {"find-parenthesis", gl_find_parenthesis}, - {"read-init-files", gl_read_init_files}, - {"list-history", gl_list_history}, -}; - -/* - * Define the default key-bindings in emacs mode. - */ -static const KtKeyBinding gl_emacs_bindings[] = { - {"right", "cursor-right"}, - {"^F", "cursor-right"}, - {"left", "cursor-left"}, - {"^B", "cursor-left"}, - {"M-i", "insert-mode"}, - {"M-I", "insert-mode"}, - {"^A", "beginning-of-line"}, - {"^E", "end-of-line"}, - {"^U", "delete-line"}, - {"^K", "kill-line"}, - {"M-f", "forward-word"}, - {"M-F", "forward-word"}, - {"M-b", "backward-word"}, - {"M-B", "backward-word"}, - {"^D", "del-char-or-list-or-eof"}, - {"^H", "backward-delete-char"}, - {"^?", "backward-delete-char"}, - {"M-d", "forward-delete-word"}, - {"M-D", "forward-delete-word"}, - {"M-^H", "backward-delete-word"}, - {"M-^?", "backward-delete-word"}, - {"M-u", "upcase-word"}, - {"M-U", "upcase-word"}, - {"M-l", "downcase-word"}, - {"M-L", "downcase-word"}, - {"M-c", "capitalize-word"}, - {"M-C", "capitalize-word"}, - {"^R", "redisplay"}, - {"^L", "clear-screen"}, - {"^T", "transpose-chars"}, - {"^@", "set-mark"}, - {"^X^X", "exchange-point-and-mark"}, - {"^W", "kill-region"}, - {"M-w", "copy-region-as-kill"}, - {"M-W", "copy-region-as-kill"}, - {"^Y", "yank"}, - {"^P", "up-history"}, - {"up", "up-history"}, - {"^N", "down-history"}, - {"down", "down-history"}, - {"M-p", "history-search-backward"}, - {"M-P", "history-search-backward"}, - {"M-n", "history-search-forward"}, - {"M-N", "history-search-forward"}, - {"\t", "complete-word"}, - {"^X*", "expand-filename"}, - {"^X^F", "read-from-file"}, - {"^X^R", "read-init-files"}, - {"^Xg", "list-glob"}, - {"^XG", "list-glob"}, - {"^Xh", "list-history"}, - {"^XH", "list-history"}, - {"M-<", "beginning-of-history"}, - {"M->", "end-of-history"}, - {"M-0", "digit-argument"}, - {"M-1", "digit-argument"}, - {"M-2", "digit-argument"}, - {"M-3", "digit-argument"}, - {"M-4", "digit-argument"}, - {"M-5", "digit-argument"}, - {"M-6", "digit-argument"}, - {"M-7", "digit-argument"}, - {"M-8", "digit-argument"}, - {"M-9", "digit-argument"}, - {"\r", "newline"}, - {"\n", "newline"}, - {"M-o", "repeat-history"}, - {"M-C-v", "vi-mode"}, -}; - -/* - * Define the default key-bindings in vi mode. Note that in vi-mode - * meta-key bindings are command-mode bindings. For example M-i first - * switches to command mode if not already in that mode, then moves - * the cursor one position right, as in vi. - */ -static const KtKeyBinding gl_vi_bindings[] = { - {"^D", "list-or-eof"}, - {"^G", "list-glob"}, - {"^H", "backward-delete-char"}, - {"\t", "complete-word"}, - {"\r", "newline"}, - {"\n", "newline"}, - {"^L", "clear-screen"}, - {"^N", "down-history"}, - {"^P", "up-history"}, - {"^R", "redisplay"}, - {"^U", "backward-kill-line"}, - {"^W", "backward-delete-word"}, - {"^X^F", "read-from-file"}, - {"^X^R", "read-init-files"}, - {"^X*", "expand-filename"}, - {"^?", "backward-delete-char"}, - {"M- ", "cursor-right"}, - {"M-$", "end-of-line"}, - {"M-*", "expand-filename"}, - {"M-+", "down-history"}, - {"M--", "up-history"}, - {"M-<", "beginning-of-history"}, - {"M->", "end-of-history"}, - {"M-^", "beginning-of-line"}, - {"M-;", "repeat-find-char"}, - {"M-,", "invert-refind-char"}, - {"M-|", "goto-column"}, - {"M-~", "change-case"}, - {"M-.", "vi-repeat-change"}, - {"M-%", "find-parenthesis"}, - {"M-0", "digit-argument"}, - {"M-1", "digit-argument"}, - {"M-2", "digit-argument"}, - {"M-3", "digit-argument"}, - {"M-4", "digit-argument"}, - {"M-5", "digit-argument"}, - {"M-6", "digit-argument"}, - {"M-7", "digit-argument"}, - {"M-8", "digit-argument"}, - {"M-9", "digit-argument"}, - {"M-a", "vi-append"}, - {"M-A", "vi-append-at-eol"}, - {"M-b", "backward-word"}, - {"M-B", "backward-word"}, - {"M-C", "vi-change-rest-of-line"}, - {"M-cb", "vi-backward-change-word"}, - {"M-cB", "vi-backward-change-word"}, - {"M-cc", "vi-change-line"}, - {"M-ce", "vi-forward-change-word"}, - {"M-cE", "vi-forward-change-word"}, - {"M-cw", "vi-forward-change-word"}, - {"M-cW", "vi-forward-change-word"}, - {"M-cF", "vi-backward-change-find"}, - {"M-cf", "vi-forward-change-find"}, - {"M-cT", "vi-backward-change-to"}, - {"M-ct", "vi-forward-change-to"}, - {"M-c;", "vi-change-refind"}, - {"M-c,", "vi-change-invert-refind"}, - {"M-ch", "vi-backward-change-char"}, - {"M-c^H", "vi-backward-change-char"}, - {"M-c^?", "vi-backward-change-char"}, - {"M-cl", "vi-forward-change-char"}, - {"M-c ", "vi-forward-change-char"}, - {"M-c^", "vi-change-to-bol"}, - {"M-c0", "vi-change-to-bol"}, - {"M-c$", "vi-change-rest-of-line"}, - {"M-c|", "vi-change-to-column"}, - {"M-c%", "vi-change-to-parenthesis"}, - {"M-dh", "backward-delete-char"}, - {"M-d^H", "backward-delete-char"}, - {"M-d^?", "backward-delete-char"}, - {"M-dl", "forward-delete-char"}, - {"M-d ", "forward-delete-char"}, - {"M-dd", "delete-line"}, - {"M-db", "backward-delete-word"}, - {"M-dB", "backward-delete-word"}, - {"M-de", "forward-delete-word"}, - {"M-dE", "forward-delete-word"}, - {"M-dw", "forward-delete-word"}, - {"M-dW", "forward-delete-word"}, - {"M-dF", "backward-delete-find"}, - {"M-df", "forward-delete-find"}, - {"M-dT", "backward-delete-to"}, - {"M-dt", "forward-delete-to"}, - {"M-d;", "delete-refind"}, - {"M-d,", "delete-invert-refind"}, - {"M-d^", "backward-kill-line"}, - {"M-d0", "backward-kill-line"}, - {"M-d$", "kill-line"}, - {"M-D", "kill-line"}, - {"M-d|", "delete-to-column"}, - {"M-d%", "delete-to-parenthesis"}, - {"M-e", "forward-word"}, - {"M-E", "forward-word"}, - {"M-f", "forward-find-char"}, - {"M-F", "backward-find-char"}, - {"M--", "up-history"}, - {"M-h", "cursor-left"}, - {"M-H", "beginning-of-history"}, - {"M-i", "vi-insert"}, - {"M-I", "vi-insert-at-bol"}, - {"M-j", "down-history"}, - {"M-J", "history-search-forward"}, - {"M-k", "up-history"}, - {"M-K", "history-search-backward"}, - {"M-l", "cursor-right"}, - {"M-L", "end-of-history"}, - {"M-n", "history-re-search-forward"}, - {"M-N", "history-re-search-backward"}, - {"M-p", "append-yank"}, - {"M-P", "yank"}, - {"M-r", "vi-replace-char"}, - {"M-R", "vi-overwrite"}, - {"M-s", "vi-forward-change-char"}, - {"M-S", "vi-change-line"}, - {"M-t", "forward-to-char"}, - {"M-T", "backward-to-char"}, - {"M-u", "vi-undo"}, - {"M-w", "forward-to-word"}, - {"M-W", "forward-to-word"}, - {"M-x", "forward-delete-char"}, - {"M-X", "backward-delete-char"}, - {"M-yh", "backward-copy-char"}, - {"M-y^H", "backward-copy-char"}, - {"M-y^?", "backward-copy-char"}, - {"M-yl", "forward-copy-char"}, - {"M-y ", "forward-copy-char"}, - {"M-ye", "forward-copy-word"}, - {"M-yE", "forward-copy-word"}, - {"M-yw", "forward-copy-word"}, - {"M-yW", "forward-copy-word"}, - {"M-yb", "backward-copy-word"}, - {"M-yB", "backward-copy-word"}, - {"M-yf", "forward-copy-find"}, - {"M-yF", "backward-copy-find"}, - {"M-yt", "forward-copy-to"}, - {"M-yT", "backward-copy-to"}, - {"M-y;", "copy-refind"}, - {"M-y,", "copy-invert-refind"}, - {"M-y^", "copy-to-bol"}, - {"M-y0", "copy-to-bol"}, - {"M-y$", "copy-rest-of-line"}, - {"M-yy", "copy-line"}, - {"M-Y", "copy-line"}, - {"M-y|", "copy-to-column"}, - {"M-y%", "copy-to-parenthesis"}, - {"M-^E", "emacs-mode"}, - {"M-^H", "cursor-left"}, - {"M-^?", "cursor-left"}, - {"M-^L", "clear-screen"}, - {"M-^N", "down-history"}, - {"M-^P", "up-history"}, - {"M-^R", "redisplay"}, - {"M-^D", "list-or-eof"}, - {"M-\r", "newline"}, - {"M-\t", "complete-word"}, - {"M-\n", "newline"}, - {"M-^X^R", "read-init-files"}, - {"M-^Xh", "list-history"}, - {"M-^XH", "list-history"}, - {"down", "down-history"}, - {"up", "up-history"}, - {"left", "cursor-left"}, - {"right", "cursor-right"}, -}; - -/*....................................................................... - * Create a new GetLine object. - * - * Input: - * linelen size_t The maximum line length to allow for. - * histlen size_t The number of bytes to allocate for recording - * a circular buffer of history lines. - * Output: - * return GetLine * The new object, or NULL on error. - */ -GetLine *new_GetLine(size_t linelen, size_t histlen) -{ - GetLine *gl; /* The object to be returned */ - int i; -/* - * Check the arguments. - */ - if(linelen < 10) { - fprintf(stderr, "new_GetLine: Line length too small.\n"); - return NULL; - }; -/* - * Allocate the container. - */ - gl = (GetLine *) malloc(sizeof(GetLine)); - if(!gl) { - fprintf(stderr, "new_GetLine: Insufficient memory.\n"); - return NULL; - }; -/* - * Before attempting any operation that might fail, initialize the - * container at least up to the point at which it can safely be passed - * to del_GetLine(). - */ - gl->glh = NULL; - gl->cpl = NULL; - gl->cpl_fn = cpl_file_completions; - gl->cpl_data = NULL; - gl->ef = NULL; - gl->capmem = NULL; - gl->term = NULL; - gl->is_term = 0; - gl->input_fd = -1; - gl->output_fd = -1; - gl->input_fp = NULL; - gl->output_fp = NULL; - gl->file_fp = NULL; - gl->linelen = linelen; - gl->line = NULL; - gl->cutbuf = NULL; - gl->linelen = linelen; - gl->prompt = ""; - gl->prompt_len = 0; - gl->prompt_changed = 0; - gl->prompt_style = GL_LITERAL_PROMPT; - gl->vi.undo.line = NULL; - gl->vi.undo.buff_curpos = 0; - gl->vi.undo.ntotal = 0; - gl->vi.undo.saved = 0; - gl->vi.repeat.fn = 0; - gl->vi.repeat.count = 0; - gl->vi.repeat.input_curpos = 0; - gl->vi.repeat.command_curpos = 0; - gl->vi.repeat.input_char = '\0'; - gl->vi.repeat.saved = 0; - gl->vi.repeat.active = 0; - gl->sig_mem = NULL; - gl->sigs = NULL; - sigemptyset(&gl->old_signal_set); - sigemptyset(&gl->new_signal_set); - gl->bindings = NULL; - gl->ntotal = 0; - gl->buff_curpos = 0; - gl->term_curpos = 0; - gl->buff_mark = 0; - gl->insert_curpos = 0; - gl->insert = 1; - gl->number = -1; - gl->endline = 0; - gl->current_fn = 0; - gl->current_count = 0; - gl->preload_id = 0; - gl->preload_history = 0; - gl->keyseq_count = 0; - gl->last_search = -1; - gl->editor = GL_EMACS_MODE; - gl->silence_bell = 0; - gl->vi.command = 0; - gl->vi.find_forward = 0; - gl->vi.find_onto = 0; - gl->vi.find_char = '\0'; - gl->left = NULL; - gl->right = NULL; - gl->up = NULL; - gl->down = NULL; - gl->home = NULL; - gl->bol = 0; - gl->clear_eol = NULL; - gl->clear_eod = NULL; - gl->u_arrow = NULL; - gl->d_arrow = NULL; - gl->l_arrow = NULL; - gl->r_arrow = NULL; - gl->sound_bell = NULL; - gl->bold = NULL; - gl->underline = NULL; - gl->standout = NULL; - gl->dim = NULL; - gl->reverse = NULL; - gl->blink = NULL; - gl->text_attr_off = NULL; - gl->nline = 0; - gl->ncolumn = 0; -#ifdef USE_TERMINFO - gl->left_n = NULL; - gl->right_n = NULL; -#elif defined(USE_TERMCAP) - gl->tgetent_buf = NULL; - gl->tgetstr_buf = NULL; -#endif - gl->app_file = NULL; - gl->user_file = NULL; - gl->configured = 0; - gl->echo = 1; - gl->last_signal = -1; -#ifdef HAVE_SELECT - gl->fd_node_mem = NULL; - gl->fd_nodes = NULL; - FD_ZERO(&gl->rfds); - FD_ZERO(&gl->wfds); - FD_ZERO(&gl->ufds); - gl->max_fd = 0; -#endif -/* - * Allocate the history buffer. - */ - gl->glh = _new_GlHistory(histlen); - if(!gl->glh) - return del_GetLine(gl); -/* - * Allocate the resource object for file-completion. - */ - gl->cpl = new_WordCompletion(); - if(!gl->cpl) - return del_GetLine(gl); -/* - * Allocate the resource object for file-completion. - */ - gl->ef = new_ExpandFile(); - if(!gl->ef) - return del_GetLine(gl); -/* - * Allocate a string-segment memory allocator for use in storing terminal - * capablity strings. - */ - gl->capmem = _new_StringGroup(CAPMEM_SEGMENT_SIZE); - if(!gl->capmem) - return del_GetLine(gl); -/* - * Allocate a line buffer, leaving 2 extra characters for the terminating - * '\n' and '\0' characters - */ - gl->line = (char *) malloc(linelen + 2); - if(!gl->line) { - fprintf(stderr, - "new_GetLine: Insufficient memory to allocate line buffer.\n"); - return del_GetLine(gl); - }; - gl->line[0] = '\0'; -/* - * Allocate a cut buffer. - */ - gl->cutbuf = (char *) malloc(linelen + 2); - if(!gl->cutbuf) { - fprintf(stderr, - "new_GetLine: Insufficient memory to allocate cut buffer.\n"); - return del_GetLine(gl); - }; - gl->cutbuf[0] = '\0'; -/* - * Allocate a vi undo buffer. - */ - gl->vi.undo.line = (char *) malloc(linelen + 2); - if(!gl->vi.undo.line) { - fprintf(stderr, - "new_GetLine: Insufficient memory to allocate undo buffer.\n"); - return del_GetLine(gl); - }; - gl->vi.undo.line[0] = '\0'; -/* - * Allocate a freelist from which to allocate nodes for the list - * of signals. - */ - gl->sig_mem = _new_FreeList("new_GetLine", sizeof(GlSignalNode), - GLS_FREELIST_BLOCKING); - if(!gl->sig_mem) - return del_GetLine(gl); -/* - * Install dispositions for the default list of signals that gl_get_line() - * traps. - */ - for(i=0; isigno, sig->flags, sig->after, - sig->errno_value)) - return del_GetLine(gl); - }; -/* - * Allocate an empty table of key bindings. - */ - gl->bindings = _new_KeyTab(); - if(!gl->bindings) - return del_GetLine(gl); -/* - * Define the available actions that can be bound to key sequences. - */ - for(i=0; ibindings, gl_actions[i].name, gl_actions[i].fn)) - return del_GetLine(gl); - }; -/* - * Set up the default bindings. - */ - if(gl_change_editor(gl, gl->editor)) - return del_GetLine(gl); -/* - * Allocate termcap buffers. - */ -#ifdef USE_TERMCAP - gl->tgetent_buf = (char *) malloc(TERMCAP_BUF_SIZE); - gl->tgetstr_buf = (char *) malloc(TERMCAP_BUF_SIZE); - if(!gl->tgetent_buf || !gl->tgetstr_buf) { - fprintf(stderr, "new_GetLine: Insufficient memory for termcap buffers.\n"); - return del_GetLine(gl); - }; -#endif -/* - * Set up for I/O assuming stdin and stdout. - */ - if(gl_change_terminal(gl, stdin, stdout, getenv("TERM"))) - return del_GetLine(gl); -/* - * Create a freelist for use in allocating GlFdNode list nodes. - */ -#ifdef HAVE_SELECT - gl->fd_node_mem = _new_FreeList("new_GetLine", sizeof(GlFdNode), - GLFD_FREELIST_BLOCKING); - if(!gl->fd_node_mem) - return del_GetLine(gl); -#endif -/* - * We are done for now. - */ - return gl; -} - -/*....................................................................... - * Delete a GetLine object. - * - * Input: - * gl GetLine * The object to be deleted. - * Output: - * return GetLine * The deleted object (always NULL). - */ -GetLine *del_GetLine(GetLine *gl) -{ - if(gl) { - gl->glh = _del_GlHistory(gl->glh); - gl->cpl = del_WordCompletion(gl->cpl); - gl->ef = del_ExpandFile(gl->ef); - gl->capmem = _del_StringGroup(gl->capmem); - if(gl->line) - free(gl->line); - if(gl->cutbuf) - free(gl->cutbuf); - if(gl->vi.undo.line) - free(gl->vi.undo.line); - gl->sig_mem = _del_FreeList(NULL, gl->sig_mem, 1); - gl->sigs = NULL; /* Already freed by freeing sig_mem */ - gl->bindings = _del_KeyTab(gl->bindings); -#ifdef USE_TERMCAP - if(gl->tgetent_buf) - free(gl->tgetent_buf); - if(gl->tgetstr_buf) - free(gl->tgetstr_buf); -#endif - if(gl->file_fp) - fclose(gl->file_fp); - if(gl->term) - free(gl->term); -#ifdef HAVE_SELECT - gl->fd_node_mem = _del_FreeList(NULL, gl->fd_node_mem, 1); -#endif - free(gl); - }; - return NULL; -} - -/*....................................................................... - * Bind a control or meta character to an action. - * - * Input: - * gl GetLine * The resource object of this program. - * binder KtBinder The source of the binding. - * c char The control or meta character. - * If this is '\0', the call is ignored. - * action const char * The action name to bind the key to. - * Output: - * return int 0 - OK. - * 1 - Error. - */ -static int gl_bind_control_char(GetLine *gl, KtBinder binder, char c, - const char *action) -{ - char keyseq[2]; -/* - * Quietly reject binding to the NUL control character, since this - * is an ambiguous prefix of all bindings. - */ - if(c == '\0') - return 0; -/* - * Making sure not to bind characters which aren't either control or - * meta characters. - */ - if(IS_CTRL_CHAR(c) || IS_META_CHAR(c)) { - keyseq[0] = c; - keyseq[1] = '\0'; - } else { - return 0; - }; -/* - * Install the binding. - */ - return _kt_set_keybinding(gl->bindings, binder, keyseq, action); -} - -/*....................................................................... - * Read a line from the user. - * - * Input: - * gl GetLine * A resource object returned by new_GetLine(). - * prompt char * The prompt to prefix the line with. - * start_line char * The initial contents of the input line, or NULL - * if it should start out empty. - * start_pos int If start_line isn't NULL, this specifies the - * index of the character over which the cursor - * should initially be positioned within the line. - * If you just want it to follow the last character - * of the line, send -1. - * Output: - * return char * An internal buffer containing the input line, or - * NULL at the end of input. If the line fitted in - * the buffer there will be a '\n' newline character - * before the terminating '\0'. If it was truncated - * there will be no newline character, and the remains - * of the line should be retrieved via further calls - * to this function. - */ -char *gl_get_line(GetLine *gl, const char *prompt, - const char *start_line, int start_pos) -{ - int waserr = 0; /* True if an error occurs */ -/* - * Check the arguments. - */ - if(!gl || !prompt) { - fprintf(stderr, "gl_get_line: NULL argument(s).\n"); - return NULL; - }; -/* - * If this is the first call to this function since new_GetLine(), - * complete any postponed configuration. - */ - if(!gl->configured) { - (void) gl_configure_getline(gl, NULL, NULL, TECLA_CONFIG_FILE); - gl->configured = 1; - }; -/* - * If input is temporarily being taken from a file, return lines - * from the file until the file is exhausted, then revert to - * the normal input stream. - */ - if(gl->file_fp) { - if(fgets(gl->line, gl->linelen, gl->file_fp)) - return gl->line; - gl_revert_input(gl); - }; -/* - * Is input coming from a non-interactive source? - */ - if(!gl->is_term) - return fgets(gl->line, gl->linelen, gl->input_fp); -/* - * Record the new prompt and its displayed width. - */ - gl_replace_prompt(gl, prompt); -/* - * Before installing our signal handler functions, record the fact - * that there are no pending signals. - */ - gl_pending_signal = -1; -/* - * Temporarily override the signal handlers of the calling program, - * so that we can intercept signals that would leave the terminal - * in a bad state. - */ - waserr = gl_override_signal_handlers(gl); -/* - * After recording the current terminal settings, switch the terminal - * into raw input mode. - */ - waserr = waserr || gl_raw_terminal_mode(gl); -/* - * Attempt to read the line. - */ - waserr = waserr || gl_get_input_line(gl, start_line, start_pos); -/* - * Restore terminal settings. - */ - gl_restore_terminal_attributes(gl); -/* - * Restore the signal handlers. - */ - gl_restore_signal_handlers(gl); -/* - * Having restored the program terminal and signal environment, - * re-submit any signals that were received. - */ - if(gl_pending_signal != -1) { - raise(gl_pending_signal); - waserr = 1; - }; -/* - * If gl_get_input_line() aborted input due to the user asking to - * temporarily read lines from a file, read the first line from - * this file. - */ - if(!waserr && gl->file_fp) - return gl_get_line(gl, prompt, NULL, 0); -/* - * Return the new input line. - */ - return waserr ? NULL : gl->line; -} - -/*....................................................................... - * Record of the signal handlers of the calling program, so that they - * can be restored later. - * - * Input: - * gl GetLine * The resource object of this library. - * Output: - * return int 0 - OK. - * 1 - Error. - */ -static int gl_override_signal_handlers(GetLine *gl) -{ - GlSignalNode *sig; /* A node in the list of signals to be caught */ -/* - * Set up our signal handler. - */ - SigAction act; - act.sa_handler = gl_signal_handler; - sigemptyset(&act.sa_mask); - act.sa_flags = 0; -/* - * Get the process signal mask so that we can see which signals the - * calling program currently has blocked, and so that we can restore this - * mask before returning to the calling program. - */ - if(sigprocmask(SIG_SETMASK, NULL, &gl->old_signal_set) == -1) { - fprintf(stderr, "gl_get_line(): sigprocmask error: %s\n", strerror(errno)); - return 1; - }; -/* - * Form a new process signal mask from the list of signals that we have - * been asked to trap. - */ - sigemptyset(&gl->new_signal_set); - for(sig=gl->sigs; sig; sig=sig->next) { -/* - * Trap this signal? If it is blocked by the calling program and we - * haven't been told to unblock it, don't arrange to trap this signal. - */ - if(sig->flags & GLS_UNBLOCK_SIG || - !sigismember(&gl->old_signal_set, sig->signo)) { - if(sigaddset(&gl->new_signal_set, sig->signo) == -1) { - fprintf(stderr, "gl_get_line(): sigaddset error: %s\n", - strerror(errno)); - return 1; - }; - }; - }; -/* - * Before installing our signal handlers, block all of the signals - * that we are going to be trapping. - */ - if(sigprocmask(SIG_BLOCK, &gl->new_signal_set, NULL) == -1) { - fprintf(stderr, "gl_get_line(): sigprocmask error: %s\n", strerror(errno)); - return 1; - }; -/* - * Override the actions of the signals that we are trapping. - */ - for(sig=gl->sigs; sig; sig=sig->next) { - if(sigismember(&gl->new_signal_set, sig->signo) && - sigaction(sig->signo, &act, &sig->original)) { - fprintf(stderr, "gl_get_line(): sigaction error: %s\n", strerror(errno)); - return 1; - }; - }; -/* - * Just in case a SIGWINCH signal was sent to the process while our - * SIGWINCH signal handler wasn't in place, check to see if the terminal - * size needs updating. - */ -#ifdef USE_SIGWINCH - if(gl_resize_terminal(gl, 0)) - return 1; -#endif - return 0; -} - -/*....................................................................... - * Restore the signal handlers of the calling program. - * - * Input: - * gl GetLine * The resource object of this library. - * Output: - * return int 0 - OK. - * 1 - Error. - */ -static int gl_restore_signal_handlers(GetLine *gl) -{ - GlSignalNode *sig; /* A node in the list of signals to be caught */ -/* - * Restore application signal handlers that were overriden - * by gl_override_signal_handlers(). - */ - for(sig=gl->sigs; sig; sig=sig->next) { - if(sigismember(&gl->new_signal_set, sig->signo) && - sigaction(sig->signo, &sig->original, NULL)) { - fprintf(stderr, "gl_get_line(): sigaction error: %s\n", strerror(errno)); - return 1; - }; - }; -/* - * Restore the original signal mask. - */ - if(sigprocmask(SIG_SETMASK, &gl->old_signal_set, NULL) == -1) { - fprintf(stderr, "gl_get_line(): sigprocmask error: %s\n", strerror(errno)); - return 1; - }; - return 0; -} - -/*....................................................................... - * This signal handler simply records the fact that a given signal was - * caught in the file-scope gl_pending_signal variable. - */ -static void gl_signal_handler(int signo) -{ - gl_pending_signal = signo; - siglongjmp(gl_setjmp_buffer, 1); -} - -/*....................................................................... - * Switch the terminal into raw mode after storing the previous terminal - * settings in gl->attributes. - * - * Input: - * gl GetLine * The resource object of this program. - * Output: - * return int 0 - OK. - * 1 - Error. - */ -static int gl_raw_terminal_mode(GetLine *gl) -{ - Termios newattr; /* The new terminal attributes */ -/* - * Record the current terminal attributes. - */ - if(tcgetattr(gl->input_fd, &gl->oldattr)) { - fprintf(stderr, "getline(): tcgetattr error: %s\n", strerror(errno)); - return 1; - }; -/* - * This function shouldn't do anything but record the current terminal - * attritubes if editing has been disabled. - */ - if(gl->editor == GL_NO_EDITOR) - return 0; -/* - * Modify the existing attributes. - */ - newattr = gl->oldattr; -/* - * Turn off local echo, canonical input mode and extended input processing. - */ - newattr.c_lflag &= ~(ECHO | ICANON | IEXTEN); -/* - * Don't translate carriage return to newline, turn off input parity - * checking, don't strip off 8th bit, turn off output flow control. - */ - newattr.c_iflag &= ~(ICRNL | INPCK | ISTRIP); -/* - * Clear size bits, turn off parity checking, and allow 8-bit characters. - */ - newattr.c_cflag &= ~(CSIZE | PARENB); - newattr.c_cflag |= CS8; -/* - * Turn off output processing. - */ - newattr.c_oflag &= ~(OPOST); -/* - * Request one byte at a time, without waiting. - */ - newattr.c_cc[VMIN] = 1; - newattr.c_cc[VTIME] = 0; -/* - * Install the new terminal modes. - */ - while(tcsetattr(gl->input_fd, TCSADRAIN, &newattr)) { - if (errno != EINTR) { - fprintf(stderr, "getline(): tcsetattr error: %s\n", strerror(errno)); - return 1; - }; - }; - return 0; -} - -/*....................................................................... - * Restore the terminal attributes recorded in gl->oldattr. - * - * Input: - * gl GetLine * The resource object of this library. - * Output: - * return int 0 - OK. - * 1 - Error. - */ -static int gl_restore_terminal_attributes(GetLine *gl) -{ - int waserr = 0; -/* - * Before changing the terminal attributes, make sure that all output - * has been passed to the terminal. - */ - if(gl_flush_output(gl)) - waserr = 1; -/* - * Reset the terminal attributes to the values that they had on - * entry to gl_get_line(). - */ - while(tcsetattr(gl->input_fd, TCSADRAIN, &gl->oldattr)) { - if(errno != EINTR) { - fprintf(stderr, "gl_get_line(): tcsetattr error: %s\n", strerror(errno)); - waserr = 1; - break; - }; - }; - return waserr; -} - -/*....................................................................... - * Read a new input line from the user. - * - * Input: - * gl GetLine * The resource object of this library. - * start_line char * The initial contents of the input line, or NULL - * if it should start out empty. - * start_pos int If start_line isn't NULL, this specifies the - * index of the character over which the cursor - * should initially be positioned within the line. - * If you just want it to follow the last character - * of the line, send -1. - * Output: - * return int 0 - OK. - * 1 - Error. - */ -static int gl_get_input_line(GetLine *gl, const char *start_line, int start_pos) -{ - char c; /* The character being read */ -/* - * Reset the properties of the line. - */ - gl->ntotal = 0; - gl->buff_curpos = 0; - gl->term_curpos = 0; - gl->insert_curpos = 0; - gl->number = -1; - gl->endline = 0; - gl->vi.command = 0; - gl->vi.undo.line[0] = '\0'; - gl->vi.undo.ntotal = 0; - gl->vi.undo.buff_curpos = 0; - gl->vi.repeat.fn = 0; - gl->last_signal = -1; -/* - * Reset the history search pointers. - */ - if(_glh_cancel_search(gl->glh)) - return 1; -/* - * Draw the prompt at the start of the line. - */ - if(gl_display_prompt(gl)) - return 1; -/* - * Present an initial line? - */ - if(start_line) { - char *cptr; /* A pointer into gl->line[] */ -/* - * Load the line into the buffer, and display it. - */ - if(start_line != gl->line) - strncpy(gl->line, start_line, gl->linelen); - gl->line[gl->linelen] = '\0'; - gl->ntotal = strlen(gl->line); -/* - * Strip off any trailing newline and carriage return characters. - */ - for(cptr=gl->line + gl->ntotal - 1; cptr >= gl->line && - (*cptr=='\n' || *cptr=='\r'); cptr--,gl->ntotal--) - ; - if(gl->ntotal < 0) - gl->ntotal = 0; - gl->line[gl->ntotal] = '\0'; -/* - * Display the string that remains. - */ - if(gl_output_string(gl, gl->line, '\0')) - return 1; -/* - * Where should the cursor be placed within the line? - */ - if(start_pos < 0 || start_pos > gl->ntotal) { - if(gl_place_cursor(gl, gl->ntotal)) - return 1; - } else { - if(gl_place_cursor(gl, start_pos)) - return 1; - }; - } else { - gl->line[0] = '\0'; - }; -/* - * Preload a history line? - */ - if(gl->preload_history) { - gl->preload_history = 0; - if(gl->preload_id) { - if(_glh_recall_line(gl->glh, gl->preload_id, gl->line, gl->linelen)) { - gl->ntotal = strlen(gl->line); - gl->buff_curpos = strlen(gl->line); - }; - gl->preload_id = 0; - }; - gl_redisplay(gl, 1); - }; -/* - * Read one character at a time. - */ - while(gl_read_character(gl, &c) == 0) { -/* - * Increment the count of the number of key sequences entered. - */ - gl->keyseq_count++; -/* - * Interpret the character either as the start of a new key-sequence, - * as a continuation of a repeat count, or as a printable character - * to be added to the line. - */ - if(gl_interpret_char(gl, c)) - break; -/* - * If we just ran an action function which temporarily asked for - * input to be taken from a file, abort this call. - */ - if(gl->file_fp) - return 0; -/* - * Has the line been completed? - */ - if(gl->endline) - return gl_line_ended(gl, isprint((int)(unsigned char) c) ? c : '\n', - gl->echo && (c=='\n' || c=='\r')); - }; -/* - * To get here, gl_read_character() must have returned non-zero. See - * if this was because a signal was caught that requested that the - * current line be returned. - */ - if(gl->endline) - return gl_line_ended(gl, '\n', gl->echo); - return 1; -} - -/*....................................................................... - * Add a character to the line buffer at the current cursor position, - * inserting or overwriting according the current mode. - * - * Input: - * gl GetLine * The resource object of this library. - * c char The character to be added. - * Output: - * return int 0 - OK. - * 1 - Insufficient room. - */ -static int gl_add_char_to_line(GetLine *gl, char c) -{ -/* - * Keep a record of the current cursor position. - */ - int buff_curpos = gl->buff_curpos; - int term_curpos = gl->term_curpos; -/* - * Work out the displayed width of the new character. - */ - int width = gl_displayed_char_width(gl, c, term_curpos); -/* - * If we are in insert mode, or at the end of the line, - * check that we can accomodate a new character in the buffer. - * If not, simply return, leaving it up to the calling program - * to check for the absence of a newline character. - */ - if((gl->insert || buff_curpos >= gl->ntotal) && gl->ntotal >= gl->linelen) - return 0; -/* - * Are we adding characters to the line (ie. inserting or appending)? - */ - if(gl->insert || buff_curpos >= gl->ntotal) { -/* - * If inserting, make room for the new character. - */ - if(buff_curpos < gl->ntotal) { - memmove(gl->line + buff_curpos + 1, gl->line + buff_curpos, - gl->ntotal - buff_curpos); - }; -/* - * Copy the character into the buffer. - */ - gl->line[buff_curpos] = c; - gl->buff_curpos++; -/* - * If the line was extended, update the record of the string length - * and terminate the extended string. - */ - gl->ntotal++; - gl->line[gl->ntotal] = '\0'; -/* - * Redraw the line from the cursor position to the end of the line, - * and move the cursor to just after the added character. - */ - if(gl_output_string(gl, gl->line + buff_curpos, '\0') || - gl_set_term_curpos(gl, term_curpos + width)) - return 1; -/* - * Are we overwriting an existing character? - */ - } else { -/* - * Get the widths of the character to be overwritten and the character - * that is going to replace it. - */ - int old_width = gl_displayed_char_width(gl, gl->line[buff_curpos], - term_curpos); -/* - * Overwrite the character in the buffer. - */ - gl->line[buff_curpos] = c; -/* - * If we are replacing with a narrower character, we need to - * redraw the terminal string to the end of the line, then - * overwrite the trailing old_width - width characters - * with spaces. - */ - if(old_width > width) { - if(gl_output_string(gl, gl->line + buff_curpos, '\0')) - return 1; -/* - * Clear to the end of the terminal. - */ - if(gl_output_control_sequence(gl, gl->nline, gl->clear_eod)) - return 1; -/* - * Move the cursor to the end of the new character. - */ - if(gl_set_term_curpos(gl, term_curpos + width)) - return 1; - gl->buff_curpos++; -/* - * If we are replacing with a wider character, then we will be - * inserting new characters, and thus extending the line. - */ - } else if(width > old_width) { -/* - * Redraw the line from the cursor position to the end of the line, - * and move the cursor to just after the added character. - */ - if(gl_output_string(gl, gl->line + buff_curpos, '\0') || - gl_set_term_curpos(gl, term_curpos + width)) - return 1; - gl->buff_curpos++; -/* - * The original and replacement characters have the same width, - * so simply overwrite. - */ - } else { -/* - * Copy the character into the buffer. - */ - gl->line[buff_curpos] = c; - gl->buff_curpos++; -/* - * Overwrite the original character. - */ - if(gl_output_char(gl, c, gl->line[gl->buff_curpos])) - return 1; - }; - }; - return 0; -} - -/*....................................................................... - * Insert/append a string to the line buffer and terminal at the current - * cursor position. - * - * Input: - * gl GetLine * The resource object of this library. - * s char * The string to be added. - * Output: - * return int 0 - OK. - * 1 - Insufficient room. - */ -static int gl_add_string_to_line(GetLine *gl, const char *s) -{ - int buff_slen; /* The length of the string being added to line[] */ - int term_slen; /* The length of the string being written to the terminal */ - int buff_curpos; /* The original value of gl->buff_curpos */ - int term_curpos; /* The original value of gl->term_curpos */ -/* - * Keep a record of the current cursor position. - */ - buff_curpos = gl->buff_curpos; - term_curpos = gl->term_curpos; -/* - * How long is the string to be added? - */ - buff_slen = strlen(s); - term_slen = gl_displayed_string_width(gl, s, buff_slen, term_curpos); -/* - * Check that we can accomodate the string in the buffer. - * If not, simply return, leaving it up to the calling program - * to check for the absence of a newline character. - */ - if(gl->ntotal + buff_slen > gl->linelen) - return 0; -/* - * Move the characters that follow the cursor in the buffer by - * buff_slen characters to the right. - */ - if(gl->ntotal > gl->buff_curpos) { - memmove(gl->line + gl->buff_curpos + buff_slen, gl->line + gl->buff_curpos, - gl->ntotal - gl->buff_curpos); - }; -/* - * Copy the string into the buffer. - */ - memcpy(gl->line + gl->buff_curpos, s, buff_slen); - gl->ntotal += buff_slen; - gl->buff_curpos += buff_slen; -/* - * Maintain the buffer properly terminated. - */ - gl->line[gl->ntotal] = '\0'; -/* - * Write the modified part of the line to the terminal, then move - * the terminal cursor to the end of the displayed input string. - */ - if(gl_output_string(gl, gl->line + buff_curpos, '\0') || - gl_set_term_curpos(gl, term_curpos + term_slen)) - return 1; - return 0; -} - -/*....................................................................... - * Read a single character from the terminal. - * - * Input: - * gl GetLine * The resource object of this library. - * Output: - * return int 0 - OK. - * 1 - Either an I/O error occurred, or a signal was - * caught who's disposition is to abort gl_get_line() - * or to have gl_get_line() return the current line - * as though the user had pressed return. In the - * latter case gl->endline will be non-zero. - */ -static int gl_read_character(GetLine *gl, char *c) -{ -/* - * Before waiting for a new character to be input, flush unwritten - * characters to the terminal. - */ - if(gl_flush_output(gl)) - return 1; -/* - * We may have to repeat the read if window change signals are received. - */ - for(;;) { -/* - * If the endline flag becomes set, don't wait for another character. - */ - if(gl->endline) - return 1; -/* - * Since the code in this function can block, trap signals. - */ - if(sigsetjmp(gl_setjmp_buffer, 1)==0) { -/* - * Unblock the signals that we are trapping. - */ - if(sigprocmask(SIG_UNBLOCK, &gl->new_signal_set, NULL) == -1) { - fprintf(stderr, "getline(): sigprocmask error: %s\n", strerror(errno)); - return 1; - }; -/* - * If select() is available, watch for activity on any file descriptors - * that the user has registered, and for data available on the terminal - * file descriptor. - */ -#ifdef HAVE_SELECT - if(gl_event_handler(gl)) - return 1; -#endif -/* - * Read one character from the terminal. This could take more - * than one call if an interrupt that we aren't trapping is - * received. - */ - while(read(gl->input_fd, (void *)c, 1) != 1) { - if(errno != EINTR) { -#ifdef EAGAIN - if(!errno) /* This can happen with SysV O_NDELAY */ - errno = EAGAIN; -#endif - return 1; - }; - }; -/* - * Block all of the signals that we are trapping. - */ - if(sigprocmask(SIG_BLOCK, &gl->new_signal_set, NULL) == -1) { - fprintf(stderr, "getline(): sigprocmask error: %s\n", strerror(errno)); - return 1; - }; - return 0; - }; -/* - * To get here, one of the signals that we are trapping must have - * been received. Note that by using sigsetjmp() instead of setjmp() - * the signal mask that was blocking these signals will have been - * reinstated, so we can be sure that no more of these signals will - * be received until we explicitly unblock them again. - */ - if(gl_check_caught_signal(gl)) - return 1; - }; -} - -/*....................................................................... - * This function is called to handle signals caught between calls to - * sigsetjmp() and siglongjmp(). - * - * Input: - * gl GetLine * The resource object of this library. - * Output: - * return int 0 - Signal handled internally. - * 1 - Signal requires gl_get_line() to abort. - */ -static int gl_check_caught_signal(GetLine *gl) -{ - GlSignalNode *sig; /* The signal disposition */ - SigAction keep_action; /* The signal disposition of tecla signal handlers */ -/* - * Was no signal caught? - */ - if(gl_pending_signal == -1) - return 0; -/* - * Record the signal that was caught, so that the user can query it later. - */ - gl->last_signal = gl_pending_signal; -/* - * Did we receive a terminal size signal? - */ -#ifdef USE_SIGWINCH - if(gl_pending_signal == SIGWINCH && gl_resize_terminal(gl, 1)) - return 1; -#endif -/* - * Lookup the requested disposition of this signal. - */ - for(sig=gl->sigs; sig && sig->signo != gl_pending_signal; sig=sig->next) - ; - if(!sig) - return 0; -/* - * Start a fresh line? - */ - if(sig->flags & GLS_RESTORE_LINE) { - if(gl_set_term_curpos(gl, gl_buff_curpos_to_term_curpos(gl, gl->ntotal)) || - gl_output_raw_string(gl, "\r\n")) - return 1; - }; -/* - * Restore terminal settings to how they were before gl_get_line() was - * called? - */ - if(sig->flags & GLS_RESTORE_TTY) - gl_restore_terminal_attributes(gl); -/* - * Restore signal handlers to how they were before gl_get_line() was - * called? If this hasn't been requested, only reinstate the signal - * handler of the signal that we are handling. - */ - if(sig->flags & GLS_RESTORE_SIG) { - gl_restore_signal_handlers(gl); - } else { - (void) sigaction(sig->signo, &sig->original, &keep_action); - (void) sigprocmask(SIG_UNBLOCK, &sig->proc_mask, NULL); - }; -/* - * Forward the signal to the application's signal handler. - */ - if(!(sig->flags & GLS_DONT_FORWARD)) - raise(gl_pending_signal); - gl_pending_signal = -1; -/* - * Reinstate our signal handlers. - */ - if(sig->flags & GLS_RESTORE_SIG) { - gl_override_signal_handlers(gl); - } else { - (void) sigaction(sig->signo, &keep_action, NULL); - (void) sigprocmask(SIG_BLOCK, &sig->proc_mask, NULL); - }; -/* - * Do we need to reinstate our terminal settings? - */ - if(sig->flags & GLS_RESTORE_TTY) - gl_raw_terminal_mode(gl); -/* - * Redraw the line? - */ - if(sig->flags & GLS_REDRAW_LINE && gl_redisplay(gl, 1)) - return 1; -/* - * Set errno. - */ - errno = sig->errno_value; -/* - * What next? - */ - switch(sig->after) { - case GLS_RETURN: - return gl_newline(gl, 1); - break; - case GLS_ABORT: - return 1; - break; - case GLS_CONTINUE: - return 0; - break; - }; - return 0; -} - -/*....................................................................... - * Get pertinent terminal control strings and the initial terminal size. - * - * Input: - * gl GetLine * The resource object of this library. - * term char * The type of the terminal. - * Output: - * return int 0 - OK. - * 1 - Error. - */ -static int gl_control_strings(GetLine *gl, const char *term) -{ - int bad_term = 0; /* True if term is unusable */ -/* - * Discard any existing control strings from a previous terminal. - */ - gl->left = NULL; - gl->right = NULL; - gl->up = NULL; - gl->down = NULL; - gl->home = NULL; - gl->bol = 0; - gl->clear_eol = NULL; - gl->clear_eod = NULL; - gl->u_arrow = NULL; - gl->d_arrow = NULL; - gl->l_arrow = NULL; - gl->r_arrow = NULL; - gl->sound_bell = NULL; - gl->bold = NULL; - gl->underline = NULL; - gl->standout = NULL; - gl->dim = NULL; - gl->reverse = NULL; - gl->blink = NULL; - gl->text_attr_off = NULL; - gl->nline = 0; - gl->ncolumn = 0; -#ifdef USE_TERMINFO - gl->left_n = NULL; - gl->right_n = NULL; -#endif -/* - * If possible lookup the information in a terminal information - * database. - */ -#ifdef USE_TERMINFO - if(!term || setupterm((char *)term, gl->input_fd, NULL) == ERR) { - bad_term = 1; - } else { - _clr_StringGroup(gl->capmem); - gl->left = gl_tigetstr(gl, "cub1"); - gl->right = gl_tigetstr(gl, "cuf1"); - gl->up = gl_tigetstr(gl, "cuu1"); - gl->down = gl_tigetstr(gl, "cud1"); - gl->home = gl_tigetstr(gl, "home"); - gl->clear_eol = gl_tigetstr(gl, "el"); - gl->clear_eod = gl_tigetstr(gl, "ed"); - gl->u_arrow = gl_tigetstr(gl, "kcuu1"); - gl->d_arrow = gl_tigetstr(gl, "kcud1"); - gl->l_arrow = gl_tigetstr(gl, "kcub1"); - gl->r_arrow = gl_tigetstr(gl, "kcuf1"); - gl->left_n = gl_tigetstr(gl, "cub"); - gl->right_n = gl_tigetstr(gl, "cuf"); - gl->sound_bell = gl_tigetstr(gl, "bel"); - gl->bold = gl_tigetstr(gl, "bold"); - gl->underline = gl_tigetstr(gl, "smul"); - gl->standout = gl_tigetstr(gl, "smso"); - gl->dim = gl_tigetstr(gl, "dim"); - gl->reverse = gl_tigetstr(gl, "rev"); - gl->blink = gl_tigetstr(gl, "blink"); - gl->text_attr_off = gl_tigetstr(gl, "sgr0"); - }; -#elif defined(USE_TERMCAP) - if(!term || tgetent(gl->tgetent_buf, (char *)term) < 0) { - bad_term = 1; - } else { - char *tgetstr_buf_ptr = gl->tgetstr_buf; - _clr_StringGroup(gl->capmem); - gl->left = gl_tgetstr(gl, "le", &tgetstr_buf_ptr); - gl->right = gl_tgetstr(gl, "nd", &tgetstr_buf_ptr); - gl->up = gl_tgetstr(gl, "up", &tgetstr_buf_ptr); - gl->down = gl_tgetstr(gl, "do", &tgetstr_buf_ptr); - gl->home = gl_tgetstr(gl, "ho", &tgetstr_buf_ptr); - gl->clear_eol = gl_tgetstr(gl, "ce", &tgetstr_buf_ptr); - gl->clear_eod = gl_tgetstr(gl, "cd", &tgetstr_buf_ptr); - gl->u_arrow = gl_tgetstr(gl, "ku", &tgetstr_buf_ptr); - gl->d_arrow = gl_tgetstr(gl, "kd", &tgetstr_buf_ptr); - gl->l_arrow = gl_tgetstr(gl, "kl", &tgetstr_buf_ptr); - gl->r_arrow = gl_tgetstr(gl, "kr", &tgetstr_buf_ptr); - gl->sound_bell = gl_tgetstr(gl, "bl", &tgetstr_buf_ptr); - gl->bold = gl_tgetstr(gl, "md", &tgetstr_buf_ptr); - gl->underline = gl_tgetstr(gl, "us", &tgetstr_buf_ptr); - gl->standout = gl_tgetstr(gl, "so", &tgetstr_buf_ptr); - gl->dim = gl_tgetstr(gl, "mh", &tgetstr_buf_ptr); - gl->reverse = gl_tgetstr(gl, "mr", &tgetstr_buf_ptr); - gl->blink = gl_tgetstr(gl, "mb", &tgetstr_buf_ptr); - gl->text_attr_off = gl_tgetstr(gl, "me", &tgetstr_buf_ptr); - }; -#endif -/* - * Report term being unusable. - */ - if(bad_term) { - fprintf(stderr, "Bad terminal type: \"%s\". Will assume vt100.\n", - term ? term : "(null)"); - }; -/* - * Fill in missing information with ANSI VT100 strings. - */ - if(!gl->left) - gl->left = "\b"; /* ^H */ - if(!gl->right) - gl->right = GL_ESC_STR "[C"; - if(!gl->up) - gl->up = GL_ESC_STR "[A"; - if(!gl->down) - gl->down = "\n"; - if(!gl->home) - gl->home = GL_ESC_STR "[H"; - if(!gl->bol) - gl->bol = "\r"; - if(!gl->clear_eol) - gl->clear_eol = GL_ESC_STR "[K"; - if(!gl->clear_eod) - gl->clear_eod = GL_ESC_STR "[J"; - if(!gl->u_arrow) - gl->u_arrow = GL_ESC_STR "[A"; - if(!gl->d_arrow) - gl->d_arrow = GL_ESC_STR "[B"; - if(!gl->l_arrow) - gl->l_arrow = GL_ESC_STR "[D"; - if(!gl->r_arrow) - gl->r_arrow = GL_ESC_STR "[C"; - if(!gl->sound_bell) - gl->sound_bell = "\a"; - if(!gl->bold) - gl->bold = GL_ESC_STR "[1m"; - if(!gl->underline) - gl->underline = GL_ESC_STR "[4m"; - if(!gl->standout) - gl->standout = GL_ESC_STR "[1;7m"; - if(!gl->dim) - gl->dim = ""; /* Not available */ - if(!gl->reverse) - gl->reverse = GL_ESC_STR "[7m"; - if(!gl->blink) - gl->blink = GL_ESC_STR "[5m"; - if(!gl->text_attr_off) - gl->text_attr_off = GL_ESC_STR "[m"; -/* - * Find out the current terminal size. - */ - (void) gl_terminal_size(gl, GL_DEF_NCOLUMN, GL_DEF_NLINE); - return 0; -} - -#ifdef USE_TERMINFO -/*....................................................................... - * This is a private function of gl_control_strings() used to look up - * a termninal capability string from the terminfo database and make - * a private copy of it. - * - * Input: - * gl GetLine * The resource object of gl_get_line(). - * name const char * The name of the terminfo string to look up. - * Output: - * return const char * The local copy of the capability, or NULL - * if not available. - */ -static const char *gl_tigetstr(GetLine *gl, const char *name) -{ - const char *value = tigetstr((char *)name); - if(!value || value == (char *) -1) - return NULL; - return _sg_store_string(gl->capmem, value, 0); -} -#elif defined(USE_TERMCAP) -/*....................................................................... - * This is a private function of gl_control_strings() used to look up - * a termninal capability string from the termcap database and make - * a private copy of it. Note that some emulations of tgetstr(), such - * as that used by Solaris, ignores the buffer pointer that is past to - * it, so we can't assume that a private copy has been made that won't - * be trashed by another call to gl_control_strings() by another - * GetLine object. So we make what may be a redundant private copy - * of the string in gl->capmem. - * - * Input: - * gl GetLine * The resource object of gl_get_line(). - * name const char * The name of the terminfo string to look up. - * Input/Output: - * bufptr char ** On input *bufptr points to the location in - * gl->tgetstr_buf at which to record the - * capability string. On output *bufptr is - * incremented over the stored string. - * Output: - * return const char * The local copy of the capability, or NULL - * on error. - */ -static const char *gl_tgetstr(GetLine *gl, const char *name, char **bufptr) -{ - const char *value = tgetstr((char *)name, bufptr); - if(!value || value == (char *) -1) - return NULL; - return _sg_store_string(gl->capmem, value, 0); -} -#endif - -/*....................................................................... - * This is an action function that implements a user interrupt (eg. ^C). - */ -static KT_KEY_FN(gl_user_interrupt) -{ - raise(SIGINT); - return 1; -} - -/*....................................................................... - * This is an action function that implements the abort signal. - */ -static KT_KEY_FN(gl_abort) -{ - raise(SIGABRT); - return 1; -} - -/*....................................................................... - * This is an action function that sends a suspend signal (eg. ^Z) to the - * the parent process. - */ -static KT_KEY_FN(gl_suspend) -{ - raise(SIGTSTP); - return 0; -} - -/*....................................................................... - * This is an action function that halts output to the terminal. - */ -static KT_KEY_FN(gl_stop_output) -{ - tcflow(gl->output_fd, TCOOFF); - return 0; -} - -/*....................................................................... - * This is an action function that resumes halted terminal output. - */ -static KT_KEY_FN(gl_start_output) -{ - tcflow(gl->output_fd, TCOON); - return 0; -} - -/*....................................................................... - * This is an action function that allows the next character to be accepted - * without any interpretation as a special character. - */ -static KT_KEY_FN(gl_literal_next) -{ - char c; /* The character to be added to the line */ - int i; -/* - * Get the character to be inserted literally. - */ - if(gl_read_character(gl, &c)) - return 1; -/* - * Add the character to the line 'count' times. - */ - for(i=0; incolumn) % TAB_WIDTH); - if(IS_CTRL_CHAR(c)) - return 2; - if(!isprint((int)(unsigned char) c)) { - char string[TAB_WIDTH + 4]; - sprintf(string, "\\%o", (int)(unsigned char)c); - return strlen(string); - }; - return 1; -} - -/*....................................................................... - * Work out the length of given string of characters on the terminal. - * - * Input: - * gl GetLine * The resource object of this library. - * string char * The string to be measured. - * nc int The number of characters to be measured, or -1 - * to measure the whole string. - * term_curpos int The destination terminal location of the character. - * This is needed because the width of tab characters - * depends on where they are, relative to the - * preceding tab stops. - * Output: - * return int The number of displayed characters. - */ -static int gl_displayed_string_width(GetLine *gl, const char *string, int nc, - int term_curpos) -{ - int slen=0; /* The displayed number of characters */ - int i; -/* - * How many characters are to be measured? - */ - if(nc < 0) - nc = strlen(string); -/* - * Add up the length of the displayed string. - */ - for(i=0; iecho) { - int ndone = 0; /* The number of characters written so far */ -/* - * How long is the string to be written? - */ - int slen = strlen(string); -/* - * Attempt to write the string to the terminal, restarting the - * write if a signal is caught. - */ - while(ndone < slen) { - int nnew = fwrite(string + ndone, sizeof(char), slen-ndone, - gl->output_fp); - if(nnew > 0) - ndone += nnew; - else if(errno != EINTR) - return 1; - }; - }; - return 0; -} - -/*....................................................................... - * Output a terminal control sequence. When using terminfo, - * this must be a sequence returned by tgetstr() or tigetstr() - * respectively. - * - * Input: - * gl GetLine * The resource object of this library. - * nline int The number of lines affected by the operation, - * or 1 if not relevant. - * string char * The control sequence to be sent. - * Output: - * return int 0 - OK. - * 1 - Error. - */ -static int gl_output_control_sequence(GetLine *gl, int nline, - const char *string) -{ - if(gl->echo) { -#if defined(USE_TERMINFO) || defined(USE_TERMCAP) - tputs_fp = gl->output_fp; - errno = 0; - tputs((char *)string, nline, gl_tputs_putchar); - return errno != 0; -#else - return gl_output_raw_string(gl, string); -#endif - }; - return 0; -} - -/*....................................................................... - * Move the terminal cursor n characters to the left or right. - * - * Input: - * gl GetLine * The resource object of this program. - * n int number of positions to the right (> 0) or left (< 0). - * Output: - * return int 0 - OK. - * 1 - Error. - */ -static int gl_terminal_move_cursor(GetLine *gl, int n) -{ - int cur_row, cur_col; /* The current terminal row and column index of */ - /* the cursor wrt the start of the input line. */ - int new_row, new_col; /* The target terminal row and column index of */ - /* the cursor wrt the start of the input line. */ -/* - * How far can we move left? - */ - if(gl->term_curpos + n < 0) - n = gl->term_curpos; -/* - * Break down the current and target cursor locations into rows and columns. - */ - cur_row = gl->term_curpos / gl->ncolumn; - cur_col = gl->term_curpos % gl->ncolumn; - new_row = (gl->term_curpos + n) / gl->ncolumn; - new_col = (gl->term_curpos + n) % gl->ncolumn; -/* - * Move down to the next line. - */ - for(; cur_row < new_row; cur_row++) { - if(gl_output_control_sequence(gl, 1, gl->down)) - return 1; - }; -/* - * Move up to the previous line. - */ - for(; cur_row > new_row; cur_row--) { - if(gl_output_control_sequence(gl, 1, gl->up)) - return 1; - }; -/* - * Move to the right within the target line? - */ - if(cur_col < new_col) { -#ifdef USE_TERMINFO -/* - * Use a parameterized control sequence if it generates less control - * characters (guess based on ANSI terminal termcap entry). - */ - if(gl->right_n != NULL && new_col - cur_col > 1) { - if(gl_output_control_sequence(gl, 1, tparm((char *)gl->right_n, - (long)(new_col - cur_col), 0l, 0l, 0l, 0l, 0l, 0l, 0l, 0l))) - return 1; - } else -#endif - { - for(; cur_col < new_col; cur_col++) { - if(gl_output_control_sequence(gl, 1, gl->right)) - return 1; - }; - }; -/* - * Move to the left within the target line? - */ - } else if(cur_col > new_col) { -#ifdef USE_TERMINFO -/* - * Use a parameterized control sequence if it generates less control - * characters (guess based on ANSI terminal termcap entry). - */ - if(gl->left_n != NULL && cur_col - new_col > 3) { - if(gl_output_control_sequence(gl, 1, tparm((char *)gl->left_n, - (long)(cur_col - new_col), 0l, 0l, 0l, 0l, 0l, 0l, 0l, 0l))) - return 1; - } else -#endif - { - for(; cur_col > new_col; cur_col--) { - if(gl_output_control_sequence(gl, 1, gl->left)) - return 1; - }; - }; - } -/* - * Update the recorded position of the terminal cursor. - */ - gl->term_curpos += n; - return 0; -} - -/*....................................................................... - * Write a character to the terminal after expanding tabs and control - * characters to their multi-character representations. - * - * Input: - * gl GetLine * The resource object of this program. - * c char The character to be output. - * pad char Many terminals have the irritating feature that - * when one writes a character in the last column of - * of the terminal, the cursor isn't wrapped to the - * start of the next line until one more character - * is written. Some terminals don't do this, so - * after such a write, we don't know where the - * terminal is unless we output an extra character. - * This argument specifies the character to write. - * If at the end of the input line send '\0' or a - * space, and a space will be written. Otherwise, - * pass the next character in the input line - * following the one being written. - * Output: - * return int 0 - OK. - */ -static int gl_output_char(GetLine *gl, char c, char pad) -{ - char string[TAB_WIDTH + 4]; /* A work area for composing compound strings */ - int nchar; /* The number of terminal characters */ - int i; -/* - * Check for special characters. - */ - if(c == '\t') { -/* - * How many spaces do we need to represent a tab at the current terminal - * column? - */ - nchar = gl_displayed_char_width(gl, '\t', gl->term_curpos); -/* - * Compose the tab string. - */ - for(i=0; iterm_curpos += nchar; -/* - * If the new character ended exactly at the end of a line, - * most terminals won't move the cursor onto the next line until we - * have written a character on the next line, so append an extra - * space then move the cursor back. - */ - if(gl->term_curpos % gl->ncolumn == 0) { - int term_curpos = gl->term_curpos; - if(gl_output_char(gl, pad ? pad : ' ', ' ') || - gl_set_term_curpos(gl, term_curpos)) - return 1; - }; - return 0; -} - -/*....................................................................... - * Write a string to the terminal after expanding tabs and control - * characters to their multi-character representations. - * - * Input: - * gl GetLine * The resource object of this program. - * string char * The string to be output. - * pad char Many terminals have the irritating feature that - * when one writes a character in the last column of - * of the terminal, the cursor isn't wrapped to the - * start of the next line until one more character - * is written. Some terminals don't do this, so - * after such a write, we don't know where the - * terminal is unless we output an extra character. - * This argument specifies the character to write. - * If at the end of the input line send '\0' or a - * space, and a space will be written. Otherwise, - * pass the next character in the input line - * following the one being written. - * Output: - * return int 0 - OK. - */ -static int gl_output_string(GetLine *gl, const char *string, char pad) -{ - const char *cptr; /* A pointer into string[] */ - for(cptr=string; *cptr; cptr++) { - char nextc = cptr[1]; - if(gl_output_char(gl, *cptr, nextc ? nextc : pad)) - return 1; - }; - return 0; -} - -/*....................................................................... - * Given a character position within gl->line[], work out the - * corresponding gl->term_curpos position on the terminal. - * - * Input: - * gl GetLine * The resource object of this library. - * buff_curpos int The position within gl->line[]. - * - * Output: - * return int The gl->term_curpos position on the terminal. - */ -static int gl_buff_curpos_to_term_curpos(GetLine *gl, int buff_curpos) -{ - return gl->prompt_len + gl_displayed_string_width(gl, gl->line, buff_curpos, - gl->prompt_len); -} - -/*....................................................................... - * Move the terminal cursor position. - * - * Input: - * gl GetLine * The resource object of this library. - * term_curpos int The destination terminal cursor position. - * Output: - * return int 0 - OK. - * 1 - Error. - */ -static int gl_set_term_curpos(GetLine *gl, int term_curpos) -{ - return gl_terminal_move_cursor(gl, term_curpos - gl->term_curpos); -} - -/*....................................................................... - * This is an action function that moves the buffer cursor one character - * left, and updates the terminal cursor to match. - */ -static KT_KEY_FN(gl_cursor_left) -{ - return gl_place_cursor(gl, gl->buff_curpos - count); -} - -/*....................................................................... - * This is an action function that moves the buffer cursor one character - * right, and updates the terminal cursor to match. - */ -static KT_KEY_FN(gl_cursor_right) -{ - return gl_place_cursor(gl, gl->buff_curpos + count); -} - -/*....................................................................... - * This is an action function that toggles between overwrite and insert - * mode. - */ -static KT_KEY_FN(gl_insert_mode) -{ - gl->insert = !gl->insert; - return 0; -} - -/*....................................................................... - * This is an action function which moves the cursor to the beginning of - * the line. - */ -static KT_KEY_FN(gl_beginning_of_line) -{ - return gl_place_cursor(gl, 0); -} - -/*....................................................................... - * This is an action function which moves the cursor to the end of - * the line. - */ -static KT_KEY_FN(gl_end_of_line) -{ - return gl_place_cursor(gl, gl->ntotal); -} - -/*....................................................................... - * This is an action function which deletes the entire contents of the - * current line. - */ -static KT_KEY_FN(gl_delete_line) -{ -/* - * If in vi command mode, preserve the current line for potential - * use by vi-undo. - */ - gl_save_for_undo(gl); -/* - * Copy the contents of the line to the cut buffer. - */ - strcpy(gl->cutbuf, gl->line); -/* - * Clear the buffer. - */ - gl->ntotal = 0; - gl->line[0] = '\0'; -/* - * Move the terminal cursor to just after the prompt. - */ - if(gl_place_cursor(gl, 0)) - return 1; -/* - * Clear from the end of the prompt to the end of the terminal. - */ - if(gl_output_control_sequence(gl, gl->nline, gl->clear_eod)) - return 1; - return 0; -} - -/*....................................................................... - * This is an action function which deletes all characters between the - * current cursor position and the end of the line. - */ -static KT_KEY_FN(gl_kill_line) -{ -/* - * If in vi command mode, preserve the current line for potential - * use by vi-undo. - */ - gl_save_for_undo(gl); -/* - * Copy the part of the line that is about to be deleted to the cut buffer. - */ - strcpy(gl->cutbuf, gl->line + gl->buff_curpos); -/* - * Terminate the buffered line at the current cursor position. - */ - gl->ntotal = gl->buff_curpos; - gl->line[gl->ntotal] = '\0'; -/* - * Clear the part of the line that follows the cursor. - */ - if(gl_output_control_sequence(gl, gl->nline, gl->clear_eod)) - return 1; -/* - * Explicitly reset the cursor position to allow vi command mode - * constraints on its position to be set. - */ - return gl_place_cursor(gl, gl->buff_curpos); -} - -/*....................................................................... - * This is an action function which deletes all characters between the - * start of the line and the current cursor position. - */ -static KT_KEY_FN(gl_backward_kill_line) -{ -/* - * How many characters are to be deleted from before the cursor? - */ - int nc = gl->buff_curpos - gl->insert_curpos; - if (!nc) - return 0; -/* - * Move the cursor to the start of the line, or in vi input mode, - * the start of the sub-line at which insertion started, and delete - * up to the old cursor position. - */ - return gl_place_cursor(gl, gl->insert_curpos) || - gl_delete_chars(gl, nc, gl->editor == GL_EMACS_MODE || gl->vi.command); -} - -/*....................................................................... - * This is an action function which moves the cursor forward by a word. - */ -static KT_KEY_FN(gl_forward_word) -{ - return gl_place_cursor(gl, gl_nth_word_end_forward(gl, count) + - (gl->editor==GL_EMACS_MODE)); -} - -/*....................................................................... - * This is an action function which moves the cursor forward to the start - * of the next word. - */ -static KT_KEY_FN(gl_forward_to_word) -{ - return gl_place_cursor(gl, gl_nth_word_start_forward(gl, count)); -} - -/*....................................................................... - * This is an action function which moves the cursor backward by a word. - */ -static KT_KEY_FN(gl_backward_word) -{ - return gl_place_cursor(gl, gl_nth_word_start_backward(gl, count)); -} - -/*....................................................................... - * Delete one or more characters, starting with the one under the cursor. - * - * Input: - * gl GetLine * The resource object of this library. - * nc int The number of characters to delete. - * cut int If true, copy the characters to the cut buffer. - * Output: - * return int 0 - OK. - * 1 - Error. - */ -static int gl_delete_chars(GetLine *gl, int nc, int cut) -{ -/* - * If in vi command mode, preserve the current line for potential - * use by vi-undo. - */ - gl_save_for_undo(gl); -/* - * If there are fewer than nc characters following the cursor, limit - * nc to the number available. - */ - if(gl->buff_curpos + nc > gl->ntotal) - nc = gl->ntotal - gl->buff_curpos; -/* - * Copy the about to be deleted region to the cut buffer. - */ - if(cut) { - memcpy(gl->cutbuf, gl->line + gl->buff_curpos, nc); - gl->cutbuf[nc] = '\0'; - } -/* - * Nothing to delete? - */ - if(nc <= 0) - return 0; -/* - * In vi overwrite mode, restore any previously overwritten characters - * from the undo buffer. - */ - if(gl->editor == GL_VI_MODE && !gl->vi.command && !gl->insert) { -/* - * How many of the characters being deleted can be restored from the - * undo buffer? - */ - int nrestore = gl->buff_curpos + nc <= gl->vi.undo.ntotal ? - nc : gl->vi.undo.ntotal - gl->buff_curpos; -/* - * Restore any available characters. - */ - if(nrestore > 0) - memcpy(gl->line + gl->buff_curpos, gl->vi.undo.line + gl->buff_curpos, - nrestore); -/* - * If their were insufficient characters in the undo buffer, then this - * implies that we are deleting from the end of the line, so we need - * to terminate the line either where the undo buffer ran out, or if - * we are deleting from beyond the end of the undo buffer, at the current - * cursor position. - */ - if(nc != nrestore) { - gl->ntotal = gl->vi.undo.ntotal > gl->buff_curpos ? gl->vi.undo.ntotal : - gl->buff_curpos; - gl->line[gl->ntotal] = '\0'; - }; - } else { -/* - * Copy the remaining part of the line back over the deleted characters. - */ - memmove(gl->line + gl->buff_curpos, gl->line + gl->buff_curpos + nc, - gl->ntotal - gl->buff_curpos - nc + 1); - gl->ntotal -= nc; - }; -/* - * Redraw the remaining characters following the cursor. - */ - if(gl_output_string(gl, gl->line + gl->buff_curpos, '\0')) - return 1; -/* - * Clear to the end of the terminal. - */ - if(gl_output_control_sequence(gl, gl->nline, gl->clear_eod)) - return 1; -/* - * Place the cursor at the start of where the deletion was performed. - */ - return gl_place_cursor(gl, gl->buff_curpos); -} - -/*....................................................................... - * This is an action function which deletes character(s) under the - * cursor without moving the cursor. - */ -static KT_KEY_FN(gl_forward_delete_char) -{ -/* - * Delete 'count' characters. - */ - return gl_delete_chars(gl, count, gl->vi.command); -} - -/*....................................................................... - * This is an action function which deletes character(s) under the - * cursor and moves the cursor back one character. - */ -static KT_KEY_FN(gl_backward_delete_char) -{ -/* - * Restrict the deletion count to the number of characters that - * precede the insertion point. - */ - if(count > gl->buff_curpos - gl->insert_curpos) - count = gl->buff_curpos - gl->insert_curpos; -/* - * If in vi command mode, preserve the current line for potential - * use by vi-undo. - */ - gl_save_for_undo(gl); - return gl_cursor_left(gl, count) || - gl_delete_chars(gl, count, gl->vi.command); -} - -/*....................................................................... - * Starting from the cursor position delete to the specified column. - */ -static KT_KEY_FN(gl_delete_to_column) -{ - if (--count >= gl->buff_curpos) - return gl_forward_delete_char(gl, count - gl->buff_curpos); - else - return gl_backward_delete_char(gl, gl->buff_curpos - count); -} - -/*....................................................................... - * Starting from the cursor position delete characters to a matching - * parenthesis. - */ -static KT_KEY_FN(gl_delete_to_parenthesis) -{ - int curpos = gl_index_of_matching_paren(gl); - if(curpos >= 0) { - gl_save_for_undo(gl); - if(curpos >= gl->buff_curpos) - return gl_forward_delete_char(gl, curpos - gl->buff_curpos + 1); - else - return gl_backward_delete_char(gl, ++gl->buff_curpos - curpos + 1); - }; - return 0; -} - -/*....................................................................... - * This is an action function which deletes from the cursor to the end - * of the word that the cursor is either in or precedes. - */ -static KT_KEY_FN(gl_forward_delete_word) -{ -/* - * If in vi command mode, preserve the current line for potential - * use by vi-undo. - */ - gl_save_for_undo(gl); -/* - * In emacs mode delete to the end of the word. In vi mode delete to the - * start of the net word. - */ - if(gl->editor == GL_EMACS_MODE) { - return gl_delete_chars(gl, - gl_nth_word_end_forward(gl,count) - gl->buff_curpos + 1, 1); - } else { - return gl_delete_chars(gl, - gl_nth_word_start_forward(gl,count) - gl->buff_curpos, - gl->vi.command); - }; -} - -/*....................................................................... - * This is an action function which deletes the word that precedes the - * cursor. - */ -static KT_KEY_FN(gl_backward_delete_word) -{ -/* - * Keep a record of the current cursor position. - */ - int buff_curpos = gl->buff_curpos; -/* - * If in vi command mode, preserve the current line for potential - * use by vi-undo. - */ - gl_save_for_undo(gl); -/* - * Move back 'count' words. - */ - if(gl_backward_word(gl, count)) - return 1; -/* - * Delete from the new cursor position to the original one. - */ - return gl_delete_chars(gl, buff_curpos - gl->buff_curpos, - gl->editor == GL_EMACS_MODE || gl->vi.command); -} - -/*....................................................................... - * Searching in a given direction, delete to the count'th - * instance of a specified or queried character, in the input line. - * - * Input: - * gl GetLine * The getline resource object. - * count int The number of times to search. - * c char The character to be searched for, or '\0' if - * the character should be read from the user. - * forward int True if searching forward. - * onto int True if the search should end on top of the - * character, false if the search should stop - * one character before the character in the - * specified search direction. - * change int If true, this function is being called upon - * to do a vi change command, in which case the - * user will be left in insert mode after the - * deletion. - * Output: - * return int 0 - OK. - * 1 - Error. - */ -static int gl_delete_find(GetLine *gl, int count, char c, int forward, - int onto, int change) -{ -/* - * Search for the character, and abort the deletion if not found. - */ - int pos = gl_find_char(gl, count, forward, onto, c); - if(pos < 0) - return 0; -/* - * If in vi command mode, preserve the current line for potential - * use by vi-undo. - */ - gl_save_for_undo(gl); -/* - * Allow the cursor to be at the end of the line if this is a change - * command. - */ - if(change) - gl->vi.command = 0; -/* - * Delete the appropriate span of characters. - */ - if(forward) { - if(gl_delete_chars(gl, pos - gl->buff_curpos + 1, 1)) - return 1; - } else { - int buff_curpos = gl->buff_curpos; - if(gl_place_cursor(gl, pos) || - gl_delete_chars(gl, buff_curpos - gl->buff_curpos, 1)) - return 1; - }; -/* - * If this is a change operation, switch the insert mode. - */ - if(change && gl_vi_insert(gl, 0)) - return 1; - return 0; -} - -/*....................................................................... - * This is an action function which deletes forward from the cursor up to and - * including a specified character. - */ -static KT_KEY_FN(gl_forward_delete_find) -{ - return gl_delete_find(gl, count, '\0', 1, 1, 0); -} - -/*....................................................................... - * This is an action function which deletes backward from the cursor back to - * and including a specified character. - */ -static KT_KEY_FN(gl_backward_delete_find) -{ - return gl_delete_find(gl, count, '\0', 0, 1, 0); -} - -/*....................................................................... - * This is an action function which deletes forward from the cursor up to but - * not including a specified character. - */ -static KT_KEY_FN(gl_forward_delete_to) -{ - return gl_delete_find(gl, count, '\0', 1, 0, 0); -} - -/*....................................................................... - * This is an action function which deletes backward from the cursor back to - * but not including a specified character. - */ -static KT_KEY_FN(gl_backward_delete_to) -{ - return gl_delete_find(gl, count, '\0', 0, 0, 0); -} - -/*....................................................................... - * This is an action function which deletes to a character specified by a - * previous search. - */ -static KT_KEY_FN(gl_delete_refind) -{ - return gl_delete_find(gl, count, gl->vi.find_char, gl->vi.find_forward, - gl->vi.find_onto, 0); -} - -/*....................................................................... - * This is an action function which deletes to a character specified by a - * previous search, but in the opposite direction. - */ -static KT_KEY_FN(gl_delete_invert_refind) -{ - return gl_delete_find(gl, count, gl->vi.find_char, - !gl->vi.find_forward, gl->vi.find_onto, 0); -} - -/*....................................................................... - * This is an action function which converts the characters in the word - * following the cursor to upper case. - */ -static KT_KEY_FN(gl_upcase_word) -{ -/* - * Locate the count'th word ending after the cursor. - */ - int last = gl_nth_word_end_forward(gl, count); -/* - * If in vi command mode, preserve the current line for potential - * use by vi-undo. - */ - gl_save_for_undo(gl); -/* - * Upcase characters from the current cursor position to 'last'. - */ - while(gl->buff_curpos <= last) { - char *cptr = gl->line + gl->buff_curpos++; -/* - * Convert the character to upper case? - */ - if(islower((int)(unsigned char) *cptr)) - *cptr = toupper((int) *cptr); -/* - * Write the possibly modified character back. Note that for non-modified - * characters we want to do this as well, so as to advance the cursor. - */ - if(gl_output_char(gl, *cptr, cptr[1])) - return 1; - }; - return gl_place_cursor(gl, gl->buff_curpos); /* bounds check */ -} - -/*....................................................................... - * This is an action function which converts the characters in the word - * following the cursor to lower case. - */ -static KT_KEY_FN(gl_downcase_word) -{ -/* - * Locate the count'th word ending after the cursor. - */ - int last = gl_nth_word_end_forward(gl, count); -/* - * If in vi command mode, preserve the current line for potential - * use by vi-undo. - */ - gl_save_for_undo(gl); -/* - * Upcase characters from the current cursor position to 'last'. - */ - while(gl->buff_curpos <= last) { - char *cptr = gl->line + gl->buff_curpos++; -/* - * Convert the character to upper case? - */ - if(isupper((int)(unsigned char) *cptr)) - *cptr = tolower((int) *cptr); -/* - * Write the possibly modified character back. Note that for non-modified - * characters we want to do this as well, so as to advance the cursor. - */ - if(gl_output_char(gl, *cptr, cptr[1])) - return 1; - }; - return gl_place_cursor(gl, gl->buff_curpos); /* bounds check */ -} - -/*....................................................................... - * This is an action function which converts the first character of the - * following word to upper case, in order to capitalize the word, and - * leaves the cursor at the end of the word. - */ -static KT_KEY_FN(gl_capitalize_word) -{ - char *cptr; /* &gl->line[gl->buff_curpos] */ - int first; /* True for the first letter of the word */ - int i; -/* - * Keep a record of the current insert mode and the cursor position. - */ - int insert = gl->insert; -/* - * If in vi command mode, preserve the current line for potential - * use by vi-undo. - */ - gl_save_for_undo(gl); -/* - * We want to overwrite the modified word. - */ - gl->insert = 0; -/* - * Capitalize 'count' words. - */ - for(i=0; ibuff_curpos < gl->ntotal; i++) { - int pos = gl->buff_curpos; -/* - * If we are not already within a word, skip to the start of the word. - */ - for(cptr = gl->line + pos ; posntotal && !gl_is_word_char((int) *cptr); - pos++, cptr++) - ; -/* - * Move the cursor to the new position. - */ - if(gl_place_cursor(gl, pos)) - return 1; -/* - * While searching for the end of the word, change lower case letters - * to upper case. - */ - for(first=1; gl->buff_curposntotal && gl_is_word_char((int) *cptr); - gl->buff_curpos++, cptr++) { -/* - * Convert the character to upper case? - */ - if(first) { - if(islower((int)(unsigned char) *cptr)) - *cptr = toupper((int) *cptr); - } else { - if(isupper((int)(unsigned char) *cptr)) - *cptr = tolower((int) *cptr); - }; - first = 0; -/* - * Write the possibly modified character back. Note that for non-modified - * characters we want to do this as well, so as to advance the cursor. - */ - if(gl_output_char(gl, *cptr, cptr[1])) - return 1; - }; - }; -/* - * Restore the insertion mode. - */ - gl->insert = insert; - return gl_place_cursor(gl, gl->buff_curpos); /* bounds check */ -} - -/*....................................................................... - * This is an action function which redraws the current line. - */ -static KT_KEY_FN(gl_redisplay) -{ -/* - * Keep a record of the current cursor position. - */ - int buff_curpos = gl->buff_curpos; -/* - * Move the cursor to the start of the terminal line, and clear from there - * to the end of the display. - */ - if(gl_set_term_curpos(gl, 0) || - gl_output_control_sequence(gl, gl->nline, gl->clear_eod)) - return 1; -/* - * Display the current prompt. - */ - if(gl_display_prompt(gl)) - return 1; -/* - * Render the part of the line that the user has typed in so far. - */ - if(gl_output_string(gl, gl->line, '\0')) - return 1; -/* - * Restore the cursor position. - */ - if(gl_place_cursor(gl, buff_curpos)) - return 1; -/* - * Flush the redisplayed line to the terminal. - */ - return gl_flush_output(gl); -} - -/*....................................................................... - * This is an action function which clears the display and redraws the - * input line from the home position. - */ -static KT_KEY_FN(gl_clear_screen) -{ -/* - * Record the current cursor position. - */ - int buff_curpos = gl->buff_curpos; -/* - * Home the cursor and clear from there to the end of the display. - */ - if(gl_output_control_sequence(gl, gl->nline, gl->home) || - gl_output_control_sequence(gl, gl->nline, gl->clear_eod)) - return 1; -/* - * Redisplay the line. - */ - gl->term_curpos = 0; - gl->buff_curpos = 0; - if(gl_redisplay(gl,1)) - return 1; -/* - * Restore the cursor position. - */ - return gl_place_cursor(gl, buff_curpos); -} - -/*....................................................................... - * This is an action function which swaps the character under the cursor - * with the character to the left of the cursor. - */ -static KT_KEY_FN(gl_transpose_chars) -{ - char from[3]; /* The original string of 2 characters */ - char swap[3]; /* The swapped string of two characters */ -/* - * If we are at the beginning or end of the line, there aren't two - * characters to swap. - */ - if(gl->buff_curpos < 1 || gl->buff_curpos >= gl->ntotal) - return 0; -/* - * If in vi command mode, preserve the current line for potential - * use by vi-undo. - */ - gl_save_for_undo(gl); -/* - * Get the original and swapped strings of the two characters. - */ - from[0] = gl->line[gl->buff_curpos - 1]; - from[1] = gl->line[gl->buff_curpos]; - from[2] = '\0'; - swap[0] = gl->line[gl->buff_curpos]; - swap[1] = gl->line[gl->buff_curpos - 1]; - swap[2] = '\0'; -/* - * Move the cursor to the start of the two characters. - */ - if(gl_place_cursor(gl, gl->buff_curpos-1)) - return 1; -/* - * Swap the two characters in the buffer. - */ - gl->line[gl->buff_curpos] = swap[0]; - gl->line[gl->buff_curpos+1] = swap[1]; -/* - * If the sum of the displayed width of the two characters - * in their current and final positions is the same, swapping can - * be done by just overwriting with the two swapped characters. - */ - if(gl_displayed_string_width(gl, from, -1, gl->term_curpos) == - gl_displayed_string_width(gl, swap, -1, gl->term_curpos)) { - int insert = gl->insert; - gl->insert = 0; - if(gl_output_char(gl, swap[0], swap[1]) || - gl_output_char(gl, swap[1], gl->line[gl->buff_curpos+2])) - return 1; - gl->insert = insert; -/* - * If the swapped substring has a different displayed size, we need to - * redraw everything after the first of the characters. - */ - } else { - if(gl_output_string(gl, gl->line + gl->buff_curpos, '\0') || - gl_output_control_sequence(gl, gl->nline, gl->clear_eod)) - return 1; - }; -/* - * Advance the cursor to the character after the swapped pair. - */ - return gl_place_cursor(gl, gl->buff_curpos + 2); -} - -/*....................................................................... - * This is an action function which sets a mark at the current cursor - * location. - */ -static KT_KEY_FN(gl_set_mark) -{ - gl->buff_mark = gl->buff_curpos; - return 0; -} - -/*....................................................................... - * This is an action function which swaps the mark location for the - * cursor location. - */ -static KT_KEY_FN(gl_exchange_point_and_mark) -{ -/* - * Get the old mark position, and limit to the extent of the input - * line. - */ - int old_mark = gl->buff_mark <= gl->ntotal ? gl->buff_mark : gl->ntotal; -/* - * Make the current cursor position the new mark. - */ - gl->buff_mark = gl->buff_curpos; -/* - * Move the cursor to the old mark position. - */ - return gl_place_cursor(gl, old_mark); -} - -/*....................................................................... - * This is an action function which deletes the characters between the - * mark and the cursor, recording them in gl->cutbuf for later pasting. - */ -static KT_KEY_FN(gl_kill_region) -{ -/* - * If in vi command mode, preserve the current line for potential - * use by vi-undo. - */ - gl_save_for_undo(gl); -/* - * Limit the mark to be within the line. - */ - if(gl->buff_mark > gl->ntotal) - gl->buff_mark = gl->ntotal; -/* - * If there are no characters between the cursor and the mark, simply clear - * the cut buffer. - */ - if(gl->buff_mark == gl->buff_curpos) { - gl->cutbuf[0] = '\0'; - return 0; - }; -/* - * If the mark is before the cursor, swap the cursor and the mark. - */ - if(gl->buff_mark < gl->buff_curpos && gl_exchange_point_and_mark(gl,1)) - return 1; -/* - * Delete the characters. - */ - if(gl_delete_chars(gl, gl->buff_mark - gl->buff_curpos, 1)) - return 1; -/* - * Make the mark the same as the cursor position. - */ - gl->buff_mark = gl->buff_curpos; - return 0; -} - -/*....................................................................... - * This is an action function which records the characters between the - * mark and the cursor, in gl->cutbuf for later pasting. - */ -static KT_KEY_FN(gl_copy_region_as_kill) -{ - int ca, cb; /* The indexes of the first and last characters in the region */ - int mark; /* The position of the mark */ -/* - * Get the position of the mark, limiting it to lie within the line. - */ - mark = gl->buff_mark > gl->ntotal ? gl->ntotal : gl->buff_mark; -/* - * If there are no characters between the cursor and the mark, clear - * the cut buffer. - */ - if(mark == gl->buff_curpos) { - gl->cutbuf[0] = '\0'; - return 0; - }; -/* - * Get the line indexes of the first and last characters in the region. - */ - if(mark < gl->buff_curpos) { - ca = mark; - cb = gl->buff_curpos - 1; - } else { - ca = gl->buff_curpos; - cb = mark - 1; - }; -/* - * Copy the region to the cut buffer. - */ - memcpy(gl->cutbuf, gl->line + ca, cb + 1 - ca); - gl->cutbuf[cb + 1 - ca] = '\0'; - return 0; -} - -/*....................................................................... - * This is an action function which inserts the contents of the cut - * buffer at the current cursor location. - */ -static KT_KEY_FN(gl_yank) -{ - int i; -/* - * Set the mark at the current location. - */ - gl->buff_mark = gl->buff_curpos; -/* - * Do nothing else if the cut buffer is empty. - */ - if(gl->cutbuf[0] == '\0') - return gl_ring_bell(gl, 1); -/* - * If in vi command mode, preserve the current line for potential - * use by vi-undo. - */ - gl_save_for_undo(gl); -/* - * Insert the string count times. - */ - for(i=0; icutbuf)) - return 1; - }; -/* - * gl_add_string_to_line() leaves the cursor after the last character that - * was pasted, whereas vi leaves the cursor over the last character pasted. - */ - if(gl->editor == GL_VI_MODE && gl_cursor_left(gl, 1)) - return 1; - return 0; -} - -/*....................................................................... - * This is an action function which inserts the contents of the cut - * buffer one character beyond the current cursor location. - */ -static KT_KEY_FN(gl_append_yank) -{ - int was_command = gl->vi.command; - int i; -/* - * If the cut buffer is empty, ring the terminal bell. - */ - if(gl->cutbuf[0] == '\0') - return gl_ring_bell(gl, 1); -/* - * Set the mark at the current location + 1. - */ - gl->buff_mark = gl->buff_curpos + 1; -/* - * If in vi command mode, preserve the current line for potential - * use by vi-undo. - */ - gl_save_for_undo(gl); -/* - * Arrange to paste the text in insert mode after the current character. - */ - if(gl_vi_append(gl, 0)) - return 1; -/* - * Insert the string count times. - */ - for(i=0; icutbuf)) - return 1; - }; -/* - * Switch back to command mode if necessary. - */ - if(was_command) - gl_vi_command_mode(gl); - return 0; -} - -#ifdef USE_SIGWINCH -/*....................................................................... - * Respond to the receipt of a window change signal. - * - * Input: - * gl GetLine * The resource object of this library. - * redisplay int If true redisplay the current line after - * getting the new window size. - * Output: - * return int 0 - OK. - * 1 - Error. - */ -static int gl_resize_terminal(GetLine *gl, int redisplay) -{ - int lines_used; /* The number of lines currently in use */ - struct winsize size; /* The new size information */ - int i; -/* - * Record the fact that the sigwinch signal has been noted. - */ - if(gl_pending_signal == SIGWINCH) - gl_pending_signal = -1; -/* - * Query the new terminal window size. Ignore invalid responses. - */ - if(ioctl(gl->output_fd, TIOCGWINSZ, &size) == 0 && - size.ws_row > 0 && size.ws_col > 0) { -/* - * Redisplay the input line? - */ - if(redisplay) { -/* - * How many lines are currently displayed. - */ - lines_used = (gl_displayed_string_width(gl,gl->line,-1,gl->prompt_len) + - gl->prompt_len + gl->ncolumn - 1) / gl->ncolumn; -/* - * Move to the cursor to the start of the line. - */ - for(i=1; iup)) - return 1; - }; - if(gl_output_control_sequence(gl, 1, gl->bol)) - return 1; -/* - * Clear to the end of the terminal. - */ - if(gl_output_control_sequence(gl, size.ws_row, gl->clear_eod)) - return 1; -/* - * Record the fact that the cursor is now at the beginning of the line. - */ - gl->term_curpos = 0; - }; -/* - * Update the recorded window size. - */ - gl->nline = size.ws_row; - gl->ncolumn = size.ws_col; - }; -/* - * Redisplay the line? - */ - return redisplay ? gl_redisplay(gl,1) : 0; -} -#endif - -/*....................................................................... - * This is the action function that recalls the previous line in the - * history buffer. - */ -static KT_KEY_FN(gl_up_history) -{ -/* - * In vi mode, switch to command mode, since the user is very - * likely to want to move around newly recalled lines. - */ - gl_vi_command_mode(gl); -/* - * Forget any previous recall session. - */ - gl->preload_id = 0; -/* - * We don't want a search prefix for this function. - */ - if(_glh_search_prefix(gl->glh, gl->line, 0)) - return 1; -/* - * Recall the count'th next older line in the history list. If the first one - * fails we can return since nothing has changed otherwise we must continue - * and update the line state. - */ - if(_glh_find_backwards(gl->glh, gl->line, gl->linelen) == NULL) - return 0; - while(--count && _glh_find_backwards(gl->glh, gl->line, gl->linelen)) - ; -/* - * Record the number of characters in the new string. - */ - gl->ntotal = strlen(gl->line); -/* - * Arrange to have the cursor placed at the end of the new line. - */ - gl->buff_curpos = strlen(gl->line); -/* - * Erase and display the new line. - */ - return gl_redisplay(gl,1); -} - -/*....................................................................... - * This is the action function that recalls the next line in the - * history buffer. - */ -static KT_KEY_FN(gl_down_history) -{ -/* - * In vi mode, switch to command mode, since the user is very - * likely to want to move around newly recalled lines. - */ - gl_vi_command_mode(gl); -/* - * If no search is currently in progress continue a previous recall - * session from a previous entered line if possible. - */ - if(_glh_line_id(gl->glh, 0) == 0 && gl->preload_id) { - _glh_recall_line(gl->glh, gl->preload_id, gl->line, gl->linelen); - gl->preload_id = 0; - } else { -/* - * We don't want a search prefix for this function. - */ - if(_glh_search_prefix(gl->glh, gl->line, 0)) - return 1; -/* - * Recall the count'th next newer line in the history list. If the first one - * fails we can return since nothing has changed otherwise we must continue - * and update the line state. - */ - if(_glh_find_forwards(gl->glh, gl->line, gl->linelen) == NULL) - return 0; - while(--count && _glh_find_forwards(gl->glh, gl->line, gl->linelen)) - ; - }; -/* - * Record the number of characters in the new string. - */ - gl->ntotal = strlen(gl->line); -/* - * Arrange to have the cursor placed at the end of the new line. - */ - gl->buff_curpos = strlen(gl->line); -/* - * Erase and display the new line. - */ - return gl_redisplay(gl,1); -} - -/*....................................................................... - * This is the action function that recalls the previous line in the - * history buffer whos prefix matches the characters that currently - * precede the cursor. By setting count=-1, this can be used internally - * to force searching for the prefix used in the last search. - */ -static KT_KEY_FN(gl_history_search_backward) -{ -/* - * In vi mode, switch to command mode, since the user is very - * likely to want to move around newly recalled lines. - */ - gl_vi_command_mode(gl); -/* - * Forget any previous recall session. - */ - gl->preload_id = 0; -/* - * If the previous thing that the user did wasn't to execute a history - * search function, set the search prefix equal to the string that - * precedes the cursor. In vi command mode include the character that - * is under the cursor in the string. If count<0 force a repeat search - * even if the last command wasn't a history command. - */ - if(gl->last_search != gl->keyseq_count - 1 && count>=0 && - _glh_search_prefix(gl->glh, gl->line, gl->buff_curpos + - (gl->editor==GL_VI_MODE && gl->ntotal>0))) - return 1; -/* - * Record the key sequence number in which this search function is - * being executed, so that the next call to this function or - * gl_history_search_forward() knows if any other operations - * were performed in between. - */ - gl->last_search = gl->keyseq_count; -/* - * Search backwards for a match to the part of the line which precedes the - * cursor. - */ - if(_glh_find_backwards(gl->glh, gl->line, gl->linelen) == NULL) - return 0; -/* - * Record the number of characters in the new string. - */ - gl->ntotal = strlen(gl->line); -/* - * Arrange to have the cursor placed at the end of the new line. - */ - gl->buff_curpos = strlen(gl->line); -/* - * Erase and display the new line. - */ - return gl_redisplay(gl,1); -} - -/*....................................................................... - * This is the action function that recalls the previous line in the - * history buffer who's prefix matches that specified in an earlier call - * to gl_history_search_backward() or gl_history_search_forward(). - */ -static KT_KEY_FN(gl_history_re_search_backward) -{ - return gl_history_search_backward(gl, -1); -} - -/*....................................................................... - * This is the action function that recalls the next line in the - * history buffer who's prefix matches that specified in the earlier call - * to gl_history_search_backward) which started the history search. - * By setting count=-1, this can be used internally to force searching - * for the prefix used in the last search. - */ -static KT_KEY_FN(gl_history_search_forward) -{ -/* - * In vi mode, switch to command mode, since the user is very - * likely to want to move around newly recalled lines. - */ - gl_vi_command_mode(gl); -/* - * If the previous thing that the user did wasn't to execute a history - * search function, set the search prefix equal to the string that - * precedes the cursor. In vi command mode include the character that - * is under the cursor in the string. If count<0 force a repeat search - * even if the last command wasn't a history command. - */ - if(gl->last_search != gl->keyseq_count - 1 && count>=0 && - _glh_search_prefix(gl->glh, gl->line, gl->buff_curpos + - (gl->editor==GL_VI_MODE && gl->ntotal>0))) - return 1; -/* - * Record the key sequence number in which this search function is - * being executed, so that the next call to this function or - * gl_history_search_backward() knows if any other operations - * were performed in between. - */ - gl->last_search = gl->keyseq_count; -/* - * Search forwards for the next matching line. - */ - if(_glh_find_forwards(gl->glh, gl->line, gl->linelen) == NULL) - return 0; -/* - * Record the number of characters in the new string. - */ - gl->ntotal = strlen(gl->line); -/* - * Arrange for the cursor to be placed at the end of the new line. - */ - gl->buff_curpos = strlen(gl->line); -/* - * Erase and display the new line. - */ - return gl_redisplay(gl,1); -} - -/*....................................................................... - * This is the action function that recalls the next line in the - * history buffer who's prefix matches that specified in an earlier call - * to gl_history_search_backward() or gl_history_search_forward(). - */ -static KT_KEY_FN(gl_history_re_search_forward) -{ - return gl_history_search_forward(gl, -1); -} - -/*....................................................................... - * This is the tab completion function that completes the filename that - * precedes the cursor position. - */ -static KT_KEY_FN(gl_complete_word) -{ - CplMatches *matches; /* The possible completions */ - int redisplay=0; /* True if the whole line needs to be redrawn */ - int suffix_len; /* The length of the completion extension */ - int cont_len; /* The length of any continuation suffix */ - int nextra; /* The number of characters being added to the */ - /* total length of the line. */ - int buff_pos; /* The buffer index at which the completion is */ - /* to be inserted. */ -/* - * In vi command mode, switch to append mode so that the character below - * the character is included in the completion (otherwise people can't - * complete at the end of the line). - */ - if(gl->vi.command && gl_vi_append(gl, 0)) - return 1; -/* - * Get the cursor position at which the completion is to be inserted. - */ - buff_pos = gl->buff_curpos; -/* - * Perform the completion. - */ - matches = cpl_complete_word(gl->cpl, gl->line, gl->buff_curpos, gl->cpl_data, - gl->cpl_fn); - if(!matches) { - if(gl->echo && - fprintf(gl->output_fp, "\r\n%s\n", cpl_last_error(gl->cpl)) < 0) - return 1; - gl->term_curpos = 0; - redisplay = 1; -/* - * Are there any completions? - */ - } else if(matches->nmatch >= 1) { -/* - * If there any ambiguous matches, report them, starting on a new line. - */ - if(matches->nmatch > 1 && gl->echo) { - if(fprintf(gl->output_fp, "\r\n") < 0) - return 1; - cpl_list_completions(matches, gl->output_fp, gl->ncolumn); - redisplay = 1; - }; -/* - * If the callback called gl_change_prompt(), we will need to redisplay - * the whole line. - */ - if(gl->prompt_changed) - redisplay = 1; -/* - * Get the length of the suffix and any continuation suffix to add to it. - */ - suffix_len = strlen(matches->suffix); - cont_len = strlen(matches->cont_suffix); -/* - * If there is an unambiguous match, and the continuation suffix ends in - * a newline, strip that newline and arrange to have getline return - * after this action function returns. - */ - if(matches->nmatch==1 && cont_len > 0 && - matches->cont_suffix[cont_len - 1] == '\n') { - cont_len--; - if(gl_newline(gl, 1)) - return 1; - }; -/* - * Work out the number of characters that are to be added. - */ - nextra = suffix_len + cont_len; -/* - * Is there anything to be added? - */ - if(nextra) { -/* - * Will there be space for the expansion in the line buffer? - */ - if(gl->ntotal + nextra < gl->linelen) { -/* - * Make room to insert the filename extension. - */ - memmove(gl->line + gl->buff_curpos + nextra, gl->line + gl->buff_curpos, - gl->ntotal - gl->buff_curpos); -/* - * Insert the filename extension. - */ - memcpy(gl->line + gl->buff_curpos, matches->suffix, suffix_len); -/* - * Add the terminating characters. - */ - memcpy(gl->line + gl->buff_curpos + suffix_len, matches->cont_suffix, - cont_len); -/* - * Record the increased length of the line. - */ - gl->ntotal += nextra; -/* - * Place the cursor position at the end of the completion. - */ - gl->buff_curpos += nextra; -/* - * Terminate the extended line. - */ - gl->line[gl->ntotal] = '\0'; -/* - * If we don't have to redisplay the whole line, redisplay the part - * of the line which follows the original cursor position, and place - * the cursor at the end of the completion. - */ - if(!redisplay) { - if(gl_output_control_sequence(gl, gl->nline, gl->clear_eod) || - gl_output_string(gl, gl->line + buff_pos, '\0') || - gl_place_cursor(gl, gl->buff_curpos)) - return 1; - }; - } else { - fprintf(stderr, - "\r\nInsufficient room in line for file completion.\r\n"); - redisplay = 1; - }; - }; - }; -/* - * Redisplay the whole line? - */ - if(redisplay) { - gl->term_curpos = 0; - if(gl_redisplay(gl,1)) - return 1; - }; - return 0; -} - -/*....................................................................... - * This is the function that expands the filename that precedes the - * cursor position. It expands ~user/ expressions, $envvar expressions, - * and wildcards. - */ -static KT_KEY_FN(gl_expand_filename) -{ - char *start_path; /* The pointer to the start of the pathname in */ - /* gl->line[]. */ - FileExpansion *result; /* The results of the filename expansion */ - int pathlen; /* The length of the pathname being expanded */ - int redisplay=0; /* True if the whole line needs to be redrawn */ - int length; /* The number of characters needed to display the */ - /* expanded files. */ - int nextra; /* The number of characters to be added */ - int i,j; -/* - * In vi command mode, switch to append mode so that the character below - * the character is included in the completion (otherwise people can't - * complete at the end of the line). - */ - if(gl->vi.command && gl_vi_append(gl, 0)) - return 1; -/* - * Locate the start of the filename that precedes the cursor position. - */ - start_path = _pu_start_of_path(gl->line, - gl->buff_curpos > 0 ? gl->buff_curpos : 0); - if(!start_path) - return 1; -/* - * Get the length of the string that is to be expanded. - */ - pathlen = gl->buff_curpos - (start_path - gl->line); -/* - * Attempt to expand it. - */ - result = ef_expand_file(gl->ef, start_path, pathlen); -/* - * If there was an error, report the error on a new line, then redraw - * the original line. - */ - if(!result) { - if(gl->echo && - fprintf(gl->output_fp, "\r\n%s\n", ef_last_error(gl->ef)) < 0) - return 1; - gl->term_curpos = 0; - return gl_redisplay(gl,1); - }; -/* - * If no files matched, report this as well. - */ - if(result->nfile == 0 || !result->exists) { - if(gl->echo && fprintf(gl->output_fp, "\r\nNo files match.\n") < 0) - return 1; - gl->term_curpos = 0; - return gl_redisplay(gl,1); - }; -/* - * If in vi command mode, preserve the current line for potential use by - * vi-undo. - */ - gl_save_for_undo(gl); -/* - * Work out how much space we will need to display all of the matching - * filenames, taking account of the space that we need to place between - * them, and the number of additional '\' characters needed to escape - * spaces, tabs and backslash characters in the individual filenames. - */ - length = 0; - for(i=0; infile; i++) { - char *file = result->files[i]; - while(*file) { - int c = *file++; - switch(c) { - case ' ': case '\t': case '\\': case '*': case '?': case '[': - length++; /* Count extra backslash characters */ - }; - length++; /* Count the character itself */ - }; - length++; /* Count the space that follows each filename */ - }; -/* - * Work out the number of characters that are to be added. - */ - nextra = length - pathlen; -/* - * Will there be space for the expansion in the line buffer? - */ - if(gl->ntotal + nextra >= gl->linelen) { - fprintf(stderr, "\r\nInsufficient room in line for file expansion.\r\n"); - redisplay = 1; - } else { -/* - * Do we need to move the part of the line that followed the unexpanded - * filename? - */ - if(nextra != 0) { - memmove(gl->line + gl->buff_curpos + nextra, gl->line + gl->buff_curpos, - gl->ntotal - gl->buff_curpos); - }; -/* - * Insert the filenames, separated by spaces, and with internal spaces, - * tabs and backslashes escaped with backslashes. - */ - for(i=0,j=start_path - gl->line; infile; i++) { - char *file = result->files[i]; - while(*file) { - int c = *file++; - switch(c) { - case ' ': case '\t': case '\\': case '*': case '?': case '[': - gl->line[j++] = '\\'; - }; - gl->line[j++] = c; - }; - gl->line[j++] = ' '; - }; -/* - * Record the increased length of the line. - */ - gl->ntotal += nextra; -/* - * Place the cursor position at the end of the expansion. - */ - gl->buff_curpos += nextra; -/* - * Terminate the extended line. - */ - gl->line[gl->ntotal] = '\0'; - }; -/* - * Display the whole line on a new line? - */ - if(redisplay) { - gl->term_curpos = 0; - return gl_redisplay(gl,1); - }; -/* - * Otherwise redisplay the part of the line which follows the start of - * the original filename. - */ - if(gl_set_term_curpos(gl, gl_buff_curpos_to_term_curpos(gl, start_path - gl->line)) || - gl_output_control_sequence(gl, gl->nline, gl->clear_eod) || - gl_output_string(gl, start_path, gl->line[gl->buff_curpos])) - return 1; -/* - * Restore the cursor position to the end of the expansion. - */ - return gl_place_cursor(gl, gl->buff_curpos); -} - -/*....................................................................... - * This is the action function that lists glob expansions of the - * filename that precedes the cursor position. It expands ~user/ - * expressions, $envvar expressions, and wildcards. - */ -static KT_KEY_FN(gl_list_glob) -{ - char *start_path; /* The pointer to the start of the pathname in */ - /* gl->line[]. */ - FileExpansion *result; /* The results of the filename expansion */ - int pathlen; /* The length of the pathname being expanded */ -/* - * Locate the start of the filename that precedes the cursor position. - */ - start_path = _pu_start_of_path(gl->line, - gl->buff_curpos > 0 ? gl->buff_curpos : 0); - if(!start_path) - return 1; -/* - * Get the length of the string that is to be expanded. - */ - pathlen = gl->buff_curpos - (start_path - gl->line); -/* - * Attempt to expand it. - */ - result = ef_expand_file(gl->ef, start_path, pathlen); -/* - * If there was an error, report the error. - */ - if(!result) { - if(gl->echo && - fprintf(gl->output_fp, "\r\n%s\n", ef_last_error(gl->ef)) < 0) - return 1; -/* - * If no files matched, report this as well. - */ - } else if(result->nfile == 0 || !result->exists) { - if(gl->echo && fprintf(gl->output_fp, "\r\nNo files match.\n") < 0) - return 1; -/* - * List the matching expansions. - */ - } else if(gl->echo) { - if(fprintf(gl->output_fp, "\r\n") < 0) - return 1; - ef_list_expansions(result, gl->output_fp, gl->ncolumn); - }; -/* - * Redisplay the line being edited. - */ - gl->term_curpos = 0; - return gl_redisplay(gl,1); -} - -/*....................................................................... - * Return non-zero if a character should be considered a part of a word. - * - * Input: - * c int The character to be tested. - * Output: - * return int True if the character should be considered part of a word. - */ -static int gl_is_word_char(int c) -{ - return isalnum((int)(unsigned char)c) || strchr(GL_WORD_CHARS, c) != NULL; -} - -/*....................................................................... - * Override the builtin file-completion callback that is bound to the - * "complete_word" action function. - * - * Input: - * gl GetLine * The resource object of the command-line input - * module. - * data void * This is passed to match_fn() whenever it is - * called. It could, for example, point to a - * symbol table where match_fn() could look - * for possible completions. - * match_fn CplMatchFn * The function that will identify the prefix - * to be completed from the input line, and - * report matching symbols. - * Output: - * return int 0 - OK. - * 1 - Error. - */ -int gl_customize_completion(GetLine *gl, void *data, CplMatchFn *match_fn) -{ -/* - * Check the arguments. - */ - if(!gl || !match_fn) { - fprintf(stderr, "gl_customize_completion: NULL argument(s).\n"); - return 1; - }; -/* - * Record the new completion function and its callback data. - */ - gl->cpl_fn = match_fn; - gl->cpl_data = data; - return 0; -} - -/*....................................................................... - * Change the terminal (or stream) that getline interacts with. - * - * Input: - * gl GetLine * The resource object of the command-line input - * module. - * input_fp FILE * The stdio stream to read from. - * output_fp FILE * The stdio stream to write to. - * term char * The terminal type. This can be NULL if - * either or both of input_fp and output_fp don't - * refer to a terminal. Otherwise it should refer - * to an entry in the terminal information database. - * Output: - * return int 0 - OK. - * 1 - Error. - */ -int gl_change_terminal(GetLine *gl, FILE *input_fp, FILE *output_fp, - const char *term) -{ - int is_term = 0; /* True if both input_fd and output_fd are associated */ - /* with a terminal. */ -/* - * Require that input_fp and output_fp both be valid. - */ - if(!input_fp || !output_fp) { - fprintf(stderr, "\r\ngl_change_terminal: Bad input/output stream(s).\n"); - return 1; - }; -/* - * If we are displacing a previous terminal, remove it from the list - * of fds being watched. - */ -#ifdef HAVE_SELECT - if(gl->input_fd >= 0) - FD_CLR(gl->input_fd, &gl->rfds); -#endif -/* - * Record the file descriptors and streams. - */ - gl->input_fp = input_fp; - gl->input_fd = fileno(input_fp); - gl->output_fp = output_fp; - gl->output_fd = fileno(output_fp); -/* - * Make sure that the file descriptor will be visible in the set to - * be watched. - */ -#ifdef HAVE_SELECT - FD_SET(gl->input_fd, &gl->rfds); - if(gl->input_fd > gl->max_fd) - gl->max_fd = gl->input_fd; -#endif -/* - * Disable terminal interaction until we have enough info to interact - * with the terminal. - */ - gl->is_term = 0; -/* - * For terminal editing, we need both output_fd and input_fd to refer to - * a terminal. While we can't verify that they both point to the same - * terminal, we can verify that they point to terminals. - */ - is_term = isatty(gl->input_fd) && isatty(gl->output_fd); -/* - * If we are interacting with a terminal and no terminal type has been - * specified, treat it as a generic ANSI terminal. - */ - if(is_term && !term) - term = "ansi"; -/* - * Make a copy of the terminal type string. - */ - if(term != gl->term) { -/* - * Delete any old terminal type string. - */ - if(gl->term) { - free(gl->term); - gl->term = NULL; - }; -/* - * Make a copy of the new terminal-type string, if any. - */ - if(term) { - gl->term = (char *) malloc(strlen(term)+1); - if(gl->term) - strcpy(gl->term, term); - }; - }; -/* - * Clear any terminal-specific key bindings that were taken from the - * settings of the last terminal. - */ - _kt_clear_bindings(gl->bindings, KTB_TERM); -/* - * If we have a terminal install new bindings for it. - */ - if(is_term) { -/* - * Get the current settings of the terminal. - */ - if(tcgetattr(gl->input_fd, &gl->oldattr)) { - fprintf(stderr, "\r\ngl_change_terminal: tcgetattr error: %s\n", - strerror(errno)); - return 1; - }; -/* - * Lookup the terminal control string and size information. - */ - if(gl_control_strings(gl, term)) - return 1; -/* - * We now have enough info to interact with the terminal. - */ - gl->is_term = 1; -/* - * Bind terminal-specific keys. - */ - if(gl_bind_terminal_keys(gl)) - return 1; - }; - return 0; -} - -/*....................................................................... - * Set up terminal-specific key bindings. - * - * Input: - * gl GetLine * The resource object of the command-line input - * module. - * Output: - * return int 0 - OK. - * 1 - Error. - */ -static int gl_bind_terminal_keys(GetLine *gl) -{ -/* - * Install key-bindings for the special terminal characters. - */ - if(gl_bind_control_char(gl, KTB_TERM, gl->oldattr.c_cc[VINTR], - "user-interrupt") || - gl_bind_control_char(gl, KTB_TERM, gl->oldattr.c_cc[VQUIT], "abort") || - gl_bind_control_char(gl, KTB_TERM, gl->oldattr.c_cc[VSUSP], "suspend")) - return 1; -/* - * In vi-mode, arrange for the above characters to be seen in command - * mode. - */ - if(gl->editor == GL_VI_MODE) { - if(gl_bind_control_char(gl, KTB_TERM, MAKE_META(gl->oldattr.c_cc[VINTR]), - "user-interrupt") || - gl_bind_control_char(gl, KTB_TERM, MAKE_META(gl->oldattr.c_cc[VQUIT]), - "abort") || - gl_bind_control_char(gl, KTB_TERM, MAKE_META(gl->oldattr.c_cc[VSUSP]), - "suspend")) - return 1; - }; -/* - * Non-universal special keys. - */ -#ifdef VLNEXT - if(gl_bind_control_char(gl, KTB_TERM, gl->oldattr.c_cc[VLNEXT], - "literal-next")) - return 1; -#else - if(_kt_set_keybinding(gl->bindings, KTB_TERM, "^V", "literal-next")) - return 1; -#endif -/* - * Bind action functions to the terminal-specific arrow keys - * looked up by gl_control_strings(). - */ - if(_gl_bind_arrow_keys(gl)) - return 1; - return 0; -} - -/*....................................................................... - * This function is normally bound to control-D. When it is invoked within - * a line it deletes the character which follows the cursor. When invoked - * at the end of the line it lists possible file completions, and when - * invoked on an empty line it causes gl_get_line() to return EOF. This - * function emulates the one that is normally bound to control-D by tcsh. - */ -static KT_KEY_FN(gl_del_char_or_list_or_eof) -{ -/* - * If we have an empty line arrange to return EOF. - */ - if(gl->ntotal < 1) { - return 1; -/* - * If we are at the end of the line list possible completions. - */ - } else if(gl->buff_curpos >= gl->ntotal) { -/* - * Get the list of possible completions. - */ - CplMatches *matches = cpl_complete_word(gl->cpl, gl->line, gl->buff_curpos, - gl->cpl_data, gl->cpl_fn); - if(!matches) { - if(gl->echo && - fprintf(gl->output_fp, "\r\n%s\n", cpl_last_error(gl->cpl)) < 0) - return 1; - gl->term_curpos = 0; -/* - * List the matches. - */ - } else if(matches->nmatch > 0 && gl->echo) { - if(fprintf(gl->output_fp, "\r\n") < 0) - return 1; - cpl_list_completions(matches, gl->output_fp, gl->ncolumn); - }; -/* - * Redisplay the line unchanged. - */ - return gl_redisplay(gl,1); -/* - * Within the line delete the character that follows the cursor. - */ - } else { -/* - * If in vi command mode, first preserve the current line for potential use - * by vi-undo. - */ - gl_save_for_undo(gl); -/* - * Delete 'count' characters. - */ - return gl_forward_delete_char(gl, count); - }; -} - -/*....................................................................... - * This function is normally bound to control-D in vi mode. When it is - * invoked within a line it lists possible file completions, and when - * invoked on an empty line it causes gl_get_line() to return EOF. This - * function emulates the one that is normally bound to control-D by tcsh. - */ -static KT_KEY_FN(gl_list_or_eof) -{ -/* - * If we have an empty line arrange to return EOF. - */ - if(gl->ntotal < 1) { - return 1; -/* - * Otherwise list possible completions. - */ - } else { -/* - * Get the list of possible completions. - */ - CplMatches *matches = cpl_complete_word(gl->cpl, gl->line, gl->buff_curpos, - gl->cpl_data, gl->cpl_fn); - if(!matches) { - if(gl->echo && - fprintf(gl->output_fp, "\r\n%s\n", cpl_last_error(gl->cpl)) < 0) - return 1; - gl->term_curpos = 0; -/* - * List the matches. - */ - } else if(matches->nmatch > 0 && gl->echo) { - if(fprintf(gl->output_fp, "\r\n") < 0) - return 1; - cpl_list_completions(matches, gl->output_fp, gl->ncolumn); - }; -/* - * Redisplay the line unchanged. - */ - return gl_redisplay(gl,1); - }; -} - -/*....................................................................... - * Where the user has used the symbolic arrow-key names to specify - * arrow key bindings, bind the specified action functions to the default - * and terminal specific arrow key sequences. - * - * Input: - * gl GetLine * The getline resource object. - * Output: - * return int 0 - OK. - * 1 - Error. - */ -static int _gl_bind_arrow_keys(GetLine *gl) -{ -/* - * Process each of the arrow keys. - */ - if(_gl_rebind_arrow_key(gl->bindings, "up", gl->u_arrow, "^[[A", "^[OA") || - _gl_rebind_arrow_key(gl->bindings, "down", gl->d_arrow, "^[[B", "^[OB") || - _gl_rebind_arrow_key(gl->bindings, "left", gl->l_arrow, "^[[D", "^[OD") || - _gl_rebind_arrow_key(gl->bindings, "right", gl->r_arrow, "^[[C", "^[OC")) - return 1; - return 0; -} - -/*....................................................................... - * Lookup the action function of a symbolic arrow-key binding, and bind - * it to the terminal-specific and default arrow-key sequences. Note that - * we don't trust the terminal-specified key sequences to be correct. - * The main reason for this is that on some machines the xterm terminfo - * entry is for hardware X-terminals, rather than xterm terminal emulators - * and the two terminal types emit different character sequences when the - * their cursor keys are pressed. As a result we also supply a couple - * of default key sequences. - * - * Input: - * bindings KeyTab * The table of key bindings. - * name char * The symbolic name of the arrow key. - * term_seq char * The terminal-specific arrow-key sequence. - * def_seq1 char * The first default arrow-key sequence. - * def_seq2 char * The second arrow-key sequence. - * Output: - * return int 0 - OK. - * 1 - Error. - */ -static int _gl_rebind_arrow_key(KeyTab *bindings, const char *name, - const char *term_seq, const char *def_seq1, - const char *def_seq2) -{ - int first,last; /* The indexes of the first and last matching entries */ -/* - * Lookup the key binding for the symbolic name of the arrow key. This - * will either be the default action, or a user provided one. - */ - if(_kt_lookup_keybinding(bindings, name, strlen(name), &first, &last) - == KT_EXACT_MATCH) { -/* - * Get the action function. - */ - KtKeyFn *key_fn = bindings->table[first].keyfn; -/* - * Bind this to each of the specified key sequences. - */ - if((term_seq && _kt_set_keyfn(bindings, KTB_TERM, term_seq, key_fn)) || - (def_seq1 && _kt_set_keyfn(bindings, KTB_NORM, def_seq1, key_fn)) || - (def_seq2 && _kt_set_keyfn(bindings, KTB_NORM, def_seq2, key_fn))) - return 1; - }; - return 0; -} - -/*....................................................................... - * Read getline configuration information from a given file. - * - * Input: - * gl GetLine * The getline resource object. - * filename const char * The name of the file to read configuration - * information from. The contents of this file - * are as described in the gl_get_line(3) man - * page for the default ~/.teclarc configuration - * file. - * who KtBinder Who bindings are to be installed for. - * Output: - * return int 0 - OK. - * 1 - Irrecoverable error. - */ -static int _gl_read_config_file(GetLine *gl, const char *filename, KtBinder who) -{ - FileExpansion *expansion; /* The expansion of the filename */ - FILE *fp; /* The opened file */ - int waserr = 0; /* True if an error occurred while reading */ - int lineno = 1; /* The line number being processed */ -/* - * Check the arguments. - */ - if(!gl || !filename) { - fprintf(stderr, "_gl_read_config_file: Invalid arguments.\n"); - return 1; - }; -/* - * Expand the filename. - */ - expansion = ef_expand_file(gl->ef, filename, -1); - if(!expansion) { - fprintf(stderr, "Unable to expand %s (%s).\n", filename, - ef_last_error(gl->ef)); - return 1; - }; -/* - * Attempt to open the file. - */ - fp = fopen(expansion->files[0], "r"); -/* - * It isn't an error for there to be no configuration file. - */ - if(!fp) - return 0; -/* - * Parse the contents of the file. - */ - while(!waserr && !feof(fp)) - waserr = _gl_parse_config_line(gl, fp, glc_file_getc, filename, who, - &lineno); -/* - * Bind action functions to the terminal-specific arrow keys. - */ - if(_gl_bind_arrow_keys(gl)) - return 1; -/* - * Clean up. - */ - (void) fclose(fp); - return waserr; -} - -/*....................................................................... - * Read GetLine configuration information from a string. The contents of - * the string are the same as those described in the gl_get_line(3) - * man page for the contents of the ~/.teclarc configuration file. - */ -static int _gl_read_config_string(GetLine *gl, const char *buffer, KtBinder who) -{ - const char *bptr; /* A pointer into buffer[] */ - int waserr = 0; /* True if an error occurred while reading */ - int lineno = 1; /* The line number being processed */ -/* - * Check the arguments. - */ - if(!gl || !buffer) { - fprintf(stderr, "_gl_read_config_string: Invalid arguments.\n"); - return 1; - }; -/* - * Get a pointer to the start of the buffer. - */ - bptr = buffer; -/* - * Parse the contents of the buffer. - */ - while(!waserr && *bptr) - waserr = _gl_parse_config_line(gl, &bptr, glc_buff_getc, "", who, &lineno); -/* - * Bind action functions to the terminal-specific arrow keys. - */ - if(_gl_bind_arrow_keys(gl)) - return 1; - return waserr; -} - -/*....................................................................... - * Parse the next line of a getline configuration file. - * - * Input: - * gl GetLine * The getline resource object. - * stream void * The pointer representing the stream to be read - * by getc_fn(). - * getc_fn GlcGetcFn * A callback function which when called with - * 'stream' as its argument, returns the next - * unread character from the stream. - * origin const char * The name of the entity being read (eg. a - * file name). - * who KtBinder Who bindings are to be installed for. - * Input/Output: - * lineno int * The line number being processed is to be - * maintained in *lineno. - * Output: - * return int 0 - OK. - * 1 - Irrecoverable error. - */ -static int _gl_parse_config_line(GetLine *gl, void *stream, GlcGetcFn *getc_fn, - const char *origin, KtBinder who, int *lineno) -{ - char buffer[GL_CONF_BUFLEN+1]; /* The input line buffer */ - char *argv[GL_CONF_MAXARG]; /* The argument list */ - int argc = 0; /* The number of arguments in argv[] */ - int c; /* A character from the file */ - int escaped = 0; /* True if the next character is escaped */ - int i; -/* - * Skip spaces and tabs. - */ - do c = getc_fn(stream); while(c==' ' || c=='\t'); -/* - * Comments extend to the end of the line. - */ - if(c=='#') - do c = getc_fn(stream); while(c != '\n' && c != EOF); -/* - * Ignore empty lines. - */ - if(c=='\n' || c==EOF) { - (*lineno)++; - return 0; - }; -/* - * Record the buffer location of the start of the first argument. - */ - argv[argc] = buffer; -/* - * Read the rest of the line, stopping early if a comment is seen, or - * the buffer overflows, and replacing sequences of spaces with a - * '\0', and recording the thus terminated string as an argument. - */ - i = 0; - while(i= GL_CONF_MAXARG) { - fprintf(stderr, "%s:%d: Too many arguments.\n", origin, *lineno); - do c = getc_fn(stream); while(c != '\n' && c != EOF); /* Skip past eol */ - return 0; - }; - argv[argc] = buffer + i; -/* - * The next character was preceded by spaces, so it isn't escaped. - */ - escaped = 0; - } else { -/* - * If we hit an unescaped backslash, this means that we should arrange - * to treat the next character like a simple alphabetical character. - */ - if(c=='\\' && !escaped) { - escaped = 1; -/* - * Splice lines where the newline is escaped. - */ - } else if(c=='\n' && escaped) { - (*lineno)++; -/* - * Record a normal character, preserving any preceding backslash. - */ - } else { - if(escaped) - buffer[i++] = '\\'; - if(i>=GL_CONF_BUFLEN) - break; - escaped = 0; - buffer[i++] = c; - }; -/* - * Get the next character. - */ - c = getc_fn(stream); - }; - }; -/* - * Did the buffer overflow? - */ - if(i>=GL_CONF_BUFLEN) { - fprintf(stderr, "%s:%d: Line too long.\n", origin, *lineno); - return 0; - }; -/* - * The first argument should be a command name. - */ - if(strcmp(argv[0], "bind") == 0) { - const char *action = NULL; /* A NULL action removes a keybinding */ - const char *keyseq = NULL; - switch(argc) { - case 3: - action = argv[2]; - case 2: /* Note the intentional fallthrough */ - keyseq = argv[1]; -/* - * Attempt to record the new keybinding. - */ - if(_kt_set_keybinding(gl->bindings, who, keyseq, action)) { - fprintf(stderr, "The error occurred at line %d of %s.\n", *lineno, - origin); - }; - break; - default: - fprintf(stderr, "%s:%d: Wrong number of arguments.\n", origin, *lineno); - }; - } else if(strcmp(argv[0], "edit-mode") == 0) { - if(argc == 2 && strcmp(argv[1], "emacs") == 0) { - gl_change_editor(gl, GL_EMACS_MODE); - } else if(argc == 2 && strcmp(argv[1], "vi") == 0) { - gl_change_editor(gl, GL_VI_MODE); - } else if(argc == 2 && strcmp(argv[1], "none") == 0) { - gl_change_editor(gl, GL_NO_EDITOR); - } else { - fprintf(stderr, "%s:%d: The argument of editor should be vi or emacs.\n", - origin, *lineno); - }; - } else if(strcmp(argv[0], "nobeep") == 0) { - gl->silence_bell = 1; - } else { - fprintf(stderr, "%s:%d: Unknown command name '%s'.\n", origin, *lineno, - argv[0]); - }; -/* - * Skip any trailing comment. - */ - while(c != '\n' && c != EOF) - c = getc_fn(stream); - (*lineno)++; - return 0; -} - -/*....................................................................... - * This is the _gl_parse_config_line() callback function which reads the - * next character from a configuration file. - */ -static GLC_GETC_FN(glc_file_getc) -{ - return fgetc((FILE *) stream); -} - -/*....................................................................... - * This is the _gl_parse_config_line() callback function which reads the - * next character from a buffer. Its stream argument is a pointer to a - * variable which is, in turn, a pointer into the buffer being read from. - */ -static GLC_GETC_FN(glc_buff_getc) -{ - const char **lptr = (char const **) stream; - return **lptr ? *(*lptr)++ : EOF; -} - -/*....................................................................... - * When this action is triggered, it arranges to temporarily read command - * lines from the regular file whos name precedes the cursor. - * The current line is first discarded. - */ -static KT_KEY_FN(gl_read_from_file) -{ - char *start_path; /* The pointer to the start of the pathname in */ - /* gl->line[]. */ - FileExpansion *result; /* The results of the filename expansion */ - int pathlen; /* The length of the pathname being expanded */ - int error_reported = 0; /* True after an error has been reported */ -/* - * Locate the start of the filename that precedes the cursor position. - */ - start_path = _pu_start_of_path(gl->line, - gl->buff_curpos > 0 ? gl->buff_curpos : 0); - if(!start_path) - return 1; -/* - * Get the length of the pathname string. - */ - pathlen = gl->buff_curpos - (start_path - gl->line); -/* - * Attempt to expand the pathname. - */ - result = ef_expand_file(gl->ef, start_path, pathlen); -/* - * If there was an error, report the error on a new line, then redraw - * the original line. - */ - if(!result) { - if(gl->echo && - fprintf(gl->output_fp, "\r\n%s\n", ef_last_error(gl->ef)) < 0) - return 1; - error_reported = 1; -/* - * If no files matched, report this as well. - */ - } else if(result->nfile == 0 || !result->exists) { - if(gl->echo && fprintf(gl->output_fp, "\r\nNo files match.\n") < 0) - return 1; - error_reported = 1; -/* - * Complain if more than one file matches. - */ - } else if(result->nfile > 1) { - if(gl->echo && - fprintf(gl->output_fp, "\r\nMore than one file matches.\n") < 0) - return 1; - error_reported = 1; -/* - * Disallow input from anything but normal files. In principle we could - * also support input from named pipes. Terminal files would be a problem - * since we wouldn't know the terminal type, and other types of files - * might cause the library to lock up. - */ - } else if(!_pu_path_is_file(result->files[0])) { - if(gl->echo && fprintf(gl->output_fp, "\r\nNot a normal file.\n") < 0) - return 1; - error_reported = 1; - } else { -/* - * Attempt to open and install the specified file for reading. - */ - gl->file_fp = fopen(result->files[0], "r"); - if(!gl->file_fp) { - if(gl->echo && fprintf(gl->output_fp, "\r\nUnable to open: %s\n", - result->files[0]) < 0) - return 1; - error_reported = 1; - }; -/* - * Inform the user what is happening. - */ - if(gl->echo && fprintf(gl->output_fp, "\r\n\n", - result->files[0]) < 0) - return 1; - }; -/* - * If an error was reported, redisplay the current line. - */ - if(error_reported) { - gl->term_curpos = 0; - return gl_redisplay(gl,1); - }; - return 0; -} - -/*....................................................................... - * Close any temporary file that is being used for input. - * - * Input: - * gl GetLine * The getline resource object. - */ -static void gl_revert_input(GetLine *gl) -{ - if(gl->file_fp) - fclose(gl->file_fp); - gl->file_fp = NULL; -} - -/*....................................................................... - * This is the action function that recalls the oldest line in the - * history buffer. - */ -static KT_KEY_FN(gl_beginning_of_history) -{ -/* - * In vi mode, switch to command mode, since the user is very - * likely to want to move around newly recalled lines. - */ - gl_vi_command_mode(gl); -/* - * Forget any previous recall session. - */ - gl->preload_id = 0; -/* - * Recall the next oldest line in the history list. - */ - if(_glh_oldest_line(gl->glh, gl->line, gl->linelen) == NULL) - return 0; -/* - * Record the number of characters in the new string. - */ - gl->ntotal = strlen(gl->line); -/* - * Arrange to have the cursor placed at the end of the new line. - */ - gl->buff_curpos = strlen(gl->line); -/* - * Erase and display the new line. - */ - return gl_redisplay(gl,1); -} - -/*....................................................................... - * If a history session is currently in progress, this action function - * recalls the line that was being edited when the session started. If - * no history session is in progress, it does nothing. - */ -static KT_KEY_FN(gl_end_of_history) -{ -/* - * In vi mode, switch to command mode, since the user is very - * likely to want to move around newly recalled lines. - */ - gl_vi_command_mode(gl); -/* - * Forget any previous recall session. - */ - gl->preload_id = 0; -/* - * Recall the next oldest line in the history list. - */ - if(_glh_current_line(gl->glh, gl->line, gl->linelen) == NULL) - return 0; -/* - * Record the number of characters in the new string. - */ - gl->ntotal = strlen(gl->line); -/* - * Arrange to have the cursor placed at the end of the new line. - */ - gl->buff_curpos = strlen(gl->line); -/* - * Erase and display the new line. - */ - return gl_redisplay(gl,1); -} - -/*....................................................................... - * This action function is treated specially, in that its count argument - * is set to the end keystroke of the keysequence that activated it. - * It accumulates a numeric argument, adding one digit on each call in - * which the last keystroke was a numeric digit. - */ -static KT_KEY_FN(gl_digit_argument) -{ -/* - * Was the last keystroke a digit? - */ - int is_digit = isdigit((int)(unsigned char) count); -/* - * In vi command mode, a lone '0' means goto-start-of-line. - */ - if(gl->vi.command && gl->number < 0 && count == '0') - return gl_beginning_of_line(gl, count); -/* - * Are we starting to accumulate a new number? - */ - if(gl->number < 0 || !is_digit) - gl->number = 0; -/* - * Was the last keystroke a digit? - */ - if(is_digit) { -/* - * Read the numeric value of the digit, without assuming ASCII. - */ - int n; - char s[2]; s[0] = count; s[1] = '\0'; - n = atoi(s); -/* - * Append the new digit. - */ - gl->number = gl->number * 10 + n; - }; - return 0; -} - -/*....................................................................... - * The newline action function sets gl->endline to tell - * gl_get_input_line() that the line is now complete. - */ -static KT_KEY_FN(gl_newline) -{ - GlhLineID id; /* The last history line recalled while entering this line */ -/* - * Flag the line as ended. - */ - gl->endline = 1; -/* - * Record the next position in the history buffer, for potential - * recall by an action function on the next call to gl_get_line(). - */ - id = _glh_line_id(gl->glh, 1); - if(id) - gl->preload_id = id; - return 0; -} - -/*....................................................................... - * The 'repeat' action function sets gl->endline to tell - * gl_get_input_line() that the line is now complete, and records the - * ID of the next history line in gl->preload_id so that the next call - * to gl_get_input_line() will preload the line with that history line. - */ -static KT_KEY_FN(gl_repeat_history) -{ - gl->endline = 1; - gl->preload_id = _glh_line_id(gl->glh, 1); - gl->preload_history = 1; - return 0; -} - -/*....................................................................... - * Flush unwritten characters to the terminal. - * - * Input: - * gl GetLine * The getline resource object. - * Output: - * return int 0 - OK. - * 1 - Error. - */ -static int gl_flush_output(GetLine *gl) -{ -/* - * Attempt to flush output to the terminal, restarting the output - * if a signal is caught. - */ - while(fflush(gl->output_fp) != 0) { - if(errno!=EINTR) - return 1; - }; - return 0; -} - -/*....................................................................... - * Change the style of editing to emulate a given editor. - * - * Input: - * gl GetLine * The getline resource object. - * editor GlEditor The type of editor to emulate. - * Output: - * return int 0 - OK. - * 1 - Error. - */ -static int gl_change_editor(GetLine *gl, GlEditor editor) -{ -/* - * Install the default key-bindings of the requested editor. - */ - switch(editor) { - case GL_EMACS_MODE: - _kt_clear_bindings(gl->bindings, KTB_NORM); - _kt_clear_bindings(gl->bindings, KTB_TERM); - (void) _kt_add_bindings(gl->bindings, KTB_NORM, gl_emacs_bindings, - sizeof(gl_emacs_bindings)/sizeof(gl_emacs_bindings[0])); - break; - case GL_VI_MODE: - _kt_clear_bindings(gl->bindings, KTB_NORM); - _kt_clear_bindings(gl->bindings, KTB_TERM); - (void) _kt_add_bindings(gl->bindings, KTB_NORM, gl_vi_bindings, - sizeof(gl_vi_bindings)/sizeof(gl_vi_bindings[0])); - break; - case GL_NO_EDITOR: - break; - default: - fprintf(stderr, "gl_change_editor: Unknown editor.\n"); - return 1; - }; -/* - * Record the new editing mode. - */ - gl->editor = editor; - gl->vi.command = 0; /* Start in input mode */ - gl->insert_curpos = 0; -/* - * Reinstate terminal-specific bindings. - */ - if(gl->editor != GL_NO_EDITOR && gl->input_fp) - (void) gl_bind_terminal_keys(gl); - return 0; -} - -/*....................................................................... - * This is an action function that switches to editing using emacs bindings - */ -static KT_KEY_FN(gl_emacs_editing_mode) -{ - return gl_change_editor(gl, GL_EMACS_MODE); -} - -/*....................................................................... - * This is an action function that switches to editing using vi bindings - */ -static KT_KEY_FN(gl_vi_editing_mode) -{ - return gl_change_editor(gl, GL_VI_MODE); -} - -/*....................................................................... - * This is the action function that switches to insert mode. - */ -static KT_KEY_FN(gl_vi_insert) -{ -/* - * If in vi command mode, preserve the current line for potential - * use by vi-undo. - */ - gl_save_for_undo(gl); -/* - * Switch to vi insert mode. - */ - gl->insert = 1; - gl->vi.command = 0; - gl->insert_curpos = gl->buff_curpos; - return 0; -} - -/*....................................................................... - * This is an action function that switches to overwrite mode. - */ -static KT_KEY_FN(gl_vi_overwrite) -{ -/* - * If in vi command mode, preserve the current line for potential - * use by vi-undo. - */ - gl_save_for_undo(gl); -/* - * Switch to vi overwrite mode. - */ - gl->insert = 0; - gl->vi.command = 0; - gl->insert_curpos = gl->buff_curpos; - return 0; -} - -/*....................................................................... - * This action function toggles the case of the character under the - * cursor. - */ -static KT_KEY_FN(gl_change_case) -{ - int i; -/* - * Keep a record of the current insert mode and the cursor position. - */ - int insert = gl->insert; -/* - * If in vi command mode, preserve the current line for potential - * use by vi-undo. - */ - gl_save_for_undo(gl); -/* - * We want to overwrite the modified word. - */ - gl->insert = 0; -/* - * Toggle the case of 'count' characters. - */ - for(i=0; ibuff_curpos < gl->ntotal; i++) { - char *cptr = gl->line + gl->buff_curpos++; -/* - * Convert the character to upper case? - */ - if(islower((int)(unsigned char) *cptr)) - *cptr = toupper((int) *cptr); - else if(isupper((int)(unsigned char) *cptr)) - *cptr = tolower((int) *cptr); -/* - * Write the possibly modified character back. Note that for non-modified - * characters we want to do this as well, so as to advance the cursor. - */ - if(gl_output_char(gl, *cptr, cptr[1])) - return 1; - }; -/* - * Restore the insertion mode. - */ - gl->insert = insert; - return gl_place_cursor(gl, gl->buff_curpos); /* bounds check */ -} - -/*....................................................................... - * This is the action function which implements the vi-style action which - * moves the cursor to the start of the line, then switches to insert mode. - */ -static KT_KEY_FN(gl_vi_insert_at_bol) -{ - gl_save_for_undo(gl); - return gl_beginning_of_line(gl, 0) || - gl_vi_insert(gl, 0); - -} - -/*....................................................................... - * This is the action function which implements the vi-style action which - * moves the cursor to the end of the line, then switches to insert mode - * to allow text to be appended to the line. - */ -static KT_KEY_FN(gl_vi_append_at_eol) -{ - gl_save_for_undo(gl); - gl->vi.command = 0; /* Allow cursor at EOL */ - return gl_end_of_line(gl, 0) || - gl_vi_insert(gl, 0); -} - -/*....................................................................... - * This is the action function which implements the vi-style action which - * moves the cursor to right one then switches to insert mode, thus - * allowing text to be appended after the next character. - */ -static KT_KEY_FN(gl_vi_append) -{ - gl_save_for_undo(gl); - gl->vi.command = 0; /* Allow cursor at EOL */ - return gl_cursor_right(gl, 1) || - gl_vi_insert(gl, 0); -} - -/*....................................................................... - * This action function moves the cursor to the column specified by the - * numeric argument. Column indexes start at 1. - */ -static KT_KEY_FN(gl_goto_column) -{ - return gl_place_cursor(gl, count - 1); -} - -/*....................................................................... - * Starting with the character under the cursor, replace 'count' - * characters with the next character that the user types. - */ -static KT_KEY_FN(gl_vi_replace_char) -{ - char c; /* The replacement character */ - int i; -/* - * Keep a record of the current insert mode. - */ - int insert = gl->insert; -/* - * Get the replacement character. - */ - if(gl->vi.repeat.active) { - c = gl->vi.repeat.input_char; - } else { - if(gl_read_character(gl, &c)) - return 1; - gl->vi.repeat.input_char = c; - }; -/* - * Are there 'count' characters to be replaced? - */ - if(gl->ntotal - gl->buff_curpos >= count) { -/* - * If in vi command mode, preserve the current line for potential - * use by vi-undo. - */ - gl_save_for_undo(gl); -/* - * Temporarily switch to overwrite mode. - */ - gl->insert = 0; -/* - * Overwrite the current character plus count-1 subsequent characters - * with the replacement character. - */ - for(i=0; iinsert = insert; - }; - return gl_place_cursor(gl, gl->buff_curpos); /* bounds check */ -} - -/*....................................................................... - * This is an action function which changes all characters between the - * current cursor position and the end of the line. - */ -static KT_KEY_FN(gl_vi_change_rest_of_line) -{ - gl_save_for_undo(gl); - gl->vi.command = 0; /* Allow cursor at EOL */ - return gl_kill_line(gl, count) || gl_vi_insert(gl, 0); -} - -/*....................................................................... - * This is an action function which changes all characters between the - * start of the line and the current cursor position. - */ -static KT_KEY_FN(gl_vi_change_to_bol) -{ - return gl_backward_kill_line(gl, count) || gl_vi_insert(gl, 0); -} - -/*....................................................................... - * This is an action function which deletes the entire contents of the - * current line and switches to insert mode. - */ -static KT_KEY_FN(gl_vi_change_line) -{ - return gl_delete_line(gl, count) || gl_vi_insert(gl, 0); -} - -/*....................................................................... - * Starting from the cursor position and looking towards the end of the - * line, copy 'count' characters to the cut buffer. - */ -static KT_KEY_FN(gl_forward_copy_char) -{ -/* - * Limit the count to the number of characters available. - */ - if(gl->buff_curpos + count >= gl->ntotal) - count = gl->ntotal - gl->buff_curpos; - if(count < 0) - count = 0; -/* - * Copy the characters to the cut buffer. - */ - memcpy(gl->cutbuf, gl->line + gl->buff_curpos, count); - gl->cutbuf[count] = '\0'; - return 0; -} - -/*....................................................................... - * Starting from the character before the cursor position and looking - * backwards towards the start of the line, copy 'count' characters to - * the cut buffer. - */ -static KT_KEY_FN(gl_backward_copy_char) -{ -/* - * Limit the count to the number of characters available. - */ - if(count > gl->buff_curpos) - count = gl->buff_curpos; - if(count < 0) - count = 0; - gl_place_cursor(gl, gl->buff_curpos - count); -/* - * Copy the characters to the cut buffer. - */ - memcpy(gl->cutbuf, gl->line + gl->buff_curpos, count); - gl->cutbuf[count] = '\0'; - return 0; -} - -/*....................................................................... - * Starting from the cursor position copy to the specified column into the - * cut buffer. - */ -static KT_KEY_FN(gl_copy_to_column) -{ - if (--count >= gl->buff_curpos) - return gl_forward_copy_char(gl, count - gl->buff_curpos); - else - return gl_backward_copy_char(gl, gl->buff_curpos - count); -} - -/*....................................................................... - * Starting from the cursor position copy characters up to a matching - * parenthesis into the cut buffer. - */ -static KT_KEY_FN(gl_copy_to_parenthesis) -{ - int curpos = gl_index_of_matching_paren(gl); - if(curpos >= 0) { - gl_save_for_undo(gl); - if(curpos >= gl->buff_curpos) - return gl_forward_copy_char(gl, curpos - gl->buff_curpos + 1); - else - return gl_backward_copy_char(gl, ++gl->buff_curpos - curpos + 1); - }; - return 0; -} - -/*....................................................................... - * Starting from the cursor position copy the rest of the line into the - * cut buffer. - */ -static KT_KEY_FN(gl_copy_rest_of_line) -{ -/* - * Copy the characters to the cut buffer. - */ - memcpy(gl->cutbuf, gl->line + gl->buff_curpos, gl->ntotal - gl->buff_curpos); - gl->cutbuf[gl->ntotal - gl->buff_curpos] = '\0'; - return 0; -} - -/*....................................................................... - * Copy from the beginning of the line to the cursor position into the - * cut buffer. - */ -static KT_KEY_FN(gl_copy_to_bol) -{ -/* - * Copy the characters to the cut buffer. - */ - memcpy(gl->cutbuf, gl->line, gl->buff_curpos); - gl->cutbuf[gl->buff_curpos] = '\0'; - gl_place_cursor(gl, 0); - return 0; -} - -/*....................................................................... - * Copy the entire line into the cut buffer. - */ -static KT_KEY_FN(gl_copy_line) -{ -/* - * Copy the characters to the cut buffer. - */ - memcpy(gl->cutbuf, gl->line, gl->ntotal); - gl->cutbuf[gl->ntotal] = '\0'; - return 0; -} - -/*....................................................................... - * Search forwards for the next character that the user enters. - */ -static KT_KEY_FN(gl_forward_find_char) -{ - int pos = gl_find_char(gl, count, 1, 1, '\0'); - return pos >= 0 && gl_place_cursor(gl, pos); -} - -/*....................................................................... - * Search backwards for the next character that the user enters. - */ -static KT_KEY_FN(gl_backward_find_char) -{ - int pos = gl_find_char(gl, count, 0, 1, '\0'); - return pos >= 0 && gl_place_cursor(gl, pos); -} - -/*....................................................................... - * Search forwards for the next character that the user enters. Move up to, - * but not onto, the found character. - */ -static KT_KEY_FN(gl_forward_to_char) -{ - int pos = gl_find_char(gl, count, 1, 0, '\0'); - return pos >= 0 && gl_place_cursor(gl, pos); -} - -/*....................................................................... - * Search backwards for the next character that the user enters. Move back to, - * but not onto, the found character. - */ -static KT_KEY_FN(gl_backward_to_char) -{ - int pos = gl_find_char(gl, count, 0, 0, '\0'); - return pos >= 0 && gl_place_cursor(gl, pos); -} - -/*....................................................................... - * Searching in a given direction, return the index of a given (or - * read) character in the input line, or the character that precedes - * it in the specified search direction. Return -1 if not found. - * - * Input: - * gl GetLine * The getline resource object. - * count int The number of times to search. - * forward int True if searching forward. - * onto int True if the search should end on top of the - * character, false if the search should stop - * one character before the character in the - * specified search direction. - * c char The character to be sought, or '\0' if the - * character should be read from the user. - * Output: - * return int The index of the character in gl->line[], or - * -1 if not found. - */ -static int gl_find_char(GetLine *gl, int count, int forward, int onto, char c) -{ - int pos; /* The index reached in searching the input line */ - int i; -/* - * Get a character from the user? - */ - if(!c) { -/* - * If we are in the process of repeating a previous change command, substitute - * the last find character. - */ - if(gl->vi.repeat.active) { - c = gl->vi.find_char; - } else { - if(gl_read_character(gl, &c)) - return -1; -/* - * Record the details of the new search, for use by repeat finds. - */ - gl->vi.find_forward = forward; - gl->vi.find_onto = onto; - gl->vi.find_char = c; - }; - }; -/* - * Which direction should we search? - */ - if(forward) { -/* - * Search forwards 'count' times for the character, starting with the - * character that follows the cursor. - */ - for(i=0, pos=gl->buff_curpos; intotal; i++) { -/* - * Advance past the last match (or past the current cursor position - * on the first search). - */ - pos++; -/* - * Search for the next instance of c. - */ - for( ; posntotal && c!=gl->line[pos]; pos++) - ; - }; -/* - * If the character was found and we have been requested to return the - * position of the character that precedes the desired character, then - * we have gone one character too far. - */ - if(!onto && posntotal) - pos--; - } else { -/* - * Search backwards 'count' times for the character, starting with the - * character that precedes the cursor. - */ - for(i=0, pos=gl->buff_curpos; i= gl->insert_curpos; i++) { -/* - * Step back one from the last match (or from the current cursor - * position on the first search). - */ - pos--; -/* - * Search for the next instance of c. - */ - for( ; pos>=gl->insert_curpos && c!=gl->line[pos]; pos--) - ; - }; -/* - * If the character was found and we have been requested to return the - * position of the character that precedes the desired character, then - * we have gone one character too far. - */ - if(!onto && pos>=gl->insert_curpos) - pos++; - }; -/* - * If found, return the cursor position of the count'th match. - * Otherwise ring the terminal bell. - */ - if(pos >= gl->insert_curpos && pos < gl->ntotal) { - return pos; - } else { - (void) gl_ring_bell(gl, 1); - return -1; - } -} - -/*....................................................................... - * Repeat the last character search in the same direction as the last - * search. - */ -static KT_KEY_FN(gl_repeat_find_char) -{ - int pos = gl->vi.find_char ? - gl_find_char(gl, count, gl->vi.find_forward, gl->vi.find_onto, - gl->vi.find_char) : -1; - return pos >= 0 && gl_place_cursor(gl, pos); -} - -/*....................................................................... - * Repeat the last character search in the opposite direction as the last - * search. - */ -static KT_KEY_FN(gl_invert_refind_char) -{ - int pos = gl->vi.find_char ? - gl_find_char(gl, count, !gl->vi.find_forward, gl->vi.find_onto, - gl->vi.find_char) : -1; - return pos >= 0 && gl_place_cursor(gl, pos); -} - -/*....................................................................... - * Search forward from the current position of the cursor for 'count' - * word endings, returning the index of the last one found, or the end of - * the line if there were less than 'count' words. - * - * Input: - * gl GetLine * The getline resource object. - * n int The number of word boundaries to search for. - * Output: - * return int The buffer index of the located position. - */ -static int gl_nth_word_end_forward(GetLine *gl, int n) -{ - int bufpos; /* The buffer index being checked. */ - int i; -/* - * In order to guarantee forward motion to the next word ending, - * we need to start from one position to the right of the cursor - * position, since this may already be at the end of a word. - */ - bufpos = gl->buff_curpos + 1; -/* - * If we are at the end of the line, return the index of the last - * real character on the line. Note that this will be -1 if the line - * is empty. - */ - if(bufpos >= gl->ntotal) - return gl->ntotal - 1; -/* - * Search 'n' times, unless the end of the input line is reached first. - */ - for(i=0; intotal; i++) { -/* - * If we are not already within a word, skip to the start of the next word. - */ - for( ; bufposntotal && !gl_is_word_char((int)gl->line[bufpos]); - bufpos++) - ; -/* - * Find the end of the next word. - */ - for( ; bufposntotal && gl_is_word_char((int)gl->line[bufpos]); - bufpos++) - ; - }; -/* - * We will have overshot. - */ - return bufpos > 0 ? bufpos-1 : bufpos; -} - -/*....................................................................... - * Search forward from the current position of the cursor for 'count' - * word starts, returning the index of the last one found, or the end of - * the line if there were less than 'count' words. - * - * Input: - * gl GetLine * The getline resource object. - * n int The number of word boundaries to search for. - * Output: - * return int The buffer index of the located position. - */ -static int gl_nth_word_start_forward(GetLine *gl, int n) -{ - int bufpos; /* The buffer index being checked. */ - int i; -/* - * Get the current cursor position. - */ - bufpos = gl->buff_curpos; -/* - * Search 'n' times, unless the end of the input line is reached first. - */ - for(i=0; intotal; i++) { -/* - * Find the end of the current word. - */ - for( ; bufposntotal && gl_is_word_char((int)gl->line[bufpos]); - bufpos++) - ; -/* - * Skip to the start of the next word. - */ - for( ; bufposntotal && !gl_is_word_char((int)gl->line[bufpos]); - bufpos++) - ; - }; - return bufpos; -} - -/*....................................................................... - * Search backward from the current position of the cursor for 'count' - * word starts, returning the index of the last one found, or the start - * of the line if there were less than 'count' words. - * - * Input: - * gl GetLine * The getline resource object. - * n int The number of word boundaries to search for. - * Output: - * return int The buffer index of the located position. - */ -static int gl_nth_word_start_backward(GetLine *gl, int n) -{ - int bufpos; /* The buffer index being checked. */ - int i; -/* - * Get the current cursor position. - */ - bufpos = gl->buff_curpos; -/* - * Search 'n' times, unless the beginning of the input line (or vi insertion - * point) is reached first. - */ - for(i=0; i gl->insert_curpos; i++) { -/* - * Starting one character back from the last search, so as not to keep - * settling on the same word-start, search backwards until finding a - * word character. - */ - while(--bufpos >= gl->insert_curpos && - !gl_is_word_char((int)gl->line[bufpos])) - ; -/* - * Find the start of the word. - */ - while(--bufpos >= gl->insert_curpos && - gl_is_word_char((int)gl->line[bufpos])) - ; -/* - * We will have gone one character too far. - */ - bufpos++; - }; - return bufpos >= gl->insert_curpos ? bufpos : gl->insert_curpos; -} - -/*....................................................................... - * Copy one or more words into the cut buffer without moving the cursor - * or deleting text. - */ -static KT_KEY_FN(gl_forward_copy_word) -{ -/* - * Find the location of the count'th start or end of a word - * after the cursor, depending on whether in emacs or vi mode. - */ - int next = gl->editor == GL_EMACS_MODE ? - gl_nth_word_end_forward(gl, count) : - gl_nth_word_start_forward(gl, count); -/* - * How many characters are to be copied into the cut buffer? - */ - int n = next - gl->buff_curpos; -/* - * Copy the specified segment and terminate the string. - */ - memcpy(gl->cutbuf, gl->line + gl->buff_curpos, n); - gl->cutbuf[n] = '\0'; - return 0; -} - -/*....................................................................... - * Copy one or more words preceding the cursor into the cut buffer, - * without moving the cursor or deleting text. - */ -static KT_KEY_FN(gl_backward_copy_word) -{ -/* - * Find the location of the count'th start of word before the cursor. - */ - int next = gl_nth_word_start_backward(gl, count); -/* - * How many characters are to be copied into the cut buffer? - */ - int n = gl->buff_curpos - next; - gl_place_cursor(gl, next); -/* - * Copy the specified segment and terminate the string. - */ - memcpy(gl->cutbuf, gl->line + next, n); - gl->cutbuf[n] = '\0'; - return 0; -} - -/*....................................................................... - * Copy the characters between the cursor and the count'th instance of - * a specified character in the input line, into the cut buffer. - * - * Input: - * gl GetLine * The getline resource object. - * count int The number of times to search. - * c char The character to be searched for, or '\0' if - * the character should be read from the user. - * forward int True if searching forward. - * onto int True if the search should end on top of the - * character, false if the search should stop - * one character before the character in the - * specified search direction. - * Output: - * return int 0 - OK. - * 1 - Error. - * - */ -static int gl_copy_find(GetLine *gl, int count, char c, int forward, int onto) -{ - int n; /* The number of characters in the cut buffer */ -/* - * Search for the character, and abort the operation if not found. - */ - int pos = gl_find_char(gl, count, forward, onto, c); - if(pos < 0) - return 0; -/* - * Copy the specified segment. - */ - if(forward) { - n = pos + 1 - gl->buff_curpos; - memcpy(gl->cutbuf, gl->line + gl->buff_curpos, n); - } else { - n = gl->buff_curpos - pos; - memcpy(gl->cutbuf, gl->line + pos, n); - if(gl->editor == GL_VI_MODE) - gl_place_cursor(gl, pos); - } -/* - * Terminate the copy. - */ - gl->cutbuf[n] = '\0'; - return 0; -} - -/*....................................................................... - * Copy a section up to and including a specified character into the cut - * buffer without moving the cursor or deleting text. - */ -static KT_KEY_FN(gl_forward_copy_find) -{ - return gl_copy_find(gl, count, '\0', 1, 1); -} - -/*....................................................................... - * Copy a section back to and including a specified character into the cut - * buffer without moving the cursor or deleting text. - */ -static KT_KEY_FN(gl_backward_copy_find) -{ - return gl_copy_find(gl, count, '\0', 0, 1); -} - -/*....................................................................... - * Copy a section up to and not including a specified character into the cut - * buffer without moving the cursor or deleting text. - */ -static KT_KEY_FN(gl_forward_copy_to) -{ - return gl_copy_find(gl, count, '\0', 1, 0); -} - -/*....................................................................... - * Copy a section back to and not including a specified character into the cut - * buffer without moving the cursor or deleting text. - */ -static KT_KEY_FN(gl_backward_copy_to) -{ - return gl_copy_find(gl, count, '\0', 0, 0); -} - -/*....................................................................... - * Copy to a character specified in a previous search into the cut - * buffer without moving the cursor or deleting text. - */ -static KT_KEY_FN(gl_copy_refind) -{ - return gl_copy_find(gl, count, gl->vi.find_char, gl->vi.find_forward, - gl->vi.find_onto); -} - -/*....................................................................... - * Copy to a character specified in a previous search, but in the opposite - * direction, into the cut buffer without moving the cursor or deleting text. - */ -static KT_KEY_FN(gl_copy_invert_refind) -{ - return gl_copy_find(gl, count, gl->vi.find_char, !gl->vi.find_forward, - gl->vi.find_onto); -} - -/*....................................................................... - * Set the position of the cursor in the line input buffer and the - * terminal. - * - * Input: - * gl GetLine * The getline resource object. - * buff_curpos int The new buffer cursor position. - * Output: - * return int 0 - OK. - * 1 - Error. - */ -static int gl_place_cursor(GetLine *gl, int buff_curpos) -{ -/* - * Don't allow the cursor position to go out of the bounds of the input - * line. - */ - if(buff_curpos >= gl->ntotal) - buff_curpos = gl->vi.command ? gl->ntotal-1 : gl->ntotal; - if(buff_curpos < 0) - buff_curpos = 0; -/* - * Record the new buffer position. - */ - gl->buff_curpos = buff_curpos; -/* - * Move the terminal cursor to the corresponding character. - */ - return gl_set_term_curpos(gl, gl_buff_curpos_to_term_curpos(gl, buff_curpos)); -} - -/*....................................................................... - * In vi command mode, this function saves the current line to the - * historical buffer needed by the undo command. In emacs mode it does - * nothing. In order to allow action functions to call other action - * functions, gl_interpret_char() sets gl->vi.undo.saved to 0 before - * invoking an action, and thereafter once any call to this function - * has set it to 1, further calls are ignored. - * - * Input: - * gl GetLine * The getline resource object. - */ -static void gl_save_for_undo(GetLine *gl) -{ - if(gl->vi.command && !gl->vi.undo.saved) { - strcpy(gl->vi.undo.line, gl->line); - gl->vi.undo.buff_curpos = gl->buff_curpos; - gl->vi.undo.ntotal = gl->ntotal; - gl->vi.undo.saved = 1; - }; - if(gl->vi.command && !gl->vi.repeat.saved && - gl->current_fn != gl_vi_repeat_change) { - gl->vi.repeat.fn = gl->current_fn; - gl->vi.repeat.count = gl->current_count; - gl->vi.repeat.saved = 1; - }; - return; -} - -/*....................................................................... - * In vi mode, restore the line to the way it was before the last command - * mode operation, storing the current line in the buffer so that the - * undo operation itself can subsequently be undone. - */ -static KT_KEY_FN(gl_vi_undo) -{ -/* - * Get pointers into the two lines. - */ - char *undo_ptr = gl->vi.undo.line; - char *line_ptr = gl->line; -/* - * Swap the characters of the two buffers up to the length of the shortest - * line. - */ - while(*undo_ptr && *line_ptr) { - char c = *undo_ptr; - *undo_ptr++ = *line_ptr; - *line_ptr++ = c; - }; -/* - * Copy the rest directly. - */ - if(gl->ntotal > gl->vi.undo.ntotal) { - strcpy(undo_ptr, line_ptr); - *line_ptr = '\0'; - } else { - strcpy(line_ptr, undo_ptr); - *undo_ptr = '\0'; - }; -/* - * Swap the length information. - */ - { - int ntotal = gl->ntotal; - gl->ntotal = gl->vi.undo.ntotal; - gl->vi.undo.ntotal = ntotal; - }; -/* - * Set both cursor positions to the leftmost of the saved and current - * cursor positions to emulate what vi does. - */ - if(gl->buff_curpos < gl->vi.undo.buff_curpos) - gl->vi.undo.buff_curpos = gl->buff_curpos; - else - gl->buff_curpos = gl->vi.undo.buff_curpos; -/* - * Since we have bipassed calling gl_save_for_undo(), record repeat - * information inline. - */ - gl->vi.repeat.fn = gl_vi_undo; - gl->vi.repeat.count = 1; -/* - * Display the restored line. - */ - return gl_redisplay(gl,1); -} - -/*....................................................................... - * Delete the following word and leave the user in vi insert mode. - */ -static KT_KEY_FN(gl_vi_forward_change_word) -{ - gl_save_for_undo(gl); - gl->vi.command = 0; /* Allow cursor at EOL */ - return gl_forward_delete_word(gl, count) || gl_vi_insert(gl, 0); -} - -/*....................................................................... - * Delete the preceding word and leave the user in vi insert mode. - */ -static KT_KEY_FN(gl_vi_backward_change_word) -{ - return gl_backward_delete_word(gl, count) || gl_vi_insert(gl, 0); -} - -/*....................................................................... - * Delete the following section and leave the user in vi insert mode. - */ -static KT_KEY_FN(gl_vi_forward_change_find) -{ - return gl_delete_find(gl, count, '\0', 1, 1, 1); -} - -/*....................................................................... - * Delete the preceding section and leave the user in vi insert mode. - */ -static KT_KEY_FN(gl_vi_backward_change_find) -{ - return gl_delete_find(gl, count, '\0', 0, 1, 1); -} - -/*....................................................................... - * Delete the following section and leave the user in vi insert mode. - */ -static KT_KEY_FN(gl_vi_forward_change_to) -{ - return gl_delete_find(gl, count, '\0', 1, 0, 1); -} - -/*....................................................................... - * Delete the preceding section and leave the user in vi insert mode. - */ -static KT_KEY_FN(gl_vi_backward_change_to) -{ - return gl_delete_find(gl, count, '\0', 0, 0, 1); -} - -/*....................................................................... - * Delete to a character specified by a previous search and leave the user - * in vi insert mode. - */ -static KT_KEY_FN(gl_vi_change_refind) -{ - return gl_delete_find(gl, count, gl->vi.find_char, gl->vi.find_forward, - gl->vi.find_onto, 1); -} - -/*....................................................................... - * Delete to a character specified by a previous search, but in the opposite - * direction, and leave the user in vi insert mode. - */ -static KT_KEY_FN(gl_vi_change_invert_refind) -{ - return gl_delete_find(gl, count, gl->vi.find_char, !gl->vi.find_forward, - gl->vi.find_onto, 1); -} - -/*....................................................................... - * Delete the following character and leave the user in vi insert mode. - */ -static KT_KEY_FN(gl_vi_forward_change_char) -{ - gl_save_for_undo(gl); - gl->vi.command = 0; /* Allow cursor at EOL */ - return gl_delete_chars(gl, count, 1) || gl_vi_insert(gl, 0); -} - -/*....................................................................... - * Delete the preceding character and leave the user in vi insert mode. - */ -static KT_KEY_FN(gl_vi_backward_change_char) -{ - return gl_backward_delete_char(gl, count) || gl_vi_insert(gl, 0); -} - -/*....................................................................... - * Starting from the cursor position change characters to the specified column. - */ -static KT_KEY_FN(gl_vi_change_to_column) -{ - if (--count >= gl->buff_curpos) - return gl_vi_forward_change_char(gl, count - gl->buff_curpos); - else - return gl_vi_backward_change_char(gl, gl->buff_curpos - count); -} - -/*....................................................................... - * Starting from the cursor position change characters to a matching - * parenthesis. - */ -static KT_KEY_FN(gl_vi_change_to_parenthesis) -{ - int curpos = gl_index_of_matching_paren(gl); - if(curpos >= 0) { - gl_save_for_undo(gl); - if(curpos >= gl->buff_curpos) - return gl_vi_forward_change_char(gl, curpos - gl->buff_curpos + 1); - else - return gl_vi_backward_change_char(gl, ++gl->buff_curpos - curpos + 1); - }; - return 0; -} - -/*....................................................................... - * If in vi mode, switch to vi command mode. - * - * Input: - * gl GetLine * The getline resource object. - */ -static void gl_vi_command_mode(GetLine *gl) -{ - if(gl->editor == GL_VI_MODE && !gl->vi.command) { - gl->insert = 1; - gl->vi.command = 1; - gl->vi.repeat.input_curpos = gl->insert_curpos; - gl->vi.repeat.command_curpos = gl->buff_curpos; - gl->insert_curpos = 0; /* unrestrict left motion boundary */ - gl_cursor_left(gl, 1); /* Vi moves left one on entering command mode */ - }; -} - -/*....................................................................... - * This is an action function which rings the terminal bell. - */ -static KT_KEY_FN(gl_ring_bell) -{ - return gl->silence_bell ? 0 : - gl_output_control_sequence(gl, 1, gl->sound_bell); -} - -/*....................................................................... - * This is the action function which implements the vi-repeat-change - * action. - */ -static KT_KEY_FN(gl_vi_repeat_change) -{ - int status; /* The return status of the repeated action function */ - int i; -/* - * Nothing to repeat? - */ - if(!gl->vi.repeat.fn) - return gl_ring_bell(gl, 1); -/* - * Provide a way for action functions to know whether they are being - * called by us. - */ - gl->vi.repeat.active = 1; -/* - * Re-run the recorded function. - */ - status = gl->vi.repeat.fn(gl, gl->vi.repeat.count); -/* - * Mark the repeat as completed. - */ - gl->vi.repeat.active = 0; -/* - * Is we are repeating a function that has just switched to input - * mode to allow the user to type, re-enter the text that the user - * previously entered. - */ - if(status==0 && !gl->vi.command) { -/* - * Make sure that the current line has been saved. - */ - gl_save_for_undo(gl); -/* - * Repeat a previous insertion or overwrite? - */ - if(gl->vi.repeat.input_curpos >= 0 && - gl->vi.repeat.input_curpos <= gl->vi.repeat.command_curpos && - gl->vi.repeat.command_curpos <= gl->vi.undo.ntotal) { -/* - * Using the current line which is saved in the undo buffer, plus - * the range of characters therein, as recorded by gl_vi_command_mode(), - * add the characters that the user previously entered, to the input - * line. - */ - for(i=gl->vi.repeat.input_curpos; ivi.repeat.command_curpos; i++) { - if(gl_add_char_to_line(gl, gl->vi.undo.line[i])) - return 1; - }; - }; -/* - * Switch back to command mode, now that the insertion has been repeated. - */ - gl_vi_command_mode(gl); - }; - return status; -} - -/*....................................................................... - * If the cursor is currently over a parenthesis character, return the - * index of its matching parenthesis. If not currently over a parenthesis - * character, return the next close parenthesis character to the right of - * the cursor. If the respective parenthesis character isn't found, - * ring the terminal bell and return -1. - * - * Input: - * gl GetLine * The getline resource object. - * Output: - * return int Either the index of the matching parenthesis, - * or -1 if not found. - */ -static int gl_index_of_matching_paren(GetLine *gl) -{ - int i; -/* - * List the recognized parentheses, and their matches. - */ - const char *o_paren = "([{"; - const char *c_paren = ")]}"; - const char *cptr; -/* - * Get the character that is currently under the cursor. - */ - char c = gl->line[gl->buff_curpos]; -/* - * If the character under the cursor is an open parenthesis, look forward - * for the matching close parenthesis. - */ - if((cptr=strchr(o_paren, c))) { - char match = c_paren[cptr - o_paren]; - int matches_needed = 1; - for(i=gl->buff_curpos+1; intotal; i++) { - if(gl->line[i] == c) - matches_needed++; - else if(gl->line[i] == match && --matches_needed==0) - return i; - }; -/* - * If the character under the cursor is an close parenthesis, look forward - * for the matching open parenthesis. - */ - } else if((cptr=strchr(c_paren, c))) { - char match = o_paren[cptr - c_paren]; - int matches_needed = 1; - for(i=gl->buff_curpos-1; i>=0; i--) { - if(gl->line[i] == c) - matches_needed++; - else if(gl->line[i] == match && --matches_needed==0) - return i; - }; -/* - * If not currently over a parenthesis character, search forwards for - * the first close parenthesis (this is what the vi % binding does). - */ - } else { - for(i=gl->buff_curpos+1; intotal; i++) - if(strchr(c_paren, gl->line[i]) != NULL) - return i; - }; -/* - * Not found. - */ - (void) gl_ring_bell(gl, 1); - return -1; -} - -/*....................................................................... - * If the cursor is currently over a parenthesis character, this action - * function moves the cursor to its matching parenthesis. - */ -static KT_KEY_FN(gl_find_parenthesis) -{ - int curpos = gl_index_of_matching_paren(gl); - if(curpos >= 0) - return gl_place_cursor(gl, curpos); - return 0; -} - -/*....................................................................... - * Handle the receipt of the potential start of a new key-sequence from - * the user. - * - * Input: - * gl GetLine * The resource object of this library. - * first_char char The first character of the sequence. - * Output: - * return int 0 - OK. - * 1 - Error. - */ -static int gl_interpret_char(GetLine *gl, char first_char) -{ - KtKeyFn *keyfn; /* An action function */ - char keyseq[GL_KEY_MAX+1]; /* A special key sequence being read */ - int nkey=0; /* The number of characters in the key sequence */ - int count; /* The repeat count of an action function */ - int ret; /* The return value of an action function */ - int i; -/* - * Get the first character. - */ - char c = first_char; -/* - * If editting is disabled, just add newly entered characters to the - * input line buffer, and watch for the end of the line. - */ - if(gl->editor == GL_NO_EDITOR) { - if(gl->ntotal >= gl->linelen) - return 0; - if(c == '\n' || c == '\r') - return gl_newline(gl, 1); - gl->line[gl->ntotal++] = c; - return 0; - }; -/* - * If the user is in the process of specifying a repeat count and the - * new character is a digit, increment the repeat count accordingly. - */ - if(gl->number >= 0 && isdigit((int)(unsigned char) c)) - return gl_digit_argument(gl, c); -/* - * In vi command mode, all key-sequences entered need to be - * either implicitly or explicitly prefixed with an escape character. - */ - else if(gl->vi.command && c != GL_ESC_CHAR) - keyseq[nkey++] = GL_ESC_CHAR; -/* - * If the first character of the sequence is a printable character, - * then to avoid confusion with the special "up", "down", "left" - * or "right" cursor key bindings, we need to prefix the - * printable character with a backslash escape before looking it up. - */ - else if(!IS_META_CHAR(c) && !IS_CTRL_CHAR(c)) - keyseq[nkey++] = '\\'; -/* - * Compose a potentially multiple key-sequence in gl->keyseq. - */ - while(nkey < GL_KEY_MAX) { - int first, last; /* The matching entries in gl->keys */ -/* - * If the character is an unprintable meta character, split it - * into two characters, an escape character and the character - * that was modified by the meta key. - */ - if(IS_META_CHAR(c)) { - keyseq[nkey++] = GL_ESC_CHAR; - c = META_TO_CHAR(c); - continue; - }; -/* - * Append the latest character to the key sequence. - */ - keyseq[nkey++] = c; -/* - * When doing vi-style editing, an escape at the beginning of any binding - * switches to command mode. - */ - if(keyseq[0] == GL_ESC_CHAR && !gl->vi.command) - gl_vi_command_mode(gl); -/* - * Lookup the key sequence. - */ - switch(_kt_lookup_keybinding(gl->bindings, keyseq, nkey, &first, &last)) { - case KT_EXACT_MATCH: -/* - * Get the matching action function. - */ - keyfn = gl->bindings->table[first].keyfn; -/* - * Get the repeat count, passing the last keystroke if executing the - * digit-argument action. - */ - if(keyfn == gl_digit_argument) { - count = c; - } else { - count = gl->number >= 0 ? gl->number : 1; - }; -/* - * Record the function that is being invoked. - */ - gl->current_fn = keyfn; - gl->current_count = count; -/* - * Mark the current line as not yet preserved for use by the vi undo command. - */ - gl->vi.undo.saved = 0; - gl->vi.repeat.saved = 0; -/* - * Execute the action function. Note the action function can tell - * whether the provided repeat count was defaulted or specified - * explicitly by looking at whether gl->number is -1 or not. If - * it is negative, then no repeat count was specified by the user. - */ - ret = keyfn(gl, count); -/* - * Reset the repeat count after running action functions (other - * than digit-argument). - */ - if(keyfn != gl_digit_argument) - gl->number = -1; - if(ret) - return 1; - return 0; - break; - case KT_AMBIG_MATCH: /* Ambiguous match - so look ahead */ - if(gl_read_character(gl, &c)) /* Get the next character */ - return 1; - break; - case KT_NO_MATCH: -/* - * If the first character looked like it might be a prefix of a key-sequence - * but it turned out not to be, ring the bell to tell the user that it - * wasn't recognised. - */ - if(keyseq[0] != '\\' && keyseq[0] != '\t') { - gl_ring_bell(gl, 0); - } else { -/* - * The user typed a single printable character that doesn't match - * the start of any keysequence, so add it to the line in accordance - * with the current repeat count. - */ - count = gl->number >= 0 ? gl->number : 1; - for(i=0; inumber = -1; - }; - return 0; - break; - case KT_BAD_MATCH: - return 1; - break; - }; - }; -/* - * Key sequence too long to match. - */ - return 0; -} - -/*....................................................................... - * Configure the application and/or user-specific behavior of - * gl_get_line(). - * - * Note that calling this function between calling new_GetLine() and - * the first call to new_GetLine(), disables the otherwise automatic - * reading of ~/.teclarc on the first call to gl_get_line(). - * - * Input: - * gl GetLine * The resource object of this library. - * app_string const char * Either NULL, or a string containing one - * or more .teclarc command lines, separated - * by newline characters. This can be used to - * establish an application-specific - * configuration, without the need for an external - * file. This is particularly useful in embedded - * environments where there is no filesystem. - * app_file const char * Either NULL, or the pathname of an - * application-specific .teclarc file. The - * contents of this file, if provided, are - * read after the contents of app_string[]. - * user_file const char * Either NULL, or the pathname of a - * user-specific .teclarc file. Except in - * embedded applications, this should - * usually be "~/.teclarc". - * Output: - * return int 0 - OK. - * 1 - Bad argument(s). - */ -int gl_configure_getline(GetLine *gl, const char *app_string, - const char *app_file, const char *user_file) -{ -/* - * Check the arguments. - */ - if(!gl) { - fprintf(stderr, "gl_configure_getline: NULL gl argument.\n"); - return 1; - }; -/* - * Mark getline as having been explicitly configured. - */ - gl->configured = 1; -/* - * Start by parsing the configuration string, if provided. - */ - if(app_string) - (void) _gl_read_config_string(gl, app_string, KTB_NORM); -/* - * Now parse the application-specific configuration file, if provided. - */ - if(app_file) - (void) _gl_read_config_file(gl, app_file, KTB_NORM); -/* - * Finally, parse the user-specific configuration file, if provided. - */ - if(user_file) - (void) _gl_read_config_file(gl, user_file, KTB_USER); -/* - * Record the names of the configuration files to allow them to - * be re-read if requested at a later time. - */ - if(gl_record_string(&gl->app_file, app_file) || - gl_record_string(&gl->user_file, user_file)) { - fprintf(stderr, - "Insufficient memory to record tecla configuration file names.\n"); - return 1; - }; - return 0; -} - -/*....................................................................... - * Replace a malloc'd string (or NULL), with another malloc'd copy of - * a string (or NULL). - * - * Input: - * sptr char ** On input if *sptr!=NULL, *sptr will be - * free'd and *sptr will be set to NULL. Then, - * on output, if string!=NULL a malloc'd copy - * of this string will be assigned to *sptr. - * string const char * The string to be copied, or NULL to simply - * discard any existing string. - * Output: - * return int 0 - OK. - * 1 - Malloc failure (no error message is generated). - */ -static int gl_record_string(char **sptr, const char *string) -{ -/* - * If the original string is the same string, don't do anything. - */ - if(*sptr == string || (*sptr && string && strcmp(*sptr, string)==0)) - return 0; -/* - * Discard any existing cached string. - */ - if(*sptr) { - free(*sptr); - *sptr = NULL; - }; -/* - * Allocate memory for a copy of the specified string. - */ - if(string) { - *sptr = (char *) malloc(strlen(string) + 1); - if(!*sptr) - return 1; -/* - * Copy the string. - */ - strcpy(*sptr, string); - }; - return 0; -} - -/*....................................................................... - * Re-read any application-specific and user-specific files previously - * specified via the gl_configure_getline() function. - */ -static KT_KEY_FN(gl_read_init_files) -{ - return gl_configure_getline(gl, NULL, gl->app_file, gl->user_file); -} - -/*....................................................................... - * Save the contents of the history buffer to a given new file. - * - * Input: - * gl GetLine * The resource object of this library. - * filename const char * The name of the new file to write to. - * comment const char * Extra information such as timestamps will - * be recorded on a line started with this - * string, the idea being that the file can - * double as a command file. Specify "" if - * you don't care. - * max_lines int The maximum number of lines to save, or -1 - * to save all of the lines in the history - * list. - * Output: - * return int 0 - OK. - * 1 - Error. - */ -int gl_save_history(GetLine *gl, const char *filename, const char *comment, - int max_lines) -{ - FileExpansion *expansion; /* The expansion of the filename */ -/* - * Check the arguments. - */ - if(!gl || !filename || !comment) { - fprintf(stderr, "gl_save_history: NULL argument(s).\n"); - return 1; - }; -/* - * Expand the filename. - */ - expansion = ef_expand_file(gl->ef, filename, -1); - if(!expansion) { - fprintf(stderr, "Unable to expand %s (%s).\n", filename, - ef_last_error(gl->ef)); - return 1; - }; -/* - * Attempt to save to the specified file. - */ - return _glh_save_history(gl->glh, expansion->files[0], comment, max_lines); -} - -/*....................................................................... - * Restore the contents of the history buffer from a given new file. - * - * Input: - * gl GetLine * The resource object of this library. - * filename const char * The name of the new file to write to. - * comment const char * This must be the same string that was - * passed to gl_save_history() when the file - * was written. - * Output: - * return int 0 - OK. - * 1 - Error. - */ -int gl_load_history(GetLine *gl, const char *filename, const char *comment) -{ - FileExpansion *expansion; /* The expansion of the filename */ -/* - * Check the arguments. - */ - if(!gl || !filename || !comment) { - fprintf(stderr, "gl_load_history: NULL argument(s).\n"); - return 1; - }; -/* - * Expand the filename. - */ - expansion = ef_expand_file(gl->ef, filename, -1); - if(!expansion) { - fprintf(stderr, "Unable to expand %s (%s).\n", filename, - ef_last_error(gl->ef)); - return 1; - }; -/* - * Attempt to load from the specified file. - */ - if(_glh_load_history(gl->glh, expansion->files[0], comment, - gl->cutbuf, gl->linelen)) { - gl->cutbuf[0] = '\0'; - return 1; - }; - gl->cutbuf[0] = '\0'; - return 0; -} - -/*....................................................................... - * Where possible, register a function and associated data to be called - * whenever a specified event is seen on a file descriptor. - * - * Input: - * gl GetLine * The resource object of the command-line input - * module. - * fd int The file descriptor to watch. - * event GlFdEvent The type of activity to watch for. - * callback GlFdEventFn * The function to call when the specified - * event occurs. Setting this to 0 removes - * any existing callback. - * data void * A pointer to arbitrary data to pass to the - * callback function. - * Output: - * return int 0 - OK. - * 1 - Either gl==NULL, or this facility isn't - * available on the the host system - * (ie. select() isn't available). No - * error message is generated in the latter - * case. - */ -int gl_watch_fd(GetLine *gl, int fd, GlFdEvent event, - GlFdEventFn *callback, void *data) -#if !defined(HAVE_SELECT) -{return 1;} /* The facility isn't supported on this system */ -#else -{ - GlFdNode *prev; /* The node that precedes 'node' in gl->fd_nodes */ - GlFdNode *node; /* The file-descriptor node being checked */ -/* - * Check the arguments. - */ - if(!gl) { - fprintf(stderr, "gl_watch_fd: NULL gl argument.\n"); - return 1; - }; - if(fd < 0) { - fprintf(stderr, "gl_watch_fd: Error fd < 0.\n"); - return 1; - }; -/* - * Search the list of already registered fd activity nodes for the specified - * file descriptor. - */ - for(prev=NULL,node=gl->fd_nodes; node && node->fd != fd; - prev=node, node=node->next) - ; -/* - * Hasn't a node been allocated for this fd yet? - */ - if(!node) { -/* - * If there is no callback to record, just ignore the call. - */ - if(!callback) - return 0; -/* - * Allocate the new node. - */ - node = (GlFdNode *) _new_FreeListNode(gl->fd_node_mem); - if(!node) { - fprintf(stderr, "gl_watch_fd: Insufficient memory.\n"); - return 1; - }; -/* - * Prepend the node to the list. - */ - node->next = gl->fd_nodes; - gl->fd_nodes = node; -/* - * Initialize the node. - */ - node->fd = fd; - node->rd.fn = 0; - node->rd.data = NULL; - node->ur = node->wr = node->rd; - }; -/* - * Record the new callback. - */ - switch(event) { - case GLFD_READ: - node->rd.fn = callback; - node->rd.data = data; - if(callback) - FD_SET(fd, &gl->rfds); - else - FD_CLR(fd, &gl->rfds); - break; - case GLFD_WRITE: - node->wr.fn = callback; - node->wr.data = data; - if(callback) - FD_SET(fd, &gl->wfds); - else - FD_CLR(fd, &gl->wfds); - break; - case GLFD_URGENT: - node->ur.fn = callback; - node->ur.data = data; - if(callback) - FD_SET(fd, &gl->ufds); - else - FD_CLR(fd, &gl->ufds); - break; - }; -/* - * Keep a record of the largest file descriptor being watched. - */ - if(fd > gl->max_fd) - gl->max_fd = fd; -/* - * If we are deleting an existing callback, also delete the parent - * activity node if no callbacks are registered to the fd anymore. - */ - if(!callback) { - if(!node->rd.fn && !node->wr.fn && !node->ur.fn) { - if(prev) - prev->next = node->next; - else - gl->fd_nodes = node->next; - node = (GlFdNode *) _del_FreeListNode(gl->fd_node_mem, node); - }; - }; - return 0; -} - -/*....................................................................... - * When select() is available, this function is called by - * gl_read_character() to respond to file-descriptor events registered by - * the caller. - * - * Input: - * gl GetLine * The resource object of this module. - * Output: - * return int 0 - A character is waiting to be read from the - * terminal. - * 1 - An error occurred. - */ -static int gl_event_handler(GetLine *gl) -{ -/* - * If at any time no external callbacks remain, quit the loop return, - * so that we can simply wait in read(). This is designed as an - * optimization for when no callbacks have been registered on entry to - * this function, but since callbacks can delete themselves, it can - * also help later. - */ - while(gl->fd_nodes) { -/* - * Get the set of descriptors to be watched. - */ - fd_set rfds = gl->rfds; - fd_set wfds = gl->wfds; - fd_set ufds = gl->ufds; -/* - * Wait for activity on any of the file descriptors. - */ - int nready = select(gl->max_fd+1, &rfds, &wfds, &ufds, NULL); -/* - * If select() returns but none of the file descriptors are reported - * to have activity, then select() timed out. - */ - if(nready == 0) { - fprintf(stdout, "\r\nUnexpected select() timeout\r\n"); - return 1; -/* - * If nready < 0, this means an error occurred. - */ - } else if(nready < 0) { - if(errno != EINTR) { -#ifdef EAGAIN - if(!errno) /* This can happen with SysV O_NDELAY */ - errno = EAGAIN; -#endif - return 1; - }; -/* - * If the terminal input file descriptor has data available, return. - */ - } else if(FD_ISSET(gl->input_fd, &rfds)) { - return 0; -/* - * Check for activity on any of the file descriptors registered by the - * calling application, and call the associated callback functions. - */ - } else { - GlFdNode *node; /* The fd event node being checked */ -/* - * Search the list for the file descriptor that caused select() to return. - */ - for(node=gl->fd_nodes; node; node=node->next) { -/* - * Is there urgent out of band data waiting to be read on fd? - */ - if(node->ur.fn && FD_ISSET(node->fd, &ufds)) { - if(gl_call_fd_handler(gl, &node->ur, node->fd, GLFD_URGENT)) - return 1; - break; /* The callback may have changed the list of nodes */ -/* - * Is the fd readable? - */ - } else if(node->rd.fn && FD_ISSET(node->fd, &rfds)) { - if(gl_call_fd_handler(gl, &node->rd, node->fd, GLFD_READ)) - return 1; - break; /* The callback may have changed the list of nodes */ -/* - * Is the fd writable? - */ - } else if(node->wr.fn && FD_ISSET(node->fd, &rfds)) { - if(gl_call_fd_handler(gl, &node->wr, node->fd, GLFD_WRITE)) - return 1; - break; /* The callback may have changed the list of nodes */ - }; - }; - }; - }; - return 0; -} - -/*....................................................................... - * This is a private function of gl_event_handler(), used to call a - * file-descriptor callback. - * - * Input: - * gl GetLine * The resource object of gl_get_line(). - * gfh GlFdHandler * The I/O handler. - * fd int The file-descriptor being reported. - * event GlFdEvent The I/O event being reported. - * Output: - * return int 0 - OK. - * 1 - Error. - */ -static int gl_call_fd_handler(GetLine *gl, GlFdHandler *gfh, int fd, - GlFdEvent event) -{ - Termios attr; /* The terminal attributes */ - int redisplay = 0; /* True to have the input line redisplayed */ - int waserr = 0; /* True after any error */ -/* - * We don't want to do a longjmp in the middle of a callback that - * might be modifying global or heap data, so block all the signals - * that we are trapping. - */ - if(sigprocmask(SIG_BLOCK, &gl->new_signal_set, NULL) == -1) { - fprintf(stderr, "getline(): sigprocmask error: %s\n", strerror(errno)); - return 1; - }; -/* - * Re-enable conversion of newline characters to carriage-return/linefeed, - * so that the callback can write to the terminal without having to do - * anything special. - */ - if(tcgetattr(gl->input_fd, &attr)) { - fprintf(stderr, "\r\ngetline(): tcgetattr error: %s\r\n", strerror(errno)); - return 1; - }; - attr.c_oflag |= OPOST; - while(tcsetattr(gl->input_fd, TCSADRAIN, &attr)) { - if (errno != EINTR) { - fprintf(stderr, "\r\ngetline(): tcsetattr error: %s\r\n", - strerror(errno)); - return 1; - }; - }; -/* - * Invoke the application's callback function. - */ - switch(gfh->fn(gl, gfh->data, fd, event)) { - default: - case GLFD_ABORT: - waserr = 1; - break; - case GLFD_REFRESH: - redisplay = 1; - break; - case GLFD_CONTINUE: - redisplay = gl->prompt_changed; - break; - }; -/* - * Disable conversion of newline characters to carriage-return/linefeed. - */ - attr.c_oflag &= ~(OPOST); - while(tcsetattr(gl->input_fd, TCSADRAIN, &attr)) { - if(errno != EINTR) { - fprintf(stderr, "\ngetline(): tcsetattr error: %s\n", strerror(errno)); - return 1; - }; - }; -/* - * If requested, redisplay the input line. - */ - if(redisplay && gl_redisplay(gl, 1)) - return 1; -/* - * Unblock the signals that we were trapping before this function - * was called. - */ - if(sigprocmask(SIG_UNBLOCK, &gl->new_signal_set, NULL) == -1) { - fprintf(stderr, "getline(): sigprocmask error: %s\n", strerror(errno)); - return 1; - }; - return waserr; -} -#endif /* HAVE_SELECT */ - -/*....................................................................... - * Switch history groups. History groups represent separate history - * lists recorded within a single history buffer. Different groups - * are distinguished by integer identifiers chosen by the calling - * appplicaton. Initially new_GetLine() sets the group identifier to - * 0. Whenever a new line is appended to the history list, the current - * group identifier is recorded with it, and history lookups only - * consider lines marked with the current group identifier. - * - * Input: - * gl GetLine * The resource object of gl_get_line(). - * id unsigned The new history group identifier. - * Output: - * return int 0 - OK. - * 1 - Error. - */ -int gl_group_history(GetLine *gl, unsigned id) -{ -/* - * Check the arguments. - */ - if(!gl) { - fprintf(stderr, "gl_group_history: NULL argument(s).\n"); - return 1; - }; -/* - * If the group isn't being changed, do nothing. - */ - if(_glh_get_group(gl->glh) == id) - return 0; -/* - * Establish the new group. - */ - if(_glh_set_group(gl->glh, id)) - return 1; -/* - * Prevent history information from the previous group being - * inappropriately used by the next call to gl_get_line(). - */ - gl->preload_history = 0; - gl->last_search = -1; - return 0; -} - -/*....................................................................... - * Display the contents of the history list. - * - * Input: - * gl GetLine * The resource object of gl_get_line(). - * fp FILE * The stdio output stream to write to. - * fmt const char * A format string. This containing characters to be - * written verbatim, plus any of the following - * format directives: - * %D - The date, formatted like 2001-11-20 - * %T - The time of day, formatted like 23:59:59 - * %N - The sequential entry number of the - * line in the history buffer. - * %G - The number of the history group that - * the line belongs to. - * %% - A literal % character. - * %H - The history line itself. - * Note that a '\n' newline character is not - * appended by default. - * all_groups int If true, display history lines from all - * history groups. Otherwise only display - * those of the current history group. - * max_lines int If max_lines is < 0, all available lines - * are displayed. Otherwise only the most - * recent max_lines lines will be displayed. - * Output: - * return int 0 - OK. - * 1 - Error. - */ -int gl_show_history(GetLine *gl, FILE *fp, const char *fmt, int all_groups, - int max_lines) -{ -/* - * Check the arguments. - */ - if(!gl || !fp || !fmt) { - fprintf(stderr, "gl_show_history: NULL argument(s).\n"); - return 1; - }; - return _glh_show_history(gl->glh, fp, fmt, all_groups, max_lines); -} - -/*....................................................................... - * Update if necessary, and return the current size of the terminal. - * - * Input: - * gl GetLine * The resource object of gl_get_line(). - * def_ncolumn int If the number of columns in the terminal - * can't be determined, substitute this number. - * def_nline int If the number of lines in the terminal can't - * be determined, substitute this number. - * Output: - * return GlTerminalSize The current terminal size. - */ -GlTerminalSize gl_terminal_size(GetLine *gl, int def_ncolumn, int def_nline) -{ - GlTerminalSize size; /* The return value */ - const char *env; /* The value of an environment variable */ - int n; /* A number read from env[] */ -/* - * Set the number of lines and columns to non-sensical values so that - * we know later if they have been set. - */ - gl->nline = 0; - gl->ncolumn = 0; -/* - * Are we reading from a terminal? - */ - if(gl->is_term) { -/* - * Ask the terminal directly if possible. - */ -#ifdef USE_SIGWINCH - (void) gl_resize_terminal(gl, 0); -#endif -/* - * If gl_resize_terminal() couldn't be used, or it returned non-sensical - * values for the number of lines, see if the LINES environment variable - * exists and specifies a believable number. If this doesn't work, - * look up the default size in the terminal information database, - * where available. - */ - if(gl->nline < 1) { - if((env = getenv("LINES")) && (n=atoi(env)) > 0) - gl->nline = n; -#ifdef USE_TERMINFO - else - gl->nline = tigetnum((char *)"lines"); -#elif defined(USE_TERMCAP) - else - gl->nline = tgetnum("li"); -#endif - }; -/* - * If gl_resize_terminal() couldn't be used, or it returned non-sensical - * values for the number of columns, see if the COLUMNS environment variable - * exists and specifies a believable number. If this doesn't work, fall - * lookup the default size in the terminal information database, - * where available. - */ - if(gl->ncolumn < 1) { - if((env = getenv("COLUMNS")) && (n=atoi(env)) > 0) - gl->ncolumn = n; -#ifdef USE_TERMINFO - else - gl->ncolumn = tigetnum((char *)"cols"); -#elif defined(USE_TERMCAP) - else - gl->ncolumn = tgetnum("co"); -#endif - }; - }; -/* - * If we still haven't been able to acquire reasonable values, substitute - * the default values specified by the caller. - */ - if(gl->nline <= 0) - gl->nline = def_nline; - if(gl->ncolumn <= 0) - gl->ncolumn = def_ncolumn; -/* - * Copy the new size into the return value. - */ - size.nline = gl->nline; - size.ncolumn = gl->ncolumn; - return size; -} - -/*....................................................................... - * Resize or delete the history buffer. - * - * Input: - * gl GetLine * The resource object of gl_get_line(). - * bufsize size_t The number of bytes in the history buffer, or 0 - * to delete the buffer completely. - * Output: - * return int 0 - OK. - * 1 - Insufficient memory (the previous buffer - * will have been retained). No error message - * will be displayed. - */ -int gl_resize_history(GetLine *gl, size_t bufsize) -{ - return gl ? _glh_resize_history(gl->glh, bufsize) : 1; -} - -/*....................................................................... - * Set an upper limit to the number of lines that can be recorded in the - * history list, or remove a previously specified limit. - * - * Input: - * gl GetLine * The resource object of gl_get_line(). - * max_lines int The maximum number of lines to allow, or -1 to - * cancel a previous limit and allow as many lines - * as will fit in the current history buffer size. - */ -void gl_limit_history(GetLine *gl, int max_lines) -{ - if(gl) - _glh_limit_history(gl->glh, max_lines); -} - -/*....................................................................... - * Discard either all historical lines, or just those associated with the - * current history group. - * - * Input: - * gl GetLine * The resource object of gl_get_line(). - * all_groups int If true, clear all of the history. If false, - * clear only the stored lines associated with the - * currently selected history group. - */ -void gl_clear_history(GetLine *gl, int all_groups) -{ - if(gl) - _glh_clear_history(gl->glh, all_groups); -} - -/*....................................................................... - * Temporarily enable or disable the gl_get_line() history mechanism. - * - * Input: - * gl GetLine * The resource object of gl_get_line(). - * enable int If true, turn on the history mechanism. If - * false, disable it. - */ -void gl_toggle_history(GetLine *gl, int enable) -{ - if(gl) - _glh_toggle_history(gl->glh, enable); -} - -/*....................................................................... - * Lookup a history line by its sequential number of entry in the - * history buffer. - * - * Input: - * gl GetLine * The resource object of gl_get_line(). - * id unsigned long The identification number of the line to - * be returned, where 0 denotes the first line - * that was entered in the history list, and - * each subsequently added line has a number - * one greater than the previous one. For - * the range of lines currently in the list, - * see the gl_range_of_history() function. - * Input/Output: - * line GlHistoryLine * A pointer to the variable in which to - * return the details of the line. - * Output: - * return int 0 - The line is no longer in the history - * list, and *line has not been changed. - * 1 - The requested line can be found in - * *line. Note that line->line is part - * of the history buffer, so a - * private copy should be made if you - * wish to use it after subsequent calls - * to any functions that take *gl as an - * argument. - */ -int gl_lookup_history(GetLine *gl, unsigned long id, GlHistoryLine *line) -{ - return gl ? _glh_lookup_history(gl->glh, (GlhLineID) id, &line->line, - &line->group, &line->timestamp) : 0; -} - -/*....................................................................... - * Query the state of the history list. Note that any of the input/output - * pointers can be specified as NULL. - * - * Input: - * gl GetLine * The resource object of gl_get_line(). - * Input/Output: - * state GlHistoryState * A pointer to the variable in which to record - * the return values. - */ -void gl_state_of_history(GetLine *gl, GlHistoryState *state) -{ - if(gl && state) - _glh_state_of_history(gl->glh, &state->enabled, &state->group, - &state->max_lines); -} - -/*....................................................................... - * Query the number and range of lines in the history buffer. - * - * Input: - * gl GetLine * The resource object of gl_get_line(). - * range GlHistoryRange * A pointer to the variable in which to record - * the return values. If range->nline=0, the - * range of lines will be given as 0-0. - */ -void gl_range_of_history(GetLine *gl, GlHistoryRange *range) -{ - if(gl && range) - _glh_range_of_history(gl->glh, &range->oldest, &range->newest, - &range->nlines); -} - -/*....................................................................... - * Return the size of the history buffer and the amount of the - * buffer that is currently in use. - * - * Input: - * gl GetLine * The gl_get_line() resource object. - * Input/Output: - * GlHistorySize size * A pointer to the variable in which to return - * the results. - */ -void gl_size_of_history(GetLine *gl, GlHistorySize *size) -{ - if(gl && size) - _glh_size_of_history(gl->glh, &size->size, &size->used); -} - -/*....................................................................... - * This is the action function that lists the contents of the history - * list. - */ -static KT_KEY_FN(gl_list_history) -{ -/* - * Start a new line. - */ - if(fprintf(gl->output_fp, "\r\n") < 0) - return 1; -/* - * List history lines that belong to the current group. - */ - _glh_show_history(gl->glh, gl->output_fp, "%N %T %H\r\n", 0, - count<=1 ? -1 : count); -/* - * Redisplay the line being edited. - */ - gl->term_curpos = 0; - return gl_redisplay(gl,1); -} - -/*....................................................................... - * Specify whether text that users type should be displayed or hidden. - * In the latter case, only the prompt is displayed, and the final - * input line is not archived in the history list. - * - * Input: - * gl GetLine * The gl_get_line() resource object. - * enable int 0 - Disable echoing. - * 1 - Enable echoing. - * -1 - Just query the mode without changing it. - * Output: - * return int The echoing disposition that was in effect - * before this function was called: - * 0 - Echoing was disabled. - * 1 - Echoing was enabled. - */ -int gl_echo_mode(GetLine *gl, int enable) -{ - if(gl) { - int was_echoing = gl->echo; - if(enable >= 0) - gl->echo = enable; - return was_echoing; - }; - return 1; -} - -/*....................................................................... - * Display the prompt. - * - * Input: - * gl GetLine * The resource object of gl_get_line(). - * Output: - * return int 0 - OK. - * 1 - Error. - */ -static int gl_display_prompt(GetLine *gl) -{ - const char *pptr; /* A pointer into gl->prompt[] */ - unsigned old_attr=0; /* The current text display attributes */ - unsigned new_attr=0; /* The requested text display attributes */ -/* - * Temporarily switch to echoing output characters. - */ - int kept_echo = gl->echo; - gl->echo = 1; -/* - * In case the screen got messed up, send a carriage return to - * put the cursor at the beginning of the current terminal line. - */ - if(gl_output_control_sequence(gl, 1, gl->bol)) - return 1; -/* - * Write the prompt, using the currently selected prompt style. - */ - switch(gl->prompt_style) { - case GL_LITERAL_PROMPT: - if(gl_output_string(gl, gl->prompt, '\0')) - return 1; - break; - case GL_FORMAT_PROMPT: - for(pptr=gl->prompt; *pptr; pptr++) { -/* - * Does the latest character appear to be the start of a directive? - */ - if(*pptr == '%') { -/* - * Check for and act on attribute changing directives. - */ - switch(pptr[1]) { -/* - * Add or remove a text attribute from the new set of attributes. - */ - case 'B': case 'U': case 'S': case 'P': case 'F': case 'V': - case 'b': case 'u': case 's': case 'p': case 'f': case 'v': - switch(*++pptr) { - case 'B': /* Switch to a bold font */ - new_attr |= GL_TXT_BOLD; - break; - case 'b': /* Switch to a non-bold font */ - new_attr &= ~GL_TXT_BOLD; - break; - case 'U': /* Start underlining */ - new_attr |= GL_TXT_UNDERLINE; - break; - case 'u': /* Stop underlining */ - new_attr &= ~GL_TXT_UNDERLINE; - break; - case 'S': /* Start highlighting */ - new_attr |= GL_TXT_STANDOUT; - break; - case 's': /* Stop highlighting */ - new_attr &= ~GL_TXT_STANDOUT; - break; - case 'P': /* Switch to a pale font */ - new_attr |= GL_TXT_DIM; - break; - case 'p': /* Switch to a non-pale font */ - new_attr &= ~GL_TXT_DIM; - break; - case 'F': /* Switch to a flashing font */ - new_attr |= GL_TXT_BLINK; - break; - case 'f': /* Switch to a steady font */ - new_attr &= ~GL_TXT_BLINK; - break; - case 'V': /* Switch to reverse video */ - new_attr |= GL_TXT_REVERSE; - break; - case 'v': /* Switch out of reverse video */ - new_attr &= ~GL_TXT_REVERSE; - break; - }; - continue; -/* - * A literal % is represented by %%. Skip the leading %. - */ - case '%': - pptr++; - break; - }; - }; -/* - * Many terminals, when asked to turn off a single text attribute, turn - * them all off, so the portable way to turn one off individually is to - * explicitly turn them all off, then specify those that we want from - * scratch. - */ - if(old_attr & ~new_attr) { - if(gl_output_control_sequence(gl, 1, gl->text_attr_off)) - return 1; - old_attr = 0; - }; -/* - * Install new text attributes? - */ - if(new_attr != old_attr) { - if(new_attr & GL_TXT_BOLD && !(old_attr & GL_TXT_BOLD) && - gl_output_control_sequence(gl, 1, gl->bold)) - return 1; - if(new_attr & GL_TXT_UNDERLINE && !(old_attr & GL_TXT_UNDERLINE) && - gl_output_control_sequence(gl, 1, gl->underline)) - return 1; - if(new_attr & GL_TXT_STANDOUT && !(old_attr & GL_TXT_STANDOUT) && - gl_output_control_sequence(gl, 1, gl->standout)) - return 1; - if(new_attr & GL_TXT_DIM && !(old_attr & GL_TXT_DIM) && - gl_output_control_sequence(gl, 1, gl->dim)) - return 1; - if(new_attr & GL_TXT_REVERSE && !(old_attr & GL_TXT_REVERSE) && - gl_output_control_sequence(gl, 1, gl->reverse)) - return 1; - if(new_attr & GL_TXT_BLINK && !(old_attr & GL_TXT_BLINK) && - gl_output_control_sequence(gl, 1, gl->blink)) - return 1; - old_attr = new_attr; - }; -/* - * Display the latest character. - */ - if(gl_output_char(gl, *pptr, pptr[1])) - return 1; - }; -/* - * Turn off all text attributes now that we have finished drawing - * the prompt. - */ - if(gl_output_control_sequence(gl, 1, gl->text_attr_off)) - return 1; - break; - }; -/* - * Restore the original echo mode. - */ - gl->echo = kept_echo; -/* - * The prompt has now been displayed at least once. - */ - gl->prompt_changed = 0; - return 0; -} - -/*....................................................................... - * This function can be called from gl_get_line() callbacks to have - * the prompt changed when they return. It has no effect if gl_get_line() - * is not currently being invoked. - * - * Input: - * gl GetLine * The resource object of gl_get_line(). - * prompt const char * The new prompt. - */ -void gl_replace_prompt(GetLine *gl, const char *prompt) -{ - if(gl) { - gl->prompt = prompt ? prompt : ""; - gl->prompt_len = gl_displayed_prompt_width(gl); - gl->prompt_changed = 1; - }; -} - -/*....................................................................... - * Work out the length of the current prompt on the terminal, according - * to the current prompt formatting style. - * - * Input: - * gl GetLine * The resource object of this library. - * Output: - * return int The number of displayed characters. - */ -static int gl_displayed_prompt_width(GetLine *gl) -{ - int slen=0; /* The displayed number of characters */ - const char *pptr; /* A pointer into prompt[] */ -/* - * The length differs according to the prompt display style. - */ - switch(gl->prompt_style) { - case GL_LITERAL_PROMPT: - return gl_displayed_string_width(gl, gl->prompt, -1, 0); - break; - case GL_FORMAT_PROMPT: -/* - * Add up the length of the displayed string, while filtering out - * attribute directives. - */ - for(pptr=gl->prompt; *pptr; pptr++) { -/* - * Does the latest character appear to be the start of a directive? - */ - if(*pptr == '%') { -/* - * Check for and skip attribute changing directives. - */ - switch(pptr[1]) { - case 'B': case 'b': case 'U': case 'u': case 'S': case 's': - pptr++; - continue; -/* - * A literal % is represented by %%. Skip the leading %. - */ - case '%': - pptr++; - break; - }; - }; - slen += gl_displayed_char_width(gl, *pptr, slen); - }; - break; - }; - return slen; -} - -/*....................................................................... - * Specify whether to heed text attribute directives within prompt - * strings. - * - * Input: - * gl GetLine * The resource object of gl_get_line(). - * style GlPromptStyle The style of prompt (see the definition of - * GlPromptStyle in libtecla.h for details). - */ -void gl_prompt_style(GetLine *gl, GlPromptStyle style) -{ - if(gl) { - if(style != gl->prompt_style) { - gl->prompt_style = style; - gl->prompt_len = gl_displayed_prompt_width(gl); - gl->prompt_changed = 1; - }; - }; -} - -/*....................................................................... - * Tell gl_get_line() how to respond to a given signal. This can be used - * both to override the default responses to signals that gl_get_line() - * normally catches and to add new signals to the list that are to be - * caught. - * - * Input: - * gl GetLine * The resource object of gl_get_line(). - * signo int The number of the signal to be caught. - * flags unsigned A bitwise union of GlSignalFlags enumerators. - * after GlAfterSignal What to do after the application's signal - * handler has been called. - * errno_value int The value to set errno to. - * Output: - * return int 0 - OK. - * 1 - Error. - */ -int gl_trap_signal(GetLine *gl, int signo, unsigned flags, - GlAfterSignal after, int errno_value) -{ - GlSignalNode *sig; -/* - * Check the arguments. - */ - if(!gl) { - fprintf(stderr, "gl_trap_signal: NULL argument(s).\n"); - return 1; - }; -/* - * See if the signal has already been registered. - */ - for(sig=gl->sigs; sig && sig->signo != signo; sig = sig->next) - ; -/* - * If the signal hasn't already been registered, allocate a node for - * it. - */ - if(!sig) { - sig = (GlSignalNode *) _new_FreeListNode(gl->sig_mem); - if(!sig) - return 1; -/* - * Add the new node to the head of the list. - */ - sig->next = gl->sigs; - gl->sigs = sig; -/* - * Record the signal number. - */ - sig->signo = signo; -/* - * Create a signal set that includes just this signal. - */ - sigemptyset(&sig->proc_mask); - if(sigaddset(&sig->proc_mask, signo) == -1) { - fprintf(stderr, "gl_trap_signal: sigaddset error: %s\n", - strerror(errno)); - sig = (GlSignalNode *) _del_FreeListNode(gl->sig_mem, sig); - return 1; - }; - }; -/* - * Record the new signal attributes. - */ - sig->flags = flags; - sig->after = after; - sig->errno_value = errno_value; - return 0; -} - -/*....................................................................... - * Remove a signal from the list of signals that gl_get_line() traps. - * - * Input: - * gl GetLine * The resource object of gl_get_line(). - * signo int The number of the signal to be ignored. - * Output: - * return int 0 - OK. - * 1 - Error. - */ -int gl_ignore_signal(GetLine *gl, int signo) -{ - GlSignalNode *sig; /* The gl->sigs list node of the specified signal */ - GlSignalNode *prev; /* The node that precedes sig in the list */ -/* - * Check the arguments. - */ - if(!gl) { - fprintf(stderr, "gl_ignore_signal: NULL argument(s).\n"); - return 1; - }; -/* - * Find the node of the gl->sigs list which records the disposition - * of the specified signal. - */ - for(prev=NULL,sig=gl->sigs; sig && sig->signo != signo; - prev=sig,sig=sig->next) - ; - if(sig) { -/* - * Remove the node from the list. - */ - if(prev) - prev->next = sig->next; - else - gl->sigs = sig->next; -/* - * Return the node to the freelist. - */ - sig = (GlSignalNode *) _del_FreeListNode(gl->sig_mem, sig); - }; - return 0; -} - -/*....................................................................... - * This function is called when an input line has been completed. It - * appends the specified newline character, terminates the line, - * records the line in the history buffer if appropriate, and positions - * the terminal cursor at the start of the next line. - * - * Input: - * gl GetLine * The resource object of gl_get_line(). - * newline_char int The newline character to add to the end - * of the line. - * archive int True to have the line archived in the - * history buffer. - * Output: - * return int 0 - OK. - * 1 - Error. - */ -static int gl_line_ended(GetLine *gl, int newline_char, int archive) -{ -/* - * If the newline character is printable, display it. - */ - if(isprint((int)(unsigned char) newline_char)) { - if(gl_end_of_line(gl, 1) || gl_add_char_to_line(gl, newline_char)) - return 1; - } else { -/* - * Otherwise just append it to the input line buffer. - */ - gl->line[gl->ntotal++] = newline_char; - gl->line[gl->ntotal] = '\0'; - }; -/* - * Add the line to the history buffer if it was entered with a - * newline or carriage return character. - */ - if(archive) - (void) _glh_add_history(gl->glh, gl->line, 0); -/* - * Unless depending on the system-provided line editing, start a new - * line after the end of the line that has just been entered. - */ - if(gl->editor != GL_NO_EDITOR) { - if(gl_end_of_line(gl, 1) || - gl_output_raw_string(gl, "\r\n")) - return 1; - }; - return 0; -} - -/*....................................................................... - * Return the last signal that was caught by the most recent call to - * gl_get_line(), or -1 if no signals were caught. This is useful if - * gl_get_line() returns errno=EINTR and you need to find out what signal - * caused it to abort. - * - * Input: - * gl GetLine * The resource object of gl_get_line(). - * Output: - * return int The last signal caught by the most recent - * call to gl_get_line(), or -1 if no signals - * were caught. - */ -int gl_last_signal(const GetLine *gl) -{ - return gl ? gl->last_signal : -1; -} diff --git a/libtecla-1.4.1/getline.h b/libtecla-1.4.1/getline.h deleted file mode 100644 index 24b0875..0000000 --- a/libtecla-1.4.1/getline.h +++ /dev/null @@ -1,88 +0,0 @@ -#ifndef getline_h -#define getline_h - -/* - * Copyright (c) 2000, 2001 by Martin C. Shepherd. - * - * All rights reserved. - * - * Permission is hereby granted, free of charge, to any person obtaining a - * copy of this software and associated documentation files (the - * "Software"), to deal in the Software without restriction, including - * without limitation the rights to use, copy, modify, merge, publish, - * distribute, and/or sell copies of the Software, and to permit persons - * to whom the Software is furnished to do so, provided that the above - * copyright notice(s) and this permission notice appear in all copies of - * the Software and that both the above copyright notice(s) and this - * permission notice appear in supporting documentation. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS - * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT - * OF THIRD PARTY RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR - * HOLDERS INCLUDED IN THIS NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL - * INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING - * FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, - * NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION - * WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - * - * Except as contained in this notice, the name of a copyright holder - * shall not be used in advertising or otherwise to promote the sale, use - * or other dealings in this Software without prior written authorization - * of the copyright holder. - */ - -/* - * Set the name of the getline configuration file. - */ -#define TECLA_CONFIG_FILE "~/.teclarc" - -/* - * The following macro returns non-zero if a character is - * a control character. - */ -#define IS_CTRL_CHAR(c) ((unsigned char)(c) < ' ' || (unsigned char)(c)=='\177') - -/* - * The following macro returns non-zero if a character is - * a meta character. - */ -#define IS_META_CHAR(c) (((unsigned char)(c) & 0x80) && !isprint((int)(unsigned char)(c))) - -/* - * Return the character that would be produced by pressing the - * specified key plus the control key. - */ -#define MAKE_CTRL(c) ((c)=='?' ? '\177' : ((unsigned char)toupper(c) & ~0x40)) - -/* - * Return the character that would be produced by pressing the - * specified key plus the meta key. - */ -#define MAKE_META(c) ((unsigned char)(c) | 0x80) - -/* - * Given a binary control character, return the character that - * had to be pressed at the same time as the control key. - */ -#define CTRL_TO_CHAR(c) (toupper((unsigned char)(c) | 0x40)) - -/* - * Given a meta character, return the character that was pressed - * at the same time as the meta key. - */ -#define META_TO_CHAR(c) ((unsigned char)(c) & ~0x80) - -/* - * Specify the string of characters other than the alphanumeric characters, - * that are to be considered parts of words. - */ -#define GL_WORD_CHARS "_*\?\\[]" - -/* - * Define the escape character, both as a string and as a character. - */ -#define GL_ESC_STR "\033" -#define GL_ESC_CHAR '\033' - -#endif diff --git a/libtecla-1.4.1/hash.c b/libtecla-1.4.1/hash.c deleted file mode 100644 index 89f8245..0000000 --- a/libtecla-1.4.1/hash.c +++ /dev/null @@ -1,748 +0,0 @@ -/* - * Copyright (c) 2000, 2001 by Martin C. Shepherd. - * - * All rights reserved. - * - * Permission is hereby granted, free of charge, to any person obtaining a - * copy of this software and associated documentation files (the - * "Software"), to deal in the Software without restriction, including - * without limitation the rights to use, copy, modify, merge, publish, - * distribute, and/or sell copies of the Software, and to permit persons - * to whom the Software is furnished to do so, provided that the above - * copyright notice(s) and this permission notice appear in all copies of - * the Software and that both the above copyright notice(s) and this - * permission notice appear in supporting documentation. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS - * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT - * OF THIRD PARTY RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR - * HOLDERS INCLUDED IN THIS NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL - * INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING - * FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, - * NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION - * WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - * - * Except as contained in this notice, the name of a copyright holder - * shall not be used in advertising or otherwise to promote the sale, use - * or other dealings in this Software without prior written authorization - * of the copyright holder. - */ - -#include -#include -#include -#include - -#include "hash.h" -#include "strngmem.h" -#include "freelist.h" - -/* - * The following container object contains free-lists to be used - * for allocation of HashTable containers and nodes. - */ -struct HashMemory { - FreeList *hash_memory; /* HashTable free-list */ - FreeList *node_memory; /* HashNode free-list */ - StringMem *string_memory; /* Memory used to allocate hash strings */ -}; - -/* - * Define a hash symbol-table entry. - * See symbol.h for the definition of the Symbol container type. - */ -typedef struct HashNode HashNode; -struct HashNode { - Symbol symbol; /* The symbol stored in the hash-entry */ - HashNode *next; /* The next hash-table entry in a bucket list */ -}; - -/* - * Each hash-table bucket contains a linked list of entries that - * hash to the same bucket. - */ -typedef struct { - HashNode *head; /* The head of the bucket hash-node list */ - int count; /* The number of entries in the list */ -} HashBucket; - -/* - * Set the max length of the error-reporting string. There is no point - * in this being longer than the width of a typical terminal window. - * In composing error messages, I have assumed that this number is - * at least 80, so you don't decrease it below this number. - */ -#define ERRLEN 200 - -/* - * A hash-table consists of 'size' hash buckets. - * Note that the HashTable typedef for this struct is contained in hash.h. - */ -struct HashTable { - char errmsg[ERRLEN+1];/* Error-report buffer */ - HashMemory *mem; /* HashTable free-list */ - int internal_mem; /* True if 'mem' was allocated by _new_HashTable() */ - int case_sensitive; /* True if case is significant in lookup keys */ - int size; /* The number of hash buckets */ - HashBucket *bucket; /* An array of 'size' hash buckets */ - int (*keycmp)(const char *, const char *); /* Key comparison function */ - void *app_data; /* Application-provided data */ - HASH_DEL_FN(*del_fn); /* Application-provided 'app_data' destructor */ -}; - -static HashNode *_del_HashNode(HashTable *hash, HashNode *node); -static HashNode *_new_HashNode(HashTable *hash, const char *name, int code, - void (*fn)(void), void *data, SYM_DEL_FN(*del_fn)); -static HashNode *_find_HashNode(HashTable *hash, HashBucket *bucket, - const char *name, HashNode **prev); -static HashBucket *_find_HashBucket(HashTable *hash, const char *name); -static int _ht_lower_strcmp(const char *node_key, const char *look_key); -static int _ht_strcmp(const char *node_key, const char *look_key); - -/*....................................................................... - * Allocate a free-list for use in allocating hash tables and their nodes. - * - * Input: - * list_count int The number of HashTable containers per free-list - * block. - * node_count int The number of HashTable nodes per free-list block. - * Output: - * return HashMemory * The new free-list for use in allocating hash tables - * and their nodes. - */ -HashMemory *_new_HashMemory(int hash_count, int node_count) -{ - HashMemory *mem; -/* - * Allocate the free-list container. - */ - mem = (HashMemory *) malloc(sizeof(HashMemory)); - if(!mem) { - fprintf(stderr, "_new_HashMemory: Insufficient memory.\n"); - return NULL; - }; -/* - * Initialize the container at least up to the point at which it can - * safely be passed to _del_HashMemory(). - */ - mem->hash_memory = NULL; - mem->node_memory = NULL; - mem->string_memory = NULL; -/* - * Allocate the two free-lists. - */ - mem->hash_memory = _new_FreeList("_new_HashMemory", sizeof(HashTable), - hash_count); - if(!mem->hash_memory) - return _del_HashMemory(mem, 1); - mem->node_memory = _new_FreeList("_new_HashMemory", sizeof(HashNode), - node_count); - if(!mem->node_memory) - return _del_HashMemory(mem, 1); - mem->string_memory = _new_StringMem("_new_HashMemory", 64); - if(!mem->string_memory) - return _del_HashMemory(mem, 1); -/* - * Return the free-list container. - */ - return mem; -} - -/*....................................................................... - * Delete a HashTable free-list. An error will be displayed if the list is - * still in use and the deletion will be aborted. - * - * Input: - * mem HashMemory * The free-list container to be deleted. - * force int If force==0 then _del_HashMemory() will complain - * and refuse to delete the free-list if any - * of nodes have not been returned to the free-list. - * If force!=0 then _del_HashMemory() will not check - * whether any nodes are still in use and will - * always delete the list. - * Output: - * return HashMemory * Always NULL (even if the memory could not be - * deleted). - */ -HashMemory *_del_HashMemory(HashMemory *mem, int force) -{ - const char *caller = "_del_HashMemory"; - if(mem) { - if(!force && (_busy_FreeListNodes(mem->hash_memory) > 0 || - _busy_FreeListNodes(mem->node_memory) > 0)) { - fprintf(stderr, "%s: Free-list in use.\n", caller); - return NULL; - }; - mem->hash_memory = _del_FreeList(caller, mem->hash_memory, force); - mem->node_memory = _del_FreeList(caller, mem->node_memory, force); - mem->string_memory = _del_StringMem(caller, mem->string_memory, force); - free(mem); - }; - return NULL; -} - -/*....................................................................... - * Create a new hash table. - * - * Input: - * mem HashMemory * An optional free-list for use in allocating - * HashTable containers and nodes. See explanation - * in hash.h. If you are going to allocate more - * than one hash table, then it will be more - * efficient to allocate a single free-list for - * all of them than to force each hash table - * to allocate its own private free-list. - * size int The size of the hash table. Best performance - * will be acheived if this is a prime number. - * hcase HashCase Specify how symbol case is considered when - * looking up symbols, from: - * IGNORE_CASE - Upper and lower case versions - * of a letter are treated as - * being identical. - * HONOUR_CASE - Upper and lower case versions - * of a letter are treated as - * being distinct. - * characters in a lookup name is significant. - * app_data void * Optional application data to be registered - * to the table. This is presented to user - * provided SYM_DEL_FN() symbol destructors along - * with the symbol data. - * del_fn() HASH_DEL_FN(*) If you want app_data to be free'd when the - * hash-table is destroyed, register a suitable - * destructor function here. - * Output: - * return HashTable * The new hash table, or NULL on error. - */ -HashTable *_new_HashTable(HashMemory *mem, int size, HashCase hcase, - void *app_data, HASH_DEL_FN(*del_fn)) -{ - HashTable *hash; /* The table to be returned */ - int allocate_mem = !mem; /* True if mem should be internally allocated */ - int i; -/* - * Check arguments. - */ - if(size <= 0) { - fprintf(stderr, "_new_HashTable: Illegal table size (%d).\n", size); - return NULL; - }; -/* - * Allocate an internal free-list? - */ - if(allocate_mem) { - mem = _new_HashMemory(1, 100); - if(!mem) - return NULL; - }; -/* - * Allocate the container. - */ - hash = (HashTable *) _new_FreeListNode(mem->hash_memory); - if(!hash) { - fprintf(stderr, "_new_HashTable: Insufficient memory.\n"); - if(allocate_mem) - mem = _del_HashMemory(mem, 1); - return NULL; - }; -/* - * Before attempting any operation that might fail, initialize - * the container at least up to the point at which it can safely - * be passed to _del_HashTable(). - */ - hash->errmsg[0] = '\0'; - hash->mem = mem; - hash->internal_mem = allocate_mem; - hash->case_sensitive = hcase==HONOUR_CASE; - hash->size = size; - hash->bucket = NULL; - hash->keycmp = hash->case_sensitive ? _ht_strcmp : _ht_lower_strcmp; - hash->app_data = app_data; - hash->del_fn = del_fn; -/* - * Allocate the array of 'size' hash buckets. - */ - hash->bucket = (HashBucket *) malloc(sizeof(HashBucket) * size); - if(!hash->bucket) { - fprintf(stderr, "_new_HashTable: Insufficient memory for %d buckets.\n", - size); - return _del_HashTable(hash); - }; -/* - * Initialize the bucket array. - */ - for(i=0; ibucket + i; - b->head = NULL; - b->count = 0; - }; -/* - * The table is ready for use - albeit currently empty. - */ - return hash; -} - -/*....................................................................... - * Delete a hash-table. - * - * Input: - * hash HashTable * The hash table to be deleted. - * Output: - * return HashTable * The deleted hash table (always NULL). - */ -HashTable *_del_HashTable(HashTable *hash) -{ - if(hash) { -/* - * Clear and delete the bucket array. - */ - if(hash->bucket) { - _clear_HashTable(hash); - free(hash->bucket); - hash->bucket = NULL; - }; -/* - * Delete application data. - */ - if(hash->del_fn) - hash->del_fn(hash->app_data); -/* - * If the hash table was allocated from an internal free-list, delete - * it and the hash table by deleting the free-list. Otherwise just - * return the hash-table to the external free-list. - */ - if(hash->internal_mem) - _del_HashMemory(hash->mem, 1); - else - hash = (HashTable *) _del_FreeListNode(hash->mem->hash_memory, hash); - }; - return NULL; -} - -/*....................................................................... - * Create and install a new entry in a hash table. If an entry with the - * same name already exists, replace its contents with the new data. - * - * Input: - * hash HashTable * The hash table to insert the symbol into. - * name const char * The name to tag the entry with. - * code int An application-specific code to be stored in - * the entry. - * fn void (*)(void) An application-specific function to be stored - * in the entry. - * data void * An application-specific pointer to data to be - * associated with the entry, or NULL if not - * relevant. - * del_fn SYM_DEL_FN(*) An optional destructor function. When the - * symbol is deleted this function will be called - * with the 'code' and 'data' arguments given - * above. Any application data that was registered - * to the table via the app_data argument of - * _new_HashTable() will also be passed. - * Output: - * return HashNode * The new entry, or NULL if there was insufficient - * memory or the arguments were invalid. - */ -Symbol *_new_HashSymbol(HashTable *hash, const char *name, int code, - void (*fn)(void), void *data, SYM_DEL_FN(*del_fn)) -{ - HashBucket *bucket; /* The hash-bucket associated with the name */ - HashNode *node; /* The new node */ -/* - * Check arguments. - */ - if(!hash || !name) - return NULL; -/* - * Get the hash bucket of the specified name. - */ - bucket = _find_HashBucket(hash, name); -/* - * See if a node with the same name already exists. - */ - node = _find_HashNode(hash, bucket, name, NULL); -/* - * If found, delete its contents by calling the user-supplied - * destructor function, if provided. - */ - if(node) { - if(node->symbol.data && node->symbol.del_fn) { - node->symbol.data = node->symbol.del_fn(hash->app_data, node->symbol.code, - node->symbol.data); - }; -/* - * Allocate a new node if necessary. - */ - } else { - node = _new_HashNode(hash, name, code, fn, data, del_fn); - if(!node) - return NULL; - }; -/* - * Install the node at the head of the hash-bucket list. - */ - node->next = bucket->head; - bucket->head = node; - bucket->count++; - return &node->symbol; -} - -/*....................................................................... - * Remove and delete a given hash-table entry. - * - * Input: - * hash HashTable * The hash table to find the symbol in. - * name const char * The name of the entry. - * Output: - * return HashNode * The deleted hash node (always NULL). - */ -Symbol *_del_HashSymbol(HashTable *hash, const char *name) -{ - if(hash && name) { - HashBucket *bucket = _find_HashBucket(hash, name); - HashNode *prev; /* The node preceding the located node */ - HashNode *node = _find_HashNode(hash, bucket, name, &prev); -/* - * Node found? - */ - if(node) { -/* - * Remove the node from the bucket list. - */ - if(prev) { - prev->next = node->next; - } else { - bucket->head = node->next; - }; -/* - * Record the loss of a node. - */ - bucket->count--; -/* - * Delete the node. - */ - (void) _del_HashNode(hash, node); - }; - }; - return NULL; -} - -/*....................................................................... - * Look up a symbol in the hash table. - * - * Input: - * hash HashTable * The table to look up the string in. - * name const char * The name of the symbol to look up. - * Output: - * return Symbol * The located hash-table symbol, or NULL if not - * found. - */ -Symbol *_find_HashSymbol(HashTable *hash, const char *name) -{ - HashBucket *bucket; /* The hash-table bucket associated with name[] */ - HashNode *node; /* The hash-table node of the requested symbol */ -/* - * Check arguments. - */ - if(!hash) - return NULL; -/* - * Nothing to lookup? - */ - if(!name) - return NULL; -/* - * Hash the name to a hash-table bucket. - */ - bucket = _find_HashBucket(hash, name); -/* - * Find the bucket entry that exactly matches the name. - */ - node = _find_HashNode(hash, bucket, name, NULL); - if(!node) - return NULL; - return &node->symbol; -} - -/*....................................................................... - * Private function used to allocate a hash-table node. - * The caller is responsible for checking that the specified symbol - * is unique and for installing the returned entry in the table. - * - * Input: - * hash HashTable * The table to allocate the node for. - * name const char * The name of the new entry. - * code int A user-supplied context code. - * fn void (*)(void) A user-supplied function pointer. - * data void * A user-supplied data pointer. - * del_fn SYM_DEL_FN(*) An optional 'data' destructor function. - * Output: - * return HashNode * The new node, or NULL on error. - */ -static HashNode *_new_HashNode(HashTable *hash, const char *name, int code, - void (*fn)(void), void *data, SYM_DEL_FN(*del_fn)) -{ - HashNode *node; /* The new node */ -/* - * Allocate the new node from the free list. - */ - node = (HashNode *) _new_FreeListNode(hash->mem->node_memory); - if(!node) - return NULL; -/* - * Before attempting any operation that might fail, initialize the - * contents of 'node' at least up to the point at which it can be - * safely passed to _del_HashNode(). - */ - node->symbol.name = NULL; - node->symbol.code = code; - node->symbol.fn = fn; - node->symbol.data = data; - node->symbol.del_fn = del_fn; - node->next = NULL; -/* - * Allocate a copy of 'name'. - */ - node->symbol.name = _new_StringMemString(hash->mem->string_memory, - strlen(name) + 1); - if(!node->symbol.name) - return _del_HashNode(hash, node); -/* - * If character-case is insignificant in the current table, convert the - * name to lower case while copying it. - */ - if(hash->case_sensitive) { - strcpy(node->symbol.name, name); - } else { - const char *src = name; - char *dst = node->symbol.name; - for( ; *src; src++,dst++) - *dst = tolower(*src); - *dst = '\0'; - }; - return node; -} - -/*....................................................................... - * Private function used to delete a hash-table node. - * The node must have been removed from its list before calling this - * function. - * - * Input: - * hash HashTable * The table for which the node was originally - * allocated. - * node HashNode * The node to be deleted. - * Output: - * return HashNode * The deleted node (always NULL). - */ -static HashNode *_del_HashNode(HashTable *hash, HashNode *node) -{ - if(node) { - node->symbol.name = _del_StringMemString(hash->mem->string_memory, - node->symbol.name); -/* - * Call the user-supplied data-destructor if provided. - */ - if(node->symbol.data && node->symbol.del_fn) - node->symbol.data = node->symbol.del_fn(hash->app_data, - node->symbol.code, - node->symbol.data); -/* - * Return the node to the free-list. - */ - node->next = NULL; - node = (HashNode *) _del_FreeListNode(hash->mem->node_memory, node); - }; - return NULL; -} - -/*....................................................................... - * Private function to locate the hash bucket associated with a given - * name. - * - * This uses a hash-function described in the dragon-book - * ("Compilers - Principles, Techniques and Tools", by Aho, Sethi and - * Ullman; pub. Adison Wesley) page 435. - * - * Input: - * hash HashTable * The table to look up the string in. - * name const char * The name of the symbol to look up. - * Output: - * return HashBucket * The located hash-bucket. - */ -static HashBucket *_find_HashBucket(HashTable *hash, const char *name) -{ - unsigned const char *kp; - unsigned long h = 0L; - if(hash->case_sensitive) { - for(kp=(unsigned const char *) name; *kp; kp++) - h = 65599UL * h + *kp; /* 65599 is a prime close to 2^16 */ - } else { - for(kp=(unsigned const char *) name; *kp; kp++) - h = 65599UL * h + tolower((int)*kp); /* 65599 is a prime close to 2^16 */ - }; - return hash->bucket + (h % hash->size); -} - -/*....................................................................... - * Search for a given name in the entries of a given bucket. - * - * Input: - * hash HashTable * The hash-table being searched. - * bucket HashBucket * The bucket to search (use _find_HashBucket()). - * name const char * The name to search for. - * Output: - * prev HashNode ** If prev!=NULL then the pointer to the node - * preceding the located node in the list will - * be recorded in *prev. This will be NULL either - * if the name is not found or the located node is - * at the head of the list of entries. - * return HashNode * The located hash-table node, or NULL if not - * found. - */ -static HashNode *_find_HashNode(HashTable *hash, HashBucket *bucket, - const char *name, HashNode **prev) -{ - HashNode *last; /* The previously searched node */ - HashNode *node; /* The node that is being searched */ -/* - * Search the list for a node containing the specified name. - */ - for(last=NULL, node=bucket->head; - node && hash->keycmp(node->symbol.name, name)!=0; - last = node, node=node->next) - ; - if(prev) - *prev = node ? last : NULL; - return node; -} - -/*....................................................................... - * When hash->case_sensitive is zero this function is called - * in place of strcmp(). In such cases the hash-table names are stored - * as lower-case versions of the original strings so this function - * performs the comparison against lower-case copies of the characters - * of the string being compared. - * - * Input: - * node_key const char * The lower-case hash-node key being compared - * against. - * look_key const char * The lookup key. - * Output: - * return int <0 if node_key < look_key. - * 0 if node_key == look_key. - * >0 if node_key > look_key. - */ -static int _ht_lower_strcmp(const char *node_key, const char *look_key) -{ - int cn; /* The latest character from node_key[] */ - int cl; /* The latest character from look_key[] */ - do { - cn = *node_key++; - cl = *look_key++; - } while(cn && cn==tolower(cl)); - return cn - tolower(cl); -} - -/*....................................................................... - * This is a wrapper around strcmp for comparing hash-keys in a case - * sensitive manner. The reason for having this wrapper, instead of using - * strcmp() directly, is to make some C++ compilers happy. The problem - * is that when the library is compiled with a C++ compiler, the - * declaration of the comparison function is a C++ declaration, whereas - * strcmp() is a pure C function and thus although it appears to have the - * same declaration, the compiler disagrees. - * - * Input: - * node_key char * The lower-case hash-node key being compared against. - * look_key char * The lookup key. - * Output: - * return int <0 if node_key < look_key. - * 0 if node_key == look_key. - * >0 if node_key > look_key. - */ -static int _ht_strcmp(const char *node_key, const char *look_key) -{ - return strcmp(node_key, look_key); -} - -/*....................................................................... - * Empty a hash-table by deleting all of its entries. - * - * Input: - * hash HashTable * The hash table to clear. - * Output: - * return int 0 - OK. - * 1 - Invalid arguments. - */ -int _clear_HashTable(HashTable *hash) -{ - int i; -/* - * Check the arguments. - */ - if(!hash) - return 1; -/* - * Clear the contents of the bucket array. - */ - for(i=0; isize; i++) { - HashBucket *bucket = hash->bucket + i; -/* - * Delete the list of active hash nodes from the bucket. - */ - HashNode *node = bucket->head; - while(node) { - HashNode *next = node->next; - (void) _del_HashNode(hash, node); - node = next; - }; -/* - * Mark the bucket as empty. - */ - bucket->head = NULL; - bucket->count = 0; - }; - return 0; -} - -/*....................................................................... - * Execute a given function on each entry of a hash table, returning - * before completion if the the specified function returns non-zero. - * - * Input: - * hash HashTable * The table to traverse. - * scan_fn HASH_SCAN_FN(*) The function to call. - * context void * Optional caller-specific context data - * to be passed to scan_fn(). - * Output: - * return int 0 - OK. - * 1 - Either the arguments were invalid, or - * scan_fn() returned non-zero at some - * point. - */ -int _scan_HashTable(HashTable *hash, HASH_SCAN_FN(*scan_fn), void *context) -{ - int i; -/* - * Check the arguments. - */ - if(!hash || !scan_fn) - return 1; -/* - * Iterate through the buckets of the table. - */ - for(i=0; isize; i++) { - HashBucket *bucket = hash->bucket + i; - HashNode *node; -/* - * Iterate through the list of symbols that fall into bucket i, - * passing each one to the caller-specified function. - */ - for(node=bucket->head; node; node=node->next) { - if(scan_fn(&node->symbol, context)) - return 1; - }; - }; - return 0; -} diff --git a/libtecla-1.4.1/hash.h b/libtecla-1.4.1/hash.h deleted file mode 100644 index 13c0a2b..0000000 --- a/libtecla-1.4.1/hash.h +++ /dev/null @@ -1,157 +0,0 @@ -#ifndef hash_h -#define hash_h - -/* - * Copyright (c) 2000, 2001 by Martin C. Shepherd. - * - * All rights reserved. - * - * Permission is hereby granted, free of charge, to any person obtaining a - * copy of this software and associated documentation files (the - * "Software"), to deal in the Software without restriction, including - * without limitation the rights to use, copy, modify, merge, publish, - * distribute, and/or sell copies of the Software, and to permit persons - * to whom the Software is furnished to do so, provided that the above - * copyright notice(s) and this permission notice appear in all copies of - * the Software and that both the above copyright notice(s) and this - * permission notice appear in supporting documentation. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS - * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT - * OF THIRD PARTY RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR - * HOLDERS INCLUDED IN THIS NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL - * INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING - * FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, - * NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION - * WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - * - * Except as contained in this notice, the name of a copyright holder - * shall not be used in advertising or otherwise to promote the sale, use - * or other dealings in this Software without prior written authorization - * of the copyright holder. - */ - -/* - * The following macro can be used to prototype or define a - * function that deletes the data of a symbol-table entry. - * - * Input: - * app_data void * The _new_HashTable() app_data argument. - * code int The Symbol::code argument. - * sym_data void * The Symbol::data argument to be deleted. - * Output: - * return void * The deleted data (always return NULL). - */ -#define SYM_DEL_FN(fn) void *(fn)(void *app_data, int code, void *sym_data) - -/* - * The following macro can be used to prototype or define a - * function that deletes the application-data of a hash-table. - * - * Input: - * data void * The _new_HashTable() 'app_data' argument to be - * deleted. - * Output: - * return void * The deleted data (always return NULL). - */ -#define HASH_DEL_FN(fn) void *(fn)(void *app_data) - -/* - * The following is a container for recording the context - * of a symbol in a manner that is independant of the particular - * symbol-table implementation. Each hash-table entry contains - * the following user supplied parameters: - * - * 1. An optional integral parameter 'code'. This is useful for - * enumerating a symbol or for describing what type of data - * or function is stored in the symbol. - * - * 2. An optional generic function pointer. This is useful for - * associating functions with names. The user is responsible - * for casting between the generic function type and the - * actual function type. The code field could be used to - * enumerate what type of function to cast to. - * - * 3. An optional generic pointer to a static or heap-allocated - * object. It is up to the user to cast this back to the - * appropriate object type. Again, the code field could be used - * to describe what type of object is stored there. - * If the object is dynamically allocated and should be discarded - * when the symbol is deleted from the symbol table, send a - * destructor function to have it deleted automatically. - */ -typedef struct { - char *name; /* The name of the symbol */ - int code; /* Application supplied integral code */ - void (*fn)(void); /* Application supplied generic function */ - void *data; /* Application supplied context data */ - SYM_DEL_FN(*del_fn); /* Data destructor function */ -} Symbol; - -/* - * HashNode's and HashTable's are small objects. Separately allocating - * many such objects would normally cause memory fragmentation. To - * counter this, HashMemory objects are used. These contain - * dedicated free-lists formed from large dynamically allocated arrays - * of objects. One HashMemory object can be shared between multiple hash - * tables (within a single thread). - */ -typedef struct HashMemory HashMemory; - - /* Create a free-list for allocation of hash tables and their nodes */ - -HashMemory *_new_HashMemory(int hash_count, int node_count); - - /* Delete a redundant free-list if not being used */ - -HashMemory *_del_HashMemory(HashMemory *mem, int force); - -/* - * Declare an alias for the private HashTable structure defined in - * hash.c. - */ -typedef struct HashTable HashTable; - -/* - * Enumerate case-sensitivity options. - */ -typedef enum { - IGNORE_CASE, /* Ignore case when looking up symbols */ - HONOUR_CASE /* Honor case when looking up symbols */ -} HashCase; - - /* Create a new hash-table */ - -HashTable *_new_HashTable(HashMemory *mem, int size, HashCase hcase, - void *app_data, HASH_DEL_FN(*del_fn)); - - /* Delete a reference to a hash-table */ - -HashTable *_del_HashTable(HashTable *hash); - - /* Add an entry to a hash table */ - -Symbol *_new_HashSymbol(HashTable *hash, const char *key, int code, - void (*fn)(void), void *data, SYM_DEL_FN(*del_fn)); - - /* Remove and delete all the entries in a given hash table */ - -int _clear_HashTable(HashTable *hash); - - /* Remove and delete a given hash-table entry */ - -Symbol *_del_HashSymbol(HashTable *hash, const char *key); - - /* Lookup a given hash-table entry */ - -Symbol *_find_HashSymbol(HashTable *hash, const char *key); - - /* Execute a given function on each entry of a hash table, returning */ - /* before completion if the specified function returns non-zero. */ - -#define HASH_SCAN_FN(fn) int (fn)(Symbol *sym, void *context) - -int _scan_HashTable(HashTable *hash, HASH_SCAN_FN(*scan_fn), void *context); - -#endif diff --git a/libtecla-1.4.1/history.c b/libtecla-1.4.1/history.c deleted file mode 100644 index 5a8d94f..0000000 --- a/libtecla-1.4.1/history.c +++ /dev/null @@ -1,2003 +0,0 @@ -/* - * Copyright (c) 2000, 2001 by Martin C. Shepherd. - * - * All rights reserved. - * - * Permission is hereby granted, free of charge, to any person obtaining a - * copy of this software and associated documentation files (the - * "Software"), to deal in the Software without restriction, including - * without limitation the rights to use, copy, modify, merge, publish, - * distribute, and/or sell copies of the Software, and to permit persons - * to whom the Software is furnished to do so, provided that the above - * copyright notice(s) and this permission notice appear in all copies of - * the Software and that both the above copyright notice(s) and this - * permission notice appear in supporting documentation. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS - * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT - * OF THIRD PARTY RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR - * HOLDERS INCLUDED IN THIS NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL - * INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING - * FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, - * NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION - * WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - * - * Except as contained in this notice, the name of a copyright holder - * shall not be used in advertising or otherwise to promote the sale, use - * or other dealings in this Software without prior written authorization - * of the copyright holder. - */ - -#include -#include -#include -#include -#include -#include - -#include "history.h" -#include "freelist.h" - -/* - * GlLineNode's record the location and length of historical lines in - * a buffer array. - */ -typedef struct GlLineNode GlLineNode; -struct GlLineNode { - long id; /* The unique identifier of this history line */ - time_t timestamp; /* The time at which the line was archived */ - unsigned group; /* The identifier of the history group to which the */ - /* the line belongs. */ - GlLineNode *next; /* The next youngest line in the list */ - GlLineNode *prev; /* The next oldest line in the list */ - int start; /* The start index of the line in the buffer */ - int nchar; /* The total length of the line, including the '\0' */ -}; - -/* - * The number of GlLineNode elements per freelist block. - */ -#define LINE_NODE_BLK 100 - -/* - * Lines are organised in the buffer from oldest to newest. The - * positions of the lines are recorded in a doubly linked list - * of GlLineNode objects. - */ -typedef struct { - FreeList *node_mem; /* A freelist of GlLineNode objects */ - GlLineNode *head; /* The head of the list of lines */ - GlLineNode *tail; /* The tail of the list of lines */ -} GlLineList; - -/* - * All elements of the history mechanism are recorded in an object of - * the following type. - */ -struct GlHistory { - char *buffer; /* A circular buffer used to record historical input */ - /* lines. */ - size_t buflen; /* The length of the buffer array */ - GlLineList list; /* A list of the start of lines in buffer[] */ - GlLineNode *recall; /* The last line recalled, or NULL if no recall */ - /* session is currently active. */ - GlLineNode *id_node;/* The node at which the last ID search terminated */ - const char *prefix; /* A pointer to the line containing the prefix that */ - /* is being searched for. */ - int prefix_len; /* The length of the prefix */ - unsigned long seq; /* The next ID to assign to a line node */ - unsigned group; /* The identifier of the current history group */ - int nline; /* The number of lines currently in the history list */ - int max_lines; /* Either -1 or a ceiling on the number of lines */ - int enable; /* If false, ignore history additions and lookups */ -}; - -static char *_glh_restore_line(GlHistory *glh, char *line, size_t dim); -static int _glh_cant_load_history(GlHistory *glh, const char *filename, - int lineno, const char *message, FILE *fp); -static int _glh_write_timestamp(FILE *fp, time_t timestamp); -static int _glh_decode_timestamp(char *string, char **endp, time_t *t); -static void _glh_discard_node(GlHistory *glh, GlLineNode *node); -static GlLineNode *_glh_find_id(GlHistory *glh, GlhLineID id); - -/*....................................................................... - * Create a line history maintenance object. - * - * Input: - * buflen size_t The number of bytes to allocate to the circular - * buffer that is used to record all of the - * most recent lines of user input that will fit. - * If buflen==0, no buffer will be allocated. - * Output: - * return GlHistory * The new object, or NULL on error. - */ -GlHistory *_new_GlHistory(size_t buflen) -{ - GlHistory *glh; /* The object to be returned */ -/* - * Allocate the container. - */ - glh = (GlHistory *) malloc(sizeof(GlHistory)); - if(!glh) { - fprintf(stderr, "_new_GlHistory: Insufficient memory.\n"); - return NULL; - }; -/* - * Before attempting any operation that might fail, initialize the - * container at least up to the point at which it can safely be passed - * to _del_GlHistory(). - */ - glh->buffer = NULL; - glh->buflen = buflen; - glh->list.node_mem = NULL; - glh->list.head = NULL; - glh->list.tail = NULL; - glh->recall = NULL; - glh->id_node = NULL; - glh->prefix = NULL; - glh->prefix_len = 0; - glh->seq = 0; - glh->group = 0; - glh->nline = 0; - glh->max_lines = -1; - glh->enable = 1; -/* - * Allocate the buffer, if required. - */ - if(buflen > 0) { - glh->buffer = (char *) malloc(sizeof(char) * buflen); - if(!glh->buffer) { - fprintf(stderr, "_new_GlHistory: Insufficient memory.\n"); - return _del_GlHistory(glh); - }; - }; -/* - * Allocate the GlLineNode freelist. - */ - glh->list.node_mem = _new_FreeList("_new_GlHistory", sizeof(GlLineNode), - LINE_NODE_BLK); - if(!glh->list.node_mem) - return _del_GlHistory(glh); - return glh; -} - -/*....................................................................... - * Delete a GlHistory object. - * - * Input: - * glh GlHistory * The object to be deleted. - * Output: - * return GlHistory * The deleted object (always NULL). - */ -GlHistory *_del_GlHistory(GlHistory *glh) -{ - if(glh) { -/* - * Delete the buffer. - */ - if(glh->buffer) { - free(glh->buffer); - glh->buffer = NULL; - }; -/* - * Delete the freelist of GlLineNode's. - */ - glh->list.node_mem = _del_FreeList("_del_GlHistory", glh->list.node_mem, 1); -/* - * The contents of the list were deleted by deleting the freelist. - */ - glh->list.head = NULL; - glh->list.tail = NULL; -/* - * Delete the container. - */ - free(glh); - }; - return NULL; -} - -/*....................................................................... - * Add a new line to the end of the history buffer, wrapping round to the - * start of the buffer if needed. - * - * Input: - * glh GlHistory * The input-line history maintenance object. - * line char * The line to be archived. - * force int Unless this flag is non-zero, empty lines and - * lines which match the previous line in the history - * buffer, aren't archived. This flag requests that - * the line be archived regardless. - * Output: - * return int 0 - OK. - * 1 - Error. - */ -int _glh_add_history(GlHistory *glh, const char *line, int force) -{ - GlLineList *list; /* The line location list */ - int nchar; /* The number of characters needed to record the line */ - GlLineNode *node; /* The new line location list node */ - int empty; /* True if the string is empty */ - const char *nlptr;/* A pointer to a newline character in line[] */ - int i; -/* - * Check the arguments. - */ - if(!glh || !line) - return 1; -/* - * Is history enabled? - */ - if(!glh->enable || !glh->buffer || glh->max_lines == 0) - return 0; -/* - * Get the line location list. - */ - list = &glh->list; -/* - * Cancel any ongoing search. - */ - if(_glh_cancel_search(glh)) - return 1; -/* - * See how much buffer space will be needed to record the line? - * - * If the string contains a terminating newline character, arrange to - * have the archived line NUL terminated at this point. - */ - nlptr = strchr(line, '\n'); - if(nlptr) - nchar = (nlptr - line) + 1; - else - nchar = strlen(line) + 1; -/* - * If the line is too big to fit in the buffer, truncate it. - */ - if(nchar > glh->buflen) - nchar = glh->buflen; -/* - * Is the line empty? - */ - empty = 1; - for(i=0; itail && strlen(glh->buffer + list->tail->start) == nchar-1 && - strncmp(line, glh->buffer + list->tail->start, nchar-1)==0) - return 0; -/* - * Allocate the list node that will record the line location. - */ - node = (GlLineNode *) _new_FreeListNode(list->node_mem); - if(!node) - return 1; -/* - * Is the buffer empty? - */ - if(!list->head) { -/* - * Place the line at the beginning of the buffer. - */ - strncpy(glh->buffer, line, nchar); - glh->buffer[nchar-1] = '\0'; -/* - * Record the location of the line. - */ - node->start = 0; -/* - * The buffer has one or more lines in it. - */ - } else { -/* - * Place the start of the new line just after the most recently - * added line. - */ - int start = list->tail->start + list->tail->nchar; -/* - * If there is insufficient room between the end of the most - * recently added line and the end of the buffer, we place the - * line at the beginning of the buffer. To make as much space - * as possible for this line, we first delete any old lines - * at the end of the buffer, then shift the remaining contents - * of the buffer to the end of the buffer. - */ - if(start + nchar >= glh->buflen) { - GlLineNode *last; /* The last line in the buffer */ - GlLineNode *ln; /* A member of the list of line locations */ - int shift; /* The shift needed to move the contents of the */ - /* buffer to its end. */ -/* - * Delete any old lines between the most recent line and the end of the - * buffer. - */ - while(list->head && list->head->start > list->tail->start) - _glh_discard_node(glh, list->head); -/* - * Find the line that is nearest the end of the buffer. - */ - last = NULL; - for(ln=list->head; ln; ln=ln->next) { - if(!last || ln->start > last->start) - last = ln; - }; -/* - * How big a shift is needed to move the existing contents of the - * buffer to the end of the buffer? - */ - shift = last ? (glh->buflen - (last->start + last->nchar)) : 0; -/* - * Is any shift needed? - */ - if(shift > 0) { -/* - * Move the buffer contents to the end of the buffer. - */ - memmove(glh->buffer + shift, glh->buffer, glh->buflen - shift); -/* - * Update the listed locations to reflect the shift. - */ - for(ln=list->head; ln; ln=ln->next) - ln->start += shift; - }; -/* - * The new line should now be located at the start of the buffer. - */ - start = 0; - }; -/* - * Make space for the new line at the beginning of the buffer by - * deleting the oldest lines. This just involves removing them - * from the list of used locations. Also enforce the current - * maximum number of lines. - */ - while(list->head && - ((list->head->start >= start && list->head->start - start < nchar) || - (glh->max_lines >= 0 && glh->nline>=glh->max_lines))) { - _glh_discard_node(glh, list->head); - }; -/* - * Copy the new line into the buffer. - */ - memcpy(glh->buffer + start, line, nchar); - glh->buffer[start + nchar - 1] = '\0'; -/* - * Record its location. - */ - node->start = start; - }; -/* - * Append the line location node to the end of the list. - */ - node->id = glh->seq++; - node->timestamp = time(NULL); - node->group = glh->group; - node->nchar = nchar; - node->next = NULL; - node->prev = list->tail; - if(list->tail) - list->tail->next = node; - else - list->head = node; - list->tail = node; - glh->nline++; - return 0; -} - -/*....................................................................... - * Recall the next oldest line that has the search prefix last recorded - * by _glh_search_prefix(). - * - * Input: - * glh GlHistory * The input-line history maintenance object. - * line char * The input line buffer. On input this should contain - * the current input line, and on output, if anything - * was found, its contents will have been replaced - * with the matching line. - * dim size_t The allocated dimensions of the line buffer. - * Output: - * return char * A pointer to line[0], or NULL if not found. - */ -char *_glh_find_backwards(GlHistory *glh, char *line, size_t dim) -{ - GlLineNode *node; /* The line location node being checked */ - int first; /* True if this is the start of a new search */ -/* - * Check the arguments. - */ - if(!glh || !line) { - fprintf(stderr, "_glh_find_backwards: NULL argument(s).\n"); - return NULL; - }; -/* - * Is history enabled? - */ - if(!glh->enable || !glh->buffer || glh->max_lines == 0) - return NULL; -/* - * Check the line dimensions. - */ - if(dim < strlen(line) + 1) { - fprintf(stderr, - "_glh_find_backwards: 'dim' inconsistent with strlen(line) contents.\n"); - return NULL; - }; -/* - * Is this the start of a new search? - */ - first = glh->recall==NULL; -/* - * If this is the first search backwards, save the current line - * for potential recall later, and mark it as the last line - * recalled. - */ - if(first) { - if(_glh_add_history(glh, line, 1)) - return NULL; - glh->recall = glh->list.tail; - }; -/* - * If there is no search prefix, the prefix last set by glh_search_prefix() - * doesn't exist in the history buffer. - */ - if(!glh->prefix) - return NULL; -/* - * From where should we start the search? - */ - if(glh->recall) - node = glh->recall->prev; - else - node = glh->list.tail; -/* - * Search backwards through the list for the first match with the - * prefix string. - */ - for( ; node && - (node->group != glh->group || - strncmp(glh->buffer + node->start, glh->prefix, glh->prefix_len) != 0); - node = node->prev) - ; -/* - * Was a matching line found? - */ - if(node) { -/* - * Recall the found node as the starting point for subsequent - * searches. - */ - glh->recall = node; -/* - * Copy the matching line into the provided line buffer. - */ - strncpy(line, glh->buffer + node->start, dim); - line[dim-1] = '\0'; - return line; - }; -/* - * No match was found. - */ - return NULL; -} - -/*....................................................................... - * Recall the next newest line that has the search prefix last recorded - * by _glh_search_prefix(). - * - * Input: - * glh GlHistory * The input-line history maintenance object. - * line char * The input line buffer. On input this should contain - * the current input line, and on output, if anything - * was found, its contents will have been replaced - * with the matching line. - * dim size_t The allocated dimensions of the line buffer. - * Output: - * return char * The line requested, or NULL if no matching line - * was found. - */ -char *_glh_find_forwards(GlHistory *glh, char *line, size_t dim) -{ - GlLineNode *node; /* The line location node being checked */ -/* - * Check the arguments. - */ - if(!glh || !line) { - fprintf(stderr, "_glh_find_forwards: NULL argument(s).\n"); - return NULL; - }; -/* - * Is history enabled? - */ - if(!glh->enable || !glh->buffer || glh->max_lines == 0) - return NULL; -/* - * Check the line dimensions. - */ - if(dim < strlen(line) + 1) { - fprintf(stderr, - "_glh_find_forwards: 'dim' inconsistent with strlen(line) contents.\n"); - return NULL; - }; -/* - * From where should we start the search? - */ - if(glh->recall) - node = glh->recall->next; - else - return NULL; -/* - * If there is no search prefix, the prefix last set by glh_search_prefix() - * doesn't exist in the history buffer. - */ - if(!glh->prefix) - return NULL; -/* - * Search forwards through the list for the first match with the - * prefix string. - */ - for( ; node && - (node->group != glh->group || - strncmp(glh->buffer + node->start, glh->prefix, glh->prefix_len) != 0); - node = node->next) - ; -/* - * Was a matching line found? - */ - if(node) { -/* - * Did we hit the line that was originally being edited when the - * current history traversal started? - */ - if(node == glh->list.tail) - return _glh_restore_line(glh, line, dim); -/* - * Copy the matching line into the provided line buffer. - */ - strncpy(line, glh->buffer + node->start, dim); - line[dim-1] = '\0'; -/* - * Record the starting point of the next search. - */ - glh->recall = node; -/* - * Return the matching line to the user. - */ - return line; - }; -/* - * No match was found. - */ - return NULL; -} - -/*....................................................................... - * If a search is in progress, cancel it. - * - * This involves discarding the line that was temporarily saved by - * _glh_find_backwards() when the search was originally started, - * and reseting the search iteration pointer to NULL. - * - * Input: - * glh GlHistory * The input-line history maintenance object. - * Output: - * return int 0 - OK. - * 1 - Error. - */ -int _glh_cancel_search(GlHistory *glh) -{ -/* - * Check the arguments. - */ - if(!glh) { - fprintf(stderr, "_glh_cancel_search: NULL argument(s).\n"); - return 1; - }; -/* - * If there wasn't a search in progress, do nothing. - */ - if(!glh->recall) - return 0; -/* - * Delete the node of the preserved line. - */ - _glh_discard_node(glh, glh->list.tail); -/* - * Reset the search pointers. - */ - glh->recall = NULL; - glh->prefix = ""; - glh->prefix_len = 0; - return 0; -} - -/*....................................................................... - * Set the prefix of subsequent history searches. - * - * Input: - * glh GlHistory * The input-line history maintenance object. - * line char * The command line who's prefix is to be used. - * prefix_len int The length of the prefix. - * Output: - * return int 0 - OK. - * 1 - Error. - */ -int _glh_search_prefix(GlHistory *glh, const char *line, int prefix_len) -{ - GlLineNode *node; /* The line location node being checked */ -/* - * Check the arguments. - */ - if(!glh) { - fprintf(stderr, "_glh_search_prefix: NULL argument(s).\n"); - return 1; - }; -/* - * Is history enabled? - */ - if(!glh->enable || !glh->buffer || glh->max_lines == 0) - return 0; -/* - * Record a zero length search prefix? - */ - if(prefix_len <= 0) { - glh->prefix_len = 0; - glh->prefix = ""; - return 0; - }; -/* - * Record the length of the new search prefix. - */ - glh->prefix_len = prefix_len; -/* - * If any history line starts with the specified prefix, record a - * pointer to it for comparison in subsequent searches. If the prefix - * doesn't match any of the lines, then simply record NULL to indicate - * that there is no point in searching. Note that _glh_add_history() - * clears this pointer by calling _glh_cancel_search(), so there is - * no danger of it being used after the buffer has been modified. - */ - for(node = glh->list.tail ; node && - (node->group != glh->group || - strncmp(glh->buffer + node->start, line, prefix_len) != 0); - node = node->prev) - ; -/* - * If a matching line was found record it for use as the search - * prefix. - */ - glh->prefix = node ? glh->buffer + node->start : NULL; - return 0; -} - -/*....................................................................... - * Recall the oldest recorded line. - * - * Input: - * glh GlHistory * The input-line history maintenance object. - * line char * The input line buffer. On input this should contain - * the current input line, and on output, its contents - * will have been replaced with the oldest line. - * dim size_t The allocated dimensions of the line buffer. - * Output: - * return char * A pointer to line[0], or NULL if not found. - */ -char *_glh_oldest_line(GlHistory *glh, char *line, size_t dim) -{ - GlLineNode *node; /* The line location node being checked */ - int first; /* True if this is the start of a new search */ -/* - * Check the arguments. - */ - if(!glh || !line) { - fprintf(stderr, "_glh_oldest_line: NULL argument(s).\n"); - return NULL; - }; -/* - * Is history enabled? - */ - if(!glh->enable || !glh->buffer || glh->max_lines == 0) - return NULL; -/* - * Check the line dimensions. - */ - if(dim < strlen(line) + 1) { - fprintf(stderr, - "_glh_oldest_line: 'dim' inconsistent with strlen(line) contents.\n"); - return NULL; - }; -/* - * Is this the start of a new search? - */ - first = glh->recall==NULL; -/* - * If this is the first search backwards, save the current line - * for potential recall later, and mark it as the last line - * recalled. - */ - if(first) { - if(_glh_add_history(glh, line, 1)) - return NULL; - glh->recall = glh->list.tail; - }; -/* - * Locate the oldest line that belongs to the current group. - */ - for(node=glh->list.head; node && node->group != glh->group; - node = node->next) - ; -/* - * No line found? - */ - if(!node) - return NULL; -/* - * Record the above node as the starting point for subsequent - * searches. - */ - glh->recall = node; -/* - * Copy the recalled line into the provided line buffer. - */ - strncpy(line, glh->buffer + node->start, dim); - line[dim-1] = '\0'; - return line; -} - -/*....................................................................... - * Recall the line that was being entered when the search started. - * - * Input: - * glh GlHistory * The input-line history maintenance object. - * line char * The input line buffer. On input this should contain - * the current input line, and on output, its contents - * will have been replaced with the line that was - * being entered when the search was started. - * dim size_t The allocated dimensions of the line buffer. - * Output: - * return char * A pointer to line[0], or NULL if not found. - */ -char *_glh_current_line(GlHistory *glh, char *line, size_t dim) -{ -/* - * Check the arguments. - */ - if(!glh || !line) { - fprintf(stderr, "_glh_current_line: NULL argument(s).\n"); - return NULL; - }; -/* - * Is history enabled? - */ - if(!glh->enable || !glh->buffer || glh->max_lines == 0) - return NULL; -/* - * Check the line dimensions. - */ - if(dim < strlen(line) + 1) { - fprintf(stderr, - "_glh_current_line: 'dim' inconsistent with strlen(line) contents.\n"); - return NULL; - }; -/* - * Restore the original line. - */ - return _glh_restore_line(glh, line, dim); -} - -/*....................................................................... - * Remove the line that was originally being edited when the history - * traversal was started, from its saved position in the history list, - * and place it in the provided line buffer. - * - * Input: - * glh GlHistory * The input-line history maintenance object. - * line char * The input line buffer. On input this should contain - * the current input line, and on output, its contents - * will have been replaced with the saved line. - * dim size_t The allocated dimensions of the line buffer. - * Output: - * return char * A pointer to line[0], or NULL if not found. - */ -static char *_glh_restore_line(GlHistory *glh, char *line, size_t dim) -{ - GlLineNode *tail; /* The tail node to be discarded */ -/* - * If there wasn't a search in progress, do nothing. - */ - if(!glh->recall) - return NULL; -/* - * Get the list node that is to be removed. - */ - tail = glh->list.tail; -/* - * If a pointer to the saved line is being used to record the - * current search prefix, reestablish the search prefix, to - * have it recorded by another history line if possible. - */ - if(glh->prefix == glh->buffer + tail->start) - (void) _glh_search_prefix(glh, glh->buffer + tail->start, glh->prefix_len); -/* - * Copy the recalled line into the input-line buffer. - */ - strncpy(line, glh->buffer + tail->start, dim); - line[dim-1] = '\0'; -/* - * Discard the line-location node. - */ - _glh_discard_node(glh, tail); -/* - * Mark the search as ended. - */ - glh->recall = NULL; - return line; -} - -/*....................................................................... - * Query the id of a history line offset by a given number of lines from - * the one that is currently being recalled. If a recall session isn't - * in progress, or the offset points outside the history list, 0 is - * returned. - * - * Input: - * glh GlHistory * The input-line history maintenance object. - * offset int The line offset (0 for the current line, < 0 - * for an older line, > 0 for a newer line. - * Output: - * return GlhLineID The identifier of the line that is currently - * being recalled, or 0 if no recall session is - * currently in progress. - */ -GlhLineID _glh_line_id(GlHistory *glh, int offset) -{ - GlLineNode *node; /* The line location node being checked */ -/* - * Is history enabled? - */ - if(!glh->enable || !glh->buffer || glh->max_lines == 0) - return 0; -/* - * Search forward 'offset' lines to find the required line. - */ - if(offset >= 0) { - for(node=glh->recall; node && offset != 0; node=node->next) { - if(node->group == glh->group) - offset--; - }; - } else { - for(node=glh->recall; node && offset != 0; node=node->prev) { - if(node->group == glh->group) - offset++; - }; - }; - return node ? node->id : 0; -} - -/*....................................................................... - * Recall a line by its history buffer ID. If the line is no longer - * in the buffer, or the id is zero, NULL is returned. - * - * Input: - * glh GlHistory * The input-line history maintenance object. - * id GlhLineID The ID of the line to be returned. - * line char * The input line buffer. On input this should contain - * the current input line, and on output, its contents - * will have been replaced with the saved line. - * dim size_t The allocated dimensions of the line buffer. - * Output: - * return char * A pointer to line[0], or NULL if not found. - */ -char *_glh_recall_line(GlHistory *glh, GlhLineID id, char *line, size_t dim) -{ - GlLineNode *node; /* The line location node being checked */ -/* - * Is history enabled? - */ - if(!glh->enable || !glh->buffer || glh->max_lines == 0) - return NULL; -/* - * If we are starting a new recall session, save the current line - * for potential recall later. - */ - if(!glh->recall && _glh_add_history(glh, line, 1)) - return NULL; -/* - * Search for the specified line. - */ - node = _glh_find_id(glh, id); -/* - * Not found? - */ - if(!node || node->group != glh->group) - return NULL; -/* - * Record the node of the matching line as the starting point - * for subsequent searches. - */ - glh->recall = node; -/* - * Copy the recalled line into the provided line buffer. - */ - strncpy(line, glh->buffer + node->start, dim); - line[dim-1] = '\0'; - return line; -} - -/*....................................................................... - * Save the current history in a specified file. - * - * Input: - * glh GlHistory * The input-line history maintenance object. - * filename const char * The name of the new file to record the - * history in. - * comment const char * Extra information such as timestamps will - * be recorded on a line started with this - * string, the idea being that the file can - * double as a command file. Specify "" if - * you don't care. - * max_lines int The maximum number of lines to save, or -1 - * to save all of the lines in the history - * list. - * Output: - * return int 0 - OK. - * 1 - Error. - */ -int _glh_save_history(GlHistory *glh, const char *filename, const char *comment, - int max_lines) -{ - FILE *fp; /* The output file */ - GlLineNode *node; /* The line being saved */ - GlLineNode *head; /* The head of the list of lines to be saved */ -/* - * Check the arguments. - */ - if(!glh || !filename || !comment) { - fprintf(stderr, "_glh_save_history: NULL argument(s).\n"); - return 1; - }; -/* - * Attempt to open the specified file. - */ - fp = fopen(filename, "w"); - if(!fp) { - fprintf(stderr, "_glh_save_history: Can't open %s (%s).\n", - filename, strerror(errno)); - return 1; - }; -/* - * If a ceiling on the number of lines to save was specified, count - * that number of lines backwards, to find the first line to be saved. - */ - head = NULL; - if(max_lines >= 0) { - for(head=glh->list.tail; head && --max_lines > 0; head=head->prev) - ; - }; - if(!head) - head = glh->list.head; -/* - * Write the contents of the history buffer to the history file, writing - * associated data such as timestamps, to a line starting with the - * specified comment string. - */ - for(node=head; node; node=node->next) { -/* - * Write peripheral information associated with the line, as a comment. - */ - if(fprintf(fp, "%s ", comment) < 0 || - _glh_write_timestamp(fp, node->timestamp) || - fprintf(fp, " %u\n", node->group) < 0) { - fprintf(stderr, "Error writing %s (%s).\n", filename, strerror(errno)); - (void) fclose(fp); - return 1; - }; -/* - * Write the history line. - */ - if(fprintf(fp, "%s\n", glh->buffer + node->start) < 0) { - fprintf(stderr, "Error writing %s (%s).\n", filename, strerror(errno)); - (void) fclose(fp); - return 1; - }; - }; -/* - * Close the history file. - */ - if(fclose(fp) == EOF) { - fprintf(stderr, "Error writing %s (%s).\n", filename, strerror(errno)); - return 1; - }; - return 0; -} - -/*....................................................................... - * Restore previous history lines from a given file. - * - * Input: - * glh GlHistory * The input-line history maintenance object. - * filename const char * The name of the file to read from. - * comment const char * The same comment string that was passed to - * _glh_save_history() when this file was - * written. - * line char * A buffer into which lines can be read. - * dim size_t The allocated dimension of line[]. - * Output: - * return int 0 - OK. - * 1 - Error. - */ -int _glh_load_history(GlHistory *glh, const char *filename, const char *comment, - char *line, size_t dim) -{ - FILE *fp; /* The output file */ - size_t comment_len; /* The length of the comment string */ - time_t timestamp; /* The timestamp of the history line */ - unsigned group; /* The identifier of the history group to which */ - /* the line belongs. */ - int lineno; /* The line number being read */ -/* - * Check the arguments. - */ - if(!glh || !filename || !comment || !line) { - fprintf(stderr, "_glh_load_history: NULL argument(s).\n"); - return 1; - }; -/* - * Measure the length of the comment string. - */ - comment_len = strlen(comment); -/* - * Clear the history list. - */ - _glh_clear_history(glh, 1); -/* - * Attempt to open the specified file. Don't treat it as an error - * if the file doesn't exist. - */ - fp = fopen(filename, "r"); - if(!fp) - return 0; -/* - * Attempt to read each line and preceding peripheral info, and add these - * to the history list. - */ - for(lineno=1; fgets(line, dim, fp) != NULL; lineno++) { - char *lptr; /* A pointer into the input line */ -/* - * Check that the line starts with the comment string. - */ - if(strncmp(line, comment, comment_len) != 0) { - return _glh_cant_load_history(glh, filename, lineno, - "Corrupt history parameter line", fp); - }; -/* - * Skip spaces and tabs after the comment. - */ - for(lptr=line+comment_len; *lptr && (*lptr==' ' || *lptr=='\t'); lptr++) - ; -/* - * The next word must be a timestamp. - */ - if(_glh_decode_timestamp(lptr, &lptr, ×tamp)) { - return _glh_cant_load_history(glh, filename, lineno, - "Corrupt timestamp", fp); - }; -/* - * Skip spaces and tabs. - */ - while(*lptr==' ' || *lptr=='\t') - lptr++; -/* - * The next word must be an unsigned integer group number. - */ - group = (int) strtoul(lptr, &lptr, 10); - if(*lptr != ' ' && *lptr != '\n') { - return _glh_cant_load_history(glh, filename, lineno, - "Corrupt group id", fp); - }; -/* - * Skip spaces and tabs. - */ - while(*lptr==' ' || *lptr=='\t') - lptr++; -/* - * There shouldn't be anything left on the line. - */ - if(*lptr != '\n') { - return _glh_cant_load_history(glh, filename, lineno, - "Corrupt parameter line", fp); - }; -/* - * Now read the history line itself. - */ - lineno++; - if(fgets(line, dim, fp) == NULL) - return _glh_cant_load_history(glh, filename, lineno, "Read error", fp); -/* - * Append the line to the history buffer. - */ - if(_glh_add_history(glh, line, 1)) { - return _glh_cant_load_history(glh, filename, lineno, - "Insufficient memory to record line", fp); - }; -/* - * Record the group and timestamp information along with the line. - */ - if(glh->list.tail) { - glh->list.tail->timestamp = timestamp; - glh->list.tail->group = group; - }; - }; -/* - * Close the file. - */ - (void) fclose(fp); - return 0; -} - -/*....................................................................... - * This is a private error return function of _glh_load_history(). - */ -static int _glh_cant_load_history(GlHistory *glh, const char *filename, - int lineno, const char *message, FILE *fp) -{ - fprintf(stderr, "%s:%d: %s.\n", filename, lineno, message); - (void) fclose(fp); - return 1; -} - -/*....................................................................... - * Switch history groups. - * - * Input: - * glh GlHistory * The input-line history maintenance object. - * group unsigned The new group identifier. This will be recorded - * with subsequent history lines, and subsequent - * history searches will only return lines with - * this group identifier. This allows multiple - * separate history lists to exist within - * a single GlHistory object. Note that the - * default group identifier is 0. - * Output: - * return int 0 - OK. - * 1 - Error. - */ -int _glh_set_group(GlHistory *glh, unsigned group) -{ -/* - * Check the arguments. - */ - if(!glh) { - fprintf(stderr, "_glh_set_group: NULL argument(s).\n"); - return 1; - }; -/* - * Is the group being changed? - */ - if(group != glh->group) { -/* - * Cancel any ongoing search. - */ - if(_glh_cancel_search(glh)) - return 1; -/* - * Record the new group. - */ - glh->group = group; - }; - return 0; -} - -/*....................................................................... - * Query the current history group. - * - * Input: - * glh GlHistory * The input-line history maintenance object. - * Output: - * return unsigned The group identifier. - */ -int _glh_get_group(GlHistory *glh) -{ - return glh ? glh->group : 0; -} - -/*....................................................................... - * Write a timestamp to a given stdio stream, in the format - * yyyymmddhhmmss - * - * Input: - * fp FILE * The stream to write to. - * timestamp time_t The timestamp to be written. - * Output: - * return int 0 - OK. - * 1 - Error. - */ -static int _glh_write_timestamp(FILE *fp, time_t timestamp) -{ - struct tm *t; /* THe broken-down calendar time */ -/* - * Get the calendar components corresponding to the given timestamp. - */ - if(timestamp < 0 || (t = localtime(×tamp)) == NULL) { - if(fprintf(fp, "?") < 0) - return 1; - return 0; - }; -/* - * Write the calendar time as yyyymmddhhmmss. - */ - if(fprintf(fp, "%04d%02d%02d%02d%02d%02d", t->tm_year + 1900, t->tm_mon + 1, - t->tm_mday, t->tm_hour, t->tm_min, t->tm_sec) < 0) - return 1; - return 0; -} - -/*....................................................................... - * Read a timestamp from a string. - * - * Input: - * string char * The string to read from. - * Input/Output: - * endp char ** On output *endp will point to the next unprocessed - * character in string[]. - * timestamp time_t * The timestamp will be assigned to *t. - * Output: - * return int 0 - OK. - * 1 - Error. - */ -static int _glh_decode_timestamp(char *string, char **endp, time_t *timestamp) -{ - unsigned year,month,day,hour,min,sec; /* Calendar time components */ - struct tm t; -/* - * There are 14 characters in the date format yyyymmddhhmmss. - */ - enum {TSLEN=14}; - char timestr[TSLEN+1]; /* The timestamp part of the string */ -/* - * If the time wasn't available at the time that the line was recorded - * it will have been written as "?". Check for this before trying - * to read the timestamp. - */ - if(string[0] == '\?') { - *endp = string+1; - *timestamp = -1; - return 0; - }; -/* - * The timestamp is expected to be written in the form yyyymmddhhmmss. - */ - if(strlen(string) < TSLEN) { - *endp = string; - return 1; - }; -/* - * Copy the timestamp out of the string. - */ - strncpy(timestr, string, TSLEN); - timestr[TSLEN] = '\0'; -/* - * Decode the timestamp. - */ - if(sscanf(timestr, "%4u%2u%2u%2u%2u%2u", &year, &month, &day, &hour, &min, - &sec) != 6) { - *endp = string; - return 1; - }; -/* - * Advance the string pointer over the successfully read timestamp. - */ - *endp = string + TSLEN; -/* - * Copy the read values into a struct tm. - */ - t.tm_sec = sec; - t.tm_min = min; - t.tm_hour = hour; - t.tm_mday = day; - t.tm_wday = 0; - t.tm_yday = 0; - t.tm_mon = month - 1; - t.tm_year = year - 1900; - t.tm_isdst = -1; -/* - * Convert the contents of the struct tm to a time_t. - */ - *timestamp = mktime(&t); - return 0; -} - -/*....................................................................... - * Display the contents of the history list. - * - * Input: - * glh GlHistory * The input-line history maintenance object. - * fp FILE * The stdio stream to write to. - * fmt const char * A format string. This can contain arbitrary - * characters, which are written verbatim, plus - * any of the following format directives: - * %D - The date, like 2001-11-20 - * %T - The time of day, like 23:59:59 - * %N - The sequential entry number of the - * line in the history buffer. - * %G - The history group number of the line. - * %% - A literal % character. - * %H - The history line. - * all_groups int If true, display history lines from all - * history groups. Otherwise only display - * those of the current history group. - * max_lines int If max_lines is < 0, all available lines - * are displayed. Otherwise only the most - * recent max_lines lines will be displayed. - * Output: - * return int 0 - OK. - * 1 - Error. - */ -int _glh_show_history(GlHistory *glh, FILE *fp, const char *fmt, - int all_groups, int max_lines) -{ - GlLineNode *node; /* The line being displayed */ - GlLineNode *oldest; /* The oldest line to display */ - enum {TSMAX=32}; /* The maximum length of the date and time string */ - char buffer[TSMAX+1]; /* The buffer in which to write the date and time */ - int idlen; /* The length of displayed ID strings */ - unsigned grpmax; /* The maximum group number in the buffer */ - int grplen; /* The number of characters needed to print grpmax */ -/* - * Check the arguments. - */ - if(!glh || !fp || !fmt) { - fprintf(stderr, "_glh_show_history: NULL argument(s).\n"); - return 1; - }; -/* - * Is history enabled? - */ - if(!glh->enable || !glh->list.head) - return 0; -/* - * Work out the length to display ID numbers, choosing the length of - * the biggest number in the buffer. Smaller numbers will be padded - * with leading zeroes if needed. - */ - sprintf(buffer, "%lu", (unsigned long) glh->list.tail->id); - idlen = strlen(buffer); -/* - * Find the largest group number. - */ - grpmax = 0; - for(node=glh->list.head; node; node=node->next) { - if(node->group > grpmax) - grpmax = node->group; - }; -/* - * Find out how many characters are needed to display the group number. - */ - sprintf(buffer, "%u", (unsigned) grpmax); - grplen = strlen(buffer); -/* - * Find the node that follows the oldest line to be displayed. - */ - if(max_lines < 0) { - oldest = glh->list.head; - } else if(max_lines==0) { - return 0; - } else { - for(oldest=glh->list.tail; oldest; oldest=oldest->prev) { - if((all_groups || oldest->group == glh->group) && --max_lines <= 0) - break; - }; -/* - * If the number of lines in the buffer doesn't exceed the specified - * maximum, start from the oldest line in the buffer. - */ - if(!oldest) - oldest = glh->list.head; - }; -/* - * List the history lines in increasing time order. - */ - for(node=oldest; node; node=node->next) { -/* - * Only display lines from the current history group, unless - * told otherwise. - */ - if(all_groups || node->group == glh->group) { - const char *fptr; /* A pointer into the format string */ - struct tm *t = NULL; /* The broken time version of the timestamp */ -/* - * Work out the calendar representation of the node timestamp. - */ - if(node->timestamp != (time_t) -1) - t = localtime(&node->timestamp); -/* - * Parse the format string. - */ - fptr = fmt; - while(*fptr) { -/* - * Search for the start of the next format directive or the end of the string. - */ - const char *start = fptr; - while(*fptr && *fptr != '%') - fptr++; -/* - * Display any literal characters that precede the located directive. - */ - if(fptr > start && fprintf(fp, "%.*s", (int) (fptr - start), start) < 0) - return 1; -/* - * Did we hit a new directive before the end of the line? - */ - if(*fptr) { -/* - * Obey the directive. Ignore unknown directives. - */ - switch(*++fptr) { - case 'D': /* Display the date */ - if(t && strftime(buffer, TSMAX, "%Y-%m-%d", t) != 0 && - fprintf(fp, "%s", buffer) < 0) - return 1; - break; - case 'T': /* Display the time of day */ - if(t && strftime(buffer, TSMAX, "%H:%M:%S", t) != 0 && - fprintf(fp, "%s", buffer) < 0) - return 1; - break; - case 'N': /* Display the sequential entry number */ - if(fprintf(fp, "%*lu", idlen, (unsigned long) node->id) < 0) - return 1; - break; - case 'G': - if(fprintf(fp, "%*u", grplen, (unsigned) node->group) < 0) - return 1; - break; - case 'H': /* Display the history line */ - if(fprintf(fp, "%s", glh->buffer + node->start) < 0) - return 1; - break; - case '%': /* A literal % symbol */ - if(fputc('%', fp) == EOF) - return 1; - break; - }; -/* - * Skip the directive. - */ - if(*fptr) - fptr++; - }; - }; - }; - }; - return 0; -} - -/*....................................................................... - * Change the size of the history buffer. - * - * Input: - * glh GlHistory * The input-line history maintenance object. - * bufsize size_t The number of bytes in the history buffer, or 0 - * to delete the buffer completely. - * Output: - * return int 0 - OK. - * 1 - Insufficient memory (the previous buffer - * will have been retained). No error message - * will be displayed. - */ -int _glh_resize_history(GlHistory *glh, size_t bufsize) -{ - GlLineNode *node; /* A line location node in the list of lines */ - GlLineNode *prev; /* The line location node preceding 'node' */ -/* - * Check the arguments. - */ - if(!glh) - return bufsize > 0; -/* - * If the new size doesn't differ from the existing size, do nothing. - */ - if(glh->buflen == bufsize) - return 0; -/* - * Cancel any ongoing search. - */ - (void) _glh_cancel_search(glh); -/* - * Create a wholly new buffer? - */ - if(glh->buflen == 0) { - glh->buffer = (char *) malloc(bufsize); - if(!glh->buffer) - return 1; - glh->buflen = bufsize; -/* - * Delete an existing buffer? - */ - } else if(bufsize == 0) { - _glh_clear_history(glh, 1); - free(glh->buffer); - glh->buffer = NULL; - glh->buflen = 0; -/* - * To get here, we must be shrinking or expanding from one - * finite size to another. - */ - } else { -/* - * If we are shrinking the size of the buffer, then we first need - * to discard the oldest lines that won't fit in the new buffer. - */ - if(bufsize < glh->buflen) { - size_t nbytes = 0; /* The number of bytes used in the new buffer */ - GlLineNode *oldest; /* The oldest node to be kept */ -/* - * Searching backwards from the youngest line, find the oldest - * line for which there will be sufficient room in the new buffer. - */ - for(oldest = glh->list.tail; - oldest && (nbytes += oldest->nchar) <= bufsize; - oldest = oldest->prev) - ; -/* - * We will have gone one node too far, unless we reached the oldest line - * without exceeding the target length. - */ - if(oldest) { - nbytes -= oldest->nchar; - oldest = oldest->next; - }; -/* - * Discard the nodes that can't be retained. - */ - while(glh->list.head && glh->list.head != oldest) - _glh_discard_node(glh, glh->list.head); -/* - * If we are increasing the size of the buffer, we need to reallocate - * the buffer before shifting the lines into their new positions. - */ - } else { - char *new_buffer = (char *) realloc(glh->buffer, bufsize); - if(!new_buffer) - return 1; - glh->buffer = new_buffer; - glh->buflen = bufsize; - }; -/* - * If there are any lines to be preserved, copy the block of lines - * that precedes the end of the existing buffer to what will be - * the end of the new buffer. - */ - if(glh->list.head) { - int shift; /* The number of bytes to shift lines in the buffer */ -/* - * Get the oldest line to be kept. - */ - GlLineNode *oldest = glh->list.head; -/* - * Count the number of characters that are used in the lines that - * precede the end of the current buffer (ie. not including those - * lines that have been wrapped to the start of the buffer). - */ - int n = 0; - for(node=oldest,prev=oldest->prev; node && node->start >= oldest->start; - prev=node, node=node->next) - n += node->nchar; -/* - * Move these bytes to the end of the resized buffer. - */ - memmove(glh->buffer + bufsize - n, glh->buffer + oldest->start, n); -/* - * Adjust the buffer pointers to reflect the new locations of the moved - * lines. - */ - shift = bufsize - n - oldest->start; - for(node=prev; node && node->start >= oldest->start; node=node->prev) - node->start += shift; - }; -/* - * Shrink the buffer? - */ - if(bufsize < glh->buflen) { - char *new_buffer = (char *) realloc(glh->buffer, bufsize); - if(new_buffer) - glh->buffer = new_buffer; - glh->buflen = bufsize; /* Mark it as shrunk, regardless of success */ - }; - }; - return 0; -} - -/*....................................................................... - * Set an upper limit to the number of lines that can be recorded in the - * history list, or remove a previously specified limit. - * - * Input: - * glh GlHistory * The input-line history maintenance object. - * max_lines int The maximum number of lines to allow, or -1 to - * cancel a previous limit and allow as many lines - * as will fit in the current history buffer size. - */ -void _glh_limit_history(GlHistory *glh, int max_lines) -{ - if(!glh) - return; -/* - * Apply a new limit? - */ - if(max_lines >= 0 && max_lines != glh->max_lines) { -/* - * Count successively older lines until we reach the start of the - * list, or until we have seen max_lines lines (at which point 'node' - * will be line number max_lines+1). - */ - int nline = 0; - GlLineNode *node; - for(node=glh->list.tail; node && ++nline <= max_lines; node=node->prev) - ; -/* - * Discard any lines that exceed the limit. - */ - if(node) { - GlLineNode *oldest = node->next; /* The oldest line to be kept */ -/* - * Delete nodes from the head of the list until we reach the node that - * is to be kept. - */ - while(glh->list.head && glh->list.head != oldest) - _glh_discard_node(glh, glh->list.head); - }; - }; -/* - * Record the new limit. - */ - glh->max_lines = max_lines; - return; -} - -/*....................................................................... - * Discard either all history, or the history associated with the current - * history group. - * - * Input: - * glh GlHistory * The input-line history maintenance object. - * all_groups int If true, clear all of the history. If false, - * clear only the stored lines associated with the - * currently selected history group. - */ -void _glh_clear_history(GlHistory *glh, int all_groups) -{ -/* - * Check the arguments. - */ - if(!glh) - return; -/* - * Cancel any ongoing search. - */ - (void) _glh_cancel_search(glh); -/* - * Delete all history lines regardless of group? - */ - if(all_groups) { - _rst_FreeList(glh->list.node_mem); - glh->list.head = glh->list.tail = NULL; - glh->nline = 0; - glh->id_node = NULL; -/* - * Just delete lines of the current group? - */ - } else { - GlLineNode *node; /* The line node being checked */ - GlLineNode *prev; /* The line node that precedes 'node' */ - GlLineNode *next; /* The line node that follows 'node' */ -/* - * Search out and delete the line nodes of the current group. - */ - for(node=glh->list.head; node; node=next) { -/* - * Keep a record of the following node before we delete the current - * node. - */ - next = node->next; -/* - * Discard this node? - */ - if(node->group == glh->group) - _glh_discard_node(glh, node); - }; -/* - * If there are any lines left, and we deleted any lines, there will - * be gaps in the buffer. These need to be removed. - */ - if(glh->list.head) { - int epos; /* The index of the last used element in the buffer */ -/* - * Find the line nearest the end of the buffer. - */ - GlLineNode *enode; - for(node=glh->list.head, prev=NULL; - node && node->start >= glh->list.head->start; - prev=node, node = node->next) - ; - enode = prev; -/* - * Move the end line to abutt the end of the buffer, and remove gaps - * between the lines that precede it. - */ - epos = glh->buflen; - for(node=enode; node; node=node->prev) { - int shift = epos - (node->start + node->nchar); - if(shift) { - memmove(glh->buffer + node->start + shift, - glh->buffer + node->start, node->nchar); - node->start += shift; - }; - epos = node->start; - }; -/* - * Move the first line in the buffer to the start of the buffer, and - * remove gaps between the lines that follow it. - */ - epos = 0; - for(node=enode ? enode->next : NULL; node; node=node->next) { - int shift = epos - node->start; - if(shift) { - memmove(glh->buffer + node->start + shift, - glh->buffer + node->start, node->nchar); - node->start += shift; - }; - epos = node->start + node->nchar; - }; - }; - }; - return; -} - -/*....................................................................... - * Temporarily enable or disable the history list. - * - * Input: - * glh GlHistory * The input-line history maintenance object. - * enable int If true, turn on the history mechanism. If - * false, disable it. - */ -void _glh_toggle_history(GlHistory *glh, int enable) -{ - if(glh) - glh->enable = enable; -} - -/*....................................................................... - * Remove a given line location node from the history list, and return - * it to the freelist. - * - * Input: - * glh GlHistory * The input-line history maintenance object. - * node GlLineNode * The node to be removed. This must be currently - * in the list who's head is glh->list.head, or - * be NULL. - */ -static void _glh_discard_node(GlHistory *glh, GlLineNode *node) -{ - if(node) { -/* - * Make the node that precedes the node being removed point - * to the one that follows it. - */ - if(node->prev) - node->prev->next = node->next; - else - glh->list.head = node->next; -/* - * Make the node that follows the node being removed point - * to the one that precedes it. - */ - if(node->next) - node->next->prev = node->prev; - else - glh->list.tail = node->prev; -/* - * If we are deleting the node that is marked as the start point of the - * last ID search, remove the cached starting point. - */ - if(node == glh->id_node) - glh->id_node = NULL; -/* - * Return the node to the free list. - */ - node = (GlLineNode *) _del_FreeListNode(glh->list.node_mem, node); -/* - * Decrement the count of the number of lines in the buffer. - */ - glh->nline--; - }; -} - -/*....................................................................... - * Lookup the details of a given history line, given its id. - * - * Input: - * glh GlHistory * The input-line history maintenance object. - * id GlLineID The sequential number of the line. - * Input/Output: - * line const char ** A pointer to the history line will be assigned - * to *line. - * group unsigned * The group membership of the line will be assigned - * to *group. - * timestamp time_t * The timestamp of the line will be assigned to - * *timestamp. - * Output: - * return int 0 - The requested line wasn't found. - * 1 - The line was found. - */ -int _glh_lookup_history(GlHistory *glh, GlhLineID id, const char **line, - unsigned *group, time_t *timestamp) -{ - GlLineNode *node; /* The located line location node */ -/* - * Check the arguments. - */ - if(!glh) - return 0; -/* - * Search for the line that has the specified ID. - */ - node = _glh_find_id(glh, (GlhLineID) id); -/* - * Not found? - */ - if(!node) - return 0; -/* - * Return the details of the line. - */ - if(line) - *line = glh->buffer + node->start; - if(group) - *group = node->group; - if(timestamp) - *timestamp = node->timestamp; - return 1; -} - -/*....................................................................... - * Lookup a node in the history list by its ID. - * - * Input: - * glh GlHistory * The input-line history maintenance object. - * id GlhLineID The ID of the line to be returned. - * Output: - * return GlLIneNode * The located node, or NULL if not found. - */ -static GlLineNode *_glh_find_id(GlHistory *glh, GlhLineID id) -{ - GlLineNode *node; /* The node being checked */ -/* - * Is history enabled? - */ - if(!glh->enable || !glh->list.head) - return NULL; -/* - * If possible, start at the end point of the last ID search. - * Otherwise start from the head of the list. - */ - node = glh->id_node; - if(!node) - node = glh->list.head; -/* - * Search forwards from 'node'? - */ - if(node->id < id) { - while(node && node->id != id) - node = node->next; - glh->id_node = node ? node : glh->list.tail; -/* - * Search backwards from 'node'? - */ - } else { - while(node && node->id != id) - node = node->prev; - glh->id_node = node ? node : glh->list.head; - }; -/* - * Return the located node (this will be NULL if the ID wasn't found). - */ - return node; -} - -/*....................................................................... - * Query the state of the history list. Note that any of the input/output - * pointers can be specified as NULL. - * - * Input: - * glh GlHistory * The input-line history maintenance object. - * Input/Output: - * enabled int * If history is enabled, *enabled will be - * set to 1. Otherwise it will be assigned 0. - * group unsigned * The current history group ID will be assigned - * to *group. - * max_lines int * The currently requested limit on the number - * of history lines in the list, or -1 if - * unlimited. - */ -void _glh_state_of_history(GlHistory *glh, int *enabled, unsigned *group, - int *max_lines) -{ - if(glh) { - if(enabled) - *enabled = glh->enable; - if(group) - *group = glh->group; - if(max_lines) - *max_lines = glh->max_lines; - }; -} - -/*....................................................................... - * Get the range of lines in the history buffer. - * - * Input: - * glh GlHistory * The input-line history maintenance object. - * Input/Output: - * oldest unsigned long * The sequential entry number of the oldest - * line in the history list will be assigned - * to *oldest, unless there are no lines, in - * which case 0 will be assigned. - * newest unsigned long * The sequential entry number of the newest - * line in the history list will be assigned - * to *newest, unless there are no lines, in - * which case 0 will be assigned. - * nlines int * The number of lines currently in the history - * list. - */ -void _glh_range_of_history(GlHistory *glh, unsigned long *oldest, - unsigned long *newest, int *nlines) -{ - if(glh) { - if(oldest) - *oldest = glh->list.head ? glh->list.head->id : 0; - if(newest) - *newest = glh->list.tail ? glh->list.tail->id : 0; - if(nlines) - *nlines = glh->nline; - }; -} - -/*....................................................................... - * Return the size of the history buffer and the amount of the - * buffer that is currently in use. - * - * Input: - * glh GlHistory * The input-line history maintenance object. - * Input/Output: - * buff_size size_t * The size of the history buffer (bytes). - * buff_used size_t * The amount of the history buffer that - * is currently occupied (bytes). - */ -void _glh_size_of_history(GlHistory *glh, size_t *buff_size, size_t *buff_used) -{ - if(glh) { - if(buff_size) - *buff_size = glh->buflen; -/* - * Determine the amount of buffer space that is currently occupied. - */ - if(buff_used) { - size_t used = 0; - GlLineNode *node; - for(node=glh->list.head; node; node=node->next) - used += node->nchar; - *buff_used = used; - }; - }; -} diff --git a/libtecla-1.4.1/history.h b/libtecla-1.4.1/history.h deleted file mode 100644 index 49671a4..0000000 --- a/libtecla-1.4.1/history.h +++ /dev/null @@ -1,159 +0,0 @@ -#ifndef history_h -#define history_h - -/* - * Copyright (c) 2000, 2001 by Martin C. Shepherd. - * - * All rights reserved. - * - * Permission is hereby granted, free of charge, to any person obtaining a - * copy of this software and associated documentation files (the - * "Software"), to deal in the Software without restriction, including - * without limitation the rights to use, copy, modify, merge, publish, - * distribute, and/or sell copies of the Software, and to permit persons - * to whom the Software is furnished to do so, provided that the above - * copyright notice(s) and this permission notice appear in all copies of - * the Software and that both the above copyright notice(s) and this - * permission notice appear in supporting documentation. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS - * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT - * OF THIRD PARTY RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR - * HOLDERS INCLUDED IN THIS NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL - * INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING - * FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, - * NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION - * WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - * - * Except as contained in this notice, the name of a copyright holder - * shall not be used in advertising or otherwise to promote the sale, use - * or other dealings in this Software without prior written authorization - * of the copyright holder. - */ - -#include /* FILE * */ - -/*----------------------------------------------------------------------- - * This module is used to record and traverse historical lines of user input. - */ - -typedef struct GlHistory GlHistory; - -/* - * Create a new history maintenance object. - */ -GlHistory *_new_GlHistory(size_t buflen); - -/* - * Delete a history maintenance object. - */ -GlHistory *_del_GlHistory(GlHistory *glh); - -int _glh_add_history(GlHistory *glh, const char *line, int force); - -int _glh_search_prefix(GlHistory *glh, const char *line, int prefix_len); - -char *_glh_find_backwards(GlHistory *glh, char *line, size_t dim); -char *_glh_find_forwards(GlHistory *glh, char *line, size_t dim); - -int _glh_cancel_search(GlHistory *glh); - -char *_glh_oldest_line(GlHistory *glh, char *line, size_t dim); -char *_glh_current_line(GlHistory *glh, char *line, size_t dim); - -/* - * Whenever a new line is added to the history buffer, it is given - * a unique ID, recorded in an object of the following type. - */ -typedef unsigned long GlhLineID; - -/* - * Query the id of a history line offset by a given number of lines from - * the one that is currently being recalled. If a recall session isn't - * in progress, or the offset points outside the history list, 0 is - * returned. - */ -GlhLineID _glh_line_id(GlHistory *glh, int offset); - -/* - * Recall a line by its history buffer ID. If the line is no longer - * in the buffer, or the specified id is zero, NULL is returned. - */ -char *_glh_recall_line(GlHistory *glh, GlhLineID id, char *line, size_t dim); - -/* - * Write the contents of the history buffer to a given file. Note that - * ~ and $ expansion are not performed on the filename. - */ -int _glh_save_history(GlHistory *glh, const char *filename, - const char *comment, int max_lines); - -/* - * Restore the contents of the history buffer from a given file. - * Note that ~ and $ expansion are not performed on the filename. - */ -int _glh_load_history(GlHistory *glh, const char *filename, const char *comment, - char *line, size_t dim); - -/* - * Set and query the current history group. - */ -int _glh_set_group(GlHistory *glh, unsigned group); -int _glh_get_group(GlHistory *glh); - -/* - * Display the contents of the history list to the specified stdio - * output group. - */ -int _glh_show_history(GlHistory *glh, FILE *fp, const char *fmt, - int all_groups, int max_lines); - -/* - * Change the size of the history buffer. - */ -int _glh_resize_history(GlHistory *glh, size_t bufsize); - -/* - * Set an upper limit to the number of lines that can be recorded in the - * history list, or remove a previously specified limit. - */ -void _glh_limit_history(GlHistory *glh, int max_lines); - -/* - * Discard either all history, or the history associated with the current - * history group. - */ -void _glh_clear_history(GlHistory *glh, int all_groups); - -/* - * Temporarily enable or disable the history facility. - */ -void _glh_toggle_history(GlHistory *glh, int enable); - -/* - * Lookup a history line by its sequential number of entry in the - * history buffer. - */ -int _glh_lookup_history(GlHistory *glh, GlhLineID id, const char **line, - unsigned *group, time_t *timestamp); - -/* - * Query the state of the history list. - */ -void _glh_state_of_history(GlHistory *glh, int *enabled, unsigned *group, - int *max_lines); - -/* - * Get the range of lines in the history buffer. - */ -void _glh_range_of_history(GlHistory *glh, unsigned long *oldest, - unsigned long *newest, int *nlines); - -/* - * Return the size of the history buffer and the amount of the - * buffer that is currently in use. - */ -void _glh_size_of_history(GlHistory *glh, size_t *buff_size, size_t *buff_used); - -#endif diff --git a/libtecla-1.4.1/homedir.c b/libtecla-1.4.1/homedir.c deleted file mode 100644 index f2029b7..0000000 --- a/libtecla-1.4.1/homedir.c +++ /dev/null @@ -1,399 +0,0 @@ -/* - * Copyright (c) 2000, 2001 by Martin C. Shepherd. - * - * All rights reserved. - * - * Permission is hereby granted, free of charge, to any person obtaining a - * copy of this software and associated documentation files (the - * "Software"), to deal in the Software without restriction, including - * without limitation the rights to use, copy, modify, merge, publish, - * distribute, and/or sell copies of the Software, and to permit persons - * to whom the Software is furnished to do so, provided that the above - * copyright notice(s) and this permission notice appear in all copies of - * the Software and that both the above copyright notice(s) and this - * permission notice appear in supporting documentation. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS - * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT - * OF THIRD PARTY RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR - * HOLDERS INCLUDED IN THIS NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL - * INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING - * FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, - * NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION - * WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - * - * Except as contained in this notice, the name of a copyright holder - * shall not be used in advertising or otherwise to promote the sale, use - * or other dealings in this Software without prior written authorization - * of the copyright holder. - */ - -#include -#include -#include -#include - -#include -#include -#include -#include - -#include "pathutil.h" -#include "homedir.h" - -/* - * Set the max length of the error-reporting string. There is no point - * in this being longer than the width of a typical terminal window. - * In composing error messages, I have assumed that this number is - * at least 80, so you don't decrease it below this number. - */ -#define ERRLEN 200 - -/* - * Use the reentrant POSIX threads versions of the password lookup functions? - */ -#if defined(_POSIX_C_SOURCE) && _POSIX_C_SOURCE >= 199506L -#define THREAD_COMPATIBLE 1 -#endif - -/* - * Provide a password buffer size fallback in case the max size reported - * by sysconf() is said to be indeterminate. - */ -#define DEF_GETPW_R_SIZE_MAX 1024 - -/* - * The resources needed to lookup and record a home directory are - * maintained in objects of the following type. - */ -struct HomeDir { - char errmsg[ERRLEN+1]; /* Error-report buffer */ - char *buffer; /* A buffer for reading password entries and */ - /* directory paths. */ - int buflen; /* The allocated size of buffer[] */ -#ifdef THREAD_COMPATIBLE - struct passwd pwd; /* The password entry of a user */ -#endif -}; - -static const char *hd_getpwd(HomeDir *home); - -/*....................................................................... - * Create a new HomeDir object. - * - * Output: - * return HomeDir * The new object, or NULL on error. - */ -HomeDir *_new_HomeDir(void) -{ - HomeDir *home; /* The object to be returned */ - size_t pathlen; /* The estimated maximum size of a pathname */ -/* - * Allocate the container. - */ - home = (HomeDir *) malloc(sizeof(HomeDir)); - if(!home) { - fprintf(stderr, "_new_HomeDir: Insufficient memory.\n"); - return NULL; - }; -/* - * Before attempting any operation that might fail, initialize the - * container at least up to the point at which it can safely be passed - * to _del_HomeDir(). - */ - home->errmsg[0] = '\0'; - home->buffer = NULL; - home->buflen = 0; -/* - * Allocate the buffer that is used by the reentrant POSIX password-entry - * lookup functions. - */ -#ifdef THREAD_COMPATIBLE -/* - * Get the length of the buffer needed by the reentrant version - * of getpwnam(). - */ -#ifndef _SC_GETPW_R_SIZE_MAX - home->buflen = DEF_GETPW_R_SIZE_MAX; -#else - errno = 0; - home->buflen = sysconf(_SC_GETPW_R_SIZE_MAX); -/* - * If the limit isn't available, substitute a suitably large fallback value. - */ - if(home->buflen < 0 || errno) - home->buflen = DEF_GETPW_R_SIZE_MAX; -#endif -#endif -/* - * If the existing buffer length requirement is too restrictive to record - * a pathname, increase its length. - */ - pathlen = _pu_pathname_dim(); - if(pathlen > home->buflen) - home->buflen = pathlen; -/* - * Allocate a work buffer. - */ - home->buffer = (char *) malloc(home->buflen); - if(!home->buffer) { - fprintf(stderr, "_new_HomeDir: Insufficient memory."); - return _del_HomeDir(home); - }; - return home; -} - -/*....................................................................... - * Delete a HomeDir object. - * - * Input: - * home HomeDir * The object to be deleted. - * Output: - * return HomeDir * The deleted object (always NULL). - */ -HomeDir *_del_HomeDir(HomeDir *home) -{ - if(home) { - if(home->buffer) - free(home->buffer); - free(home); - }; - return NULL; -} - -/*....................................................................... - * Lookup the home directory of a given user in the password file. - * - * Input: - * home HomeDir * The resources needed to lookup the home directory. - * user const char * The name of the user to lookup, or "" to lookup - * the home directory of the person running the - * program. - * Output: - * return const char * The home directory. If the library was compiled - * with threads, this string is part of the HomeDir - * object and will change on subsequent calls. If - * the library wasn't compiled to be reentrant, - * then the string is a pointer into a static string - * in the C library and will change not only on - * subsequent calls to this function, but also if - * any calls are made to the C library password - * file lookup functions. Thus to be safe, you should - * make a copy of this string before calling any - * other function that might do a password file - * lookup. - * - * On error, NULL is returned and a description - * of the error can be acquired by calling - * _hd_last_home_dir_error(). - */ -const char *_hd_lookup_home_dir(HomeDir *home, const char *user) -{ - const char *home_dir; /* A pointer to the home directory of the user */ -/* - * If no username has been specified, arrange to lookup the current - * user. - */ - int login_user = !user || *user=='\0'; -/* - * Check the arguments. - */ - if(!home) { - fprintf(stderr, "_hd_lookup_home_dir: NULL argument(s).\n"); - return NULL; - }; -/* - * Handle the ksh "~+". This expands to the absolute path of the - * current working directory. - */ - if (!login_user && strcmp(user, "+") == 0) { - home_dir = hd_getpwd(home); - if(!home_dir) { - strncpy(home->errmsg, "Cannot determine current directory.", ERRLEN); - home->errmsg[ERRLEN] = '\0'; - return NULL; - } - return home_dir; - }; -/* - * Look up the password entry of the user. - * First the POSIX threads version - this is painful! - */ -#ifdef THREAD_COMPATIBLE - { - struct passwd *ret; /* The returned pointer to pwd */ - int status; /* The return value of getpwnam_r() */ -/* - * Look up the password entry of the specified user. - */ - if(login_user) - status = getpwuid_r(geteuid(), &home->pwd, home->buffer, home->buflen, - &ret); - else - status = getpwnam_r(user, &home->pwd, home->buffer, home->buflen, &ret); - if(status || !ret) { - const char *fmt = "User '%.*s' doesn't exist."; - sprintf(home->errmsg, fmt, ERRLEN - strlen(fmt), user); - return NULL; - }; -/* - * Get a pointer to the string that holds the home directory. - */ - home_dir = home->pwd.pw_dir; - }; -/* - * Now the classic unix version. - */ -#else - { - struct passwd *pwd = login_user ? getpwuid(geteuid()) : getpwnam(user); - if(!pwd) { - const char *fmt = "User '%.*s' doesn't exist."; - sprintf(home->errmsg, fmt, ERRLEN - strlen(fmt), user); - return NULL; - }; -/* - * Get a pointer to the home directory. - */ - home_dir = pwd->pw_dir; - }; -#endif - return home_dir; -} - -/*....................................................................... - * Return a description of the last error that caused _hd_lookup_home_dir() - * to return NULL. - * - * Input: - * home HomeDir * The resources needed to record the home directory. - * Output: - * return char * The description of the last error. - */ -const char *_hd_last_home_dir_error(HomeDir *home) -{ - return home ? home->errmsg : "NULL HomeDir argument"; -} - -/*....................................................................... - * The _hd_scan_user_home_dirs() function calls a user-provided function - * for each username known by the system, passing the function both - * the name and the home directory of the user. - * - * Input: - * home HomeDir * The resource object for reading home - * directories. - * data void * Anonymous data to be passed to the - * callback function. - * callback_fn HOME_DIR_FN(*) The function to call for each user. - * Output: - * return int 0 - Successful completion. - * 1 - An error occurred. A description - * of the error can be obtained by - * calling _hd_last_home_dir_error(). - */ -int _hd_scan_user_home_dirs(HomeDir *home, void *data, HOME_DIR_FN(*callback_fn)) -{ - int waserr = 0; /* True after errors */ -/* - * Check the arguments. - */ - if(!home || !callback_fn) { - if(home) - strcpy(home->errmsg, - "_hd_scan_user_home_dirs: Missing callback function"); - return 1; - }; -/* - * There are no reentrant versions of getpwent() etc for scanning - * the password file, so disable username completion when the - * library is compiled to be reentrant. - */ -#if !defined(_POSIX_C_SOURCE) || _POSIX_C_SOURCE < 199506L - { - struct passwd *pwd; /* The pointer to the latest password entry */ -/* - * Open the password file. - */ - setpwent(); -/* - * Read the contents of the password file, looking for usernames - * that start with the specified prefix, and adding them to the - * list of matches. - */ - while((pwd = getpwent()) != NULL && !waserr) - waserr = callback_fn(data, pwd->pw_name, pwd->pw_dir, home->errmsg, - ERRLEN); -/* - * Close the password file. - */ - endpwent(); - }; -#endif -/* - * Under ksh ~+ stands for the absolute pathname of the current working - * directory. - */ - if (!waserr) { - const char *pwd = hd_getpwd(home); - if(pwd) { - waserr = callback_fn(data, "+", pwd, home->errmsg, ERRLEN); - } else { - waserr = 1; - strncpy(home->errmsg, "Cannot determine current directory.", ERRLEN); - home->errmsg[ERRLEN] = '\0'; - }; - }; - return waserr; -} - -/*....................................................................... - * Return the value of getenv("PWD") if this points to the current - * directory, or the return value of getcwd() otherwise. The reason for - * prefering PWD over getcwd() is that the former preserves the history - * of symbolic links that have been traversed to reach the current - * directory. This function is designed to provide the equivalent - * expansion of the ksh ~+ directive, which normally returns its value - * of PWD. - * - * Input: - * home HomeDir * The resource object for reading home directories. - * Output: - * return const char * A pointer to either home->buffer, where the - * pathname is recorded, the string returned by - * getenv("PWD"), or NULL on error. - */ -static const char *hd_getpwd(HomeDir *home) -{ -/* - * Get the absolute path of the current working directory. - */ - char *cwd = getcwd(home->buffer, home->buflen); -/* - * Some shells set PWD with the path of the current working directory. - * This will differ from cwd in that it won't have had symbolic links - * expanded. - */ - const char *pwd = getenv("PWD"); -/* - * If PWD was set, and it points to the same directory as cwd, return - * its value. Note that it won't be the same if the current shell or - * the current program has changed directories, after inheriting PWD - * from a parent shell. - */ - struct stat cwdstat, pwdstat; - if(pwd && cwd && stat(cwd, &cwdstat)==0 && stat(pwd, &pwdstat)==0 && - cwdstat.st_dev == pwdstat.st_dev && cwdstat.st_ino == pwdstat.st_ino) - return pwd; -/* - * Also return pwd if getcwd() failed, since it represents the best - * information that we have access to. - */ - if(!cwd) - return pwd; -/* - * In the absence of a valid PWD, return cwd. - */ - return cwd; -} diff --git a/libtecla-1.4.1/homedir.h b/libtecla-1.4.1/homedir.h deleted file mode 100644 index 5607802..0000000 --- a/libtecla-1.4.1/homedir.h +++ /dev/null @@ -1,81 +0,0 @@ -#ifndef homedir_h -#define homedir_h - -/* - * Copyright (c) 2000, 2001 by Martin C. Shepherd. - * - * All rights reserved. - * - * Permission is hereby granted, free of charge, to any person obtaining a - * copy of this software and associated documentation files (the - * "Software"), to deal in the Software without restriction, including - * without limitation the rights to use, copy, modify, merge, publish, - * distribute, and/or sell copies of the Software, and to permit persons - * to whom the Software is furnished to do so, provided that the above - * copyright notice(s) and this permission notice appear in all copies of - * the Software and that both the above copyright notice(s) and this - * permission notice appear in supporting documentation. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS - * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT - * OF THIRD PARTY RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR - * HOLDERS INCLUDED IN THIS NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL - * INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING - * FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, - * NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION - * WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - * - * Except as contained in this notice, the name of a copyright holder - * shall not be used in advertising or otherwise to promote the sale, use - * or other dealings in this Software without prior written authorization - * of the copyright holder. - */ - -typedef struct HomeDir HomeDir; - -/* - * The following constructor and destructor functions create and - * delete the resources needed to look up home directories. - */ -HomeDir *_new_HomeDir(void); -HomeDir *_del_HomeDir(HomeDir *home); - -/* - * Return the home directory of a specified user, or NULL if unknown. - */ -const char *_hd_lookup_home_dir(HomeDir *home, const char *user); - -/* - * Get the description of the that occured when _hd_lookup_home_dir() was - * last called. - */ -const char *_hd_last_home_dir_error(HomeDir *home); - -/* - * The _hd_scan_user_home_dirs() function calls a user-provided function - * for each username known by the system, passing the function both - * the name and the home directory of the user. - * - * The following macro can be used to declare both pointers and - * prototypes for the callback functions. The 'data' argument is - * a copy of the 'data' argument passed to _hd_scan_user_home_dirs() - * and is intended for the user of _hd_scan_user_home_dirs() to use - * to pass anonymous context data to the callback function. - * The username and home directories are passed to the callback function - * in the *usrnam and *homedir arguments respectively. - * To abort the scan, and have _hd_scan_user_home_dirs() return 1, the - * callback function can return 1. A description of up to maxerr - * characters before the terminating '\0', can be written to errmsg[]. - * This can then be examined by calling _hd_last_home_dir_error(). - * To indicate success and continue the scan, the callback function - * should return 0. _hd_scan_user_home_dirs() returns 0 on successful - * completion of the scan, or 1 if an error occurred or a call to the - * callback function returned 1. - */ -#define HOME_DIR_FN(fn) int (fn)(void *data, const char *usrnam, const char *homedir, char *errmsg, int maxerr) - -int _hd_scan_user_home_dirs(HomeDir *home, void *data, - HOME_DIR_FN(*callback_fn)); - -#endif diff --git a/libtecla-1.4.1/html/changes.html b/libtecla-1.4.1/html/changes.html deleted file mode 100644 index b309a7b..0000000 --- a/libtecla-1.4.1/html/changes.html +++ /dev/null @@ -1,1495 +0,0 @@ -The tecla library change log -
-In the following log, modification dates are listed using the European
-convention in which the day comes before the month (ie. DD/MM/YYYY).
-The most recent modifications are listed first.
-
-25/05/2002 mcs@astro.caltech.edu  (based on suggestions by Paul Smith)
-           pathutil.c
-             Apparently, under QNX pathconf("/",_PC_PATH_MAX) returns
-             EINVAL. At Paul's suggestion I have modified the code to
-             silently substitute the existing MAX_PATHLEN_FALLBACK
-             value if pathconf() returns an error of any kind.
-           homedir.c
-             Under QNX, sysconf(_SC_GETPW_R_SIZE_MAX) also apparently
-             returns EINVAL, so as with pathconf() I modified the code
-             to substitute a fallback default, rather than
-             complaining and failing.
-           enhance.c
-             Paul told me that the inclusion of sys/termios.h was
-             causing compilation of enhance.c to fail under QNX. This
-             line is a bug.  The correct thing to do is include
-             termios.h without a sub-directory prefix, as I was
-             already doing futher up in the file, so I have just
-             removed the errant include line.
-
-12/02/2002 mcs@astro.caltech.edu
-           getline.c configure.in configure
-             Mac OS X doesn't have a term.h or termcap.h, but it does
-             define prototypes for tputs() and setupterm(), so the
-             default prototypes that I was including if no headers
-             where available, upset it. I've removed these prototypes.
-             I also now conditionally include whichever is found of
-             curses.h and ncurses/curses.h for both termcap and
-             terminfo (before I wasn't including curses.h when
-             termcap was selected).
-
-12/02/2002 mcs@astro.caltech.edu
-           Updated version number to 1.4.1, ready for a micro
-           release.
-
-12/02/2002 mcs@astro.caltech.edu
-           html/index.html
-             Added Mac OS X and Cygwin to the list of systems that
-             can compile libtecla.
-
-12/02/2002 mcs@astro.caltech.edu
-           getline.c
-             Under Mac OS X, the tputs() callback function returns
-             void, instead of the int return value used by other
-             systems. This declaration is now used if both __MACH__
-             and __APPLE__ are defined. Hopefully these are the
-             correct system macros to check. Thanks for Stephan
-             Fiedler for providing information on Mac OS X.
-
-11/02/2002 mcs@astro.caltech.edu
-           configure.in configure getline.c
-             Some systems don't have term.h, and others have it hidden
-             in an ncurses sub-directory of the standard system include
-             directory. If term.h can't be found, simply don't include
-             it. If it is in an ncurses sub-directory, include
-             ncurses/term.h instead of term.h.
-
-04/02/2002 mcs@astro.caltech.edu
-           configure.in configure Makefile.in Makefile.rules
-             Use ranlib on systems that need it (Mac OS X).  Also,
-             make all components of the installation directories where
-             needed, instead of assuming that they exist.
-
-04/02/2002 mcs@astro.caltech.edu
-           getline.c
-             When the tab completion binding was unbound from the tab
-             key, hitting the tab key caused gl_get_line() to ring the
-             bell instead of inserting a tab character. This is
-             problematic when using the 'enhance' program with
-             Jython, since tabs are important in Python. I have
-             corrected this.
-
-10/12/2001 Version 1.4.0 released.
-
-10/12/2001 mcs@astro.caltech.edu
-           getline.c
-             If the TIOCGWINSZ ioctl doesn't work, as is the case when
-             running in an emacs shell, leave the size unchanged, rather
-             than returning a fatal error.
-
-07/12/2001 mcs@astro.caltech.edu
-           configure.in configure
-             Now that the configure version of CFLAGS is included in
-             the makefile, I noticed that the optimization flags -g
-             and -O2 had been added. It turns out that if CFLAGS isn't
-             already set, the autoconf AC_PROG_CC macro initializes it
-             with these two optimization flags. Since this would break
-             backwards compatibility in embedded distributions that
-             already use the OPT= makefile argument, and because
-             turning debugging on needlessly bloats the library, I now
-             make sure that CFLAGS is set before calling this macro.
-
-07/12/2001 mcs@astro.caltech.edu
-           enhance.c
-             Use argv[0] in error reports instead of using a
-             hardcoded macro.
-
-07/12/2001 mcs@astro.caltech.edu
-           getline.c
-             The cut buffer wasn't being cleared after being
-             used as a work buffer by gl_load_history().
-
-06/12/2001 mcs@astro.caltech.edu
-           configure.in configure
-             I removed my now redundant definition of SUN_TPUTS from
-             CFLAGS. I also added "-I/usr/include" to CFLAGS under
-             Solaris to prevent gcc from seeing conflicting versions
-             of system header files in /usr/local/include.
-
-06/12/2001 Markus Gyger (logged here by mcs)
-           Lots of files.
-             Lots of corrections to misspellings and typos in the
-             comments.
-           getline.c
-             Markus reverted a supposed fix that I added a day or two
-             ago. I had incorrectly thought that in Solaris 8, Sun had
-             finally brought their declaration of the callback
-             function of tputs() into line with other systems, but it
-             turned out that gcc was pulling in a GNU version of
-             term.h from /usr/local/include, and this was what
-             confused me.
-
-05/12/2001 mcs@astro.caltech.edu
-           Makefile.in
-             I added @CFLAGS@ to the CFLAGS assignment, so that
-             if CFLAGS is set as an environment variable when
-             configure is run, the corresponding make variable
-             includes its values in the output makefile.
-
-05/12/2001 mcs@astro.caltech.edu
-           getline.c libtecla.h libtecla.map man3/gl_get_line.3
-           man3/gl_last_signal.3
-             I added a function that programs can use to find out
-             which signal caused gl_get_line() to return EINTR.
-
-05/12/2001 mcs@astro.caltech.edu
-           getline.c
-             When the newline action was triggered by a printable
-             character, it failed to display that character. It now
-             does. Also, extra control codes that I had added, to
-             clear to the end of the display after the carriage return,
-             but before displaying the prompt, were confusing expect
-             scripts, so I have removed them. This step is now done
-             instead in gl_redisplay() after displaying the full input
-             line.
-
-05/12/2001 mcs@astro.caltech.edu
-           getline.c man3/gl_get_line.3
-             A user convinced me that continuing to invoke meta
-             keybindings for meta characters that are printable is a
-             bad idea, as is allowing users to ask to have setlocale()
-             called behind the application's back. I have thus changed
-             this. The setlocale configuration option has gone, and
-             gl_get_line() is now completely 8-bit clean, by default.
-             This means that if a meta character is printable, it is
-             treated as a literal character, rather than a potential
-             M-c binding.  Meta bindings can still be invoked via
-             their Esc-c equivalents, and indeed most terminal
-             emulators either output such escape pairs by default when
-             the meta character is pressed, or can be configured to do
-             so. I have documented how to configure xterm to do this,
-             in the man page.
-
-03/12/2001 mcs@astro.caltech.edu
-           getline.c man3/gl_get_line.3
-             gl_get_line() by default now prints any 8-bit printable
-             characters that don't match keybindings. Previously
-             characters > 127 were only printed if preceded by the
-             literal-next action.  Alternatively, by placing the
-             command literal_if_printable in the tecla configuration
-             file, all printable characters are treated as literal
-             characters, even if they are bound to action functions.
-
-             For international users of programs written by
-             programmers that weren't aware of the need to call
-             setlocale() to support alternate character sets, the
-             configuration file can now also contain the single-word
-             command "setlocale", which tells gl_get_line() to remedy
-             this.
-
-27/11/2001 mcs@astro.caltech.edu
-           demo.c demo2.c enhance man3/gl_get_line.3
-             All demos and programs now call setlocale(LC_CTYPE,"").
-             This makes them support character sets of different
-             locales, where specified with the LC_CTYPE, LC_ALL, or
-             LANG environment variables. I also added this to the demo
-             in the man page, and documented its effect.
-
-27/11/2001 mcs@astro.caltech.edu
-           getline.c
-             When displaying unsigned characters with values over
-             127 literally, previously it was assumed that they would
-             all be displayable. Now isprint() is consulted, and if it
-             says that a character isn't printable, the character code
-             is displayed in octal like \307. In non-C locales, some
-             characters with values > 127 are displayable, and
-             isprint() tells gl_get_line() which are and which aren't.
-
-27/11/2001 mcs@astro.caltech.edu
-           getline.c pathutil.c history.c enhance.c demo2.c
-             All arguments of the ctype.h character class functions
-             are now cast to (int)(unsigned char). Previously they
-             were cast to (int), which doesn't correctly conform to
-             the requirements of the C standard, and could cause
-             problems for characters with values > 127 on systems
-             with signed char's.
-
-26/11/2001 mcs@astro.caltech.edu
-           man3/enhance.3 man3/libtecla.3
-             I started writing a man page for the enhance program.
-
-26/11/2001 mcs@astro.caltech.edu
-           Makefile.in Makefile.rules INSTALL
-             It is now possible to specify whether the demos and other
-             programs are to be built, by overriding the default
-             values of the DEMOS, PROGRAMS and PROGRAMS_R variables.
-             I have also documented the BINDIR variable and the
-             install_bin makefile target.
-
-22/11/2001 mcs@astro.caltech.edu
-           getline.c libtecla.h libtecla.map man3/gl_get_line.3
-           man3/gl_ignore_signal.3 man3/gl_trap_signal.3
-             Signal handling has now been modified to be customizable.
-             Signals that are trapped by default can be removed from
-             the list of trapped signals, and signals that aren't
-             currently trapped, can be added to the list. Applications
-             can also specify the signal and terminal environments in
-             which an application's signal handler is invoked, and
-             what gl_get_line() does after the signal handler returns.
-
-13/11/2001 mcs@astro.caltech.edu
-           getline.c man3/gl_get_line.3
-             Added half-bright, reverse-video and blinking text to the
-             available prompt formatting options.
-           getline.c
-             Removed ^O from the default VT100 sgr0 capability
-             string.  Apparently it can cause problems with some
-             terminal emulators, and we don't need it, since it turns
-             off the alternative character set mode, which we don't
-             use.
-           getline.c
-             gl_tigetstr() and gl_tgetstr() didn't guard against the
-             error returns of tigetstr() and tgetstr() respectively.
-             They now do.
-
-11/11/2001 mcs@astro.caltech.edu
-           getline.c libtecla.h libtecla.map man3/gl_get_line.3
-           man3/gl_prompt_style.3
-             Although the default remains to display the prompt string
-             literally, the new gl_prompt_style() function can be used
-             to enable text attribute formatting directives in prompt
-             strings, such as underlining, bold font, and highlighting
-             directives.
-
-09/11/2001 mcs@astro.caltech.edu
-           enhance.c Makefile.rules configure.in configure
-             I added a new program to the distribution that allows one
-             to run most third party programs with the tecla library
-             providing command-line editing.
-
-08/11/2001 mcs@astro.caltech.edu
-           libtecla.h getline.c man3/gl_get_line.3 history.c history.h
-             I added a max_lines argument to gl_show_history() and
-             _glh_show_history(). This can optionally be used to
-             set a limit on the number of history lines displayed.
-           libtecla.h getline.c man3/gl_get_line.3
-             I added a new function called gl_replace_prompt(). This
-             can be used by gl_get_line() callback functions to
-             request that a new prompt be use when they return.
-
-06/11/2001 mcs@astro.caltech.edu
-           getline.c man3/gl_get_line.3
-             I implemented, bound and documented the list-history
-             action, used for listing historical lines of the current
-             history group.
-           getline.c man3/gl_get_line.3 man3/gl_echo_mode.3
-             I wrote functions to specify and query whether subsequent
-             lines will be visible as they are being typed.
-
-28/10/2001 mcs@astro.caltech.edu
-           getline.c man3/gl_get_line.3
-             For those cases where a terminal provides its own
-             high-level terminal editing facilities, you can now
-             specify an edit-mode argument of 'none'. This disables
-             all tecla key bindings, and by using canonical terminal
-             input mode instead of raw input mode, editing is left up
-             to the terminal driver.
-
-21/10/2001 mcs@astro.caltech.edu
-           libtecla.h getline.c history.c history.h
-           man3/gl_get_line.3 man3/gl_history_info.3
-             I added the new gl_state_of_history(),
-             gl_range_of_history() and gl_size_of_history()
-             functions for querying information about the
-             history list.
-           history.c
-             While testing the new gl_size_of_history()
-             function, I noticed that when the history buffer
-             wrapped, any location nodes of old lines between
-             the most recent line and the end of the buffer
-             weren't being removed. This could result in bogus
-             entries appearing at the start of the history list.
-             Now fixed.
-
-20/10/2001 mcs@astro.caltech.edu
-
-           libtecla.h getline.c history.c history.h
-           man3/gl_get_line.3 man3/gl_lookup_history.3
-             I added a function called gl_lookup_history(), that
-             the application can use to lookup lines in the history
-             list.
-           libtecla.h getline.c history.c history.h man3/gl_get_line.3
-             gl_show_history() now takes a format string argument
-             to control how the line is displayed, and with what
-             information. It also now provides the option of either
-             displaying all history lines or just those of the
-             current history group.
-           getline.c man3/gl_get_line.3
-             gl_get_line() only archives lines in the history buffer
-             if the newline action was invoked by a newline or
-             carriage return character.
-
-16/10/2001 mcs@astro.caltech.edu
-
-           history.c history.h getline.c libtecla.h libtecla.map
-           man3/gl_get_line.3 man3/gl_resize_history.3
-           man3/gl_limit_history.3 man3/gl_clear_history.3
-           man3/gl_toggle_history.3
-	     I added a number of miscellaneous history configuration
-	     functions. You can now resize or delete the history
-	     buffer, limit the number of lines that are allowed in the
-	     buffer, clear either all history or just the history of
-	     the current history group, and temporarily enable and
-	     disable the history mechanism.
-
-13/10/2001 mcs@astro.caltech.edu
-
-           getline.c
-             tputs_fp is now only declared if using termcap or
-             terminfo.
-           getline.c libtecla.map man3/gl_get_line.3
-           man3/gl_terminal_size.3
-             I added a public gl_terminal_size() function for
-             updating and querying the current size of the terminal.
-           update_version configure.in libtecla.h
-             A user noted that on systems where the configure script
-             couldn't be used, it was inconvenient to have the version
-             number macros set by the configure script, so they are
-             now specified in libtecla.h. To reduce the likelihood
-             that the various files where the version number now
-             appears might get out of sync, I have written the
-             update_version script, which changes the version number
-             in all of these files to a given value.
-
-01/10/2001 mcs@astro.caltech.edu
-
-           getline.c history.c history.h man3/gl_get_line.3
-             I added a max_lines argument to gl_save_history(), to
-             allow people to optionally place a ceiling on the number
-             of history lines saved. Specifying this as -1 sets the
-             ceiling to infinity.
-
-01/10/2001 mcs@astro.caltech.edu
-
-           configure.in configure
-             Under digital unix, getline wouldn't compile with
-             _POSIX_C_SOURCE set, due to type definitions needed by
-             select being excluded by this flag. Defining the
-             _OSF_SOURCE macro as well on this system, resolved this.
-
-30/09/2001 mcs@astro.caltech.edu
-
-           getline.c libtecla.h history.c history.h man3/gl_get_line.3
-           man3/gl_group_history.3
-             I implemented history streams. History streams
-             effectively allow multiple history lists to be stored in
-             a single history buffer. Lines in the buffer are tagged
-             with the current stream identification number, and
-             lookups only consider lines that are marked with the
-             current stream identifier.
-           getline.c libtecla.h history.c history.h man3/gl_get_line.3
-           man3/gl_show_history.3
-             The new gl_show_history function displays the current
-             history to a given stdio output stream.
-
-29/09/2001 mcs@astro.caltech.edu
-
-           getline.c
-             Previously new_GetLine() installed a persistent signal
-             handler to be sure to catch the SIGWINCH (terminal size
-             change) signal between calls to gl_get_line(). This had
-             the drawback that if multiple GetLine objects were
-             created, only the first GetLine object used after the
-             signal was received, would see the signal and adapt to
-             the new terminal size. Instead of this, a signal handler
-             for sigwinch is only installed while gl_get_line() is
-             running, and just after installing this handler,
-             gl_get_line() checks for terminal size changes that
-             might have occurred while the signal handler wasn't
-             installed.
-           getline.c
-             Dynamically allocated copies of capability strings looked
-             up in the terminfo or termcap databases are now made, so
-             that calls to setupterm() etc for one GetLine object
-             don't get trashed when another GetLine object calls
-             setupterm() etc. It is now safe to allocate and use
-             multiple GetLine objects, albeit only within a single
-             thread.
-           
-28/09/2001 mcs@astro.caltech.edu
-
-           version.c Makefile.rules
-             I added a function for querying the version number of
-             the library.
-
-26/09/2001 mcs@astro.caltech.edu
-
-           getline.c man3/gl_get_line.3
-             I added the new gl_watch_fd() function, which allows
-             applications to register callback functions to be invoked
-             when activity is seen on arbitrary file descriptors while
-             gl_get_line() is awaiting keyboard input from the user.
-
-           keytab.c
-             If a request is received to delete a non-existent
-             binding, which happens to be an ambiguous prefix of other
-             bindings no complaint is now generated about it being
-             ambiguous.
-
-23/09/2001 mcs@astro.caltech.edu
-
-           getline.c history.c history.h man3/gl_get_line.3
-           libtecla.map demo.c
-             I added new public functions for saving and restoring the
-             contents of the history list. The demo program now uses
-             these functions to load and save history in ~/.demo_history.
-
-23/09/2001 mcs@astro.caltech.edu
-
-           getline.c
-             On trying the demo for the first time on a KDE konsole
-             terminal, I discovered that the default M-O binding
-             to repeat history was hiding the arrow keys, which are
-             M-OA etc. I have removed this binding. The M-o (ie the
-             lower case version of this), is still bound.
-
-18/09/2001 mcs@astro.caltech.edu
-
-           getline.c man3/gl_get_line.3 libtecla.map
-             Automatic reading of ~/.teclarc is now postponed until
-             the first call to gl_get_line(), to give the application
-             the chance to specify alternative configuration sources
-             with the new function gl_configure_getline(). The latter
-             function allows configuration to be done with a string, a
-             specified application-specific file, and/or a specified
-             user-specific file. I also added a read-init-files action
-             function, for re-reading the configuration files, if any.
-             This is by default bound to ^X^R. This is all documented
-             in gl_get_line.3.
-
-08/09/2001 mcs@astro.caltech.edu
-
-           getline.c man3/gl_get_line.3
-             It is now possible to bind actions to key-sequences
-             that start with printable characters. Previously
-             keysequences were required to start with meta or control
-             characters. This is documented in gl_get_line.3.
-
-           getline.c man3/gl_get_line.3
-             A customized completion function can now arrange for
-             gl_get_line() to return the current input line whenever a
-             successful completion has been made. This is signalled by
-             setting the last character of the optional continuation
-             suffix to a newline character. This is documented in
-             gl_get_line.3.
-
-05/07/2001 Bug reported by Mike MacFaden, fixed by mcs
-
-           configure.in
-             There was a bug in the configure script that only
-             revealed itself on systems without termcap but not
-             terminfo (eg. NetBSD). I traced the bug back to a lack of
-             sufficient quoting of multi-line m4 macro arguments in
-             configure.in, and have now fixed this and recreated the
-             configure script.
-
-05/07/2001 Bug reported and patched by Mike MacFaden (patch modified
-           by mcs to match original intentions).
-
-           getline.c
-             getline.c wouldn't compile when termcap was selected as
-             the terminal information database. setupterm() was being
-             passed a non-existent variable, in place of the term[]
-             argument of gl_control_strings(). Also if
-             gl_change_terminal() is called with term==NULL, "ansi"
-             is now substituted.
-
-02/07/2001 Version 1.3.3 released.
-
-27/06/2001 mcs@astro.caltech.edu
-
-           getline.c expand.c cplmatch.c
-             Added checks to fprintf() statements that write to the
-             terminal.
-           getline.c
-             Move the cursor to the end of the line before suspending,
-             so that the cursor doesn't get left in the middle of the
-             input line.
-           Makefile.in
-             On systems that don't support shared libraries, the
-             distclean target of make deleted libtecla.h. This has
-             now been fixed.
-           getline.c
-             gl_change_terminal() was being called by gl_change_editor(),
-             with the unwanted side effect that raw terminal modes were
-             stored as those to be restored later, if called by an
-             action function. gl_change_terminal() was being called in
-             this case to re-establish terminal-specific key bindings,
-             so I have just split this part of the function out into
-             a separate function for both gl_change_editor() and
-             gl_change_terminal() to call.
-
-12/06/2001 mcs@astro.caltech.edu
-
-           getline.c
-             Signal handling has been improved. Many more signals are
-             now trapped, and instead of using a simple flag set by a
-             signal handler, race conditions are avoided by blocking
-             signals during most of the gl_get_line() code, and
-             unblocking them via calls to sigsetjmp(), just before
-             attempting to read each new character from the user.
-             The matching use of siglongjmp() in the signal
-             handlers ensures that signals are reblocked correctly
-             before they are handled. In most cases, signals cause
-             gl_get_line() to restore the terminal modes and signal
-             handlers of the calling application, then resend the
-             signal to the application. In the case of SIGINT, SIGHUP,
-             SIGPIPE, and SIGQUIT, if the process still exists after
-             the signals are resent, gl_get_line() immediately returns
-             with appropriate values assigned to errno. If SIGTSTP,
-             SIGTTIN or SIGTTOU signals are received, the process is
-             suspended. If any other signal is received, and the
-             process continues to exist after the signal is resent to
-             the calling application, line input is resumed after the
-             terminal is put back into raw mode, the gl_get_line()
-             signal handling is restored, and the input line redrawn.
-           man/gl_get_line(3)
-             I added a SIGNAL HANDLING section to the gl_get_line()
-             man page, describing the new signal handling features.
-
-21/05/2001 Version 1.3.2 released.
-
-21/05/2001 mcs@astro.caltech.edu
-
-           getline.c
-             When vi-replace-char was used to replace the character at
-             the end of the line, it left the cursor one character to
-             its right instead of on top of it. Now rememdied.
-           getline.c
-             When undoing, to properly emulate vi, the cursor is now
-             left at the leftmost of the saved and current cursor
-             positions.
-           getline.c man3/gl_get_line.3
-             Implemented find-parenthesis (%), delete-to-paren (M-d%),
-             vi-change-to-paren (M-c%), copy-to-paren (M-y%).
-           cplfile.c pcache.c
-             In three places I was comparing the last argument of
-             strncmp() to zero instead of the return value of
-             strncmp().
-
-20/05/2001 mcs@astro.caltech.edu
-
-           getline.c man3/gl_get_line.3
-             Implemented and documented the vi-repeat-change action,
-             bound to the period key. This repeats the last action
-             that modified the input line.
-
-19/05/2001 mcs@astro.caltech.edu
-
-           man3/gl_get_line.3
-             I documented the new action functions and bindings
-             provided by Tim Eliseo, plus the ring-bell action and
-             the new "nobeep" configuration option.
-           getline.c
-             I modified gl_change_editor() to remove and reinstate the
-             terminal settings as well as the default bindings, since
-             these have editor-specific differences. I also modified
-             it to not abort if a key-sequence can't be bound for some
-             reason. This allows the new vi-mode and emacs-mode
-             bindings to be used safely.
-           getline.c
-             When the line was re-displayed on receipt of a SIGWINCH
-             signal, the result wasn't visible until the next
-             character was typed, since a call to fflush() was needed.
-             gl_redisplay_line() now calls gl_flush_output() to remedy
-             this.
-
-17/05/2001 mcs@astro.catlech.edu
-
-           getline.c
-             Under Linux, calling fflush(gl->output_fd) hangs if
-             terminal output has been suspended with ^S. With the
-             tecla library taking responsability for reading the stop
-             and start characters this was a problem, because once
-             hung in fflush(), the keyboard input loop wasn't entered,
-             so the user couldn't type the start character to resume
-             output.  To remedy this, I now have the terminal process
-             these characters, rather than the library.
-
-12/05/2001 mcs@astro.caltech.edu
-
-           getline.c
-             The literal-next action is now implemented as a single
-             function which reads the next character itself.
-             Previously it just set a flag which effected the
-             interpretation of the next character read by the input
-             loop.
-           getline.c
-             Added a ring-bell action function. This is currently
-             unbound to any key by default, but it is used internally,
-             and can be used by users that want to disable any of the
-             default key-bindings.
-
-12/05/2001 Tim Eliseo    (logged here by mcs)
-
-           getline.c
-             Don't reset gl->number until after calling an action
-             function. By looking at whether gl->number is <0 or
-             not, action functions can then tell whether the count
-             that they were passed was explicitly specified by the
-             user, as opposed to being defaulted to 1.
-           getline.c
-             In vi, the position at which input mode is entered
-             acts as a barrier to backward motion for the few
-             backward moving actions that are enabled in input mode.
-             Tim added this barrier to getline.
-           getline.c
-             In gl_get_line() after reading an input line, or
-             having the read aborted by a signal, the sig_atomic_t
-             gl_pending_signal was being compared to zero instead
-             of -1 to see if no signals had been received.
-             gl_get_line() will thus have been calling raise(-1),
-             which luckily didn't seem to do anything. Tim also
-             arranged for errno to be set to EINTR when a signal
-             aborts gl_get_line().
-           getline.c
-             The test in gl_add_char_to_line() for detecting
-             when overwriting a character with a wider character,
-             had a < where it needed a >. Overwriting with a wider
-             character thus overwrote trailing characters. Tim also
-             removed a redundant copy of the character into the
-             line buffer.
-           getline.c
-             gl_cursor_left() and gl->cursor_right() were executing
-             a lot of redundant code, when the existing call to the
-             recently added gl_place_cursor() function, does all that
-             is necessary.
-           getline.c
-             Remove redundant code from backward_kill_line() by
-             re-implimenting in terms of gl_place_cursor() and
-             gl_delete_chars().
-           getline.c
-             gl_forward_delete_char() now records characters in cut
-             buffer when in vi command mode.
-           getline.c
-             In vi mode gl_backward_delete_char() now only deletes
-             up to the point at which input mode was entered. Also
-             gl_delete_chars() restores from the undo buffer when
-             deleting in vi insert mode.
-           getline.c
-             Added action functions, vi-delete-goto-column,
-             vi-change-to-bol, vi-change-line, emacs-mode, vi-mode,
-             vi-forward-change-find, vi-backward-change-find,
-             vi-forward-change-to, vi-backward-change-to,
-             vi-change-goto-col, forward-delete-find, backward-delete-find,
-             forward-delete-to, backward-delete-to,
-             delete-refind, delete-invert-refind, forward-copy-find,
-             backward-copy-find, forward-copy-to, backward-copy-to
-             copy-goto-column, copy-rest-of-line, copy-to-bol, copy-line,
-             history-re-search-forward, history-re-search-backward.
-
-06/05/2001 Version 1.3.1 released.
-
-03/05/2001 mcs@astro.caltech.edu
-
-           configure.in
-             Old versions of GNU ld don't accept version scripts.
-             Under Linux I thus added a test to try out ld with
-             the --version-script argument to see if it works.
-             If not, version scripts aren't used.
-           configure.in
-             My test for versions of Solaris earlier than 7
-             failed when confronted by a three figure version
-             number (2.5.1). Fixed.
-
-30/04/2001 mcs@astro.caltech.edu
-
-           getline.c
-             In vi mode, history-search-backward and
-             history-search-forward weren't doing anything when
-             invoked at the start of an empty line, whereas
-             they should have acted like up-history and down-history.
-           Makefile.in Makefile.rules
-             When shared libraries are being created, the build
-             procedure now arranges for any alternate library
-             links to be created as well, before linking the
-             demos. Without this the demos always linked to the
-             static libraries (which was perfectly ok, but wasn't a
-             good example).
-           Makefile.in Makefile.rules
-             On systems on which shared libraries were being created,
-             if there were no alternate list of names, make would
-             abort due to a Bourne shell 'for' statement that didn't
-             have any arguments. Currently there are no systems who's
-             shared library configurations would trigger this
-             problem.
-           Makefile.rules
-             The demos now relink to take account of changes to the
-             library.
-           configure.in configure
-             When determining whether the reentrant version of the
-             library should be compiled by default, the configure
-             script now attempts to compile a dummy program that
-             includes all of the appropriate system headers and
-             defines _POSIX_C_SOURCE. This should now be a robust test
-             on systems which use C macros to alias these function
-             names to other internal functions.
-           configure.in
-             Under Solaris 2.6 and earlier, the curses library is in
-             /usr/ccs/lib. Gcc wasn't finding this. In addition to
-             remedying this, I had to remove "-z text" from
-             LINK_SHARED under Solaris to get it to successfully
-             compile the shared library against the static curses
-             library.
-           configure.in
-             Under Linux the -soname directive was being used
-             incorrectly, citing the fully qualified name of the
-             library instead of its major version alias. This will
-             unfortunately mean that binaries linked with the 1.2.3
-             and 1.2.4 versions of the shared library won't use
-             later versions of the library unless relinked.
-
-30/04/2001 mcs@astro.caltech.edu
-
-           getline.c
-             In gl_get_input_line(), don't redundantly copy the
-             start_line if start_line == gl->line.
-
-30/04/2001 Version 1.3.0 released.
-
-28/04/2001 mcs@astro.caltech.edu
-
-           configure.in
-             I removed the --no-undefined directive from the Linux
-             LINK_SHARED command. After recent patches to our RedHat
-             7.0 systems ld started reporting some internal symbols of
-             libc as being undefined.  Using nm on libc indicated that
-             the offending symbols are indeed defined, albeit as
-             "common" symbols, so there appears to be a bug in
-             RedHat's ld. Removing this flag allows the tecla shared
-             library to compile, and programs appear to function fine.
-           man3/gl_get_line.3
-             The default key-sequence used to invoke the
-             read-from-file action was incorrectly cited as ^Xi
-             instead of ^X^F.
-
-26/04/2001 mcs@astro.caltech.edu
-
-           getline.c man3/gl_get_line.3
-             A new vi-style editing mode was added. This involved
-             adding many new action functions, adding support for
-             specifying editing modes in users' ~/.teclarc files,
-             writing a higher level cursor motion function to support
-             the different line-end bounds required in vi command
-             mode, and a few small changes to support the fact that vi
-             has two modes, input mode and command mode with different
-             bindings.
-
-             When vi editing mode is enabled, any binding that starts
-             with an escape or a meta character, is interpreted as a
-             command-mode binding, and switches the library to vi
-             command mode if not already in that mode. Once in command
-             mode the first character of all keysequences entered
-             until input mode is re-enabled, are quietly coerced to
-             meta characters before being looked up in the key-binding
-             table. So, for example, in the key-binding table, the
-             standard vi command-mode 'w' key, which moves the cursor
-             one word to the right, is represented by M-w. This
-             emulates vi's dual sets of bindings in a natural way
-             without needing large changes to the library, or new
-             binding syntaxes. Since cursor keys normally emit
-             keysequences which start with escape, it also does
-             something sensible when a cursor key is pressed during
-             input mode (unlike true vi, which gets upset).
-
-             I also added a ^Xg binding for the new list-glob action
-             to both the emacs and vi key-binding tables. This lists
-             the files that match the wild-card expression that
-             precedes it on the command line.
-
-             The function that reads in ~/.teclarc used to tell
-             new_GetLine() to abort if it encountered anything that it
-             didn't understand in this file. It now just reports an
-             error and continues onto the next line.
-           Makefile.in:
-             When passing LIBS=$(LIBS) to recursive invokations of
-             make, quotes weren't included around the $(LIBS) part.
-             This would cause problems if LIBS ever contained more
-             than one word (with the supplied configure script this
-             doesn't happen currently). I added these quotes.
-           expand.c man3/ef_expand_file.3:
-             I wrote a new public function called ef_list_expansions(),
-             to list the matching filenames returned by
-             ef_expand_file().
-
-             I also fixed the example in the man page, which cited
-             exp->file instead of exp->files, and changed the
-             dangerous name 'exp' with 'expn'.
-           keytab.c:
-             Key-binding tables start with 100 elements, and are
-             supposedly incremented in size by 100 elements whenever
-             the a table runs out of space. The realloc arguments to
-             do this were wrong. This would have caused problems if
-             anybody added a lot of personal bindings in their
-             ~/.teclarc file. I only noticed it because the number of
-             key bindings needed by the new vi mode exceeded this
-             number.
-           libtecla.map
-             ef_expand_file() is now reported as having been added in
-             the upcoming 1.3.0 release.
-
-25/03/2001 Markus Gyger  (logged here by mcs)
-
-           Makefile.in:
-             Make symbolic links to alternative shared library names
-             relative instead of absolute.
-           Makefile.rules:
-             The HP-UX libtecla.map.opt file should be made in the
-             compilation directory, to allow the source code directory
-             to be on a readonly filesystem.
-           cplmatch.c demo2.c history.c pcache.c
-             To allow the library to be compiled with a C++ compiler,
-             without generating warnings, a few casts were added where
-             void* return values were being assigned directly to
-             none void* pointer variables.
-
-25/03/2001 mcs@astro.caltech.edu
-
-           libtecla.map:
-             Added comment header to explain the purpose of the file.
-             Also added cpl_init_FileArgs to the list of exported
-             symbols. This symbol is deprecated, and no longer
-             documented, but for backwards compatibility, it should
-             still be exported.
-           configure:
-             I had forgotten to run autoconf before releasing version
-             1.2.4, so I have just belatedly done so.  This enables
-             Markus' changes to "configure.in" documented previously,
-             (see 17/03/2001).
-
-20/03/2001 John Levon   (logged here by mcs)
-
-           libtecla.h
-             A couple of the function prototypes in libtecla.h have
-             (FILE *) argument declarations, which means that stdio.h
-             needs to be included. The header file should be self
-             contained, so libtecla.h now includes stdio.h.
-
-18/03/2001 Version 1.2.4 released.
-
-           README html/index.html configure.in
-             Incremented minor version from 3 to 4.
-
-18/03/2001 mcs@astro.caltech.edu
-
-           getline.c
-             The fix for the end-of-line problem that I released a
-             couple of weeks ago, only worked for the first line,
-             because I was handling this case when the cursor position
-             was equal to the last column, rather than when the cursor
-             position modulo ncolumn was zero.
-           Makefile.in Makefile.rules
-             The demos are now made by default, their rules now being
-             int Makefile.rules instead of Makefile.in.
-           INSTALL
-             I documented how to compile the library in a different
-             directory than the distribution directory.
-             I also documented features designed to facilitate
-             configuring and building the library as part of another
-             package.
-
-17/03/2001 Markus Gyger (logged here by mcs)
-
-           getline.c
-             Until now cursor motions were done one at a time. Markus
-             has added code to make use the of the terminfo capability
-             that moves the cursor by more than one position at a
-             time. This greatly improves performance when editing near
-             the start of long lines.
-           getline.c
-             To further improve performance, Markus switched from
-             writing one character at a time to the terminal, using
-             the write() system call, to using C buffered output
-             streams. The output buffer is only flushed when
-             necessary.
-           Makefile.rules Makefile.in configure.in
-             Added support for compiling for different architectures
-             in different directories. Simply create another directory
-             and run the configure script located in the original
-             directory.
-           Makefile.in configure.in libtecla.map
-             Under Solaris, Linux and HP-UX, symbols that are to be
-             exported by tecla shared libraries are explicitly specified
-             via symbol map files. Only publicly documented functions
-             are thus visible to applications.
-           configure.in
-             When linking shared libraries under Solaris SPARC,
-             registers that are reserved for applications are marked
-             as off limits to the library, using -xregs=no%appl when
-             compiling with Sun cc, or -mno-app-regs when compiling
-             with gcc. Also removed -z redlocsym for Solaris, which
-             caused problems under some releases of ld.
-           homedir.c  (after minor changes by mcs)
-             Under ksh, ~+ expands to the current value of the ksh
-             PWD environment variable, which contains the path of
-             the current working directory, including any symbolic
-             links that were traversed to get there. The special
-             username "+" is now treated equally by tecla, except
-             that it substitutes the return value of getcwd() if PWD
-             either isn't set, or if it points at a different
-             directory than that reported by getcwd().
-
-08/03/2001 Version 1.2.3 released.
-
-08/03/2001 mcs@astro.caltech.edu
-
-           getline.c
-             On compiling the library under HP-UX for the first time
-             I encountered and fixed a couple of bugs:
-
-             1. On all systems except Solaris, the callback function
-                required by tputs() takes an int argument for the
-                character that is to be printed. Under Solaris it
-                takes a char argument. The callback function was
-                passing this argument, regardless of type, to write(),
-                which wrote the first byte of the argument.  This was
-                fine under Solaris and under little-endian systems,
-                because the first byte contained the character to be
-                written, but on big-endian systems, it always wrote
-                the zero byte at the other end of the word. As a
-                result, no control characters were being written to
-                the terminal.
-             2. While attempting to start a newline after the user hit
-                enter, the library was outputting the control sequence
-                for moving the cursor down, instead of the newline
-                character. On many systems the control sequence for
-                moving the cursor down happends to be a newline
-                character, but under HP-UX it isn't. The result was
-                that no new line was being started under HP-UX.
-
-04/03/2001 mcs@astro.caltech.edu
-
-           configure.in Makefile.in Makefile.stub configure config.guess
-           config.sub Makefile.rules install-sh PORTING README INSTALL
-             Configuration and compilation of the library is now
-             performed with the help of an autoconf configure
-             script. In addition to relieving the user of the need to
-             edit the Makefile, this also allows automatic compilation
-             of the reentrant version of the library on platforms that
-             can handle it, along with the creation of shared
-             libraries where configured. On systems that aren't known
-             to the configure script, just the static tecla library is
-             compiled. This is currently the case on all systems
-             except Linux, Solaris and HP-UX. In the hope that
-             installers will provide specific conigurations for other
-             systems, the configure.in script is heavily commented,
-             and instructions on how to use are included in a new
-             PORTING file.
-
-24/02/2001 Version 1.2b released.
-
-22/02/2001 mcs@astro.caltech.edu
-
-           getline.c
-             It turns out that most terminals, but not all, on writing
-             a character in the rightmost column, don't wrap the
-             cursor onto the next line until the next character is
-             output. This library wasn't aware of this and thus if one
-             tried to reposition the cursor from the last column,
-             gl_get_line() thought that it was moving relative to a
-             point on the next line, and thus moved the cursor up a
-             line. The fix was to write one extra character when in
-             the last column to force the cursor onto the next line,
-             then backup the cursor to the start of the new line.
-           getline.c
-             On terminal initialization, the dynamic LINES and COLUMNS
-             environment variables were ignored unless
-             terminfo/termcap didn't return sensible dimensions. In
-             practice, when present they should override the static
-             versions in the terminfo/termcap databases. This is the
-             new behavior. In reality this probably won't have caused
-             many problems, because a SIGWINCH signal which informs of
-             terminal size changes is sent when the terminal is
-             opened, so the dimensions established during
-             initialization quickly get updated on most systems.
-
-18/02/2001 Version 1.2a released.
-
-18/02/2001 mcs@astro.caltech.edu
-
-           getline.c
-             Three months ago I moved the point at which termios.h
-             was included in getline.c. Unfortunately, I didn't notice
-             that this moved it to after the test for TIOCGWINSZ being
-             defined. This resulted in SIGWINCH signals not being
-             trapped for, and thus terminal size changes went
-             unnoticed. I have now moved the test to after the 
-             inclusion of termios.h.
-
-12/02/2001 Markus Gyger     (described here by mcs)
-
-           man3/pca_lookup_file.3 man3/gl_get_line.3
-           man3/ef_expand_file.3 man3/cpl_complete_word.3
-             In the 1.2 release of the library, all functions in the
-             library were given man pages. Most of these simply
-             include one of the above 4 man pages, which describe the
-             functions while describing the modules that they are in.
-             Markus added all of these function names to the lists in
-             the "NAME" headers of the respective man pages.
-             Previously only the primary function of each module was
-             named there.
-
-11/02/2001 mcs@astro.caltech.edu
-
-           getline.c
-             On entering a line that wrapped over two or more
-             terminal, if the user pressed enter when the cursor
-             wasn't on the last of the wrapped lines, the text of the
-             wrapped lines that followed it got mixed up with the next
-             line written by the application, or the next input
-             line. Somehow this slipped through the cracks and wasn't
-             noticed until now. Anyway, it is fixed now.
-
-09/02/2001 Version 1.2 released.
-
-04/02/2001 mcs@astro.caltech.edu
-
-           pcache.c libtecla.h
-             With all filesystems local, demo2 was very fast to start
-             up, but on a Sun system with one of the target
-             directories being on a remote nfs mounted filesystem, the
-             startup time was many seconds. This was due to the
-             executable selection callback being applied to all files
-             in the path at startup. To avoid this, all files are now
-             included in the cache, and the application specified
-             file-selection callback is only called on files as they
-             are matched. Whether the callback rejected or accepted
-             them is then cached so that the next time an already
-             checked file is looked at, the callback doesn't have to
-             be called. As a result, startup is now fast on all
-             systems, and since usually there are only a few matching
-             file completions at a time, the delay during completion
-             is also usually small. The only exception is if the user
-             tries to complete an empty string, at which point all
-             files have to be checked. Having done this once, however,
-             doing it again is fast.
-           man3/pca_lookup_file.3
-             I added a man page documenting the new PathCache module.
-           man3/.3
-             I have added man pages for all of the functions in each
-             of the modules. These 1-line pages use the .so directive
-             to redirect nroff to the man page of the parent module.
-           man Makefile update_html
-             I renamed man to man3 to make it easier to test man page
-             rediction, and updated Makefile and update_html
-             accordingly. I also instructed update_html to ignore
-             1-line man pages when making html equivalents of the man
-             pages.
-           cplmatch.c
-             In cpl_list_completions() the size_t return value of
-             strlen() was being used as the length argument of a "%*s"
-             printf directive. This ought to be an int, so the return
-             value of strlen() is now cast to int. This would have
-             caused problems on architectures where the size of a
-             size_t is not equal to the size of an int.
-
-02/02/2001 mcs@astro.caltech.edu
-
-           getline.c
-             Under UNIX, certain terminal bindings are set using the
-             stty command. This, for example, specifies which control
-             key generates a user-interrupt (usually ^C or ^Y). What I
-             hadn't realized was that ASCII NUL is used as the way to
-             specify that one of these bindings is unset. I have now
-             modified the code to skip unset bindings, leaving the
-             corresponding action bound to the built-in default, or a
-             user provided binding.
-
-28/01/2001 mcs@astro.caltech.edu
-
-           pcache.c libtecla.h
-             A new module was added which supports searching for files
-             in any colon separated list of directories, such as the
-             unix execution PATH environment variable. Files in these
-             directories, after being individually okayed for
-             inclusion via an application provided callback, are
-             cached in a PathCache object. You can then look up the
-             full pathname of a given filename, or you can use the
-             provided completion callback to list possible completions
-             in the path-list. The contents of relative directories,
-             such as ".", obviously can't be cached, so these
-             directories are read on the fly during lookups and
-             completions. The obvious application of this facility is
-             to provide Tab-completion of commands, and thus a
-             callback to place executable files in the cache, is
-             provided.
-           demo2.c
-             This new program demonstrates the new PathCache
-             module. It reads and processes lines of input until the
-             word 'exit' is entered, or C-d is pressed. The default
-             tab-completion callback is replaced with one which at the
-             start of a line, looks up completions of commands in the
-             user's execution path, and when invoked in other parts of
-             the line, reverts to normal filename completion. Whenever
-             a new line is entered, it extracts the first word on the
-             line, looks it up in the user's execution path to see if
-             it corresponds to a known command file, and if so,
-             displays the full pathname of the file, along with the
-             remaining arguments.
-           cplfile.c
-             I added an optional pair of callback function/data
-             members to the new cpl_file_completions() configuration
-             structure. Where provided, this callback is asked
-             on a file-by-file basis, which files should be included
-             in the list of file completions. For example, a callback
-             is provided for listing only completions of executable
-             files.
-           cplmatch.c
-             When listing completions, the length of the type suffix
-             of each completion wasn't being taken into account
-             correctly when computing the column widths. Thus the
-             listing appeared ragged sometimes. This is now fixed.
-           pathutil.c
-             I added a function for prepending a string to a path,
-             and another for testing whether a pathname referred to
-             an executable file.
-
-28/01/2001 mcs@astro.caltech.edu
-
-           libtecla.h cplmatch.c man/cpl_complete_word.3
-             The use of a publically defined structure to configure
-             the cpl_file_completions() callback was flawed, so a new
-             approach has been designed, and the old method, albeit
-             still supported, is no longer documented in the man
-             pages. The definition of the CplFileArgs structure in
-             libtecla.h is now accompanied by comments warning people
-             not to modify it, since modifications could break
-             applications linked to shared versions of the tecla
-             library. The new method involves an opaque CplFileConf
-             object, instances of which are returned by a provided
-             constructor function, configured with provided accessor
-             functions, and when no longer needed, deleted with a
-             provided destructor function. This is documented in the
-             cpl_complete_word man page. The cpl_file_completions()
-             callback distinguishes what type of configuration
-             structure it has been sent by virtue of a code placed at
-             the beginning of the CplFileConf argument by its
-             constructor.
-
-04/01/2001 mcs@astro.caltech.edu (Release of version 1.1j)
-
-           getline.c
-             I added upper-case bindings for the default meta-letter
-             keysequences such as M-b. They thus continue to work
-             when the user has caps-lock on.
-           Makefile
-             I re-implemented the "install" target in terms of new
-             install_lib, install_inc and install_man targets. When
-             distributing the library with other packages, these new
-             targets allows for finer grained control of the
-             installation process.
-
-30/12/2000 mcs@astro.caltech.edu
-
-           getline.c man/gl_get_line.3
-             I realized that the recall-history action that I
-             implemented wasn't what Markus had asked me for. What he
-             actually wanted was for down-history to continue going
-             forwards through a previous history recall session if no
-             history recall session had been started while entering
-             the current line. I have thus removed the recall-history
-             action and modified the down-history action function
-             accordingly.
-
-24/12/2000 mcs@astro.caltech.edu
-
-           getline.c
-             I modified gl_get_line() to allow the previously returned
-             line to be passed in the start_line argument.
-           getline.c man/gl_get_line.3
-             I added a recall-history action function, bound to M^P.
-             This recalls the last recalled history line, regardless
-             of whether it was from the current or previous line.
-
-13/12/2000 mcs@astro.caltech.edu (Release of version 1.1i)
-
-           getline.c history.h history.c man/gl_get_line.3
-             I implemented the equivalent of the ksh Operate action. I
-             have named the tecla equivalent "repeat-history". This
-             causes the line that is to be edited to returned, and
-             arranges for the next most recent history line to be
-             preloaded on the next call to gl_get_line(). Repeated
-             invocations of this action thus result in successive
-             history lines being repeated - hence the
-             name. Implementing the ksh Operate action was suggested
-             by Markus Gyger. In ksh it is bound to ^O, but since ^O
-             is traditionally bound by the default terminal settings,
-             to stop-output, I have bound the tecla equivalent to M-o.
-
-01/12/2000 mcs@astro.caltech.edu (Release of version 1.1h)
-
-           getline.c keytab.c keytab.h man/gl_get_line.3
-             I added a digit-argument action, to allow repeat
-             counts for actions to be entered. As in both tcsh
-             and readline, this is bound by default to each of
-             M-0, M-1 through to M-9, the number being appended
-             to the current repeat count. Once one of these has been
-             pressed, the subsequent digits of the repeat count can be
-             typed with or without the meta key pressed. It is also
-             possible to bind digit-argument to other keys, with or
-             without a numeric final keystroke. See man page for
-             details.
-
-           getline.c man/gl_get_line.3
-             Markus noted that my choice of M-< for the default
-             binding of read-from-file, could be confusing, since
-             readline binds this to beginning-of-history. I have
-             thus rebound it to ^X^F (ie. like find-file in emacs).
-
-           getline.c history.c history.h man/gl_get_line.3
-             I have now implemented equivalents of the readline
-             beginning-of-history and end-of-history actions.
-             These are bound to M-< and M-> respectively.
-
-           history.c history.h
-             I Moved the definition of the GlHistory type, and
-             its subordinate types from history.h to history.c.
-             There is no good reason for any other module to
-             have access to the innards of this structure.
-
-27/11/2000 mcs@astro.caltech.edu (Release of version 1.1g)
-
-           getline.c man/gl_get_line.3
-             I added a "read-from-file" action function and bound it
-             by default to M-<. This causes gl_get_line() to
-             temporarily return input from the file who's name
-             precedes the cursor.
-             
-26/11/2000 mcs@astro.caltech.edu
-
-           getline.c keytab.c keytab.h man/gl_get_line.3
-             I have reworked some of the keybinding code again.
-
-             Now, within key binding strings, in addition to the
-             previously existing notation, you can now use M-a to
-             denote meta-a, and C-a to denote control-a. For example,
-             a key binding which triggers when the user presses the
-             meta key, the control key and the letter [
-             simultaneously, can now be denoted by M-C-[, or M-^[ or
-             \EC-[ or \E^[.
-
-             I also updated the man page to use M- instead of \E in
-             the list of default bindings, since this looks cleaner.
-
-           getline.c man/gl_get_line.3
-             I added a copy-region-as-kill action function and
-             gave it a default binding to M-w.
-
-22/11/2000 mcs@astro.caltech.edu
-
-           *.c
-             Markus Gyger sent me a copy of a previous version of
-             the library, with const qualifiers added in appropriate
-             places. I have done the same for the latest version.
-             Among other things, this gets rid of the warnings
-             that are generated if one tells the compiler to
-             const qualify literal strings.
-
-           getline.c getline.h glconf.c
-             I have moved the contents of glconf.c and the declaration
-             of the GetLine structure into getline.c. This is cleaner,
-             since now only functions in getline.c can mess with the
-             innards of GetLine objects. It also clears up some problems
-             with system header inclusion order under Solaris, and also
-             the possibility that this might result in inconsistent
-             system macro definitions, which in turn could cause different
-             declarations of the structure to be seen in different files.
-
-           hash.c
-             I wrote a wrapper function to go around strcmp(), such that
-             when hash.c is compiled with a C++ compiler, the pointer
-             to the wrapper function is a C++ function pointer.
-             This makes it compatible with comparison function pointer
-             recorded in the hash table.
-
-           cplmatch.c getline.c libtecla.h
-             Markus noted that the Sun C++ compiler wasn't able to
-             match up the declaration of cpl_complete_word() in
-             libtecla.h, where it is surrounded by a extern "C" {}
-             wrapper, with the definition of this function in
-             cplmatch.c. My suspicion is that the compiler looks not
-             only at the function name, but also at the function
-             arguments to see if two functions match, and that the
-             match_fn() argument, being a fully blown function pointer
-             declaration, got interpetted as that of a C function in
-             one case, and a C++ function in the other, thus
-             preventing a match.
-
-             To fix this I now define a CplMatchFn typedef in libtecla.h,
-             and use this to declare the match_fn callback.
-
-20/11/2000 (Changes suggested by Markus Gyger to support C++ compilers):
-           expand.c
-             Renamed a variable called "explicit" to "xplicit", to
-             avoid conflicts when compiling with C++ compilers.
-           *.c
-             Added explicit casts when converting from (void *) to
-             other pointer types. This isn't needed in C but it is
-             in C++.
-           getline.c
-             tputs() has a strange declaration under Solaris. I was
-             enabling this declaration when the SPARC feature-test
-             macro was set. Markus changed the test to hinge on the
-             __sun and __SVR4 macros.
-           direader.c glconf.c stringrp.c
-             I had omitted to include string.h in these two files.
-
-           Markus also suggested some other changes, which are still
-           under discussion. With the just above changes however, the
-           library compiles without complaint using g++.
-
-19/11/2000 mcs@astro.caltech.edu
-           getline.h getline.c keytab.c keytab.h glconf.c
-           man/gl_get_line.3
-             I added support for backslash escapes (include \e
-             for the keyboard escape key) and literal binary
-             characters to the characters allowed within key sequences
-             of key bindings.
-
-           getline.h getline.c keytab.c keytab.h glconf.c
-           man/gl_get_line.3
-             I introduced symbolic names for the arrow keys, and
-             modified the library to use the cursor key sequences
-             reported by terminfo/termcap in addition to the default
-             ANSI ones. Anything bound to the symbolically named arrow
-             keys also gets bound to the default and terminfo/termcap
-             cursor key sequences. Note that under Solaris
-             terminfo/termcap report the properties of hardware X
-             terminals when TERM is xterm instead of the terminal
-             emulator properties, and the cursor keys on these two
-             systems generate different key sequences. This is an
-             example of why extra default sequences are needed.
-
-           getline.h getline.c keytab.c
-             For some reason I was using \e to represent the escape
-             character. This is supported by gcc, which thus doesn't
-             emit a warning except with the -pedantic flag, but isn't
-             part of standard C. I now use a macro to define escape
-             as \033 in getline.h, and this is now used wherever the
-             escape character is needed.
-
-17/11/2000 mcs@astro.caltech.edu (Release of version 1.1d)
-
-           getline.c, man/gl_get_line(3), html/gl_get_line.html
-             In tcsh ^D is bound to a function which does different
-             things depending on where the cursor is within the input
-             line. I have implemented its equivalent in the tecla
-             library. When invoked at the end of the line this action
-             function displays possible completions. When invoked on
-             an empty line it causes gl_get_line() to return NULL,
-             thus signalling end of input. When invoked within a line
-             it invokes forward-delete-char, as before. The new action
-             function is called del-char-or-list-or-eof.
-
-           getline.c, man/gl_get_line(3), html/gl_get_line.html
-             I found that the complete-word and expand-file actions
-             had underscores in their names instead of hyphens. This
-             made them different from all other action functions, so I
-             have changed the underscores to hyphens.
-
-           homedir.c
-             On SCO UnixWare while getpwuid_r() is available, the
-             associated _SC_GETPW_R_SIZE_MAX macro used by sysconf()
-             to find out how big to make the buffer to pass to this
-             function to cater for any password entry, doesn't
-             exist. I also hadn't catered for the case where sysconf()
-             reports that this limit is indeterminate. I have thus
-             change the code to substitute a default limit of 1024 if
-             either the above macro isn't defined or if sysconf() says
-             that the associated limit is indeterminate.
-           
-17/11/2000 mcs@astro.caltech.edu (Release of version 1.1c)
-
-           getline.c, getline.h, history.c, history.h
-             I have modified the way that the history recall functions
-             operate, to make them better emulate the behavior of
-             tcsh. Previously the history search bindings always
-             searched for the prefix that preceded the cursor, then
-             left the cursor at the same point in the line, so that a
-             following search would search using the same prefix. This
-             isn't how tcsh operates. On finding a matching line, tcsh
-             puts the cursor at the end of the line, but arranges for
-             the followup search to continue with the same prefix,
-             unless the user does any cursor motion or character
-             insertion operations in between, in which case it changes
-             the search prefix to the new set of characters that are
-             before the cursor. There are other complications as well,
-             which I have attempted to emulate. As far as I can
-             tell, the tecla history recall facilities now fully
-             emulate those of tcsh.
-
-16/11/2000 mcs@astro.caltech.edu (Release of version 1.1b)
-
-           demo.c:
-             One can now quit from the demo by typing exit.
-
-           keytab.c:
-             The first entry of the table was getting deleted
-             by _kt_clear_bindings() regardless of the source
-             of the binding. This deleted the up-arrow binding.
-             Symptoms noted by gazelle@yin.interaccess.com.
-
-           getline.h:
-             Depending on which system include files were include
-             before the inclusion of getline.h, SIGWINCH and
-             TIOCGWINSZ might or might not be defined. This resulted
-             in different definitions of the GetLine object in
-             different files, and thus some very strange bugs! I have
-             now added #includes for the necessary system header files
-             in getline.h itself. The symptom was that on creating a
-             ~/.teclarc file, the demo program complained of a NULL
-             argument to kt_set_keybinding() for the first line of the
-             file.
-
-15/11/2000 mcs@astro.caltech.edu (Release of version 1.1a)
-
-           demo.c:
-             I had neglected to check the return value of
-             new_GetLine() in the demo program. Oops.
-
-           getline.c libtecla.h:
-             I wrote gl_change_terminal(). This allows one to change to
-             a different terminal or I/O stream, by specifying the
-             stdio streams to use for input and output, along with the
-             type of terminal that they are connected to.
-
-           getline.c libtecla.h:
-             Renamed GetLine::isterm to GetLine::is_term. Standard
-             C reserves names that start with "is" followed by
-             alphanumeric characters, so this avoids potential
-             clashes in the future.
-
-           keytab.c keytab.h
-             Each key-sequence can now have different binding
-             functions from different sources, with the user provided
-             binding having the highest precedence, followed by the
-             default binding, followed by any terminal specific
-             binding. This allows gl_change_terminal() to redefine the
-             terminal-specific bindings each time that
-             gl_change_terminal() is called, without overwriting the
-             user specified or default bindings. In the future, it will
-             also allow for reconfiguration of user specified
-             bindings after the call to new_GetLine(). Ie. deleting a
-             user specified binding should reinstate any default or
-             terminal specific binding.
-
-           man/cpl_complete_word.3 html/cpl_complete_word.html
-           man/ef_expand_file.3    html/ef_expand_file.html
-           man/gl_get_line.3       html/gl_get_line.html
-             I added sections on thread safety to the man pages of the
-             individual modules.
-
-           man/gl_get_line.3       html/gl_get_line.html
-             I documented the new gl_change_terminal() function.
-
-           man/gl_get_line.3       html/gl_get_line.html
-             In the description of the ~/.teclarc configuration file,
-             I had omitted the 'bind' command word in the example
-             entry. I have now remedied this.
-
diff --git a/libtecla-1.4.1/html/cpl_complete_word.html b/libtecla-1.4.1/html/cpl_complete_word.html deleted file mode 100644 index 063359d..0000000 --- a/libtecla-1.4.1/html/cpl_complete_word.html +++ /dev/null @@ -1,423 +0,0 @@ - -Manual Page - - -
-

NAME

-     cpl_complete_word,   cfc_file_start,    cfc_literal_escapes,
-     cfc_set_check_fn,  cpl_add_completion, cpl_file_completions,
-     cpl_last_error,   cpl_list_completions,    cpl_record_error,
-     del_CplFileConf,     del_WordCompletion,    new_CplFileConf,
-     new_WordCompletion - lookup possible completions for a word
-
-

SYNOPSIS

-     #include <stdio.h>
-     #include <libtecla.h>
-
-     WordCompletion *new_WordCompletion(void);
-
-     WordCompletion *del_WordCompletion(WordCompletion *cpl);
-
-     #define CPL_MATCH_FN(fn) int (fn)(WordCompletion *cpl, \
-                                       void *data, \
-                                       const char *line, \
-                                       int word_end)
-     typedef CPL_MATCH_FN(CplMatchFn);
-
-     CPL_MATCH_FN(cpl_file_completions);
-
-     CplMatches *cpl_complete_word(WordCompletion *cpl,
-                                   const char *line,
-                                   int word_end, void *data,
-                                   CplMatchFn *match_fn);
-
-     int cpl_list_completions(CplMatches *result, FILE *fp,
-                              int term_width);
-
-     int cpl_add_completion(WordCompletion *cpl,
-                            const char *line, int word_start,
-                            int word_end, const char *suffix,
-                            const char *type_suffix,
-                            const char *cont_suffix);
-
-     void cpl_record_error(WordCompletion *cpl,
-                           const char *errmsg);
-
-     const char *cpl_last_error(WordCompletion *cpl);
-
-
-
-

DESCRIPTION

-     The  cpl_complete_word()  function  is  part  of  the  tecla
-     library (see the libtecla(3) man page). It is usually called
-     behind the scenes by gl_get_line(3), but can also be  called
-     separately.
-
-     Given an input line containing an incomplete word to be com-
-     pleted,  it  calls a user-provided callback function (or the
-     provided file-completion callback function) to look  up  all
-     possible  completion  suffixes  for  that word. The callback
-     function is expected to look backward in the line,  starting
-     from the specified cursor position, to find the start of the
-     word to be completed, then to look up all  possible  comple-
-     tions of that word and record them, one at a time by calling
-     cpl_add_completion().
-
-
-     Descriptions of the functions of this module are as follows:
-
-       CompleteWord *new_CompleteWord(void)
-
-     This  function   creates   the   resources   used   by   the
-     cpl_complete_word()  function.  In  particular, it maintains
-     the memory that is used to return  the  results  of  calling
-     cpl_complete_word().
-
-       CompleteWord *del_CompleteWord(CompleteWord *cpl)
-
-     This function deletes the resources that were returned by  a
-     previous  call to new_CompleteWord(). It always returns NULL
-     (ie. a deleted object). It does nothing if the cpl  argument
-     is NULL.
-
-     The callback functions  which  lookup  possible  completions
-     should be defined with the following macro (which is defined
-     in libtecla.h).
-
-       #define CPL_MATCH_FN(fn) int (fn)(WordCompletion *cpl, \
-                                         void *data, \
-                                         const char *line, \
-                                         int word_end)
-
-     Functions of this type are  called  by  cpl_complete_word(),
-     and all of the arguments of the callback are those that were
-     passed to said function. In particular,  the  line  argument
-     contains the input line containing the word to be completed,
-     and word_end is the index of the character that follows  the
-     last  character  of  the incomplete word within this string.
-     The callback is expected to look backwards from word_end for
-     the start of the incomplete word. What constitutes the start
-     of a word clearly depends on the application,  so  it  makes
-     sense  for  the callback to take on this responsibility. For
-     example, the  builtin  filename  completion  function  looks
-     backwards  until it hits an unescaped space, or the start of
-     the line.  Having found the start of the word, the  callback
-     should  then  lookup  all possible completions of this word,
-     and  record  each   completion   via   separate   calls   to
-     cpl_add_completion().  If  the  callback  needs access to an
-     application-specific symbol table, it can pass  it  and  any
-     other  data  that  it  needs,  via  the  data argument. This
-     removes any need for globals.
-
-     The callback function should return 0 if no errors occur. On
-     failure it should return 1, and register a terse description
-     of the error by calling cpl_record_error().
-
-       void cpl_record_error(WordCompletion *cpl,
-                             const char *errmsg);
-
-     The    last    error    message    recorded    by    calling
-     cpl_record_error(),  can  subsequently be queried by calling
-     cpl_last_error(), as described later.
-
-       int cpl_add_completion(WordCompletion *cpl,
-                              const char *line, int word_start,
-                              int word_end, const char *suffix,
-                              const char *type_suffix,
-                              const char *cont_suffix);
-
-     The cpl_add_completion() function is  called  zero  or  more
-     times  by  the  completion  callback function to record each
-     possible completion in the specified WordCompletion  object.
-     These    completions    are    subsequently    returned   by
-     cpl_complete_word(), as described later. The cpl, line,  and
-     word_end  arguments  should be those that were passed to the
-     callback function. The word_start  argument  should  be  the
-     index  within the input line string of the start of the word
-     that is being completed. This should  equal  word_end  if  a
-     zero-length  string  is being completed. The suffix argument
-     is the string that would have to be appended to  the  incom-
-     plete  word  to complete it.  If this needs any quoting (eg.
-     the addition of backslashes before special charaters) to  be
-     valid  within  the  displayed  input  line,  this  should be
-     included. A copy of the suffix string  is  allocated  inter-
-     nally,  so  there  is  no  need to maintain your copy of the
-     string after cpl_add_completion() returns.
-
-     Note that in the array of  possible  completions  which  the
-     cpl_complete_word() function returns, the suffix recorded by
-     cpl_add_completion() is listed along with the concatentation
-     of  this  suffix  with the word that lies between word_start
-     and word_end in the input line.
-
-     The type_suffix argument specifies an optional string to  be
-     appended  to  the completion if it is displayed as part of a
-     list of completions by cpl_list_completions(). The intention
-     is  that  this indicate to the user the type of each comple-
-     tion. For example, the file  completion  function  places  a
-     directory  separator after completions that are directories,
-     to indicate their nature to the user. Similary, if the  com-
-     pletion were a function, you could indicate this to the user
-     by setting type_suffix to "()". Note  that  the  type_suffix
-     string  isn't  copied,  so  if  the argument isn't a literal
-     string between speech marks, be sure that the string remains
-     valid   for   at   least   as   long   as   the  results  of
-     cpl_complete_word() are needed.
-
-     The cont_suffix is a continuation suffix to  append  to  the
-     completed word in the input line if this is the only comple-
-     tion. This is something that isn't part  of  the  completion
-     itself, but that gives the user an indication about how they
-     might continue to extend the token.  For example, the  file-
-     completion  callback  function adds a directory separator if
-     the completed word is a directory.  If  the  completed  word
-     were  a  function  name, you could similarly aid the user by
-     arranging for an open parenthesis to be appended.
-
-       CplMatches *cpl_complete_word(WordCompletion *cpl,
-                                     const char *line,
-                                     int word_end, void *data,
-                                     CplMatchFn *match_fn);
-
-     The cpl_complete_word() is normally called behind the scenes
-     by  gl_get_line(3), but can also be called separately if you
-     separately allocate a  WordCompletion  object.  It  performs
-     word  completion, as described at the beginning of this sec-
-     tion. Its first argument is  a  resource  object  previously
-     returned  by  new_CompleteWord().   The line argument is the
-     input line string, containing the word to be completed.  The
-     word_end argument contains the index of the character in the
-     input line, that just follows the last character of the word
-     to  be  completed. When called by gl_get_line(), this is the
-     character over which the  user  pressed  TAB.  The  match_fn
-     argument  is  the  function pointer of the callback function
-     which will lookup  possible  completions  of  the  word,  as
-     described  above,  and  the data argument provides a way for
-     the application to pass arbitrary data to the callback func-
-     tion.
-
-     If no errors occur, the cpl_complete_word() function returns
-     a  pointer to a CplMatches container, as defined below. This
-     container is allocated as part of the cpl  object  that  was
-     passed  to cpl_complete_word(), and will thus change on each
-     call which uses the same cpl argument.
-
-       typedef struct {
-         char *completion;        /* A matching completion */
-                                  /*  string */
-         char *suffix;            /* The part of the */
-                                  /*  completion string which */
-                                  /*  would have to be */
-                                  /*  appended to complete the */
-                                  /*  original word. */
-         const char *type_suffix; /* A suffix to be added when */
-                                  /*  listing completions, to */
-                                  /*  indicate the type of the */
-                                  /*  completion. */
-       } CplMatch;
-
-       typedef struct {
-         char *suffix;            /* The common initial part */
-                                  /*  of all of the completion */
-                                  /*  suffixes. */
-         const char *cont_suffix; /* Optional continuation */
-                                  /*  string to be appended to */
-                                  /*  the sole completion when */
-                                  /*  nmatch==1. */
-         CplMatch *matches;       /* The array of possible */
-                                  /*  completion strings, */
-                                  /*  sorted into lexical */
-                                  /*  order. */
-         int nmatch;              /* The number of elements in */
-                                  /*  the above matches[] */
-                                  /*  array. */
-       } CplMatches;
-
-     If an error occurs  during  completion,  cpl_complete_word()
-     returns  NULL. A description of the error can be acquired by
-     calling the cpl_last_error() function.
-
-       const char *cpl_last_error(WordCompletion *cpl);
-
-     The cpl_last_error() function returns a terse description of
-     the   error   which   occurred   on   the   last   call   to
-     cpl_complete_word() or cpl_add_completion().
-
-       int cpl_list_completions(CplMatches *result, FILE *fp,
-                                int terminal_width);
-
-     When the cpl_complete_word() function returns multiple  pos-
-     sible  completions,  the cpl_list_completions() function can
-     be called upon to list them, suitably  arranged  across  the
-     available  width  of  the  terminal.  It  arranges  for  the
-     displayed columns of completions to all have the same width,
-     set   by   the  longest  completion.  It  also  appends  the
-     type_suffix strings that were recorded with each completion,
-     thus indicating their types to the user.
-
-
-

THE BUILT-IN FILENAME-COMPLETION CALLBACK

-     By default the gl_get_line(3) function, passes the following
-     completion  callback  function  to cpl_complete_word(). This
-     function can also be used separately, either by  sending  it
-     to  cpl_complete_word(), or by calling it directly from your
-     own completion callback function.
-
-       CPL_MATCH_FN(cpl_file_completions);
-
-     Certain aspects of the behavior  of  this  callback  can  be
-     changed  via  its  data  argument. If you are happy with its
-     default behavior you can pass NULL in this argument.  Other-
-     wise  it should be a pointer to a CplFileConf object, previ-
-     ously allocated by calling new_CplFileConf().
-
-       CplFileConf *new_CplFileConf(void);
-
-     CplFileConf objects encapsulate the configuration parameters
-     of cpl_file_completions(). These parameters, which start out
-     with default values, can be changed by calling the  accessor
-     functions described below.
-
-     By default,  the  cpl_file_completions()  callback  function
-     searches  backwards for the start of the filename being com-
-     pleted, looking for the first un-escaped space or the  start
-     of  the input line. If you wish to specify a different loca-
-     tion, call cfc_file_start() with  the  index  at  which  the
-     filename  starts  in  the input line. Passing start_index=-1
-     re-enables the default behavior.
-
-       void cfc_file_start(CplFileConf *cfc, int start_index);
-
-     By default, when cpl_file_completions() looks at a  filename
-     in  the input line, each lone backslash in the input line is
-     interpreted as being a special character which  removes  any
-     special significance of the character which follows it, such
-     as a space which should be taken as  part  of  the  filename
-     rather  than  delimiting  the  start  of the filename. These
-     backslashes are thus ignored while looking for  completions,
-     and  subsequently  added  before  spaces,  tabs  and literal
-     backslashes in the list of completions.  To  have  unescaped
-     backslashes    treated    as    normal    characters,   call
-     cfc_literal_escapes() with a non-zero value in  its  literal
-     argument.
-
-       void cfc_literal_escapes(CplFileConf *cfc, int literal);
-
-     By default, cpl_file_completions() reports all  files  who's
-     names  start with the prefix that is being completed. If you
-     only want a selected subset of these files to be reported in
-     the list of completions, you can arrange this by providing a
-     callback function which takes the full pathname of  a  file,
-     and  returns  0  if  the file should be ignored, or 1 if the
-     file should be included  in  the  list  of  completions.  To
-     register  such a function for use by cpl_file_completions(),
-     call cfc_set_check_fn(), and pass it a pointer to the  func-
-     tion,  together  with  a  pointer to any data that you would
-     like passed to this callback whenever  it  is  called.  Your
-     callback can make its decisions based on any property of the
-     file, such as the filename itself, whether the file is read-
-     able, writable or executable, or even based on what the file
-     contains.
-
-       #define CPL_CHECK_FN(fn) int (fn)(void *data, \
-                                         const char *pathname)
-       typedef CPL_CHECK_FN(CplCheckFn);
-
-       void cfc_set_check_fn(CplFileConf *cfc,
-                             CplCheckFn *chk_fn, void *chk_data);
-
-     The cpl_check_exe() function is a provided callback  of  the
-     above  type, for use with cpl_file_completions(). It returns
-     non-zero if the filename that it is given represents a  nor-
-     mal  file that the user has execute permission to. You could
-     use this to have cpl_file_completions()  only  list  comple-
-     tions of executable files.
-
-     When you have finished with a CplFileConf variable, you  can
-     pass  it  to  the  del_CplFileConf()  destructor function to
-     reclaim its memory.
-
-       CplFileConf *del_CplFileConf(CplFileConf *cfc);
-
-
-
-

THREAD SAFETY

-     In multi-threaded programs, you should use the  libtecla_r.a
-     version  of the library. This uses POSIX reentrant functions
-     where available (hence the _r suffix), and disables features
-     that  rely on non-reentrant system functions. In the case of
-     this module, the only disabled feature is  username  comple-
-     tion in ~username/ expressions, in cpl_file_completions().
-
-     Using the libtecla_r.a version of the library, it is safe to
-     use  the facilities of this module in multiple threads, pro-
-     vided that each thread uses a separately allocated  WordCom-
-     pletion  object.  In  other words, if two threads want to do
-     word completion, they should each call  new_WordCompletion()
-     to allocate their own completion objects.
-
-
-

FILES

-     libtecla.a    -    The tecla library
-     libtecla.h    -    The tecla header file.
-
-
-

SEE ALSO

-     libtecla(3),       gl_get_line(3),        ef_expand_file(3),
-     pca_lookup_file(3)
-
-
-

AUTHOR

-     Martin Shepherd  (mcs@astro.caltech.edu)
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- diff --git a/libtecla-1.4.1/html/ef_expand_file.html b/libtecla-1.4.1/html/ef_expand_file.html deleted file mode 100644 index c1e71c7..0000000 --- a/libtecla-1.4.1/html/ef_expand_file.html +++ /dev/null @@ -1,267 +0,0 @@ - -Manual Page - - -
-

NAME

-     ef_expand_file,        del_ExpandFile,        ef_last_error,
-     ef_list_expansions,  new_ExpandFile  - expand filenames con-
-     taining ~user/$envvar and wildcard expressions
-
-

SYNOPSIS

-     #include <libtecla.h>
-
-     ExpandFile *new_ExpandFile(void);
-
-     ExpandFile *del_ExpandFile(ExpandFile *ef);
-
-     FileExpansion *ef_expand_file(ExpandFile *ef,
-                                   const char *path,
-                                   int pathlen);
-
-     int ef_list_expansions(FileExpansion *result, FILE *fp,
-                            int term_width);
-
-     const char *ef_last_error(ExpandFile *ef);
-
-
-

DESCRIPTION

-     The ef_expand_file() function is part of the  tecla  library
-     (see  the  libtecla(3)  man  page).  It  expands a specified
-     filename, converting ~user/ and ~/ expressions at the  start
-     of  the  filename  to  the  corresponding  home directories,
-     replacing  $envvar  with  the  value  of  the  corresponding
-     environment  variable, and then, if there are any wildcards,
-     matching these against existing  filenames.  Backslashes  in
-     the  input  filename are interpreted as escaping any special
-     meanings  of  the  characters  that   follow   them.    Only
-     backslahes  that  are themselves preceded by backslashes are
-     preserved in the expanded filename.
-
-     In the presence of wildcards, the returned list of filenames
-     only  includes  the  names of existing files which match the
-     wildcards. Otherwise,  the  original  filename  is  returned
-     after  expansion  of  tilde  and dollar expressions, and the
-     result is not checked against existing  files.  This  mimics
-     the file-globbing behavior of the unix tcsh shell.
-
-     The supported wildcards and their meanings are:
-       *        -  Match any sequence of zero or more characters.
-       ?        -  Match any single character.
-       [chars]  -  Match any single character that appears in
-                   'chars'.  If 'chars' contains an expression of
-                   the form a-b, then any character between a and
-                   b, including a and b, matches. The '-'
-                   character looses its special meaning as a
-                   range specifier when it appears at the start
-                   of the sequence of characters. The ']'
-                   character also looses its significance as the
-                   terminator of the range expression if it
-                   appears immediately after the opening '[', at
-                   which point it is treated one of the
-                   characters of the range. If you want both '-'
-                   and ']' to be part of the range, the '-'
-                   should come first and the ']' second.
-
-       [^chars] -  The same as [chars] except that it matches any
-                   single character that doesn't appear in
-                   'chars'.
-
-     Note that wildcards never match the initial dot in filenames
-     that  start  with  '.'.  The  initial '.' must be explicitly
-     specified in the filename. This again  mimics  the  globbing
-     behavior  of  most unix shells, and its rational is based in
-     the fact that in unix, files with names that start with  '.'
-     are usually hidden configuration files, which are not listed
-     by default by the ls command.
-
-     The following is a complete example of how to use  the  file
-     expansion function.
-
-       #include <stdio.h>
-       #include <libtecla.h>
-
-       int main(int argc, char *argv[])
-       {
-         ExpandFile *ef;      /* The expansion resource object */
-         char *filename;      /* The filename being expanded */
-         FileExpansion *expn; /* The results of the expansion */
-         int i;
-
-         ef = new_ExpandFile();
-         if(!ef)
-           return 1;
-
-         for(arg = *(argv++); arg; arg = *(argv++)) {
-           if((expn = ef_expand_file(ef, arg, -1)) == NULL) {
-             fprintf(stderr, "Error expanding %s (%s).\n", arg,
-                              ef_last_error(ef));
-           } else {
-             printf("%s matches the following files:\n", arg);
-             for(i=0; i<expn->nfile; i++)
-               printf(" %s\n", expn->files[i]);
-           }
-         }
-
-         ef = del_ExpandFile(ef);
-         return 0;
-       }
-
-     Descriptions of the functions used above are as follows:
-
-       ExpandFile *new_ExpandFile(void)
-
-     This  function   creates   the   resources   used   by   the
-     ef_expand_file()  function.  In particular, it maintains the
-     memory  that  is  used  to  record  the  array  of  matching
-     filenames  that  is returned by ef_expand_file(). This array
-     is expanded as needed, so there is no built in limit to  the
-     number of files that can be matched.
-
-       ExpandFile *del_ExpandFile(ExpandFile *ef)
-
-     This function deletes the resources that were returned by  a
-     previous  call  to  new_ExpandFile(). It always returns NULL
-     (ie a deleted object). It does nothing if the ef argument is
-     NULL.
-
-     A  container  of  the  following   type   is   returned   by
-     ef_expand_file().
-
-       typedef struct {
-         int exists;   /* True if the files in files[] exist */
-         int nfile;    /* The number of files in files[] */
-         char **files; /* An array of 'nfile' filenames. */
-       } FileExpansion;
-
-       FileExpansion *ef_expand_file(ExpandFile *ef,
-                                     const char *path,
-                                     int pathlen)
-
-     The ef_expand_file() function performs  filename  expansion,
-     as  documented at the start of this section. Its first argu-
-     ment is a resource object returned  by  new_ExpandFile().  A
-     pointer to the start of the filename to be matched is passed
-     via the path argument. This must be a normal NUL  terminated
-     string, but unless a length of -1 is passed in pathlen, only
-     the first pathlen characters will be used  in  the  filename
-     expansion.   If  the length is specified as -1, the whole of
-     the string will be expanded.
-
-     The function returns a pointer to a container who's contents
-     are the results of the expansion. If there were no wildcards
-     in the filename, the nfile member will be 1, and the  exists
-     member  should  be queried if it is important to know if the
-     expanded file currently exists or not. If there  were  wild-
-     cards,  then  the  contained  files[] array will contain the
-     names of the nfile existing files  that  matched  the  wild-
-     carded  filename,  and the exists member will have the value
-     1. Note that the returned container belongs to the specified
-     ef  object, and its contents will change on each call, so if
-     you need to retain the results of  more  than  one  call  to
-     ef_expand_file(),  you  should either make a private copy of
-     the returned  results,  or  create  multiple  file-expansion
-     resource objects via multiple calls to new_ExpandFile().
-
-     On error, NULL is returned, and an explanation of the  error
-     can be determined by calling ef_last_error(ef).
-
-       const char *ef_last_error(ExpandFile *ef)
-
-     This function returns the message which describes the  error
-     that  occurred on the last call to ef_expand_file(), for the
-     given (ExpandFile *ef) resource object.
-
-       int ef_list_expansions(FileExpansion *result, FILE *fp,
-                              int terminal_width);
-
-     The ef_list_expansions() function provides a convenient  way
-     to    list    the    filename    expansions    returned   by
-     ef_expand_file(). Like the unix ls command, it arranges  the
-     filenames  into  equal width columns, each column having the
-     width of the largest file. The number  of  columns  used  is
-     thus  determined  by the length of the longest filename, and
-     the specified terminal width. Beware that filenames that are
-     longer than the specified terminal width are printed without
-     being truncated, so output longer than the specified  termi-
-     nal width can occur. The list is written to the stdio stream
-     specified by the fp argument.
-
-
-

THREAD SAFETY

-     In multi-threaded programs, you should use the  libtecla_r.a
-     version  of the library. This uses POSIX reentrant functions
-     where available (hence the _r suffix), and disables features
-     that rely on non-reentrant system functions. Currently there
-     are no features disabled in this module.
-
-     Using the libtecla_r.a version of the library, it is safe to
-     use  the facilities of this module in multiple threads, pro-
-     vided that each thread uses a separately  allocated  Expand-
-     File  object. In other words, if two threads want to do file
-     expansion, they should each call new_ExpandFile()  to  allo-
-     cate their own file-expansion objects.
-
-
-

FILES

-     libtecla.a    -    The tecla library
-     libtecla.h    -    The tecla header file.
-
-
-

SEE ALSO

-     libtecla(3),      gl_get_line(3),      cpl_complete_word(3),
-     pca_lookup_file(3)
-

AUTHOR

-     Martin Shepherd  (mcs@astro.caltech.edu)
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- diff --git a/libtecla-1.4.1/html/enhance.html b/libtecla-1.4.1/html/enhance.html deleted file mode 100644 index 9f6bb09..0000000 --- a/libtecla-1.4.1/html/enhance.html +++ /dev/null @@ -1,111 +0,0 @@ - -Manual Page - - -
-

NAME

-     enhance - A program that adds command-line editing to  third
-     party programs.
-
-

SYNOPSIS

-     enhance command [ argument ... ]
-
-
-

DESCRIPTION

-     The enhance program provides enhanced  command-line  editing
-     facilities  to  users  of third party applications, to which
-     one doesn't have any source code. It does this by placing  a
-     pseudo-terminal  between the application and the real termi-
-     nal. It uses the tecla command-line editing library to  read
-     input  from  the real terminal, then forwards each just com-
-     pleted  input  line  to  the  application  via  the  pseudo-
-     terminal.  All output from the application is forwarded back
-     unchanged to the real terminal.
-
-     Whenever the application stops generating  output  for  more
-     than  a  tenth  of  a second, the enhance program treats the
-     latest incomplete output line as the prompt, and  redisplays
-     any incompleted input line that the user has typed after it.
-     Note that the small delay, which  is  imperceptible  to  the
-     user,  isn't necessary for correct operation of the program.
-     It is just an optimization, designed to stop the input  line
-     from being redisplayed so often that it slows down output.
-
-
-

DEFICIENCIES

-     The one major problem that hasn't been solved yet, is how to
-     deal  with  applications  that change whether typed input is
-     echo'd by their controlling terminal. For example,  programs
-     that ask for a password, such as ftp and telnet, temporarily
-     tell their controlling terminal not to echo  what  the  user
-     types.  Since  this  request goes to the application side of
-     the psuedo terminal, the enhance program has no way of know-
-     ing  that  this  has  happened,  and continues to echo typed
-     input to its controlling  terminal,  while  the  user  types
-     their password.
-
-     Furthermore, before  executing  the  host  application,  the
-     enhance program initially sets the pseudo terminal to noecho
-     mode, so that  everything  that  it  sends  to  the  program
-     doesn't  get  redundantly echoed. If a program that switches
-     to  noecho  mode  explicitly  restores  echoing  afterwards,
-     rather  than  restoring  the terminal modes that were previ-
-     ously in force, then subsequently, every time that you enter
-     a  new input line, a duplicate copy will be displayed on the
-     next line.
-
-
-

FILES

-     libtecla.a    -   The tecla library.
-     ~/.teclarc    -   The tecla personal customization file.
-
-
-

SEE ALSO

-     libtecla(3)
-
-
-

AUTHOR

-     Martin Shepherd  (mcs@astro.caltech.edu)
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- diff --git a/libtecla-1.4.1/html/gl_get_line.html b/libtecla-1.4.1/html/gl_get_line.html deleted file mode 100644 index dcc45a0..0000000 --- a/libtecla-1.4.1/html/gl_get_line.html +++ /dev/null @@ -1,2295 +0,0 @@ - -Manual Page - - -
-

NAME

-     gl_get_line,            new_GetLine,            del_GetLine,
-     gl_customize_completion,                 gl_change_terminal,
-     gl_configure_getline,   gl_load_history,    gl_save_history,
-     gl_group_history,        gl_show_history,       gl_watch_fd,
-     gl_terminal_size,    gl_resize_history,    gl_limit_history,
-     gl_clear_history,    gl_toggle_history,   gl_lookup_history,
-     gl_state_of_history,                    gl_range_of_history,
-     gl_size_of_history,     gl_echo_mode,     gl_replace_prompt,
-     gl_prompt_style,      gl_ignore_signal,      gl_trap_signal,
-     gl_last_signal - allow the user to compose an input line
-
-

SYNOPSIS

-     #include <stdio.h>
-     #include <libtecla.h>
-
-     GetLine *new_GetLine(size_t linelen, size_t histlen);
-
-     GetLine *del_GetLine(GetLine *gl);
-
-     char *gl_get_line(GetLine *gl, const char *prompt,
-                      const char *start_line, int start_pos);
-
-     int gl_customize_completion(GetLine *gl, void *data,
-                                 CplMatchFn *match_fn);
-
-     int gl_change_terminal(GetLine *gl, FILE *input_fp,
-                            FILE *output_fp, const char *term);
-
-     int gl_configure_getline(GetLine *gl,
-                              const char *app_string,
-                              const char *app_file,
-                              const char *user_file);
-
-     int gl_save_history(GetLine *gl, const char *filename,
-                         const char *comment, int max_lines);
-
-     int gl_load_history(GetLine *gl, const char *filename,
-                         const char *comment);
-
-     int gl_watch_fd(GetLine *gl, int fd, GlFdEvent event,
-                     GlFdEventFn *callback, void *data);
-
-     int gl_group_history(GetLine *gl, unsigned stream);
-
-     int gl_show_history(GetLine *gl, FILE *fp,
-                         const char *fmt, int all_groups,
-                         int max_lines);
-
-     int gl_resize_history(GetLine *gl, size_t bufsize);
-
-     void gl_limit_history(GetLine *gl, int max_lines);
-     void gl_clear_history(GetLine *gl, int all_groups);
-
-     void gl_toggle_history(GetLine *gl, int enable);
-
-     GlTerminalSize gl_terminal_size(GetLine *gl,
-                                     int def_ncolumn,
-                                     int def_nline);
-
-     int gl_lookup_history(GetLine *gl, unsigned long id,
-                           GlHistoryLine *hline);
-
-     void gl_state_of_history(GetLine *gl,
-                              GlHistoryState *state);
-
-     void gl_range_of_history(GetLine *gl,
-                              GlHistoryRange *range);
-
-     void gl_size_of_history(GetLine *gl, GlHistorySize *size);
-
-     void gl_echo_mode(GetLine *gl, int enable);
-
-     void gl_replace_prompt(GetLine *gl, const char *prompt);
-
-     void gl_prompt_style(GetLine *gl, GlPromptStyle style);
-
-     int gl_ignore_signal(GetLine *gl, int signo);
-
-     int gl_trap_signal(GetLine *gl, int signo, unsigned flags,
-                        GlAfterSignal after, int errno_value);
-
-     int gl_last_signal(const GetLine *gl);
-
-
-
-

DESCRIPTION

-     The gl_get_line() function is part of the tecla library (see
-     the libtecla(3) man page). If the user is typing at a termi-
-     nal, it prompts them for an line  of  input,  then  provides
-     interactive editing facilities, similar to those of the unix
-     tcsh shell. In addition to simple command-line  editing,  it
-     supports  recall  of  previously  entered command lines, TAB
-     completion of file names, and in-line wild-card expansion of
-     filenames.
-
-
-

AN EXAMPLE

-     The following shows a complete example of  how  to  use  the
-     gl_get_line() function to get input from the user:
-
-       #include <stdio.h>
-       #include <locale.h>
-       #include <libtecla.h>
-       int main(int argc, char *argv[])
-       {
-         char *line;    /* The line that the user typed */
-         GetLine *gl;   /* The gl_get_line() resource object */
-
-         setlocale(LC_CTYPE, ""); /* Adopt the user's choice */
-                                  /* of character set. */
-
-         gl = new_GetLine(1024, 2048);
-         if(!gl)
-           return 1;
-
-         while((line=gl_get_line(gl, "$ ", NULL, -1)) != NULL &&
-                strcmp(line, "exit\n") != 0)
-           printf("You typed: %s\n", line);
-
-         gl = del_GetLine(gl);
-         return 0;
-       }
-
-     In  the  example,  first  the  resources   needed   by   the
-     gl_get_line() function are created by calling new_GetLine().
-     This allocates the memory used in subsequent  calls  to  the
-     gl_get_line()  function,  including  the  history buffer for
-     recording previously entered lines. Then one or  more  lines
-     are read from the user, until either an error occurs, or the
-     user types exit. Then finally the resources that were  allo-
-     cated  by  new_GetLine(), are returned to the system by cal-
-     ling del_GetLine(). Note the use of the NULL return value of
-     del_GetLine()  to make gl NULL. This is a safety precaution.
-     If  the  program  subsequently  attempts  to  pass   gl   to
-     gl_get_line(),  said  function  will complain, and return an
-     error, instead of attempting to  use  the  deleted  resource
-     object.
-
-
-
-

THE FUNCTIONS USED IN THE EXAMPLE

-     The descriptions of the functions used in the example are as
-     follows:
-
-       GetLine *new_GetLine(size_t linelen, size_t histlen)
-
-     This  function   creates   the   resources   used   by   the
-     gl_get_line()  function and returns an opaque pointer to the
-     object that contains them.  The maximum length of  an  input
-     line  is  specified via the linelen argument, and the number
-     of bytes to allocate for storing history lines is set by the
-     histlen argument. History lines are stored back-to-back in a
-     single buffer of this size. Note that this  means  that  the
-     number  of  history  lines  that  can be stored at any given
-     time, depends on the lengths of the  individual  lines.   If
-     you want to place an upper limit on the number of lines that
-     can be stored, see the gl_limit_history() function described
-     later.  If you don't want history at all, specify histlen as
-     zero, and no history buffer will be allocated.
-
-     On error, a  message  is  printed  to  stderr  and  NULL  is
-     returned.
-
-       GetLine *del_GetLine(GetLine *gl)
-
-     This function deletes the resources that were returned by  a
-     previous call to new_GetLine(). It always returns NULL (ie a
-     deleted object). It does nothing if the gl argument is NULL.
-
-       char *gl_get_line(GetLine *gl, const char *prompt,
-                        const char *start_line, int start_pos);
-
-     The gl_get_line() function can be called any number of times
-     to  read input from the user. The gl argument must have been
-     previously returned by a call to new_GetLine().  The  prompt
-     argument  should be a normal NUL terminated string, specify-
-     ing the prompt to present the user with. By default  prompts
-     are   displayed   literally,   but   if   enabled  with  the
-     gl_prompt_style() function (see later), prompts can  contain
-     directives to do underlining, switch to and from bold fonts,
-     or turn highlighting on and off.
-
-     If you want to specify the initial contents of the line, for
-     the user to edit, pass the desired string via the start_line
-     argument. You can then specify which character of this  line
-     the cursor is initially positioned over, using the start_pos
-     argument. This should be -1 if you want the cursor to follow
-     the  last  character of the start line. If you don't want to
-     preload the line in this manner, send  start_line  as  NULL,
-     and set start_pos to -1.
-
-     The gl_get_line() function returns a  pointer  to  the  line
-     entered  by  the user, or NULL on error or at the end of the
-     input. The returned pointer is  part  of  the  specified  gl
-     resource  object,  and  thus  should  not  be  free'd by the
-     caller, or assumed to be unchanging from  one  call  to  the
-     next.  When  reading  from  a user at a terminal, there will
-     always be a newline character at the  end  of  the  returned
-     line.   When  standard input is being taken from a pipe or a
-     file, there will similarly be a  newline  unless  the  input
-     line  was  too  long to store in the internal buffer. In the
-     latter case you should call gl_get_line() again to read  the
-     rest   of   the   line.   Note   that  this  behavior  makes
-     gl_get_line() similar to fgets().  In fact when stdin  isn't
-     connected to a terminal,gl_get_line() just calls fgets().
-
-
-

OPTIONAL PROMPT FORMATTING

-     Whereas by default the prompt string  that  you  specify  is
-     displayed  literally,  without any special interpretation of
-     the characters within it, the gl_prompt_style() function can
-     be  used to enable optional formatting directives within the
-     prompt.
-
-       void gl_prompt_style(GetLine *gl, GlPromptStyle style);
-
-     The style argument, which specifies  the  formatting  style,
-     can take any of the following values:
-
-       GL_FORMAT_PROMPT   -  In this style, the formatting
-                             directives described below, when
-                             included in prompt strings, are
-                             interpreted as follows:
-
-                               %B  -  Display subsequent
-                                      characters with a bold
-                                      font.
-                               %b  -  Stop displaying characters
-                                      with the bold font.
-                               %F  -  Make subsequent characters
-                                      flash.
-                               %f  -  Turn off flashing
-                                      characters.
-                               %U  -  Underline subsequent
-                                      characters.
-                               %u  -  Stop underlining
-                                      characters.
-                               %P  -  Switch to a pale (half
-                                      brightness) font.
-                               %p  -  Stop using the pale font.
-                               %S  -  Highlight subsequent
-                                      characters (also known as
-                                      standout mode).
-                               %s  -  Stop highlighting
-                                      characters.
-                               %V  -  Turn on reverse video.
-                               %v  -  Turn off reverse video.
-                               %%  -  Display a single %
-                                      character.
-
-                             For example, in this mode, a prompt
-                             string like "%UOK%u$ " would
-                             display the prompt "OK$ ",
-                             but with the OK part
-                             underlined.
-
-                             Note that although a pair of
-                             characters that starts with a %
-                             character, but doesn't match any of
-                             the above directives is displayed
-                             literally, if a new directive is
-                             subsequently introduced which does
-                             match, the displayed prompt will
-                             change, so it is better to always
-                             use %% to display a literal %.
-
-                             Also note that not all terminals
-                             support all of these text
-                             attributes, and that some substitute
-                             a different attribute for missing
-                             ones.
-
-       GL_LITERAL_PROMPT  -  In this style, the prompt string is
-                             printed literally. This is the
-                             default style.
-
-
-
-

THE AVAILABLE KEY BINDING FUNCTIONS

-     The gl_get_line() function provides a  number  of  functions
-     which  can  be  bound  to  key sequences. The names of these
-     functions, and what they do, are given below.
-
-       user-interrupt           -  Send a SIGINT signal to the
-                                   parent process.
-       abort                    -  Send a SIGABRT signal to the
-                                   parent process.
-       suspend                  -  Suspend the parent process.
-       stop-output              -  Pause terminal output.
-       start-output             -  Resume paused terminal output.
-       literal-next             -  Arrange for the next character
-                                   to be treated as a normal
-                                   character. This allows control
-                                   characters to be entered.
-       cursor-right             -  Move the cursor one character
-                                   right.
-       cursor-left              -  Move the cursor one character
-                                   left.
-       insert-mode              -  Toggle between insert mode and
-                                   overwrite mode.
-       beginning-of-line        -  Move the cursor to the
-                                   beginning of the line.
-       end-of-line              -  Move the cursor to the end of
-                                   the line.
-       delete-line              -  Delete the contents of the
-                                   current line.
-       kill-line                -  Delete everything that follows
-                                   the cursor.
-       backward-kill-line       -  Delete all characters between
-                                   the cursor and the start of the
-                                   line.
-       forward-word             -  Move to the end of the word
-                                   which follows the cursor.
-       forward-to-word          -  Move the cursor to the start of
-                                   the word that follows the
-                                   cursor.
-       backward-word            -  Move to the start of the word
-                                   which precedes the cursor.
-       goto-column              -  Move the cursor to the
-                                   1-relative column in the line
-                                   specified by any preceding
-                                   digit-argument sequences (see
-                                   ENTERING REPEAT COUNTS below).
-       find-parenthesis         -  If the cursor is currently
-                                   over a parenthesis character,
-                                   move it to the matching
-                                   parenthesis character. If not
-                                   over a parenthesis character
-                                   move right to the next close
-                                   parenthesis.
-       forward-delete-char      -  Delete the character under the
-                                   cursor.
-       backward-delete-char     -  Delete the character which
-                                   precedes the cursor.
-       list-or-eof              -  This is intended for binding
-                                   to ^D. When invoked when the
-                                   cursor is within the line it
-                                   displays all possible
-                                   completions then redisplays
-                                   the line unchanged. When
-                                   invoked on an empty line, it
-                                   signals end-of-input (EOF) to
-                                   the caller of gl_get_line().
-       del-char-or-list-or-eof  -  This is intended for binding
-                                   to ^D. When invoked when the
-                                   cursor is within the line it
-                                   invokes forward-delete-char.
-                                   When invoked at the end of the
-                                   line it displays all possible
-                                   completions then redisplays
-                                   the line unchanged. When
-                                   invoked on an empty line, it
-                                   signals end-of-input (EOF) to
-                                   the caller of gl_get_line().
-       forward-delete-word      -  Delete the word which follows
-                                   the cursor.
-       backward-delete-word     -  Delete the word which precedes
-                                   the cursor.
-       upcase-word              -  Convert all of the characters
-                                   of the word which follows the
-                                   cursor, to upper case.
-       downcase-word            -  Convert all of the characters
-                                   of the word which follows the
-                                   cursor, to lower case.
-       capitalize-word          -  Capitalize the word which
-                                   follows the cursor.
-       change-case              -  If the next character is upper
-                                   case, toggle it to lower case
-                                   and vice versa.
-       redisplay                -  Redisplay the line.
-       clear-screen             -  Clear the terminal, then
-                                   redisplay the current line.
-       transpose-chars          -  Swap the character under the
-                                   cursor with the character just
-                                   before the cursor.
-       set-mark                 -  Set a mark at the position of
-                                   the cursor.
-       exchange-point-and-mark  -  Move the cursor to the last
-                                   mark that was set, and move
-                                   the mark to where the cursor
-                                   used to be.
-       kill-region              -  Delete the characters that lie
-                                   between the last mark that was
-                                   set, and the cursor.
-       copy-region-as-kill      -  Copy the text between the mark
-                                   and the cursor to the cut
-                                   buffer, without deleting the
-                                   original text.
-       yank                     -  Insert the text that was last
-                                   deleted, just before the
-                                   current position of the cursor.
-       append-yank              -  Paste the current contents of
-                                   the cut buffer, after the
-                                   cursor.
-       up-history               -  Recall the next oldest line
-                                   that was entered. Note that
-                                   in vi mode you are left in
-                                   command mode.
-       down-history             -  Recall the next most recent
-                                   line that was entered. If no
-                                   history recall session is
-                                   currently active, the next
-                                   line from a previous recall
-                                   session is recalled. Note that
-                                   in vi mode you are left in
-                                   command mode.
-       history-search-backward  -  Recall the next oldest line
-                                   who's prefix matches the string
-                                   which currently precedes the
-                                   cursor (in vi command-mode the
-                                   character under the cursor is
-                                   also included in the search
-                                   string).  Note that in vi mode
-                                   you are left in command mode.
-       history-search-forward   -  Recall the next newest line
-                                   who's prefix matches the string
-                                   which currently precedes the
-                                   cursor (in vi command-mode the
-                                   character under the cursor is
-                                   also included in the search
-                                   string).  Note that in vi mode
-                                   you are left in command mode.
-       history-re-search-backward -Recall the next oldest line
-                                   who's prefix matches that
-                                   established by the last
-                                   invocation of either
-                                   history-search-forward or
-                                   history-search-backward.
-       history-re-search-forward - Recall the next newest line
-                                   who's prefix matches that
-                                   established by the last
-                                   invocation of either
-                                   history-search-forward or
-                                   history-search-backward.
-       complete-word            -  Attempt to complete the
-                                   incomplete word which
-                                   precedes the cursor. Unless
-                                   the host program has customized
-                                   word completion, filename
-                                   completion is attempted. In vi
-                                   commmand mode the character
-                                   under the cursor is also
-                                   included in the word being
-                                   completed, and you are left in
-                                   vi insert mode.
-       expand-filename          -  Within the command line, expand
-                                   wild cards, tilde expressions
-                                   and dollar expressions in the
-                                   filename which immediately
-                                   precedes the cursor. In vi
-                                   commmand mode the character
-                                   under the cursor is also
-                                   included in the filename being
-                                   expanded, and you are left in
-                                   vi insert mode.
-       list-glob                -  List any filenames which match
-                                   the wild-card, tilde and dollar
-                                   expressions in the filename
-                                   which immediately precedes the
-                                   cursor, then redraw the input
-                                   line unchanged.
-       list-history             -  Display the contents of the
-                                   history list for the current
-                                   history group. If a repeat
-                                   count of > 1 is specified,
-                                   only that many of the most
-                                   recent lines are displayed.
-                                   See the "ENTERING REPEAT
-                                   COUNTS" section.
-       read-from-file           -  Temporarily switch to reading
-                                   input from the file who's
-                                   name precedes the cursor.
-       read-init-files          -  Re-read teclarc configuration
-                                   files.
-       beginning-of-history     -  Move to the oldest line in the
-                                   history list. Note that in vi
-                                   mode you are left in command
-                                   mode.
-       end-of-history           -  Move to the newest line in the
-                                   history list (ie. the current
-                                   line). Note that in vi mode
-                                   this leaves you in command
-                                   mode.
-       digit-argument           -  Enter a repeat count for the
-                                   next key-binding function.
-                                   For details, see the ENTERING
-                                   REPEAT COUNTS section.
-       newline                  -  Terminate and return the
-                                   current contents of the
-                                   line, after appending a
-                                   newline character. The newline
-                                   character is normally '\n',
-                                   but will be the first
-                                   character of the key-sequence
-                                   that invoked the newline
-                                   action, if this happens to be
-                                   a printable character. If the
-                                   action was invoked by the
-                                   '\n' newline character or the
-                                   '\r' carriage return
-                                   character, the line is
-                                   appended to the history
-                                   buffer.
-       repeat-history           -  Return the line that is being
-                                   edited, then arrange for the
-                                   next most recent entry in the
-                                   history buffer to be recalled
-                                   when gl_get_line() is
-                                   next called. Repeatedly
-                                   invoking this action causes
-                                   successive historical input
-                                   lines to be re-executed. Note
-                                   that this action is equivalent
-                                   to the 'Operate' action in
-                                   ksh.
-       ring-bell                -  Ring the terminal bell, unless
-                                   the bell has been silenced via
-                                   the nobeep configuration
-                                   option (see the THE TECLA
-                                   CONFIGURATION FILE section).
-       forward-copy-char        -  Copy the next character into
-                                   the cut buffer (NB. use repeat
-                                   counts to copy more than one).
-       backward-copy-char       -  Copy the previous character
-                                   into the cut buffer.
-       forward-copy-word        -  Copy the next word into the cut
-                                   buffer.
-       backward-copy-word       -  Copy the previous word into the
-                                   cut buffer.
-       forward-find-char        -  Move the cursor to the next
-                                   occurrence of the next
-                                   character that you type.
-       backward-find-char       -  Move the cursor to the last
-                                   occurrence of the next
-                                   character that you type.
-       forward-to-char          -  Move the cursor to the
-                                   character just before the next
-                                   occurrence of the next
-                                   character that the user types.
-       backward-to-char         -  Move the cursor to the
-                                   character just after the last
-                                   occurrence before the cursor
-                                   of the next character that the
-                                   user types.
-       repeat-find-char         -  Repeat the last
-                                   backward-find-char,
-                                   forward-find-char,
-                                   backward-to-char or
-                                   forward-to-char.
-       invert-refind-char       -  Repeat the last
-                                   backward-find-char,
-                                   forward-find-char,
-                                   backward-to-char, or
-                                   forward-to-char in the
-                                   opposite direction.
-       delete-to-column         -  Delete the characters from the
-                                   cursor up to the column that
-                                   is specified by the repeat
-                                   count.
-       delete-to-parenthesis    -  Delete the characters from the
-                                   cursor up to and including
-                                   the matching parenthesis, or
-                                   next close parenthesis.
-       forward-delete-find      -  Delete the characters from the
-                                   cursor up to and including the
-                                   following occurence of the
-                                   next character typed.
-       backward-delete-find     -  Delete the characters from the
-                                   cursor up to and including the
-                                   preceding occurence of the
-                                   next character typed.
-       forward-delete-to        -  Delete the characters from the
-                                   cursor up to, but not
-                                   including, the following
-                                   occurence of the next
-                                   character typed.
-       backward-delete-to       -  Delete the characters from the
-                                   cursor up to, but not
-                                   including, the preceding
-                                   occurence of the next
-                                   character typed.
-       delete-refind            -  Repeat the last *-delete-find
-                                   or *-delete-to action.
-       delete-invert-refind     -  Repeat the last *-delete-find
-                                   or *-delete-to action, in the
-                                   opposite direction.
-       copy-to-column           -  Copy the characters from the
-                                   cursor up to the column that
-                                   is specified by the repeat
-                                   count, into the cut buffer.
-       copy-to-parenthesis      -  Copy the characters from the
-                                   cursor up to and including
-                                   the matching parenthesis, or
-                                   next close parenthesis, into
-                                   the cut buffer.
-       forward-copy-find        -  Copy the characters from the
-                                   cursor up to and including the
-                                   following occurence of the
-                                   next character typed, into the
-                                   cut buffer.
-       backward-copy-find       -  Copy the characters from the
-                                   cursor up to and including the
-                                   preceding occurence of the
-                                   next character typed, into the
-                                   cut buffer.
-       forward-copy-to          -  Copy the characters from the
-                                   cursor up to, but not
-                                   including, the following
-                                   occurence of the next
-                                   character typed, into the cut
-                                   buffer.
-       backward-copy-to         -  Copy the characters from the
-                                   cursor up to, but not
-                                   including, the preceding
-                                   occurence of the next
-                                   character typed, into the cut
-                                   buffer.
-       copy-refind              -  Repeat the last *-copy-find
-                                   or *-copy-to action.
-       copy-invert-refind       -  Repeat the last *-copy-find
-                                   or *-copy-to action, in the
-                                   opposite direction.
-       vi-mode                  -  Switch to vi mode from emacs
-                                   mode.
-       emacs-mode               -  Switch to emacs mode from vi
-                                   mode.
-       vi-insert                -  From vi command mode, switch to
-                                   insert mode.
-       vi-overwrite             -  From vi command mode, switch to
-                                   overwrite mode.
-       vi-insert-at-bol         -  From vi command mode, move the
-                                   cursor to the start of the line
-                                   and switch to insert mode.
-       vi-append-at-eol         -  From vi command mode, move the
-                                   cursor to the end of the line
-                                   and switch to append mode.
-       vi-append                -  From vi command mode, move the
-                                   cursor one position right, and
-                                   switch to insert mode.
-       vi-replace-char          -  From vi command mode, replace
-                                   the character under the cursor
-                                   with the the next character
-                                   entered.
-       vi-forward-change-char   -  From vi command mode, delete
-                                   the next character then enter
-                                   insert mode.
-       vi-backward-change-char  -  From vi command mode, delete
-                                   the preceding character then
-                                   enter insert mode.
-       vi-forward-change-word   -  From vi command mode, delete
-                                   the next word then enter
-                                   insert mode.
-       vi-backward-change-word  -  From vi command mode, delete
-                                   the preceding word then
-                                   enter insert mode.
-       vi-change-rest-of-line   -  From vi command mode, delete
-                                   from the cursor to the end of
-                                   the line, then enter insert
-                                   mode.
-       vi-change-line           -  From vi command mode, delete
-                                   the current line, then enter
-                                   insert mode.
-       vi-change-to-bol         -  From vi command mode, delete
-                                   all characters between the
-                                   cursor and the beginning of
-                                   the line, then enter insert
-                                   mode.
-       vi-change-to-column      -  From vi command mode, delete
-                                   the characters from the cursor
-                                   up to the column that is
-                                   specified by the repeat count,
-                                   then enter insert mode.
-       vi-change-to-parenthesis -  Delete the characters from the
-                                   cursor up to and including
-                                   the matching parenthesis, or
-                                   next close parenthesis, then
-                                   enter vi insert mode.
-       vi-forward-change-find   -  From vi command mode, delete
-                                   the characters from the
-                                   cursor up to and including the
-                                   following occurence of the
-                                   next character typed, then
-                                   enter insert mode.
-       vi-backward-change-find  -  From vi command mode, delete
-                                   the characters from the
-                                   cursor up to and including the
-                                   preceding occurence of the
-                                   next character typed, then
-                                   enter insert mode.
-       vi-forward-change-to     -  From vi command mode, delete
-                                   the characters from the
-                                   cursor up to, but not
-                                   including, the following
-                                   occurence of the next
-                                   character typed, then enter
-                                   insert mode.
-       vi-backward-change-to    -  From vi command mode, delete
-                                   the characters from the
-                                   cursor up to, but not
-                                   including, the preceding
-                                   occurence of the next
-                                   character typed, then enter
-                                   insert mode.
-       vi-change-refind         -  Repeat the last
-                                   vi-*-change-find or
-                                   vi-*-change-to action.
-       vi-change-invert-refind  -  Repeat the last
-                                   vi-*-change-find or
-                                   vi-*-change-to action, in the
-                                   opposite direction.
-       vi-undo                  -  In vi mode, undo the last
-                                   editing operation.
-       vi-repeat-change         -  In vi command mode, repeat the
-                                   last command that modified the
-                                   line.
-
-
-

DEFAULT KEY BINDINGS IN EMACS MODE

-     The following default key bindings, which can  be  overriden
-     by  the tecla configuration file, are designed to mimic most
-     of the bindings of the unix tcsh shell, when it is in  emacs
-     editing mode.
-
-     This is the default editing mode of the tecla library.
-
-     Note that a key sequence like  ^A  or  C-a  means  hold  the
-     control-key down while pressing the letter A, and that where
-     you see \E or M- in a binding, this  represents  the  escape
-     key   or   the   Meta   modifier  key.  Also  note  that  to
-     gl_get_line(), pressing the  escape  key  before  a  key  is
-     equivalent to pressing the meta key at the same time as that
-     key. Thus the key sequence M-p can be typed in two ways,  by
-     pressing  the  escape  key,  followed  by  pressing p, or by
-     pressing the Meta key at the same time as p.
-
-     Under UNIX the terminal driver sets a number of special keys
-     for certain functions. The tecla library attempts to use the
-     same keybindings to maintain consistency. The key  sequences
-     shown for the following 6 bindings are thus just examples of
-     what they will probably be set to. If you have used the stty
-     command  to  change  these  keys,  then the default bindings
-     should match.
-
-       ^C     ->   user-interrupt
-       ^\     ->   abort
-       ^Z     ->   suspend
-       ^Q     ->   start-output
-       ^S     ->   stop-output
-       ^V     ->   literal-next
-
-     The cursor keys are refered to by name, as follows. This  is
-     necessary because different types of terminals generate dif-
-     ferent key sequences when their cursor keys are pressed.
-
-       right  ->   cursor-right
-       left   ->   cursor-left
-       up     ->   up-history
-       down   ->   down-history
-
-     The remaining bindings don't depend on  the  terminal  sett-
-     tings.
-
-       ^F     ->   cursor-right
-       ^B     ->   cursor-left
-       M-i    ->   insert-mode
-       ^A     ->   beginning-of-line
-       ^E     ->   end-of-line
-       ^U     ->   delete-line
-       ^K     ->   kill-line
-       M-f    ->   forward-word
-       M-b    ->   backward-word
-       ^D     ->   del-char-or-list-or-eof
-       ^H     ->   backward-delete-char
-       ^?     ->   backward-delete-char
-       M-d    ->   forward-delete-word
-       M-^H   ->   backward-delete-word
-       M-^?   ->   backward-delete-word
-       M-u    ->   upcase-word
-       M-l    ->   downcase-word
-       M-c    ->   capitalize-word
-       ^R     ->   redisplay
-       ^L     ->   clear-screen
-       ^T     ->   transpose-chars
-       ^@     ->   set-mark
-       ^X^X   ->   exchange-point-and-mark
-       ^W     ->   kill-region
-       M-w    ->   copy-region-as-kill
-       ^Y     ->   yank
-       ^P     ->   up-history
-       ^N     ->   down-history
-       M-p    ->   history-search-backward
-       M-n    ->   history-search-forward
-       ^I     ->   complete-word
-       ^X*    ->   expand-filename
-       ^X^F   ->   read-from-file
-       ^X^R   ->   read-init-files
-       ^Xg    ->   list-glob
-       ^Xh    ->   list-history
-       M-<    ->   beginning-of-history
-       M->    ->   end-of-history
-       \n     ->   newline
-       \r     ->   newline
-       M-o    ->   repeat-history
-       M-^V   ->   vi-mode
-
-       M-0, M-1, ... M-9  ->  digit-argument  (see below)
-
-     Note that ^I is what the TAB key generates, and that ^@  can
-     be  generated not only by pressing the control key and the @
-     key simultaneously, but also by pressing the control key and
-     the space bar at the same time.
-
-
-

DEFAULT KEY BINDINGS IN VI MODE

-     The following default key bindings are designed to mimic the
-     vi  style of editing as closely as possible. This means that
-     very few editing functions are provided in the initial char-
-     acter  input  mode, editing functions instead being provided
-     by the vi command mode. Vi command mode is entered  whenever
-     the  escape character is pressed, or whenever a key-sequence
-     that starts with a meta character is entered. In addition to
-     mimicing  vi, libtecla provides bindings for tab completion,
-     wild-card expansion  of  file  names,  and  historical  line
-     recall.
-
-     To learn how to tell  the  tecla  library  to  use  vi  mode
-     instead  of  the default emacs editing mode, see the section
-     entitled THE TECLA CONFIGURATION FILE.
-
-     As already mentioned above in the emacs section, Note that a
-     key  sequence like ^A or C-a means hold the control-key down
-     while pressing the letter A, and that where you see \E or M-
-     in  a  binding,  this  represents the escape key or the Meta
-     modifier key. Also note that to gl_get_line(), pressing  the
-     escape  key  before a key is equivalent to pressing the meta
-     key at the same time as that key. Thus the key sequence  M-p
-     can  be  typed in two ways, by pressing the escape key, fol-
-     lowed by pressing p, or by pressing the Meta key at the same
-     time as p.
-
-     Under UNIX the terminal driver sets a number of special keys
-     for certain functions. The tecla library attempts to use the
-     same keybindings to maintain consistency, binding them  both
-     in  input  mode and in command mode. The key sequences shown
-     for the following 6 bindings are thus just examples of  what
-     they will probably be set to. If you have used the stty com-
-     mand to change these keys, then the default bindings  should
-     match.
-
-       ^C     ->   user-interrupt
-       ^\     ->   abort
-       ^Z     ->   suspend
-       ^Q     ->   start-output
-       ^S     ->   stop-output
-       ^V     ->   literal-next
-       M-^C   ->   user-interrupt
-       M-^\   ->   abort
-       M-^Z   ->   suspend
-       M-^Q   ->   start-output
-       M-^S   ->   stop-output
-
-     Note that above, most of the  bindings  are  defined  twice,
-     once as a raw control code like ^C and then a second time as
-     a meta character like M-^C. The former is the binding for vi
-     input mode, whereas the latter is the binding for vi command
-     mode. Once in command mode all key-sequences that  the  user
-     types  that  they don't explicitly start with an escape or a
-     meta key, have their first key secretly converted to a  meta
-     character  before  the  key sequence is looked up in the key
-     binding table. Thus, once in command mode, when you type the
-     letter  i,  for example, the tecla library actually looks up
-     the binding for M-i.
-
-     The cursor keys are refered to by name, as follows. This  is
-     necessary because different types of terminals generate dif-
-     ferent key sequences when their cursor keys are pressed.
-
-       right  ->   cursor-right
-       left   ->   cursor-left
-       up     ->   up-history
-       down   ->   down-history
-
-     The cursor keys normally generate a keysequence  that  start
-     with  an  escape  character,  so beware that using the arrow
-     keys will put you into command mode (if you  aren't  already
-     in command mode).
-
-     The following are the terminal-independent key bindings  for
-     vi input mode.
-
-       ^D     ->   list-or-eof
-       ^G     ->   list-glob
-       ^H     ->   backward-delete-char
-       ^I     ->   complete-word
-       \r     ->   newline
-       \n     ->   newline
-       ^L     ->   clear-screen
-       ^N     ->   down-history
-       ^P     ->   up-history
-       ^R     ->   redisplay
-       ^U     ->   backward-kill-line
-       ^W     ->   backward-delete-word
-       ^X*    ->   expand-filename
-       ^X^F   ->   read-from-file
-       ^X^R   ->   read-init-files
-       ^?     ->   backward-delete-char
-
-     The following are the key bindings that are  defined  in  vi
-     command mode, this being specified by them all starting with
-     a meta character. As mentioned above, once in  command  mode
-     the  initial  meta  character  is optional. For example, you
-     might enter command mode by typing Esc,  and  then  press  h
-     twice  to  move the cursor two positions to the left. Both h
-     characters get quietly converted to M-h  before  being  com-
-     pared to the key-binding table, the first one because Escape
-     followed  by  a  character  is  always  converted   to   the
-     equivalent  meta  character,  and the second because command
-     mode was already active.
-
-       M-\     ->   cursor-right     (Meta-space)
-       M-$     ->   end-of-line
-       M-*     ->   expand-filename
-       M-+     ->   down-history
-       M--     ->   up-history
-       M-<     ->   beginning-of-history
-       M->     ->   end-of-history
-       M-^     ->   beginning-of-line
-       M-;     ->   repeat-find-char
-       M-,     ->   invert-refind-char
-       M-|     ->   goto-column
-       M-~     ->   change-case
-       M-.     ->   vi-repeat-change
-       M-%     ->   find-parenthesis
-       M-a     ->   vi-append
-       M-A     ->   vi-append-at-eol
-       M-b     ->   backward-word
-       M-B     ->   backward-word
-       M-C     ->   vi-change-rest-of-line
-       M-cb    ->   vi-backward-change-word
-       M-cB    ->   vi-backward-change-word
-       M-cc    ->   vi-change-line
-       M-ce    ->   vi-forward-change-word
-       M-cE    ->   vi-forward-change-word
-       M-cw    ->   vi-forward-change-word
-       M-cW    ->   vi-forward-change-word
-       M-cF    ->   vi-backward-change-find
-       M-cf    ->   vi-forward-change-find
-       M-cT    ->   vi-backward-change-to
-       M-ct    ->   vi-forward-change-to
-       M-c;    ->   vi-change-refind
-       M-c,    ->   vi-change-invert-refind
-       M-ch    ->   vi-backward-change-char
-       M-c^H   ->   vi-backward-change-char
-       M-c^?   ->   vi-backward-change-char
-       M-cl    ->   vi-forward-change-char
-       M-c\    ->   vi-forward-change-char  (Meta-c-space)
-       M-c^    ->   vi-change-to-bol
-       M-c0    ->   vi-change-to-bol
-       M-c$    ->   vi-change-rest-of-line
-       M-c|    ->   vi-change-to-column
-       M-c%    ->   vi-change-to-parenthesis
-       M-dh    ->   backward-delete-char
-       M-d^H   ->   backward-delete-char
-       M-d^?   ->   backward-delete-char
-       M-dl    ->   forward-delete-char
-       M-d     ->   forward-delete-char    (Meta-d-space)
-       M-dd    ->   delete-line
-       M-db    ->   backward-delete-word
-       M-dB    ->   backward-delete-word
-       M-de    ->   forward-delete-word
-       M-dE    ->   forward-delete-word
-       M-dw    ->   forward-delete-word
-       M-dW    ->   forward-delete-word
-       M-dF    ->   backward-delete-find
-       M-df    ->   forward-delete-find
-       M-dT    ->   backward-delete-to
-       M-dt    ->   forward-delete-to
-       M-d;    ->   delete-refind
-       M-d,    ->   delete-invert-refind
-       M-d^    ->   backward-kill-line
-       M-d0    ->   backward-kill-line
-       M-d$    ->   kill-line
-       M-D     ->   kill-line
-       M-d|    ->   delete-to-column
-       M-d%    ->   delete-to-parenthesis
-       M-e     ->   forward-word
-       M-E     ->   forward-word
-       M-f     ->   forward-find-char
-       M-F     ->   backward-find-char
-       M--     ->   up-history
-       M-h     ->   cursor-left
-       M-H     ->   beginning-of-history
-       M-i     ->   vi-insert
-       M-I     ->   vi-insert-at-bol
-       M-j     ->   down-history
-       M-J     ->   history-search-forward
-       M-k     ->   up-history
-       M-K     ->   history-search-backward
-       M-l     ->   cursor-right
-       M-L     ->   end-of-history
-       M-n     ->   history-re-search-forward
-       M-N     ->   history-re-search-backward
-       M-p     ->   append-yank
-       M-P     ->   yank
-       M-r     ->   vi-replace-char
-       M-R     ->   vi-overwrite
-       M-s     ->   vi-forward-change-char
-       M-S     ->   vi-change-line
-       M-t     ->   forward-to-char
-       M-T     ->   backward-to-char
-       M-u     ->   vi-undo
-       M-w     ->   forward-to-word
-       M-W     ->   forward-to-word
-       M-x     ->   forward-delete-char
-       M-X     ->   backward-delete-char
-       M-yh    ->   backward-copy-char
-       M-y^H   ->   backward-copy-char
-       M-y^?   ->   backward-copy-char
-       M-yl    ->   forward-copy-char
-       M-y\    ->   forward-copy-char  (Meta-y-space)
-       M-ye    ->   forward-copy-word
-       M-yE    ->   forward-copy-word
-       M-yw    ->   forward-copy-word
-       M-yW    ->   forward-copy-word
-       M-yb    ->   backward-copy-word
-       M-yB    ->   backward-copy-word
-       M-yf    ->   forward-copy-find
-       M-yF    ->   backward-copy-find
-       M-yt    ->   forward-copy-to
-       M-yT    ->   backward-copy-to
-       M-y;    ->   copy-refind
-       M-y,    ->   copy-invert-refind
-       M-y^    ->   copy-to-bol
-       M-y0    ->   copy-to-bol
-       M-y$    ->   copy-rest-of-line
-       M-yy    ->   copy-line
-       M-Y     ->   copy-line
-       M-y|    ->   copy-to-column
-       M-y%    ->   copy-to-parenthesis
-       M-^E    ->   emacs-mode
-       M-^H    ->   cursor-left
-       M-^?    ->   cursor-left
-       M-^L    ->   clear-screen
-       M-^N    ->   down-history
-       M-^P    ->   up-history
-       M-^R    ->   redisplay
-       M-^D    ->   list-or-eof
-       M-^I    ->   complete-word
-       M-\r    ->   newline
-       M-\n    ->   newline
-       M-^X^R  ->   read-init-files
-       M-^Xh   ->   list-history
-
-       M-0, M-1, ... M-9  ->  digit-argument  (see below)
-
-     Note that ^I is what the TAB key generates.
-
-
-

ENTERING REPEAT COUNTS

-     Many of the key binding functions described previously, take
-     an  optional  count, typed in before the target keysequence.
-     This is interpreted as a repeat count by  most  bindings.  A
-     notable  exception  is the goto-column binding, which inter-
-     prets the count as a column number.
-
-     By default you can specify this count argument  by  pressing
-     the  meta key while typing in the numeric count. This relies
-     on the digit-argument action being bound to  Meta-0,  Meta-1
-     etc.  Once any one of these bindings has been activated, you
-     can optionally take your finger off the meta key to type  in
-     the rest of the number, since every numeric digit thereafter
-     is treated as part of the number, unless it is  preceded  by
-     the literal-next binding. As soon as a non-digit, or literal
-     digit key is pressed the  repeat  count  is  terminated  and
-     either  causes  the  just typed character to be added to the
-     line that many times, or causes the next  key-binding  func-
-     tion to be given that argument.
-
-     For example, in emacs mode, typing:
-
-       M-12a
-
-     causes the letter 'a' to be added  to  the  line  12  times,
-     whereas
-
-       M-4M-c
-
-     Capitalizes the next 4 words.
-
-     In vi command mode the Meta modifier is automatically  added
-     to  all  characters  typed  in,  so  to  enter a count in vi
-     command-mode, just involves typing in the number, just as at
-     it  does in the vi editor itself. So for example, in vi com-
-     mand mode, typing:
-
-       4w2x
-
-     moves the cursor four words to the right, then  deletes  two
-     characters.
-
-     You can also bind digit-argument to other key sequences.  If
-     these  end  in  a numeric digit, that digit gets appended to
-     the current repeat count. If it doesn't  end  in  a  numeric
-     digit,  a  new repeat count is started with a value of zero,
-     and can be completed by typing in the number, after  letting
-     go of the key which triggered the digit-argument action.
-
-
-

THE TECLA CONFIGURATION FILE

-     By default, the first call to gl_get_line() looks for a file
-     called .teclarc in your home directory (ie. ~/.teclarc).  If
-     it finds this file, it reads it, interpreting each  line  as
-     defining  a  new  key  binding  or  an editing configuration
-     option.  Since  the  emacs  keybindings  are  installed   by
-     default, if you want to use the non-default vi editing mode,
-     the most important item to go in this file is the  following
-     line:
-
-       edit-mode vi
-
-     This will re-configure the default bindings for vi-mode. The
-     complete set of arguments that this command accepts are:
-
-       vi     -  Install key-bindings like those of the vi
-                 editor.
-       emacs  -  Install key-bindings like those of the emacs
-                 editor. This is the default.
-       none   -  Use just the native line editing facilities
-                 provided by the terminal driver.
-
-     To prevent the terminal bell from being rung, such  as  when
-     an unrecognized control-sequence is typed, place the follow-
-     ing line in the configuration file:
-
-       nobeep
-
-     An example of a key binding line in the  configuration  file
-     is the following.
-
-       bind M-[2~ insert-mode
-
-     On many keyboards, the above key sequence is generated  when
-     one presses the insert key, so with this keybinding, one can
-     toggle between the emacs-mode insert and overwrite modes  by
-     hitting  one  key.  One  could  also do it by typing out the
-     above sequence of characters one by one. As explained above,
-     the M- part of this sequence can be typed either by pressing
-     the escape key before the following key, or by pressing  the
-     Meta  key at the same time as the following key. Thus if you
-     had set the above key binding, and the insert  key  on  your
-     keyboard  didn't  generate the above key sequence, you could
-     still type it in either of the following 2 ways.
-
-       1. Hit the escape key momentarily, then press '[', then '2', then
-          finally '~'.
-
-       2. Press the meta key at the same time as pressing the '[' key,
-          then press '2', then '~'.
-
-     If you set a keybinding for a key-sequence that  is  already
-     bound  to a function, the new binding overrides the old one.
-     If in the new binding you omit the name of the new  function
-     to  bind  to  the key-sequence, the original binding becomes
-     undefined.
-
-     Starting with versions of libtecla later than  1.3.3  it  is
-     now  possible  to bind keysequences that begin with a print-
-     able character. Previously key-sequences  were  required  to
-     start with a control or meta character.
-
-     Note that the special  keywords  "up",  "down",  "left"  and
-     "right" refer to the arrow keys, and are thus not treated as
-     keysequences. So, for example, to rebind  the  up  and  down
-     arrow  keys  to  use the history search mechanism instead of
-     the simple history recall method, you could place  the  fol-
-     lowing in your configuration file:
-
-       bind up history-search-backwards
-       bind down history-search-backwards
-
-     To unbind an existing binding, you can do this with the bind
-     command  by  omitting  to  name any action to rebind the key
-     sequence to.  For example, by not specifying an action func-
-     tion,  the  following command unbinds the default beginning-
-     of-line action from the ^A key sequence:
-
-       bind ^A
-
-
-

ALTERNATE CONFIGURATION SOURCES

-     As mentioned above, by default users have the option of con-
-     figuring  the  behavior of gl_get_line() via a configuration
-     file called .teclarc in their  home  directories.  The  fact
-     that  all applications share this same configuration file is
-     both an advantage and a disadvantage.  In most cases  it  is
-     an  advantage, since it encourages uniformity, and frees the
-     user from having to configure each  application  separately.
-     In  some  applications, however, this single means of confi-
-     guration is a problem. This is particularly true of embedded
-     software,  where  there's no filesystem to read a configura-
-     tion file from, and also in applications where  a  radically
-     different  choice  of  keybindings  is  needed  to emulate a
-     legacy keyboard interface.  To cater  for  such  cases,  the
-     following  function  allows the application to control where
-     configuration information is read from.
-
-
-       int gl_configure_getline(GetLine *gl,
-                                const char *app_string,
-                                const char *app_file,
-                                const char *user_file);
-
-
-     It allows the configuration commands that would normally  be
-     read  from  a user's ~/.teclarc file, to be read from any or
-     none of, a string,  an  application  specific  configuration
-     file,  and/or  a  user-specific  configuration file. If this
-     function is called before the first call  to  gl_get_line(),
-     the default behavior of reading ~/.teclarc on the first call
-     to gl_get_line() is disabled, so all configuration  must  be
-     achieved using the configuration sources specified with this
-     function.
-
-     If app_string != NULL, then it is interpreted  as  a  string
-     containing  one  or  more  configuration commands, separated
-     from each other in the string by  embedded  newline  charac-
-     ters. If app_file != NULL then it is interpreted as the full
-     pathname of an application-specific configuration  file.  If
-     user_file  !=  NULL then it is interpreted as the full path-
-     name  of  a  user-specific  configuration  file,   such   as
-     ~/.teclarc. For example, in the following call,
-
-       gl_configure_getline(gl, "edit-mode vi \n nobeep",
-                                "/usr/share/myapp/teclarc",
-                                "~/.teclarc");
-
-     the app_string argument causes the  calling  application  to
-     start  in  vi  edit-mode, instead of the default emacs mode,
-     and turns off the use of the terminal bell by  the  library.
-     It  then attempts to read system-wide configuration commands
-     from an optional file called /usr/share/myapp/teclarc,  then
-     finally  reads  user-specific configuration commands from an
-     optional .teclarc file in the user's  home  directory.  Note
-     that  the  arguments are listed in ascending order of prior-
-     ity, with the contents of app_string being potentially over-
-     riden  by  commands  in  app_file,  and commands in app_file
-     potentially being overriden by commands in user_file.
-     You can call this function as  many  times  as  needed,  the
-     results  being  cumulative,  but  note  that  copies  of any
-     filenames specified via the app_file and user_file arguments
-     are  recorded  internally  for  subsequent  use by the read-
-     init-files key-binding function, so if you plan to call this
-     function  multiple  times, be sure that the last call speci-
-     fies the filenames that  you  want  re-read  when  the  user
-     requests that the configuration files be re-read.
-
-
-

FILENAME AND TILDE COMPLETION

-     With the default key bindings, pressing the  TAB  key  (aka.
-     ^I)  results  in  gl_get_line()  attempting  to complete the
-     incomplete filename that precedes the cursor.  gl_get_line()
-     searches backwards from the cursor, looking for the start of
-     the filename, stopping when it hits either a  space  or  the
-     start  of  the line. If more than one file has the specified
-     prefix, gl_get_line() completes the filename up to the point
-     at  which  the ambiguous matches start to differ, then lists
-     the possible matches.
-
-     In addition to literally  written  filenames,  gl_get_line()
-     can complete files that start with ~/ and ~user/ expressions
-     and that contain $envvar expressions. In particular, if  you
-     hit    TAB   within   an   incomplete   ~user,   expression,
-     gl_get_line() will attempt to complete the username, listing
-     any ambiguous matches.
-
-     The   completion   binding   is   implemented   using    the
-     cpl_word_completions()  function,  which  is  also available
-     separately   to   users   of   this   library.    See    the
-     cpl_word_completions(3) man page for more details.
-
-
-

CUSTOMIZED WORD COMPLETION

-     If in your application, you would like to have  TAB  comple-
-     tion  complete  other  things  in  addition to or instead of
-     filenames, you can arrange this by registering an  alternate
-     completion   callback   function,   via   a   call   to  the
-     gl_customize_completion() function.
-
-       int gl_customize_completion(GetLine *gl, void *data,
-                                   CplMatchFn *match_fn);
-
-     The data argument provides a way  for  your  application  to
-     pass  arbitrary,  application-specific  information  to  the
-     callback function. This is passed to the callback every time
-     that it is called. It might for example, point to the symbol
-     table from which possible completions are to be sought.  The
-     match_fn  argument  specifies  the  callback  function to be
-     called.  The  CplMatchFn  function  type   is   defined   in
-     libtecla.h, as is a CPL_MATCH_FN() macro that you can use to
-     declare and prototype callback  functions.  The  declaration
-     and  responsibilities of callback functions are described in
-     depth in the cpl_complete_word(3) man page.
-
-     In brief, the callback function is responsible  for  looking
-     backwards  in  the  input line, back from the point at which
-     the user pressed TAB, to find the start of  the  word  being
-     completed.  It then must lookup possible completions of this
-     word, and record them  one  by  one  in  the  WordCompletion
-     object  that  is passed to it as an argument, by calling the
-     cpl_add_completion()  function.  If  the  callback  function
-     wishes to provide filename completion in addition to its own
-     specific completions, it has the option  of  itself  calling
-     the  builtin  file-name  completion  callback. This also, is
-     documented in the cpl_complete_word(3) man page.
-
-     Note that if you would  like  gl_get_line()  to  return  the
-     current  input  line  when  a  successful completion is been
-     made,   you    can    arrange    this    when    you    call
-     cpl_add_completion(),  by  making  the last character of the
-     continuation suffix a newline character. If you do this, the
-     input  line  will  be  updated  to  display  the completion,
-     together with any contiuation suffix up to the newline char-
-     acter, then gl_get_line() will return this input line.
-
-
-

FILENAME EXPANSION

-     With  the  default  key  bindings,   pressing   ^X*   causes
-     gl_get_line()  to expand the filename that precedes the cur-
-     sor,  replacing  ~/  and   ~user/   expressions   with   the
-     corresponding   home   directories,  and  replacing  $envvar
-     expressions with the  value  of  the  specified  environment
-     variable,  then if there are any wildcards, replacing the so
-     far expanded filename with a  space-separated  list  of  the
-     files which match the wild cards.
-
-     The   expansion   binding   is   implemented    using    the
-     ef_expand_file()  function.   See  the ef_expand_file(3) man
-     page for more details.
-
-
-

RECALLING PREVIOUSLY TYPED LINES

-     Every time that a new line is entered by  the  user,  it  is
-     appended  to  a  list  of  historical input lines maintained
-     within the GetLine resource object. You can traverse up  and
-     down  this  list  using the up and down arrow keys. Alterna-
-     tively, you can do the same with the ^P, and ^N keys, and in
-     vi  command mode you can alternatively use the k and j char-
-     acters. Thus pressing up-arrow once,  replaces  the  current
-     input  line  with  the previously entered line. Pressing up-
-     arrow again, replaces this with the line  that  was  entered
-     before it, etc.. Having gone back one or more lines into the
-     history list, one can return  to  newer  lines  by  pressing
-     down-arrow  one  or  more  times.  If you do this sufficient
-     times, you will return to the original line  that  you  were
-     entering when you first hit up-arrow.
-
-     Note that in vi mode, all of the  history  recall  functions
-     switch the library into command mode.
-
-     In emacs mode the M-p and M-n keys work just like the ^P and
-     ^N  keys,  except  that  they  skip all but those historical
-     lines which share the prefix that precedes the cursor. In vi
-     command  mode  the upper case K and J characters do the same
-     thing, except that the string that they search for  includes
-     the character under the cursor as well as what precedes it.
-
-     Thus for example, suppose that you were in emacs  mode,  and
-     you  had  just entered the following list of commands in the
-     order shown:
-
-       ls ~/tecla/
-       cd ~/tecla
-       ls -l getline.c
-       emacs ~/tecla/getline.c
-
-     If you next typed:
-
-       ls
-
-     and then hit M-p, then rather than returning the  previously
-     typed   emacs   line,   which   doesn't   start  with  "ls",
-     gl_get_line() would  recall  the  "ls  -l  getline.c"  line.
-     Pressing M-p again would recall the "ls ~/tecla/" line.
-
-
-

HISTORY FILES

-     To save the contents of the history buffer  before  quitting
-     your  application,  and  subsequently  restore them when you
-     next start the application, the following functions are pro-
-     vided.
-
-
-      int gl_save_history(GetLine *gl, const char *filename,
-                          const char *comment, int max_lines);
-      int gl_load_history(GetLine *gl, const char *filename,
-                          const char *comment);
-
-
-     The filename argument specifies the name to give the history
-     file  when  saving, or the name of an existing history file,
-     when loading. This may contain home-directory  and  environ-
-     ment  variable  expressions,  such  as "~/.myapp_history" or
-     "$HOME/.myapp_history".
-     Along with each history line, extra  information  about  it,
-     such  as when it was entered by the user, and what its nest-
-     ing level is, is recorded as a comment preceding the line in
-     the  history file. Writing this as a comment allows the his-
-     tory file to double as a command file, just in case you wish
-     to  replay  a whole session using it. Since comment prefixes
-     differ in different languages, the comment argument is  pro-
-     vided  for  specifying  the  comment prefix. For example, if
-     your application were a  unix  shell,  such  as  the  bourne
-     shell,  you  would specify "#" here. Whatever you choose for
-     the comment character, you must specify the same  prefix  to
-     gl_load_history()    that   you   used   when   you   called
-     gl_save_history() to write the history file.
-
-     The max_lines must be either -1 to specify that all lines in
-     the history list be saved, or a positive number specifying a
-     ceiling on how many of  the  most  recent  lines  should  be
-     saved.
-
-     Both fuctions return non-zero on  error,  after  writing  an
-     error  message  to  stderr. Note that gl_load_history() does
-     not consider the non-existence of a file to be an error.
-
-
-

MULTIPLE HISTORY LISTS

-     If your application uses a single GetLine object for  enter-
-     ing  many  different  types  of  input  lines,  you may wish
-     gl_get_line() to distinguish the different types of lines in
-     the  history  list,  and  only  recall  lines that match the
-     current  type  of  line.  To   support   this   requirement,
-     gl_get_line() marks lines being recorded in the history list
-     with an integer identifier chosen by the application.   Ini-
-     tially  this identifier is set to 0 by new_GetLine(), but it
-     can be changed subsequently by calling gl_group_history().
-
-
-       int gl_group_history(GetLine *gl, unsigned id);
-
-
-     The integer identifier id can be any number  chosen  by  the
-     application,    but    note   that   gl_save_history()   and
-     gl_load_history() preserve the association between  identif-
-     iers and historical input lines between program invokations,
-     so you should choose fixed  identifiers  for  the  different
-     types of input line used by your application.
-
-     Whenever gl_get_line() appends a new input line to the  his-
-     tory  list,  the current history identifier is recorded with
-     it, and when it is asked to recall a historical input  line,
-     it only recalls lines that are marked with the current iden-
-     tifier.
-
-

DISPLAYING HISTORY

-     The   history   list   can   be   displayed    by    calling
-     gl_show_history().
-
-
-       int gl_show_history(GetLine *gl, FILE *fp,
-                           const char *fmt,
-                           int all_groups,
-                           int max_lines);
-
-
-     This displays the current contents of the  history  list  to
-     the  stdio  output  stream  fp. If the max_lines argument is
-     greater than or equal to zero, then no more than this number
-     of   the  most  recent  lines  will  be  displayed.  If  the
-     all_groups argument is  non-zero,  lines  from  all  history
-     groups  are displayed. Otherwise just those of the currently
-     selected history group  are  displayed.  The  format  string
-     argument,  fmt,  determines  how the line is displayed. This
-     can contain arbitrary characters which are written verbatim,
-     interleaved with any of the following format directives:
-
-       %D  -  The date on which the line was originally
-              entered, formatted like 2001-11-20.
-       %T  -  The time of day when the line was entered,
-              formatted like 23:59:59.
-       %N  -  The sequential entry number of the line in
-              the history buffer.
-       %G  -  The number of the history group which the
-              line belongs to.
-       %%  -  A literal % character.
-       %H  -  The history line itself.
-
-     Thus a format string like "%D %T  %H0 would output something
-     like:
-
-       2001-11-20 10:23:34  Hello world
-
-     Note the inclusion of an explicit newline character  in  the
-     format string.
-
-
-

LOOKING UP HISTORY

-     The gl_lookup_history() function allows the calling applica-
-     tion to look up lines in the history list.
-
-
-       typedef struct {
-         const char *line;    /* The requested historical */
-                              /*  line. */
-         unsigned group;      /* The history group to which */
-                              /*  the line belongs. */
-         time_t timestamp;    /* The date and time at which */
-                              /*  the line was originally */
-                              /*  entered. */
-       } GlHistoryLine;
-
-       int gl_lookup_history(GetLine *gl, unsigned long id,
-                             GlHistoryLine *hline);
-
-
-     The id argument indicates which line to look up,  where  the
-     first  line  that  was  entered  in  the  history list after
-     new_GetLine() was called, is denoted by 0, and  subsequently
-     entered  lines are denoted with successively higher numbers.
-     Note that the range of lines currently preserved in the his-
-     tory    list    can    be    queried    by    calling    the
-     gl_range_of_history()  function,  described  later.  If  the
-     requested  line  is  in the history list, the details of the
-     line are recorded in the variable pointed to  by  the  hline
-     argument,  and  1  is returned. Otherwise 0 is returned, and
-     the variable pointed to by hline is left unchanged.
-
-     Beware that the string returned in hline->line  is  part  of
-     the  history  buffer,  so  it  must  not  be modified by the
-     caller, and will be recycled on the next call to  any  func-
-     tion  that  takes  gl  as its argument. Therefore you should
-     make a private copy of this string if you need  to  keep  it
-     around.
-
-
-

MISCELLANEOUS HISTORY CONFIGURATION

-     If you wish to change the size of the  history  buffer  that
-     was  originally  specified in the call to new_GetLine(), you
-     can do so with the gl_resize_history() function.
-
-
-       int gl_resize_history(GetLine *gl, size_t histlen);
-
-
-     The histlen argument specifies the new size in bytes, and if
-     you specify this as 0, the buffer will be deleted.
-
-     As mentioned in the discussion of new_GetLine(), the  number
-     of  lines  that can be stored in the history buffer, depends
-     on the lengths of the individual lines. For example, a  1000
-     byte  buffer  could equally store 10 lines of average length
-     100 bytes, or 2 lines of average length 50  bytes.  Although
-     the  buffer  is  never  expanded when new lines are added, a
-     list of pointers into the  buffer  does  get  expanded  when
-     needed to accomodate the number of lines currently stored in
-     the buffer. To place an upper limit on the number  of  lines
-     in  the  buffer,  and thus a ceiling on the amount of memory
-     used in this  list,  you  can  call  the  gl_limit_history()
-     function.
-
-
-       void gl_limit_history(GetLine *gl, int max_lines);
-
-
-     The max_lines should either  be  a  positive  number  >=  0,
-     specifying  an  upper  limit  on  the number of lines in the
-     buffer, or be -1 to cancel any previously  specified  limit.
-     When  a limit is in effect, only the max_lines most recently
-     appended lines are kept in the buffer. Older lines are  dis-
-     carded.
-
-     To  discard  lines  from  the  history   buffer,   use   the
-     gl_clear_history() function.
-
-       void gl_clear_history(GetLine *gl, int all_groups);
-
-     The all_groups argument tells the function whether to delete
-     just  the  lines  associated  with the current history group
-     (see gl_group_history()), or all  historical  lines  in  the
-     buffer.
-
-     The gl_toggle_history() function allows you to  toggle  his-
-     tory  on  and off without losing the current contents of the
-     history list.
-
-
-       void gl_toggle_history(GetLine *gl, int enable);
-
-
-     Setting the enable argument  to  0  turns  off  the  history
-     mechanism,  and  setting it to 1 turns it back on. When his-
-     tory is turned off, no new lines will be added to  the  his-
-     tory  list,  and  history  lookup  key-bindings  will act as
-     though there is nothing in the history buffer.
-
-
-

QUERYING HISTORY INFORMATION

-     The configured state of the history list can be queried with
-     the gl_history_state() function.
-
-
-       typedef struct {
-         int enabled;     /* True if history is enabled */
-         unsigned group;  /* The current history group */
-         int max_lines;   /* The current upper limit on the */
-                          /*  number of lines in the history */
-                          /*  list, or -1 if unlimited. */
-       } GlHistoryState;
-
-       void gl_state_of_history(GetLine *gl,
-                                GlHistoryState *state);
-
-     On return, the status information is recorded in  the  vari-
-     able pointed to by the state argument.
-
-     The gl_range_of_history() function returns  the  number  and
-     range of lines in the history list.
-
-
-     typedef struct {
-       unsigned long oldest;  /* The sequential entry number */
-                              /*  of the oldest line in the */
-                              /*  history list. */
-       unsigned long newest;  /* The sequential entry number */
-                              /*  of the newest line in the */
-                              /*  history list. */
-       int nlines;            /* The number of lines in the */
-                              /*  history list. */
-     } GlHistoryRange;
-
-     void gl_range_of_history(GetLine *gl, GlHistoryRange *range);
-
-     The return values are recorded in the variable pointed to by
-     the  range  argument. If the nlines member of this structure
-     is greater than zero, then the  oldest  and  newest  members
-     report    the    range   of   lines   in   the   list,   and
-     newest=oldest+nlines-1.  Otherwise they are both zero.
-
-     The gl_size_of_history() function returns the total size  of
-     the  history  buffer  and  the  amount of the buffer that is
-     currently occupied.
-
-       typedef struct {
-         size_t size;      /* The size of the history buffer */
-                           /*  (bytes). */
-         size_t used;      /* The number of bytes of the */
-                           /*  history buffer that are */
-                           /*  currently occupied. */
-       } GlHistorySize;
-
-       void gl_size_of_history(GetLine *gl, GlHistorySize *size);
-
-     On return, the size information is recorded in the  variable
-     pointed to by the size argument.
-
-
-

CHANGING TERMINALS

-     The new_GetLine() constructor function assumes that input is
-     to  be  read  from  stdin, and output written to stdout. The
-     following function allows you to switch to  different  input
-     and output streams.
-
-       int gl_change_terminal(GetLine *gl, FILE *input_fp,
-                              FILE *output_fp, const char *term);
-
-     The  gl  argument  is  the  object  that  was  returned   by
-     new_GetLine().   The  input_fp argument specifies the stream
-     to read from, and output_fp specifies the stream to be writ-
-     ten  to.  Only  if  both  of these refer to a terminal, will
-     interactive   terminal   input   be   enabled.     Otherwise
-     gl_get_line()  will  simply  call  fgets()  to  read command
-     input. If both streams refer to a terminal, then  they  must
-     refer  to  the  same terminal, and the type of this terminal
-     must be specified via the term argument. The  value  of  the
-     term argument is looked up in the terminal information data-
-     base (terminfo or termcap), in order to determine which spe-
-     cial control sequences are needed to control various aspects
-     of the  terminal.  new_GetLine()  for  example,  passes  the
-     return  value  of getenv("TERM") in this argument. Note that
-     if one or both of input_fp and output_fp don't  refer  to  a
-     terminal,  then it is legal to pass NULL instead of a termi-
-     nal type.
-
-     Note  that  if  you  want  to  pass  file   descriptors   to
-     gl_change_terminal(),  you  can  do  this  by creating stdio
-     stream wrappers using the POSIX fdopen() function.
-
-
-

EXTERNAL EVENT HANDLING

-     While gl_get_line() is waiting for keyboard input  from  the
-     user, you can ask it to also watch for activity on arbitrary
-     file descriptors, such as network sockets,  pipes  etc,  and
-     have  it  call  functions  of your choosing when activity is
-     seen. This works on any system that has the select()  system
-     call, which is most, if not all flavors of unix. Registering
-     a file descriptor to be watched  by  gl_get_line()  involves
-     calling the gl_watch_fd() function.
-
-
-       int gl_watch_fd(GetLine *gl, int fd, GlFdEvent event,
-                       GlFdEventFn *callback, void *data);
-
-
-     If this returns non-zero, then it  means  that  either  your
-     arguments are invalid, or that this facility isn't supported
-     on the host system.
-
-     The fd argument is the file descriptor to  be  watched.  The
-     event  argument  specifies  what  type  of  activity  is  of
-     interest, chosen from the following enumerated values:
-
-
-       GLFD_READ   -  Watch for the arrival of data to be read.
-       GLFD_WRITE  -  Watch for the ability to write to the file
-                      descriptor without blocking.
-       GLFD_URGENT -  Watch for the arrival of urgent
-                      out-of-band data on the file descriptor.
-
-
-     The callback argument is  the  function  to  call  when  the
-     selected  activity  is  seen.  It should be defined with the
-     following macro, which is defined in libtecla.h.
-
-
-       #define GL_FD_EVENT_FN(fn) GlFdStatus (fn)(GetLine *gl, \
-                                           void *data, int fd, \
-                                           GlFdEvent event)
-
-     The data argument of the gl_watch_fd() function is passed to
-     the callback function for its own use, and can point to any-
-     thing you like, including NULL. The file descriptor and  the
-     event argument are also passed to the callback function, and
-     this potentially allows the same  callback  function  to  be
-     registered  to  more than one type of event and/or more than
-     one file descriptor. The return value of the callback  func-
-     tion should be one of the following values.
-
-
-       GLFD_ABORT    -  Tell gl_get_line() to abort with an
-                        error (errno won't be set, so set it
-                        appropriately yourself if you need it).
-       GLFD_REFRESH  -  Redraw the input line then continue
-                        waiting for input. Return this if
-                        your callback wrote to the terminal.
-       GLFD_CONTINUE -  Continue to wait for input, without
-                        redrawing the line.
-
-     Note that before calling the callback, gl_get_line()  blocks
-     most  signals, and leaves its own signal handlers installed,
-     so if you need to catch a particular signal you will need to
-     both  temporarily  install  your  own  signal  handler,  and
-     unblock the signal. Be sure to re-block the  signal  (if  it
-     was  originally  blocked)  and reinstate the original signal
-     handler, if any, before returning.
-
-     Your callback shouldn't try to read from the terminal, which
-     is  left  in  raw mode as far as input is concerned. You can
-     however write to the terminal as usual, since features  like
-     conversion  of  newline  to carriage-return/linefeed are re-
-     enabled while the callback  is  running.  If  your  callback
-     function  does  write  to  the terminal, be sure to output a
-     newline  first,  and  when  your  callback   returns,   tell
-     gl_get_line()  that  the  input line needs to be redrawn, by
-     returning the GLFD_REFRESH status code.
-
-     To remove a callback function that you previously registered
-     for   a   given  file  descriptor  and  event,  simply  call
-     gl_watch_fd() with the same file descriptor and event  argu-
-     ments,  but with a callback argument of 0. The data argument
-     is ignored in this case.
-
-
-

SIGNAL HANDLING DEFAULTS

-     By default, the gl_get_line() function intercepts  a  number
-     of signals. This is particularly important for signals which
-     would by default terminate the process, since  the  terminal
-     needs  to be restored to a usable state before this happens.
-     In this section, the signals that are  trapped  by  default,
-     and how gl_get_line() responds to them, is described. Chang-
-     ing these defaults is the topic of the following section.
-
-     When  the  following   subset   of   signals   are   caught,
-     gl_get_line()  first restores the terminal settings and sig-
-     nal handling to  how  they  were  before  gl_get_line()  was
-     called,   resends   the   signal,   to   allow  the  calling
-     application's signal handlers to handle it, then if the pro-
-     cess still exists, gl_get_line() returns NULL and sets errno
-     as specified below.
-
-
-      SIGINT  -  This signal is generated both by the keyboard
-                 interrupt key (usually ^C), and the keyboard
-                 break key.
-
-                 errno=EINTR
-
-      SIGHUP  -  This signal is generated when the controlling
-                 terminal exits.
-
-                 errno=ENOTTY
-
-      SIGPIPE -  This signal is generated when a program attempts
-                 to write to a pipe who's remote end isn't being
-                 read by any process. This can happen for example
-                 if you have called gl_change_terminal() to
-                 redirect output to a pipe hidden under a pseudo
-                 terminal.
-
-                 errno=EPIPE
-
-      SIGQUIT -  This signal is generated by the keyboard quit
-                 key (usually ^\).
-
-                 errno=EINTR
-
-      SIGABRT -  This signal is generated by the standard C,
-                 abort() function. By default it both
-                 terminates the process and generates a core
-                 dump.
-
-                 errno=EINTR
-
-      SIGTERM -  This is the default signal that the UN*X
-                 kill command sends to processes.
-
-                 errno=EINTR
-
-     Note that in the case of all of  the  above  signals,  POSIX
-     mandates that by default the process is terminated, with the
-     addition of a core dump in the case of the  SIGQUIT  signal.
-     In  other words, if the calling application doesn't override
-     the default handler by supplying  its  own  signal  handler,
-     receipt  of  the  corresponding  signal  will  terminate the
-     application before gl_get_line() returns.
-
-     If gl_get_line() aborts with errno set  to  EINTR,  you  can
-     find out what signal caused it to abort, by calling the fol-
-     lowing function.
-
-       int gl_last_signal(const GetLine *gl);
-
-     This returns the numeric code (eg. SIGINT) of the last  sig-
-     nal  that  was  received  during  the  most  recent  call to
-     gl_get_line(), or -1 if no signals were received.
-
-     On systems that support it, when a SIGWINCH (window  change)
-     signal  is  received,  gl_get_line() queries the terminal to
-     find out its new size, redraws the  current  input  line  to
-     accomodate  the  new  size, then returns to waiting for key-
-     board input from the user. Unlike other signals, this signal
-     isn't resent to the application.
-
-     Finally, the following signals cause gl_get_line() to  first
-     restore  the  terminal  and signal environment to that which
-     prevailed before gl_get_line() was called, then  resend  the
-     signal to the application. If the process still exists after
-     the signal has been delivered, then gl_get_line()  then  re-
-     establishes  its  own signal handlers, switches the terminal
-     back to raw mode, redisplays the input line, and  goes  back
-     to awaiting terminal input from the user.
-
-      SIGCONT    -  This signal is generated when a suspended
-                    process is resumed.
-
-      SIGPWR     -  This signal is generated when a power failure
-                    occurs (presumably when the system is on a
-                    UPS).
-
-      SIGALRM    -  This signal is generated when a timer
-                    expires.
-      SIGUSR1    -  An application specific signal.
-
-      SIGUSR2    -  Another application specific signal.
-
-      SIGVTALRM  -  This signal is generated when a virtual
-                    timer expires (see man setitimer(2)).
-
-      SIGXCPU    -  This signal is generated when a process
-                    exceeds its soft CPU time limit.
-
-      SIGTSTP    -  This signal is generated by the terminal
-                    suspend key, which is usually ^Z, or the
-                    delayed terminal suspend key, which is
-                    usually ^Y.
-
-      SIGTTIN    -  This signal is generated if the program
-                    attempts to read from the terminal while the
-                    program is running in the background.
-
-      SIGTTOU    -  This signal is generated if the program
-                    attempts to write to the terminal while the
-                    program is running in the background.
-
-
-     Obviously not all of the above signals are supported on  all
-     systems,  so  code to support them is conditionally compiled
-     into the tecla library.
-
-     Note that if SIGKILL, which by definition can't  be  caught,
-     or  any of the hardware generated exception signals, such as
-     SIGSEGV, SIGBUS and SIGFPE, are received and unhandled while
-     gl_get_line() has the terminal in raw mode, the program will
-     be terminated without the terminal having been restored to a
-     usable  state. In practice, job-control shells usually reset
-     the terminal settings when a process relinquishes  the  con-
-     trolling  terminal,  so  this  is  only a problem with older
-     shells.
-
-
-

CUSTOMIZED SIGNAL HANDLING

-     The previous section listed the signals  that  gl_get_line()
-     traps  by  default,  and  described how it responds to them.
-     This section describes how to both add  and  remove  signals
-     from  the  list  of  trapped signals, and how to specify how
-     gl_get_line() should respond to a given signal.
-
-     If you don't need gl_get_line() to do anything  in  response
-     to  a  signal  that  it  normally  traps,  you  can  tell to
-     gl_get_line()   to   ignore   that   signal    by    calling
-     gl_ignore_signal().
-
-       int gl_ignore_signal(GetLine *gl, int signo);
-     The signo argument is the number of the signal (eg.  SIGINT)
-     that you want to have ignored. If the specified signal isn't
-     currently one of those being  trapped,  this  function  does
-     nothing.
-
-     The gl_trap_signal() function allows you to either add a new
-     signal  to  the list that gl_get_line() traps, or modify how
-     it responds to a signal that it already traps.
-
-       int gl_trap_signal(GetLine *gl, int signo, unsigned flags,
-                          GlAfterSignal after, int errno_value);
-
-     The signo argument is the number of the signal that you wish
-     to  have trapped. The flags argument is a set of flags which
-     determine the environment in which the application's  signal
-     handler  is  invoked, the after argument tells gl_get_line()
-     what to do after the application's signal  handler  returns,
-     and  errno_value tells gl_get_line() what to set errno to if
-     told to abort.
-
-     The flags argument is a bitwise OR of zero or  more  of  the
-     following enumerators:
-
-       GLS_RESTORE_SIG  -  Restore the caller's signal
-                           environment while handling the
-                           signal.
-
-       GLS_RESTORE_TTY  -  Restore the caller's terminal settings
-                           while handling the signal.
-
-       GLS_RESTORE_LINE -  Move the cursor to the start of the
-                           line following the input line before
-                           invoking the application's signal
-                           handler.
-
-       GLS_REDRAW_LINE  -  Redraw the input line when the
-                           application's signal handler returns.
-
-       GLS_UNBLOCK_SIG  -  Normally, if the calling program has
-                           a signal blocked (man sigprocmask),
-                           gl_get_line() does not trap that
-                           signal. This flag tells gl_get_line()
-                           to trap the signal and unblock it for
-                           the duration of the call to
-                           gl_get_line().
-
-       GLS_DONT_FORWARD -  If this flag is included, the signal
-                           will not be forwarded to the signal
-                           handler of the calling program.
-
-     Two commonly useful flag combinations are also enumerated as
-     follows:
-       GLS_RESTORE_ENV   = GLS_RESTORE_SIG | GLS_RESTORE_TTY |
-                           GLS_REDRAW_LINE
-
-       GLS_SUSPEND_INPUT = GLS_RESTORE_ENV | GLS_RESTORE_LINE
-
-
-     If your signal handler, or the default system signal handler
-     for  this  signal, if you haven't overriden it, never either
-     writes to the terminal, nor suspends or terminates the  cal-
-     ling  program, then you can safely set the flags argument to
-     0.
-
-     If your signal handler always writes to the terminal,  reads
-     from  it,  or suspends or terminates the program, you should
-     specify the flags argument as GL_SUSPEND_INPUT, so that:
-
-     1. The cursor doesn't get left in the middle of the input
-        line.
-     2. So that the user can type in input and have it echoed.
-     3. So that you don't need to end each output line with
-        \r\n, instead of just \n.
-
-     The   GL_RESTORE_ENV   combination   is    the    same    as
-     GL_SUSPEND_INPUT,  except  that  it doesn't move the cursor,
-     and if your signal handler doesn't read or write anything to
-     the terminal, the user won't see any visible indication that
-     a signal was caught. This can be useful if you have a signal
-     handler that only occasionally writes to the terminal, where
-     using GL_SUSPEND_LINE would  cause  the  input  line  to  be
-     unnecessarily  duplicated  when  nothing had been written to
-     the terminal.  Such a signal handler, when it does write  to
-     the  terminal,  should  be  sure  to start a new line at the
-     start of its first write,  by  writing  a  new  line  before
-     returning.  If the signal arrives while the user is entering
-     a line that only occupies a signal terminal line, or if  the
-     cursor  is on the last terminal line of a longer input line,
-     this will have the same effect as  GL_SUSPEND_INPUT.  Other-
-     wise  it  will start writing on a line that already contains
-     part of the displayed input line.  This doesn't do any harm,
-     but  it  looks a bit ugly, which is why the GL_SUSPEND_INPUT
-     combination is better if you know that you are always  going
-     to be writting to the terminal.
-
-     The after argument, which determines what gl_get_line() does
-     after  the  application's  signal  handler  returns  (if  it
-     returns), can take any one of the following values:
-
-       GLS_RETURN   - Return the completed input line, just as
-                      though the user had pressed the return
-                      key.
-
-       GLS_ABORT    - Cause gl_get_line() to return NULL.
-       GLS_CONTINUE - Resume command line editing.
-
-     The errno_value argument is intended to be combined with the
-     GLS_ABORT  option,  telling  gl_get_line()  what  to set the
-     standard errno variable to before returning NULL to the cal-
-     ling  program.  It  can  also,  however,  be  used  with the
-     GL_RETURN option, in case you wish to have a way to  distin-
-     guish  between  an  input  line  that  was entered using the
-     return key, and one that was entered by  the  receipt  of  a
-     signal.
-
-
-

THE TERMINAL SIZE

-     On most systems the combination of the TIOCGWINSZ ioctl  and
-     the  SIGWINCH signal is used to maintain an accurate idea of
-     the terminal size. The terminal size is newly queried  every
-     time  that  gl_get_line()  is called and whenever a SIGWINCH
-     signal is received.
-
-     On the few systems where this mechanism isn't available,  at
-     startup  new_GetLine() first looks for the LINES and COLUMNS
-     environment variables.  If these aren't found, or they  con-
-     tain  unusable  values, then if a terminal information data-
-     base like terminfo or termcap is available, the default size
-     of  the  terminal is looked up in this database. If this too
-     fails to provide the terminal size, a  default  size  of  80
-     columns by 24 lines is used. If this default isn't appropri-
-     ate for your system, gl_terminal_size() can be used to  sup-
-     ply a different fallback.
-
-     The gl_terminal_size() function  allows  you  to  query  the
-     current size of the terminal, and install an alternate fall-
-     back size for cases where the size isn't available.   Beware
-     that  the terminal size won't be available if reading from a
-     pipe or a file, so the default values can be important  even
-     on  systems that do support ways of finding out the terminal
-     size.
-
-       typedef struct {
-         int nline;        /* The terminal has nline lines */
-         int ncolumn;      /* The terminal has ncolumn columns */
-       } GlTerminalSize;
-
-       GlTerminalSize gl_terminal_size(GetLine *gl,
-                                       int def_ncolumn,
-                                       int def_nline);
-
-     This function first updates gl_get_line()'s idea of the ter-
-     minal size, then records its findings in the return value.
-
-     The def_ncolumn and def_nline specify the default number  of
-     terminal columns and lines to use if the terminal size can't
-     be determined.
-
-
-

HIDING WHAT YOU TYPE

-     When entering sensitive information, such as  passwords,  it
-     is best not to have the text that you are entering echoed on
-     the terminal.  Furthermore, such text should not be recorded
-     in  the  history  list, since somebody finding your terminal
-     unattended  could  then  recall  it,  or  somebody  snooping
-     through  your directories could see it in your history file.
-     With this in mind, the gl_echo_mode() function allows you to
-     toggle  on and off the display and archival of any text that
-     is subsequently entered in calls to gl_get_line().
-
-
-       int gl_echo_mode(GetLine *gl, int enable);
-
-
-     The enable argument specifies whether entered text should be
-     visible  or not. If it is 0, then subsequently entered lines
-     will not be  visible  on  the  terminal,  and  will  not  be
-     recorded  in  the  history list. If it is 1, then subsequent
-     input lines will be displayed as they are entered, and  pro-
-     vided  that  history  hasn't  been  turned off via a call to
-     gl_toggle_history(), then they will also be archived in  the
-     history  list.  Finally,  if the enable argument is -1, then
-     the echoing mode is left  unchanged,  which  allows  you  to
-     non-destructively  query  the current setting via the return
-     value. In all cases, the return value of the function  is  0
-     if  echoing was disabled before the function was called, and
-     1 if it was enabled.
-
-     When echoing is turned off, note that although  tab  comple-
-     tion will invisibly complete your prefix as far as possible,
-     ambiguous completions will not be displayed.
-
-
-

CALLBACK FUNCTION FACILITIES

-     Unless otherwise stated, callback  functions,  such  as  tab
-     completion callbacks and event callbacks should not call any
-     functions in this module. The following functions,  however,
-     are designed specifically to be used by callback functions.
-
-     Calling the gl_replace_prompt()  function  from  a  callback
-     tells  gl_get_line()  to display a different prompt when the
-     callback  returns.  It  has  no  effect   if   called   when
-     gl_get_line() is not being called.
-
-       void gl_replace_prompt(GetLine *gl, const char *prompt);
-
-
-
-

INTERNATIONAL CHARACTER SETS

-     Since libtecla version 1.4.0, gl_get_line() has  been  8-bit
-     clean.  This means that all 8-bit characters that are print-
-     able in the user's current locale are now displayed verbatim
-     and  included in the returned input line.  Assuming that the
-     calling program correctly contains a call like  the  follow-
-     ing,
-
-       setlocale(LC_CTYPE, "");
-
-     then the current locale is determined by the  first  of  the
-     environment  variables  LC_CTYPE,  LC_ALL, and LANG, that is
-     found to contain a valid locale name. If none of these vari-
-     ables  are  defined,  or the program neglects to call setlo-
-     cale, then the default C locale is used, which is  US  7-bit
-     ASCII.  On  most  unix-like platforms, you can get a list of
-     valid locales by typing the command:
-
-       locale -a
-
-     at the shell prompt.
-
-
-

Meta keys and locales

-     Beware that in most locales other than the default C locale,
-     meta  characters  become  printable,  and  they  are then no
-     longer considered to match  M-c  style  key  bindings.  This
-     allows  international characters to be entered with the com-
-     pose key without unexpectedly triggering meta key  bindings.
-     You can still invoke meta bindings, since there are actually
-     two ways to do this. For example the binding M-c can also be
-     invoked  by pressing the escape key momentarily, then press-
-     ing the c key, and this  will  work  regardless  of  locale.
-     Moreover,  many  modern  terminal emulators, such as gnome's
-     gnome-terminal's and KDE's konsole terminals,  already  gen-
-     erate  escape  pairs  like  this  when you use the meta key,
-     rather than a real meta character, and other emulators  usu-
-     ally  have  a  way to request this behavior, so you can con-
-     tinue to use the meta key on most systems.
-
-     For example, although xterm terminal emulators generate real
-     8-bit  meta characters by default when you use the meta key,
-     they can be configured to output the equivalent escape  pair
-     by  setting their EightBitInput X resource to False. You can
-     either do this by placing a line like the following in  your
-     ~/.Xdefaults file,
-
-       XTerm*EightBitInput: False
-
-     or by starting an xterm with an -xrm '*EightBitInput: False'
-     command-line  argument.  In recent versions of xterm you can
-     toggle this feature on and off with the "Meta Sends  Escape"
-     option in the menu that is displayed when you press the left
-     mouse button and the control key within an xterm window.  In
-     CDE,  dtterms  can  be  similarly coerced to generate escape
-     pairs  in  place  of  meta  characters,   by   setting   the
-     Dtterm*KshMode resource to True.
-
-
-

Entering international characters

-     If you don't have a  keyboard  that  generates  all  of  the
-     international  characters  that you need, there is usually a
-     compose key that will allow you to enter special characters,
-     or  a  way  to  create  one. For example, under X windows on
-     unix-like systems, if your keyboard doesn't have  a  compose
-     key, you can designate a redundant key to serve this purpose
-     with the xmodmap command. For example, on many PC  keyboards
-     there is a microsoft-windows key, which is otherwise useless
-     under Linux. On my PC the xev program reports that  pressing
-     this  key  generates keycode 115, so to turn this key into a
-     compose key, I do the following:
-
-       xmodmap -e 'keycode 115 = Multi_key'
-
-     I can then enter an i with a umlaut over it by  typing  this
-     key, followed by ", followed by i.
-
-
-

THREAD SAFETY

-     In a multi-threaded program, you should use the libtecla_r.a
-     version of the library. This uses reentrant versions of sys-
-     tem functions, where available. Unfortunately  neither  ter-
-     minfo  nor  termcap  were  designed  to be reentrant, so you
-     can't safely use the functions of the getline module in mul-
-     tiple  threads  (you can use the separate file-expansion and
-     word-completion  modules  in  multiple  threads,   see   the
-     corresponding man pages for details). However due to the use
-     of POSIX reentrant functions for looking up home directories
-     etc, it is safe to use this module from a single thread of a
-     multi-threaded program, provided  that  your  other  threads
-     don't use any termcap or terminfo functions.
-
-
-

FILES

-     libtecla.a      -    The tecla library
-     libtecla.h      -    The tecla header file.
-     ~/.teclarc      -    The personal tecla customization file.
-
-
-

SEE ALSO

-     libtecla(3),    ef_expand_file(3),     cpl_complete_word(3),
-     pca_lookup_file(3)
-
-
-

AUTHOR

-     Martin Shepherd  (mcs@astro.caltech.edu)
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- diff --git a/libtecla-1.4.1/html/index.html b/libtecla-1.4.1/html/index.html deleted file mode 100644 index f75a90f..0000000 --- a/libtecla-1.4.1/html/index.html +++ /dev/null @@ -1,116 +0,0 @@ -The tecla command-line editing library. - -

The Tecla command-line editing library.

- -The tecla library provides UNIX and LINUX programs with interactive -command line editing facilities, similar to those of the UNIX tcsh -shell. In addition to simple command-line editing, it supports recall -of previously entered command lines, TAB completion of file names or -other tokens, and in-line wild-card expansion of filenames. The -internal functions which perform file-name completion and wild-card -expansion are also available externally for optional use by programs. -

-In addition, the library includes a path-searching module. This -allows an application to provide completion and lookup of files -located in UNIX style paths. Although not built into the line editor -by default, it can easily be called from custom tab-completion -callback functions. This was originally conceived for completing the -names of executables and providing a way to look up their locations in -the user's PATH environment variable, but it can easily be asked to -look up and complete other types of files in any list of directories. - -

-Note that special care has been taken to allow the use of this library -in threaded programs. The option to enable this is discussed in the -Makefile, and specific discussions of thread safety are presented in -the included man pages. -

-The current version is version 1.4.1. This may be obtained from: -

- http://www.astro.caltech.edu/~mcs/tecla/libtecla-1.4.1.tar.gz -

- -For the sake of automated scripts, the following URL always points to -the latest version. Note that the version number can be found in the -README file. - -

- http://www.astro.caltech.edu/~mcs/tecla/libtecla.tar.gz -

- -The library is distributed under a permissive non-copyleft -free software license (the X11 license with -the name of the copyright holder changed). This is compatible with, -but not as restrictive as the GNU GPL. - -

Release notes

- -The list of major changes that accompany each new release can be found -here. - -

Modifications

- -The gory details of changes in the latest and previous versions of the -library can be found here. - -

Library documentation

- -The following are html versions of the libtecla man pages: - - - -

Portability

- -In principle, the standard version of the library should compile -without any problems on any UNIX or UNIX like system. So far it has -been reported to work on the following systems: - -
-  Sun Solaris 2.5,2.6,7,8, with any of gcc, Sun C, or g++.
-  Mandrake Linux 7.1 etc.., gcc
-  Red Hat Linux 7 etc.., gcc
-  Suse Linux 6.4, gcc
-  IBM AIX 4.3.3, gcc
-  HP-UX 10.20, HP-UX 11, gcc, c89
-  SCO UnixWare 7.1.1
-  FreeBSD, gcc
-  Alpha OSF1, cc, gcc
-  Mac OS X
-  Cygwin (running under Windows)
-  QNX
-
- -There haven't been many reports concerning the POSIX reentrant -version, so the absence of any of the above from the following list of -systems on which the reentrant version is known to work, shouldn't be -taken as an indication that the reentrant version doesn't work. - -
-  Sun Solaris 2.5,2.6,7,8, with any of gcc, Sun C, or g++.
-  Mandrake Linux 7.1, gcc
-  RedHat Linux 7.0,7.1, gcc
-  SuSe Linux 6.4, gcc
-  HP-UX 11, gcc
-  IBM AIX 4.3.3, gcc
-  Alpha OSF1, cc
-
- -The only system that is known to have issues with the reentrant -version of the library is SCO UnixWare 7.1.1. The problem is in the -system provided signal.h, which breaks when POSIX_C_SOURCE is -defined. It has been reported that this can be fixed by editing -signal.h. - -

-If you compile the library on a system that isn't mentioned above, -please send E-mail to mcs@astro.caltech.edu. -


-Martin Shepherd (25-May-2002) - diff --git a/libtecla-1.4.1/html/libtecla.html b/libtecla-1.4.1/html/libtecla.html deleted file mode 100644 index 914c1e9..0000000 --- a/libtecla-1.4.1/html/libtecla.html +++ /dev/null @@ -1,163 +0,0 @@ - -Manual Page - - -
-

NAME

-     libtecla - An interactive command-line input library.
-
-

SYNOPSIS

-     gcc ... -ltecla -lcurses
-
-
-

DESCRIPTION

-     The tecla library provides programs with interactive command
-     line  editing  facilities, similar to those of the unix tcsh
-     shell. In addition to simple command-line editing,  it  sup-
-     ports  recall  of previously entered command lines, TAB com-
-     pletion of file names or other tokens, and in-line wild-card
-     expansion of filenames. The internal functions which perform
-     file-name completion and wild-card expansion are also avail-
-     able externally for optional use by the calling program.
-
-     The various parts of the library are documented in the  fol-
-     lowing man pages:
-
-       gl_get_line(3)        -  The interactive line-input module.
-       cpl_complete_word(3)  -  The word completion module.
-       ef_expand_file(3)     -  The filename expansion module.
-       pca_lookup_file(3)    -  A directory-list based filename
-                                lookup and completion module.
-
-     In addition there is one  optional  application  distributed
-     with the library:
-
-       enhance(3)            -  Add command-line editing to third
-                                party applications.
-
-
-

THREAD SAFETY

-     If the library is compiled  with  -D_POSIX_C_SOURCE=199506L,
-     reentrant  versions  of  as  many  functions as possible are
-     used. This  includes  using  getpwuid_r()  and  getpwnam_r()
-     instead  of  getpwuid()  and  getpwnam() when looking up the
-     home directories of specific users in the password file (for
-     ~user/  expansion), and readdir_r() instead of readdir() for
-     reading directory entries when  doing  filename  completion.
-     The  reentrant  version  of  the  library  is usually called
-     libtecla_r.a instead of libtecla.a, so if only the latter is
-     available,  it  probably  isn't  the correct version to link
-     with threaded programs.
-
-     Reentrant functions for iterating through the password  file
-     aren't  available,  so  when  the  library is compiled to be
-     reentrant, TAB completion of incomplete usernames in  ~user-
-     name/  expressions  is disabled. This doesn't disable expan-
-     sion of complete ~username expressions, which  can  be  done
-     reentrantly,  or  expansion  of  the parts of filenames that
-     follow them, so this doesn't remove much functionality.
-
-     The terminfo functions setupterm(),  tigetstr(),  tigetnum()
-     and  tputs()  also  aren't  reentrant, but very few programs
-     will want to  interact  with  multiple  terminals,  so  this
-     shouldn't  prevent  this library from being used in threaded
-     programs.
-
-
-

LIBRARY VERSION NUMBER

-     The version number of the library can be queried  using  the
-     following function.
-
-      void libtecla_version(int *major, int *minor, int *micro);
-
-
-     On return, this function records the three components of the
-     libtecla  version number in *major, *minor, *micro. The for-
-     mal meaning of the three components is as follows.
-
-
-      major - Incrementing this number implies that a change has
-              been made to the library's public interface, which
-              makes it binary incompatible  with programs that
-              were linked with previous shared versions of the
-              tecla library.
-
-      minor - This number is incremented by one whenever
-              additional functionality, such as new functions or
-              modules, are added to the library.
-
-      micro - This is incremented whenever modifications to the
-              library are made which make no changes to the
-              public interface, but which fix bugs and/or improve
-              the behind-the-scenes implementation.
-
-
-
-

TRIVIA

-     In Spanish, a "tecla" is the key of a keyboard.  Since  this
-     library  centers  on  keyboard input, and given that I wrote
-     much of the library while working in Chile, this seemed like
-     a suitable name.
-
-
-

FILES

-     libtecla.a    -   The tecla library.
-     libtecla.h    -   The tecla header file.
-     ~/.teclarc    -   The tecla personal customization file.
-
-
-
-

SEE ALSO

-     gl_get_line(3),   ef_expand_file(3),   cpl_complete_word(3),
-     pca_lookup_file(3), enhance(3)
-
-
-

AUTHOR

-     Martin Shepherd  (mcs@astro.caltech.edu)
-
-
-

ACKNOWLEDGMENTS

-     Markus Gyger  - Lots of assistance, including help with
-                     shared libraries, configuration information,
-                     particularly for Solaris; modifications to
-                     support C++ compilers, improvements for ksh
-                     users, faster cursor motion, output
-                     buffering, and changes to make gl_get_line()
-                     8-bit clean.
-     Mike MacFaden - Suggestions, feedback and testing that led
-                     to many of the major new functions that were
-                     added in version 1.4.0.
-     Tim Eliseo    - Many vi-mode bindings and fixes.
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- diff --git a/libtecla-1.4.1/html/pca_lookup_file.html b/libtecla-1.4.1/html/pca_lookup_file.html deleted file mode 100644 index 40c9f2b..0000000 --- a/libtecla-1.4.1/html/pca_lookup_file.html +++ /dev/null @@ -1,371 +0,0 @@ - -Manual Page - - -
-

NAME

-     pca_lookup_file,       del_PathCache,       del_PcaPathConf,
-     new_PathCache,        new_PcaPathConf,       pca_last_error,
-     pca_path_completions,    pca_scan_path,    pca_set_check_fn,
-     ppc_file_start,  ppc_literal_escapes  -  lookup  a file in a
-     list of directories
-
-

SYNOPSIS

-     #include <libtecla.h>
-
-     PathCache *new_PathCache(void);
-
-     PathCache *del_PathCache(PathCache *pc);
-
-     int pca_scan_path(PathCache *pc, const char *path);
-
-     void pca_set_check_fn(PathCache *pc, CplCheckFn *check_fn,
-                           void *data);
-
-     char *pca_lookup_file(PathCache *pc, const char *name,
-                           int name_len, int literal);
-
-     const char *pca_last_error(PathCache *pc);
-
-     CPL_MATCH_FN(pca_path_completions);
-
-
-
-

DESCRIPTION

-     The PathCache object is part of the tecla library  (see  the
-     libtecla(3) man page).
-
-     PathCache objects allow an application to search  for  files
-     in any colon separated list of directories, such as the unix
-     execution  PATH  environment  variable.  Files  in  absolute
-     directories  are cached in a PathCache object, whereas rela-
-     tive directories are scanned as needed.  Using  a  PathCache
-     object,  you  can  look  up  the  full  pathname of a simple
-     filename, or you can obtain a list of the  possible  comple-
-     tions  of  a  given filename prefix. By default all files in
-     the list of directories are targets for lookup  and  comple-
-     tion, but a versatile mechanism is provided for only select-
-     ing specific types of files. The obvious application of this
-     facility  is to provide Tab-completion and lookup of execut-
-     able commands in the unix  PATH,  so  an  optional  callback
-     which rejects all but executable files, is provided.
-
-
-

AN EXAMPLE

-     Under UNIX, the  following  example  program  looks  up  and
-     displays  the full pathnames of each of the command names on
-     the command line.
-       #include <stdio.h>
-       #include <stdlib.h>
-       #include <libtecla.h>
-
-       int main(int argc, char *argv[])
-       {
-         int i;
-       /*
-        * Create a cache for executable files.
-        */
-         PathCache *pc = new_PathCache();
-         if(!pc)
-           exit(1);
-       /*
-        * Scan the user's PATH for executables.
-        */
-         if(pca_scan_path(pc, getenv("PATH"))) {
-           fprintf(stderr, "%s\n", pca_last_error(pc));
-           exit(1);
-         }
-       /*
-        * Arrange to only report executable files.
-        */
-        pca_set_check_fn(pc, cpl_check_exe, NULL);
-       /*
-        * Lookup and display the full pathname of each of the
-        * commands listed on the command line.
-        */
-         for(i=1; i<argc; i++) {
-           char *cmd = pca_lookup_file(pc, argv[i], -1, 0);
-           printf("The full pathname of '%s' is %s\n", argv[i],
-                  cmd ? cmd : "unknown");
-         }
-         pc = del_PathCache(pc);  /* Clean up */
-         return 0;
-       }
-
-     The following is an example of what this does on  my  laptop
-     under linux:
-
-       $ ./example less more blob
-       The full pathname of 'less' is /usr/bin/less
-       The full pathname of 'more' is /bin/more
-       The full pathname of 'blob' is unknown
-       $
-
-
-

FUNCTION DESCRIPTIONS

-     In order to use the facilities  of  this  module,  you  must
-     first   allocate   a   PathCache   object   by  calling  the
-     new_PathCache() constructor function.
-
-       PathCache *new_PathCache(void)
-
-     This function creates the  resources  needed  to  cache  and
-     lookup  files  in  a list of directories. It returns NULL on
-     error.
-
-
-

POPULATING THE CACHE

-     Once you have created a cache, it needs to be populated with
-     files.  To do this, call the pca_scan_path() function.
-
-       int pca_scan_path(PathCache *pc, const char *path);
-
-     Whenever this function is called, it  discards  the  current
-     contents  of  the  cache, then scans the list of directories
-     specified in its path argument for files. The path  argument
-     must be a string containing a colon-separated list of direc-
-     tories, such as "/usr/bin:/home/mcs/bin:.". This can include
-     directories   specified   by   absolute  pathnames  such  as
-     "/usr/bin", as well as sub-directories specified by relative
-     pathnames such as "." or "bin". Files in the absolute direc-
-     tories are immediately cached  in  the  specified  PathCache
-     object,  whereas sub-directories, whose identities obviously
-     change whenever the current working  directory  is  changed,
-     are  marked  to  be  scanned  on  the fly whenever a file is
-     looked up.
-
-     On success this function return 0. On error  it  returns  1,
-     and  a  description  of the error can be obtained by calling
-     pca_last_error(pc).
-
-
-

LOOKING UP FILES

-     Once the cache has been populated with files, you  can  look
-     up  the  full  pathname  of a file, simply by specifying its
-     filename to pca_lookup_file().
-
-       char *pca_lookup_file(PathCache *pc, const char *name,
-                             int name_len, int literal);
-
-     To make it possible to pass this function a  filename  which
-     is  actually  part of a longer string, the name_len argument
-     can be used to specify the length of  the  filename  at  the
-     start  of  the  name[]  argument.  If  you  pass -1 for this
-     length, the length of the string  will  be  determined  with
-     strlen().  If  the  name[]  string might contain backslashes
-     that escape the special meanings of spaces and  tabs  within
-     the filename, give the literal argument, the value 0. Other-
-     wise, if backslashes should be treated as normal characters,
-     pass 1 for the value of the literal argument.
-
-
-

FILENAME COMPLETION

-     Looking up the potential completions of a filename-prefix in
-     the  filename  cache,  is  achieved  by passing the provided
-     pca_path_completions()    callback    function    to     the
-     cpl_complete_word()  function  (see the cpl_complete_word(3)
-     man page).
-
-       CPL_MATCH_FN(pca_path_completions);
-
-     This callback requires that its data argument be  a  pointer
-     to  a PcaPathConf object. Configuration objects of this type
-     are allocated by calling new_PcaPathConf().
-
-       PcaPathConf *new_PcaPathConf(PathCache *pc);
-
-     This function returns an  object  initialized  with  default
-     configuration    parameters,   which   determine   how   the
-     cpl_path_completions() callback function behaves. The  func-
-     tions  which  allow you to individually change these parame-
-     ters are discussed below.
-
-     By default,  the  pca_path_completions()  callback  function
-     searches  backwards for the start of the filename being com-
-     pleted, looking for the first un-escaped space or the  start
-     of  the input line. If you wish to specify a different loca-
-     tion, call ppc_file_start() with  the  index  at  which  the
-     filename  starts  in  the input line. Passing start_index=-1
-     re-enables the default behavior.
-
-       void ppc_file_start(PcaPathConf *ppc, int start_index);
-
-     By default, when pca_path_completions() looks at a  filename
-     in  the input line, each lone backslash in the input line is
-     interpreted as being a special character which  removes  any
-     special significance of the character which follows it, such
-     as a space which should be taken as  part  of  the  filename
-     rather  than  delimiting  the  start  of the filename. These
-     backslashes are thus ignored while looking for  completions,
-     and  subsequently  added  before  spaces,  tabs  and literal
-     backslashes in the list of completions.  To  have  unescaped
-     backslashes    treated    as    normal    characters,   call
-     ppc_literal_escapes() with a non-zero value in  its  literal
-     argument.
-
-       void ppc_literal_escapes(PcaPathConf *ppc, int literal);
-
-     When you have finished with a PcaPathConf variable, you  can
-     pass  it  to  the  del_PcaPathConf()  destructor function to
-     reclaim its memory.
-
-       PcaPathConf *del_PcaPathConf(PcaPathConf *ppc);
-
-

BEING SELECTIVE

-     If you are only interested in certain types or  files,  such
-     as,  for example, executable files, or files whose names end
-     in a particular suffix, you can arrange for the file comple-
-     tion  and  lookup functions to be selective in the filenames
-     that they return.  This is done by  registering  a  callback
-     function  with your PathCache object. Thereafter, whenever a
-     filename is found which  either  matches  a  filename  being
-     looked  up,  or  matches  a prefix which is being completed,
-     your callback function will be called with the full pathname
-     of  the  file,  plus  any application-specific data that you
-     provide, and if the callback returns 1 the filename will  be
-     reported  as  a  match,  and  if  it  returns  0, it will be
-     ignored.  Suitable callback functions and  their  prototypes
-     should  be declared with the following macro. The CplCheckFn
-     typedef is  also  provided  in  case  you  wish  to  declare
-     pointers to such functions.
-
-       #define CPL_CHECK_FN(fn) int (fn)(void *data, \
-                                         const char *pathname)
-       typedef CPL_CHECK_FN(CplCheckFn);
-
-     Registering one of  these  functions  involves  calling  the
-     pca_set_check_fn()  function.  In  addition  to the callback
-     function, passed via the check_fn argument, you can  pass  a
-     pointer to anything via the data argument. This pointer will
-     be passed on to your callback function,  via  its  own  data
-     argument,  whenever  it is called, so this provides a way to
-     pass appplication specific data to your callback.
-
-       void pca_set_check_fn(PathCache *pc, CplCheckFn *check_fn,
-                             void *data);
-
-     Note that these callbacks are passed the  full  pathname  of
-     each  matching file, so the decision about whether a file is
-     of interest can be based on any property of  the  file,  not
-     just   its   filename.   As   an   example,   the   provided
-     cpl_check_exe() callback function looks  at  the  executable
-     permissions  of  the  file and the permissions of its parent
-     directories, and only returns 1 if the user has execute per-
-     mission to the file. This callback function can thus be used
-     to lookup or complete command names found in the directories
-     listed  in the user's PATH environment variable. The example
-     program given earlier in this man page provides a demonstra-
-     tion of this.
-
-     Beware that if somebody tries to complete an  empty  string,
-     your  callback  will  get  called once for every file in the
-     cache, which could number in the thousands. If your callback
-     does  anything time consuming, this could result in an unac-
-     ceptable delay for the user, so  callbacks  should  be  kept
-     short.
-     To improve performance, whenever one of these  callbacks  is
-     called,  the  choice  that  it makes is cached, and the next
-     time the corresponding file is looked up, instead of calling
-     the  callback  again,  the  cached  record of whether it was
-     accepted or rejected is used. Thus if somebody tries to com-
-     plete an empty string, and hits tab a second time when noth-
-     ing appears to happen, there will only be  one  long  delay,
-     since  the second pass will operate entirely from the cached
-     dispositions of the files. These cached dipositions are dis-
-     carded  whenever  pca_scan_path()  is  called,  and whenever
-     pca_set_check_fn() is called with changed callback  function
-     or data arguments.
-
-
-

ERROR HANDLING

-     If pca_scan_path() reports that an error occurred by return-
-     ing  1,  you  can obtain a terse description of the error by
-     calling pca_last_error(pc). This returns an internal  string
-     containing an error message.
-
-       const char *pca_last_error(PathCache *pc);
-
-
-
-

CLEANING UP

-     Once you have finished using a  PathCache  object,  you  can
-     reclaim  its  resources by passing it to the del_PathCache()
-     destructor function. This takes a pointer to  one  of  these
-     objects, and always returns NULL.
-
-       PathCache *del_PathCache(PathCache *pc);
-
-
-

THREAD SAFETY

-     In multi-threaded programs, you should use the  libtecla_r.a
-     version  of the library. This uses POSIX reentrant functions
-     where available (hence the _r suffix), and disables features
-     that  rely on non-reentrant system functions. In the case of
-     this module, the only disabled feature is  username  comple-
-     tion in ~username/ expressions, in cpl_path_completions().
-
-     Using the libtecla_r.a version of the library, it is safe to
-     use  the facilities of this module in multiple threads, pro-
-     vided that each thread uses a separately allocated PathCache
-     object.  In  other  words,  if  two  threads want to do path
-     searching, they should each call new_PathCache() to allocate
-     their own caches.
-
-
-

FILES

-     libtecla.a    -    The tecla library
-     libtecla.h    -    The tecla header file.
-

SEE ALSO

-     libtecla(3),       gl_get_line(3),        ef_expand_file(3),
-     cpl_complete_word(3)
-
-
-

AUTHOR

-     Martin Shepherd  (mcs@astro.caltech.edu)
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- diff --git a/libtecla-1.4.1/html/release.html b/libtecla-1.4.1/html/release.html deleted file mode 100644 index e0d219c..0000000 --- a/libtecla-1.4.1/html/release.html +++ /dev/null @@ -1,360 +0,0 @@ -The tecla library release notes -
-This file lists major changes which accompany each new release.
-
-Version 1.4.1:
-
-  This is a maintenance release. It includes minor changes to support
-  Mac OS X (Darwin), the QNX real-time operating system, and Cygwin
-  under Windows. It also fixes an oversight that was preventing the
-  tab key from inserting tab characters when users unbound the
-  complete-word action from it.
-
-Version 1.4.0:
-
-  The contents of the history list can now be saved and restored with
-  the new gl_save_history() and gl_load_history() functions.
-
-  Event handlers can now be registered to watch for and respond to I/O
-  on arbitrary file descriptors while gl_get_line() is waiting for
-  terminal input from the user. See the gl_get_line(3) man page
-  for details on gl_watch_fd().
-
-  As an optional alternative to getting configuration information only
-  from ~/.teclarc, the new gl_configure_getline() function allows
-  configuration commands to be taken from any of, a string, a
-  specified application-specific file, and/or a specified
-  user-specific file. See the gl_get_line(3) man page for details.
-
-  The version number of the library can now be queried using the
-  libtecla_version() function. See the libtecla(3) man page.
-
-  The new gl_group_history() function allows applications to group
-  different types of input line in the history buffer, and arrange for
-  only members of the appropriate group to be recalled on a given call
-  to gl_get_line(). See the gl_get_line(3) man page.
-
-  The new gl_show_history() function displays the current history list
-  to a given stdio output stream. See the gl_get_line(3) man page.
-
-  new_GetLine() now allows you to specify a history buffer size of
-  zero, thus requesting that no history buffer be allocated. You can
-  subsequently resize or delete the history buffer at any time, by
-  calling gl_resize_history(), limit the number of lines that are
-  allowed in the buffer by calling gl_limit_history(), clear either
-  all history lines from the history list, or just the history lines
-  that are associated with the current history group, by calling
-  gl_clear_history, and toggle the history mechanism on and off by
-  calling gl_toggle_history().
-
-  The new gl_terminal_size() function can be used to query the
-  current terminal size. It can also be used to supply a default
-  terminal size on systems where no mechanism is available for
-  looking up the size.
-
-  The contents and configuration of the history list can now be
-  obtained by the calling application, by calling the new
-  gl_lookup_history(), gl_state_of_history(), gl_range_of_history()
-  and gl_size_of_history() functions. See the gl_get_line(3) man page.
-
-  Echoing of the input line as it is typed, can now be turned on and
-  off via the new gl_echo_mode() function. While echoing is disabled,
-  newly entered input lines are omitted from the history list.  See
-  the gl_get_line(3) man page.
-
-  While the default remains to display the prompt string literally,
-  the new gl_prompt_style() function can be used to enable text
-  attribute formatting directives in prompt strings, such as
-  underlining, bold font, and highlighting directives.
-
-  Signal handling in gl_get_line() is now customizable. The default
-  signal handling behavior remains essentially the same, except that
-  the SIGTSTP, SIGTTIN and SIGTTOU are now forwarded to the
-  corresponding signal handler of the calling program, instead of
-  causing a SIGSTOP to be sent to the application.  It is now possible
-  to remove signals from the list that are trapped by gl_get_line(),
-  as well as add new signals to this list. The signal and terminal
-  environments in which the signal handler of the calling program is
-  invoked, and what gl_get_line() does after the signal handler
-  returns, is now customizable on a per signal basis. You can now also
-  query the last signal that was caught by gl_get_line(). This is
-  useful when gl_get_line() aborts with errno=EINTR, and you need to
-  know which signal caused it to abort.
-
-  Key-sequences bound to action functions can now start with printable
-  characters. Previously only keysequences starting with control or
-  meta characters were permitted.
-
-  gl_get_line() is now 8-bit clean. If the calling program has
-  correctly called setlocale(LC_CTYPE,""), then the user can select an
-  alternate locale by setting the standard LC_CTYPE, LC_ALL, or LANG
-  environment variables, and international characters can then be
-  entered directly, either by using a non-US keyboard, or by using a
-  compose key on a standard US keyboard. Note that in locales in which
-  meta characters become printable, meta characters no longer match
-  M-c bindings, which then have to be entered using their escape-c
-  equivalents.  Fortunately most modern terminal emulators either
-  output the escape-c version by default when the meta key is used, or
-  can be configured to do so (see the gl_get_line(3) man page), so in
-  most cases you can continue to use the meta key.
-
-  Completion callback functions can now tell gl_get_line() to return
-  the input line immediately after a successful tab completion, simply
-  by setting the last character of the optional continuation suffix to
-  a newline character (ie. in the call to cpl_add_completion()).
-
-  It is now safe to create and use multiple GetLine objects, albeit
-  still only from a single thread. In conjunction with the new
-  gl_configure_getline() function, this optionally allows multiple
-  GetLine objects with different bindings to be used to implement
-  different input modes.
-
-  The edit-mode configuration command now accepts the argument,
-  none. This tells gl_get_line() to revert to using just the native
-  line editing facilities provided by the terminal driver. This could
-  be used if the termcap or terminfo entry of the host terminal were
-  badly corrupted.
-
-  Application callback functions invoked by gl_get_line() can now
-  change the displayed prompt using the gl_replace_prompt() function.
-
-  Their is now an optional program distributed with the library. This
-  is a beta release of a program which adds tecla command-line editing
-  to virtually any third party application without the application
-  needing to be linked to the library. See the enhance(3) man page for
-  further details. Although built and installed by default, the
-  INSTALL document explains how to prevent this.
-
-  The INSTALL document now explains how you can stop the demo programs
-  from being built and installed.
-
-  NetBSD/termcap fixes. Mike MacFaden reported two problems that he
-  saw when compiling libtecla under NetBSD. Both cases were related to
-  the use of termcap.  Most systems use terminfo, so this problem has
-  gone unnoticed until now, and won't have affected the grand majority
-  of users.  The configure script had a bug which prevented the check
-  for CPP working properly, and getline.c wouldn't compile due to an
-  undeclared variable when USE_TERMCAP was defined. Both problems have
-  now been fixed. Note that if you successfully compiled version
-  1.3.3, this problem didn't affect you.
-
-  An unfortunate and undocumented binding of the key-sequence M-O was
-  shadowing the arrow-key bindings on systems that use ^[OA etc. I
-  have removed this binding (the documented lower case M-o binding
-  remains bound). Under the KDE konsole terminal this was causing the
-  arrow keys to do something other than expected.
-
-  There was a bug in the history list code which could result in
-  strange entries appearing at the start of the history list once
-  enough history lines had been added to the list to cause the
-  circular history buffer to wrap. This is now fixed.
- 
-Version 1.3.3:
-
-  Signal handling has been re-written, and documentation of its
-  behaviour has been added to the gl_get_line(3) man page. In addition
-  to eliminating race conditions, and appropriately setting errno for
-  those signals that abort gl_get_line(), many more signals are now
-  intercepted, making it less likely that the terminal will be left in
-  raw mode by a signal that isn't trapped by gl_get_line().
-
-  A bug was also fixed that was leaving the terminal in raw mode if
-  the editing mode was changed interactively between vi and emacs.
-  This was only noticeable when running programs from old shells that
-  don't reset terminal modes.
-
-Version 1.3.2:
-
-  Tim Eliseo contributed a number of improvements to vi mode,
-  including a fuller set of vi key-bindings, implementation of the vi
-  constraint that the cursor can't backup past the point at which
-  input mode was entered, and restoration of overwritten characters
-  when backspacing in overwrite mode. There are also now new bindings
-  to allow users to toggle between vi and emacs modes interactively.
-  The terminal bell is now used in some circumstances, such as when an
-  unrecognized key sequence is entered. This can be turned off by the
-  new nobeep option in the tecla configuration file.
-
-  Unrelated to the above, a problem under Linux which prevented ^Q
-  from being used to resume terminal output after the user had pressed
-  ^S, has been fixed.
-
-Version 1.3.1:
-
-  In vi mode a bug was preventing the history-search-backward and
-  history-search-forward actions from doing anything when invoked on
-  empty lines. On empty lines they now act like up-history and
-  down-history respectively, as in emacs mode.
-
-  When creating shared libraries under Linux, the -soname directive
-  was being used incorrectly. The result is that Linux binaries linked
-  with the 1.2.3, 1.2.4 and 1.3.0 versions of the tecla shared
-  libraries, will refuse to see other versions of the shared library
-  until relinked with version 1.3.1 or higher.
-
-  The configure script can now handle the fact that under Solaris-2.6
-  and earlier, the only curses library is a static one that hides in
-  /usr/ccs/lib. Under Linux it now also caters for old versions of GNU
-  ld which don't accept version scripts.
-
-  The demos are now linked against the shared version of the library
-  if possible. Previously they were always linked with the static
-  version.
-
-Version 1.3.0:
-
-  The major change in this release is the addition of an optional vi
-  command-line editing mode in gl_get_line(), along with lots of new
-  action functions to support its bindings. To enable this, first
-  create a ~/.teclarc file if you don't already have one, then add the
-  following line to it.
-
-   edit-mode vi
-
-  The default vi bindings, which are designed to mimic those of the vi
-  editor as closely as possible, are described in the gl_get_line(3)
-  man page.
-
-  A new convenience function called ef_list_expansions() has been
-  added for listing filename expansions. See the ef_list_expansions(3)
-  man page for details. This is used in a new list-glob binding, bound
-  to ^Xg in emacs mode, and ^G in vi input mode.
-
-  A bug has been fixed in the key-binding table expansion code. This
-  bug would have caused problems to anybody who defined more than
-  about 18 personalized key-bindings in their ~/.teclarc file.
-
-Version 1.2.4:
-
-  Buffered I/O is now used for writing to terminals, and where
-  supported, cursor motion is done with move-n-positions terminfo
-  capabilities instead of doing lots of move-1-position requests. This
-  greatly improves how the library feels over slow links.
-
-  You can now optionally compile different architectures in different
-  directories, without having to make multiple copies of the
-  distribution. This is documented in the INSTALL file.
-
-  The ksh ~+ directive is now supported.
-
-  Thanks to Markus Gyger for the above improvements.
-
-  Documentation has been added to the INSTALL file describing features
-  designed to facilitate configuration and installation of the library
-  as part of larger packages. These features are intended to remove
-  the need to modify the tecla distribution's configuration and build
-  procedures when embedding the libtecla distribution in other package
-  distributions.
-
-  A previous fix to stop the cursor from warping when the last
-  character of the input line was in the last column of the terminal,
-  was only being used for the first terminal line of the input line.
-  It is now used for all subsequent lines as well, as originally
-  intended.
-  
-Version 1.2.3:
-
-  The installation procedure has been better automated with the
-  addition of an autoconf configure script. This means that installers
-  can now compile and install the library by typing:
-
-    ./configure
-    make
-    make install
-
-  On all systems this makes at least the normal static version of the
-  tecla library. It also makes the reentrant version if reentrant
-  POSIX functions are detected.  Under Solaris, Linux and HP-UX the
-  configuration script arranges for shared libraries to be compiled in
-  addition to the static libraries.  It is hoped that installers will
-  return information about how to compile shared libraries on other
-  systems, for inclusion in future releases, and to this end, a new
-  PORTING guide has been provided.
-
-  The versioning number scheme has been changed. This release would
-  have been 1.2c, but instead will be refered to as 1.2.3. The
-  versioning scheme, based on conventions used by Sun Microsystems, is
-  described in configure.in.
-
-  The library was also tested under HP-UX, and this revealed two
-  serious bugs, both of which have now been fixed.
-  
-  The first bug prevented the library from writing control codes to
-  terminals on big-endian machines, with the exception of those
-  running under Solaris. This was due to an int variable being used
-  where a char was needed.
-
-  The second bug had the symptom that on systems that don't use the
-  newline character as the control code for moving the cursor down a
-  line, a newline wasn't started when the user hit enter.
-
-Version 1.2b:
-
-  Two more minor bug fixes:
-
-  Many terminals don't wrap the cursor to the next line when a
-  character is written to the rightmost terminal column. Instead, they
-  delay starting a new line until one more character is written, at
-  which point they move the cursor two positions.  gl_get_line()
-  wasn't aware of this, so cursor repositionings just after writing
-  the last character of a column, caused it to erroneously go up a
-  line. This has now been remedied, using a method that should work
-  regardless of whether a terminal exhibits this behavior or not.
-
-  Some systems dynamically record the current terminal dimensions in
-  environment variables called LINES and COLUMNS. On such systems,
-  during the initial terminal setup, these values should override the
-  static values read from the terminal information databases, and now
-  do.  Previously they were only used if the dimensions returned by
-  terminfo/termcap looked bogus.
-
-Version 1.2a:
-
-  This minor release fixes the following two bugs:
-
-  The initial terminal size and subsequent changes thereto, weren't
-  being noticed by gl_get_line(). This was because the test for the
-  existence of TIOCWINSZ was erroneously placed before the inclusion
-  of termios.h. One of the results was that on input lines that
-  spanned more than one terminal line, the cursor occasionally jumped
-  unexpectedly to the previous terminal line.
-
-  On entering a line that wrapped over multiple terminal lines,
-  gl_get_line() simply output a carriage-return line-feed at the point
-  at which the user pressed return. Thus if one typed in such a line,
-  then moved back onto one of the earlier terminal lines before
-  hitting return, the cursor was left on a line containing part of the
-  line that had just been entered. This didn't do any harm, but it
-  looked a mess.
-
-Version 1.2:
-
-  A new facility for looking up and completing filenames in UNIX-style
-  paths has now been added (eg. you can search for, or complete
-  commands using the UNIX PATH environment variable). See the
-  pca_lookup_file(3) man page.
-
-  The already existing filename completion callback can now be made
-  selective in what types of files it lists. See the
-  cpl_complete_word(3) man page.
-
-  Due to its potential to break applications when changed, the use of
-  the publically defined CplFileArgs structure to configure the
-  cpl_file_completions() callback is now deprecated.  The definition
-  of this structure has been frozen, and its documentation has been
-  removed from the man pages.  It will remain supported, but if you
-  have used it, you are recommended to switch to the new method, which
-  involves a new opaque configuration object, allocated via a provided
-  constructor function, configured via accessor functions, and
-  eventually deleted with a provided destructor function. The
-  cpl_file_completions() callback distinguishes which structure type
-  it has been sent by virtue of a code placed at the start of the new
-  structure by the constructor.  It is assumed that no existing
-  applications set the boolean 'escaped' member of the CplFileArgs
-  structure to 4568.  The new method is documented in the
-  cpl_complete_word(3) man page.
-
-Version 1.1j
-
-  This was the initial public release on freshmeat.org.
-
diff --git a/libtecla-1.4.1/install-sh b/libtecla-1.4.1/install-sh deleted file mode 100755 index e9de238..0000000 --- a/libtecla-1.4.1/install-sh +++ /dev/null @@ -1,251 +0,0 @@ -#!/bin/sh -# -# install - install a program, script, or datafile -# This comes from X11R5 (mit/util/scripts/install.sh). -# -# Copyright 1991 by the Massachusetts Institute of Technology -# -# Permission to use, copy, modify, distribute, and sell this software and its -# documentation for any purpose is hereby granted without fee, provided that -# the above copyright notice appear in all copies and that both that -# copyright notice and this permission notice appear in supporting -# documentation, and that the name of M.I.T. not be used in advertising or -# publicity pertaining to distribution of the software without specific, -# written prior permission. M.I.T. makes no representations about the -# suitability of this software for any purpose. It is provided "as is" -# without express or implied warranty. -# -# Calling this script install-sh is preferred over install.sh, to prevent -# `make' implicit rules from creating a file called install from it -# when there is no Makefile. -# -# This script is compatible with the BSD install script, but was written -# from scratch. It can only install one file at a time, a restriction -# shared with many OS's install programs. - - -# set DOITPROG to echo to test this script - -# Don't use :- since 4.3BSD and earlier shells don't like it. -doit="${DOITPROG-}" - - -# put in absolute paths if you don't have them in your path; or use env. vars. - -mvprog="${MVPROG-mv}" -cpprog="${CPPROG-cp}" -chmodprog="${CHMODPROG-chmod}" -chownprog="${CHOWNPROG-chown}" -chgrpprog="${CHGRPPROG-chgrp}" -stripprog="${STRIPPROG-strip}" -rmprog="${RMPROG-rm}" -mkdirprog="${MKDIRPROG-mkdir}" - -transformbasename="" -transform_arg="" -instcmd="$mvprog" -chmodcmd="$chmodprog 0755" -chowncmd="" -chgrpcmd="" -stripcmd="" -rmcmd="$rmprog -f" -mvcmd="$mvprog" -src="" -dst="" -dir_arg="" - -while [ x"$1" != x ]; do - case $1 in - -c) instcmd="$cpprog" - shift - continue;; - - -d) dir_arg=true - shift - continue;; - - -m) chmodcmd="$chmodprog $2" - shift - shift - continue;; - - -o) chowncmd="$chownprog $2" - shift - shift - continue;; - - -g) chgrpcmd="$chgrpprog $2" - shift - shift - continue;; - - -s) stripcmd="$stripprog" - shift - continue;; - - -t=*) transformarg=`echo $1 | sed 's/-t=//'` - shift - continue;; - - -b=*) transformbasename=`echo $1 | sed 's/-b=//'` - shift - continue;; - - *) if [ x"$src" = x ] - then - src=$1 - else - # this colon is to work around a 386BSD /bin/sh bug - : - dst=$1 - fi - shift - continue;; - esac -done - -if [ x"$src" = x ] -then - echo "install: no input file specified" - exit 1 -else - true -fi - -if [ x"$dir_arg" != x ]; then - dst=$src - src="" - - if [ -d $dst ]; then - instcmd=: - chmodcmd="" - else - instcmd=mkdir - fi -else - -# Waiting for this to be detected by the "$instcmd $src $dsttmp" command -# might cause directories to be created, which would be especially bad -# if $src (and thus $dsttmp) contains '*'. - - if [ -f $src -o -d $src ] - then - true - else - echo "install: $src does not exist" - exit 1 - fi - - if [ x"$dst" = x ] - then - echo "install: no destination specified" - exit 1 - else - true - fi - -# If destination is a directory, append the input filename; if your system -# does not like double slashes in filenames, you may need to add some logic - - if [ -d $dst ] - then - dst="$dst"/`basename $src` - else - true - fi -fi - -## this sed command emulates the dirname command -dstdir=`echo $dst | sed -e 's,[^/]*$,,;s,/$,,;s,^$,.,'` - -# Make sure that the destination directory exists. -# this part is taken from Noah Friedman's mkinstalldirs script - -# Skip lots of stat calls in the usual case. -if [ ! -d "$dstdir" ]; then -defaultIFS=' -' -IFS="${IFS-${defaultIFS}}" - -oIFS="${IFS}" -# Some sh's can't handle IFS=/ for some reason. -IFS='%' -set - `echo ${dstdir} | sed -e 's@/@%@g' -e 's@^%@/@'` -IFS="${oIFS}" - -pathcomp='' - -while [ $# -ne 0 ] ; do - pathcomp="${pathcomp}${1}" - shift - - if [ ! -d "${pathcomp}" ] ; - then - $mkdirprog "${pathcomp}" - else - true - fi - - pathcomp="${pathcomp}/" -done -fi - -if [ x"$dir_arg" != x ] -then - $doit $instcmd $dst && - - if [ x"$chowncmd" != x ]; then $doit $chowncmd $dst; else true ; fi && - if [ x"$chgrpcmd" != x ]; then $doit $chgrpcmd $dst; else true ; fi && - if [ x"$stripcmd" != x ]; then $doit $stripcmd $dst; else true ; fi && - if [ x"$chmodcmd" != x ]; then $doit $chmodcmd $dst; else true ; fi -else - -# If we're going to rename the final executable, determine the name now. - - if [ x"$transformarg" = x ] - then - dstfile=`basename $dst` - else - dstfile=`basename $dst $transformbasename | - sed $transformarg`$transformbasename - fi - -# don't allow the sed command to completely eliminate the filename - - if [ x"$dstfile" = x ] - then - dstfile=`basename $dst` - else - true - fi - -# Make a temp file name in the proper directory. - - dsttmp=$dstdir/#inst.$$# - -# Move or copy the file name to the temp name - - $doit $instcmd $src $dsttmp && - - trap "rm -f ${dsttmp}" 0 && - -# and set any options; do chmod last to preserve setuid bits - -# If any of these fail, we abort the whole thing. If we want to -# ignore errors from any of these, just make sure not to ignore -# errors from the above "$doit $instcmd $src $dsttmp" command. - - if [ x"$chowncmd" != x ]; then $doit $chowncmd $dsttmp; else true;fi && - if [ x"$chgrpcmd" != x ]; then $doit $chgrpcmd $dsttmp; else true;fi && - if [ x"$stripcmd" != x ]; then $doit $stripcmd $dsttmp; else true;fi && - if [ x"$chmodcmd" != x ]; then $doit $chmodcmd $dsttmp; else true;fi && - -# Now rename the file to the real destination. - - $doit $rmcmd -f $dstdir/$dstfile && - $doit $mvcmd $dsttmp $dstdir/$dstfile - -fi && - - -exit 0 diff --git a/libtecla-1.4.1/keytab.c b/libtecla-1.4.1/keytab.c deleted file mode 100644 index b2bab8b..0000000 --- a/libtecla-1.4.1/keytab.c +++ /dev/null @@ -1,827 +0,0 @@ -/* - * Copyright (c) 2000, 2001 by Martin C. Shepherd. - * - * All rights reserved. - * - * Permission is hereby granted, free of charge, to any person obtaining a - * copy of this software and associated documentation files (the - * "Software"), to deal in the Software without restriction, including - * without limitation the rights to use, copy, modify, merge, publish, - * distribute, and/or sell copies of the Software, and to permit persons - * to whom the Software is furnished to do so, provided that the above - * copyright notice(s) and this permission notice appear in all copies of - * the Software and that both the above copyright notice(s) and this - * permission notice appear in supporting documentation. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS - * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT - * OF THIRD PARTY RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR - * HOLDERS INCLUDED IN THIS NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL - * INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING - * FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, - * NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION - * WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - * - * Except as contained in this notice, the name of a copyright holder - * shall not be used in advertising or otherwise to promote the sale, use - * or other dealings in this Software without prior written authorization - * of the copyright holder. - */ - -#include -#include -#include -#include - -#include "keytab.h" -#include "getline.h" -#include "strngmem.h" - -static int _kt_extend_table(KeyTab *kt); -static int _kt_parse_keybinding_string(const char *keyseq, - char *binary, int *nc); -static int _kt_compare_strings(const char *s1, int n1, const char *s2, int n2); -static void _kt_assign_action(KeySym *sym, KtBinder binder, KtKeyFn *keyfn); -static char _kt_backslash_escape(const char *string, const char **endp); -static int _kt_is_emacs_meta(const char *string); -static int _kt_is_emacs_ctrl(const char *string); - -/*....................................................................... - * Create a new key-binding symbol table. - * - * Output: - * return KeyTab * The new object, or NULL on error. - */ -KeyTab *_new_KeyTab(void) -{ - KeyTab *kt; /* The object to be returned */ -/* - * Allocate the container. - */ - kt = (KeyTab *) malloc(sizeof(KeyTab)); - if(!kt) { - fprintf(stderr, "new_KeyTab: Insufficient memory.\n"); - return NULL; - }; -/* - * Before attempting any operation that might fail, initialize the - * container at least up to the point at which it can safely be passed - * to del_KeyTab(). - */ - kt->size = KT_TABLE_INC; - kt->nkey = 0; - kt->table = NULL; - kt->actions = NULL; - kt->smem = NULL; -/* - * Allocate the table. - */ - kt->table = (KeySym *) malloc(sizeof(kt->table[0]) * kt->size); - if(!kt->table) { - fprintf(stderr, "new_KeyTab: Insufficient memory for table of size %d.\n", - kt->size); - return _del_KeyTab(kt); - }; -/* - * Allocate a hash table of actions. - */ - kt->actions = _new_HashTable(NULL, KT_HASH_SIZE, IGNORE_CASE, NULL, 0); - if(!kt->actions) - return _del_KeyTab(kt); -/* - * Allocate a string allocation object. This allows allocation of - * small strings without fragmenting the heap. - */ - kt->smem = _new_StringMem("new_KeyTab", KT_TABLE_INC); - if(!kt->smem) - return _del_KeyTab(kt); - return kt; -} - -/*....................................................................... - * Delete a KeyTab object. - * - * Input: - * kt KeyTab * The object to be deleted. - * Output: - * return KeyTab * The deleted object (always NULL). - */ -KeyTab *_del_KeyTab(KeyTab *kt) -{ - if(kt) { - if(kt->table) - free(kt->table); - kt->actions = _del_HashTable(kt->actions); - kt->smem = _del_StringMem("del_KeyTab", kt->smem, 1); - free(kt); - }; - return NULL; -} - -/*....................................................................... - * Increase the size of the table to accomodate more keys. - * - * Input: - * kt KeyTab * The table to be extended. - * Output: - * return int 0 - OK. - * 1 - Error. - */ -static int _kt_extend_table(KeyTab *kt) -{ -/* - * Attempt to increase the size of the table. - */ - KeySym *newtab = (KeySym *) realloc(kt->table, sizeof(kt->table[0]) * - (kt->size + KT_TABLE_INC)); -/* - * Failed? - */ - if(!newtab) { - fprintf(stderr, - "getline(): Insufficient memory to extend keybinding table.\n"); - return 1; - }; -/* - * Install the resized table. - */ - kt->table = newtab; - kt->size += KT_TABLE_INC; - return 0; -} - -/*....................................................................... - * Add, update or remove a keybinding to the table. - * - * Input: - * kt KeyTab * The table to add the binding to. - * binder KtBinder The source of the binding. - * keyseq const char * The key-sequence to bind. - * action char * The action to associate with the key sequence, or - * NULL to remove the action associated with the - * key sequence. - * Output: - * return int 0 - OK. - * 1 - Error. - */ -int _kt_set_keybinding(KeyTab *kt, KtBinder binder, const char *keyseq, - const char *action) -{ - KtKeyFn *keyfn; /* The action function */ -/* - * Check arguments. - */ - if(kt==NULL || !keyseq) { - fprintf(stderr, "kt_set_keybinding: NULL argument(s).\n"); - return 1; - }; -/* - * Lookup the function that implements the specified action. - */ - if(!action) { - keyfn = 0; - } else { - Symbol *sym = _find_HashSymbol(kt->actions, action); - if(!sym) { - fprintf(stderr, "getline: Unknown key-binding action: %s\n", action); - return 1; - }; - keyfn = (KtKeyFn *) sym->fn; - }; -/* - * Record the action in the table. - */ - return _kt_set_keyfn(kt, binder, keyseq, keyfn); -} - -/*....................................................................... - * Add, update or remove a keybinding to the table, specifying an action - * function directly. - * - * Input: - * kt KeyTab * The table to add the binding to. - * binder KtBinder The source of the binding. - * keyseq char * The key-sequence to bind. - * keyfn KtKeyFn * The action function, or NULL to remove any existing - * action function. - * Output: - * return int 0 - OK. - * 1 - Error. - */ -int _kt_set_keyfn(KeyTab *kt, KtBinder binder, const char *keyseq, - KtKeyFn *keyfn) -{ - const char *kptr; /* A pointer into keyseq[] */ - char *binary; /* The binary version of keyseq[] */ - int nc; /* The number of characters in binary[] */ - int first,last; /* The first and last entries in the table which */ - /* minimally match. */ - int size; /* The size to allocate for the binary string */ -/* - * Check arguments. - */ - if(kt==NULL || !keyseq) { - fprintf(stderr, "kt_set_keybinding: NULL argument(s).\n"); - return 1; - }; -/* - * Work out a pessimistic estimate of how much space will be needed - * for the binary copy of the string, noting that binary meta characters - * embedded in the input string get split into two characters. - */ - for(size=0,kptr = keyseq; *kptr; kptr++) - size += IS_META_CHAR(*kptr) ? 2 : 1; -/* - * Allocate a string that has the length of keyseq[]. - */ - binary = _new_StringMemString(kt->smem, size + 1); - if(!binary) { - fprintf(stderr, - "gl_get_line: Insufficient memory to record key sequence.\n"); - return 1; - }; -/* - * Convert control and octal character specifications to binary characters. - */ - if(_kt_parse_keybinding_string(keyseq, binary, &nc)) { - binary = _del_StringMemString(kt->smem, binary); - return 1; - }; -/* - * Lookup the position in the table at which to insert the binding. - */ - switch(_kt_lookup_keybinding(kt, binary, nc, &first, &last)) { -/* - * If an exact match for the key-sequence is already in the table, - * simply replace its binding function (or delete the entry if - * the new binding is 0). - */ - case KT_EXACT_MATCH: - if(keyfn) { - _kt_assign_action(kt->table + first, binder, keyfn); - } else { - _del_StringMemString(kt->smem, kt->table[first].keyseq); - memmove(kt->table + first, kt->table + first + 1, - (kt->nkey - first - 1) * sizeof(kt->table[0])); - kt->nkey--; - }; - binary = _del_StringMemString(kt->smem, binary); - break; -/* - * If an ambiguous match has been found and we are installing a - * callback, then our new key-sequence would hide all of the ambiguous - * matches, so we shouldn't allow it. - */ - case KT_AMBIG_MATCH: - if(keyfn) { - fprintf(stderr, - "getline: Can't bind \"%s\", because it's a prefix of another binding.\n", - keyseq); - binary = _del_StringMemString(kt->smem, binary); - return 1; - }; - break; -/* - * If the entry doesn't exist, create it. - */ - case KT_NO_MATCH: -/* - * Add a new binding? - */ - if(keyfn) { - KeySym *sym; -/* - * We will need a new entry, extend the table if needed. - */ - if(kt->nkey + 1 > kt->size) { - if(_kt_extend_table(kt)) { - binary = _del_StringMemString(kt->smem, binary); - return 1; - }; - }; -/* - * Make space to insert the new key-sequence before 'last'. - */ - if(last < kt->nkey) { - memmove(kt->table + last + 1, kt->table + last, - (kt->nkey - last) * sizeof(kt->table[0])); - }; -/* - * Insert the new binding in the vacated position. - */ - sym = kt->table + last; - sym->keyseq = binary; - sym->nc = nc; - sym->user_fn = sym->term_fn = sym->norm_fn = sym->keyfn = 0; - _kt_assign_action(sym, binder, keyfn); - kt->nkey++; - }; - break; - case KT_BAD_MATCH: - binary = _del_StringMemString(kt->smem, binary); - return 1; - break; - }; - return 0; -} - -/*....................................................................... - * Perform a min-match lookup of a key-binding. - * - * Input: - * kt KeyTab * The keybinding table to lookup in. - * binary_keyseq char * The binary key-sequence to lookup. - * nc int the number of characters in keyseq[]. - * Input/Output: - * first,last int * If there is an ambiguous or exact match, the indexes - * of the first and last symbols that minimally match - * will be assigned to *first and *last respectively. - * If there is no match, then first and last will - * bracket the location where the symbol should be - * inserted. - * - * Output: - * return KtKeyMatch One of the following enumerators: - * KT_EXACT_MATCH - An exact match was found. - * KT_AMBIG_MATCH - An ambiguous match was found. - * KT_NO_MATCH - No match was found. - * KT_BAD_MATCH - An error occurred while searching. - */ -KtKeyMatch _kt_lookup_keybinding(KeyTab *kt, const char *binary_keyseq, int nc, - int *first, int *last) -{ - int mid; /* The index at which to bisect the table */ - int bot; /* The lowest index of the table not searched yet */ - int top; /* The highest index of the table not searched yet */ - int test; /* The return value of strcmp() */ -/* - * Check the arguments. - */ - if(!kt || !binary_keyseq || !first || !last || nc < 0) { - fprintf(stderr, "kt_lookup_keybinding: NULL argument(s).\n"); - return KT_BAD_MATCH; - }; -/* - * Perform a binary search for the key-sequence. - */ - bot = 0; - top = kt->nkey - 1; - while(top >= bot) { - mid = (top + bot)/2; - test = _kt_compare_strings(kt->table[mid].keyseq, kt->table[mid].nc, - binary_keyseq, nc); - if(test > 0) - top = mid - 1; - else if(test < 0) - bot = mid + 1; - else { - *first = *last = mid; - return KT_EXACT_MATCH; - }; - }; -/* - * An exact match wasn't found, but top is the index just below the - * index where a match would be found, and bot is the index just above - * where the match ought to be found. - */ - *first = top; - *last = bot; -/* - * See if any ambiguous matches exist, and if so make *first and *last - * refer to the first and last matches. - */ - if(*last < kt->nkey && kt->table[*last].nc > nc && - _kt_compare_strings(kt->table[*last].keyseq, nc, binary_keyseq, nc)==0) { - *first = *last; - while(*last+1 < kt->nkey && kt->table[*last+1].nc > nc && - _kt_compare_strings(kt->table[*last+1].keyseq, nc, binary_keyseq, nc)==0) - (*last)++; - return KT_AMBIG_MATCH; - }; -/* - * No match. - */ - return KT_NO_MATCH; -} - -/*....................................................................... - * Convert a keybinding string into a uniq binary representation. - * - * Control characters can be given directly in their binary form, - * expressed as either ^ or C-, followed by the character, expressed in - * octal, like \129 or via C-style backslash escapes, with the addition - * of '\E' to denote the escape key. Similarly, meta characters can be - * given directly in binary or expressed as M- followed by the character. - * Meta characters are recorded as two characters in the binary output - * string, the first being the escape key, and the second being the key - * that was modified by the meta key. This means that binding to - * \EA or ^[A or M-A are all equivalent. - * - * Input: - * keyseq char * The key sequence being added. - * Input/Output: - * binary char * The binary version of the key sequence will be - * assigned to binary[], which must have at least - * as many characters as keyseq[] plus the number - * of embedded binary meta characters. - * nc int * The number of characters assigned to binary[] - * will be recorded in *nc. - * Output: - * return int 0 - OK. - * 1 - Error. - */ -static int _kt_parse_keybinding_string(const char *keyseq, char *binary, - int *nc) -{ - const char *iptr = keyseq; /* Pointer into keyseq[] */ - char *optr = binary; /* Pointer into binary[] */ -/* - * Parse the input characters until they are exhausted or the - * output string becomes full. - */ - while(*iptr) { -/* - * Check for special characters. - */ - switch(*iptr) { - case '^': /* A control character specification */ -/* - * Convert the caret expression into the corresponding control - * character unless no character follows the caret, in which case - * record a literal caret. - */ - if(iptr[1]) { - *optr++ = MAKE_CTRL(iptr[1]); - iptr += 2; - } else { - *optr++ = *iptr++; - }; - break; -/* - * A backslash-escaped character? - */ - case '\\': -/* - * Convert the escape sequence to a binary character. - */ - *optr++ = _kt_backslash_escape(iptr+1, &iptr); - break; -/* - * Possibly an emacs-style meta character? - */ - case 'M': - if(_kt_is_emacs_meta(iptr)) { - *optr++ = GL_ESC_CHAR; - iptr += 2; - } else { - *optr++ = *iptr++; - }; - break; -/* - * Possibly an emacs-style control character specification? - */ - case 'C': - if(_kt_is_emacs_ctrl(iptr)) { - *optr++ = MAKE_CTRL(iptr[2]); - iptr += 3; - } else { - *optr++ = *iptr++; - }; - break; - default: - -/* - * Convert embedded meta characters into an escape character followed - * by the meta-unmodified character. - */ - if(IS_META_CHAR(*iptr)) { - *optr++ = GL_ESC_CHAR; - *optr++ = META_TO_CHAR(*iptr); - iptr++; -/* - * To allow keysequences that start with printable characters to - * be distinguished from the cursor-key keywords, prepend a backslash - * to the former. This same operation is performed in gl_interpret_char() - * before looking up a keysequence that starts with a printable character. - */ - } else if(iptr==keyseq && !IS_CTRL_CHAR(*iptr) && - strcmp(keyseq, "up") != 0 && strcmp(keyseq, "down") != 0 && - strcmp(keyseq, "left") != 0 && strcmp(keyseq, "right") != 0) { - *optr++ = '\\'; - *optr++ = *iptr++; - } else { - *optr++ = *iptr++; - }; - }; - }; -/* - * How many characters were placed in the output array? - */ - *nc = optr - binary; - return 0; -} - -/*....................................................................... - * Add, remove or modify an action. - * - * Input: - * kt KeyTab * The key-binding table. - * action char * The name of the action. - * fn KtKeyFn * The function that implements the action, or NULL - * to remove an existing action. - * Output: - * return int 0 - OK. - * 1 - Error. - */ -int _kt_set_action(KeyTab *kt, const char *action, KtKeyFn *fn) -{ - Symbol *sym; /* The symbol table entry of the action */ -/* - * Check the arguments. - */ - if(!kt || !action) { - fprintf(stderr, "kt_set_action: NULL argument(s).\n"); - return 1; - }; -/* - * If no function was provided, delete an existing action. - */ - if(!fn) { - sym = _del_HashSymbol(kt->actions, action); - return 0; - }; -/* - * If the action already exists, replace its action function. - */ - sym = _find_HashSymbol(kt->actions, action); - if(sym) { - sym->fn = (void (*)(void))fn; - return 0; - }; -/* - * Add a new action. - */ - if(!_new_HashSymbol(kt->actions, action, 0, (void (*)(void))fn, NULL, 0)) { - fprintf(stderr, "Insufficient memory to record new key-binding action.\n"); - return 1; - }; - return 0; -} - -/*....................................................................... - * Compare two strings of specified length which may contain embedded - * ascii NUL's. - * - * Input: - * s1 char * The first of the strings to be compared. - * n1 int The length of the string in s1. - * s2 char * The second of the strings to be compared. - * n2 int The length of the string in s2. - * Output: - * return int < 0 if(s1 < s2) - * 0 if(s1 == s2) - * > 0 if(s1 > s2) - */ -static int _kt_compare_strings(const char *s1, int n1, const char *s2, int n2) -{ - int i; -/* - * Find the first character where the two strings differ. - */ - for(i=0; iuser_fn = keyfn; - break; - case KTB_TERM: - sym->term_fn = keyfn; - break; - case KTB_NORM: - default: - sym->norm_fn = keyfn; - break; - }; -/* - * Which of the current set of bindings should be used? - */ - if(sym->user_fn) - sym->keyfn = sym->user_fn; - else if(sym->norm_fn) - sym->keyfn = sym->norm_fn; - else - sym->keyfn = sym->term_fn; - return; -} - -/*....................................................................... - * Remove all key bindings that came from a specified source. - * - * Input: - * kt KeyTab * The table of key bindings. - * binder KtBinder The source of the bindings to be cleared. - */ -void _kt_clear_bindings(KeyTab *kt, KtBinder binder) -{ - int oldkey; /* The index of a key in the original binding table */ - int newkey; /* The index of a key in the updated binding table */ -/* - * If there is no table, then no bindings exist to be deleted. - */ - if(!kt) - return; -/* - * Clear bindings of the given source. - */ - for(oldkey=0; oldkeynkey; oldkey++) - _kt_assign_action(kt->table + oldkey, binder, 0); -/* - * Delete entries that now don't have a binding from any source. - */ - newkey = 0; - for(oldkey=0; oldkeynkey; oldkey++) { - KeySym *sym = kt->table + oldkey; - if(!sym->keyfn) { - _del_StringMemString(kt->smem, sym->keyseq); - } else { - if(oldkey != newkey) - kt->table[newkey] = *sym; - newkey++; - }; - }; -/* - * Record the number of keys that were kept. - */ - kt->nkey = newkey; - return; -} - -/*....................................................................... - * Translate a backslash escape sequence to a binary character. - * - * Input: - * string const char * The characters that follow the backslash. - * Input/Output: - * endp const char ** If endp!=NULL, on return *endp will be made to - * point to the character in string[] which follows - * the escape sequence. - * Output: - * return char The binary character. - */ -static char _kt_backslash_escape(const char *string, const char **endp) -{ - char c; /* The output character */ -/* - * Is the backslash followed by one or more octal digits? - */ - switch(*string) { - case '0': case '1': case '2': case '3': - case '4': case '5': case '6': case '7': - c = strtol(string, (char **)&string, 8); - break; - case 'a': - c = '\a'; - string++; - break; - case 'b': - c = '\b'; - string++; - break; - case 'e': case 'E': /* Escape */ - c = GL_ESC_CHAR; - string++; - break; - case 'f': - c = '\f'; - string++; - break; - case 'n': - c = '\n'; - string++; - break; - case 'r': - c = '\r'; - string++; - break; - case 't': - c = '\t'; - string++; - break; - case 'v': - c = '\v'; - string++; - break; - case '\0': - c = '\\'; - break; - default: - c = *string++; - break; - }; -/* - * Report the character which follows the escape sequence. - */ - if(endp) - *endp = string; - return c; -} - -/*....................................................................... - * Return non-zero if the next two characters are M- and a third character - * follows. Otherwise return 0. - * - * Input: - * string const char * The sub-string to scan. - * Output: - * return int 1 - The next two characters are M- and these - * are followed by at least one character. - * 0 - The next two characters aren't M- or no - * character follows a M- pair. - */ -static int _kt_is_emacs_meta(const char *string) -{ - return *string++ == 'M' && *string++ == '-' && *string; -} - -/*....................................................................... - * Return non-zero if the next two characters are C- and a third character - * follows. Otherwise return 0. - * - * Input: - * string const char * The sub-string to scan. - * Output: - * return int 1 - The next two characters are C- and these - * are followed by at least one character. - * 0 - The next two characters aren't C- or no - * character follows a C- pair. - */ -static int _kt_is_emacs_ctrl(const char *string) -{ - return *string++ == 'C' && *string++ == '-' && *string; -} - -/*....................................................................... - * Merge an array of bindings with existing bindings. - * - * Input: - * kt KeyTab * The table of key bindings. - * binder KtBinder The source of the bindings. - * bindings const KtKeyBinding * The array of bindings. - * n int The number of bindings in bindings[]. - * Output: - * return int 0 - OK. - * 1 - Error. - */ -int _kt_add_bindings(KeyTab *kt, KtBinder binder, const KtKeyBinding *bindings, - unsigned n) -{ - int i; -/* - * Check the arguments. - */ - if(!kt || !bindings) { - fprintf(stderr, "_kt_add_bindings: NULL argument(s).\n"); - return 1; - }; -/* - * Install the array of bindings. - */ - for(i=0; i /* FILE * */ -#include /* size_t */ -#include /* time_t */ - -/* - * The following are the three components of the libtecla version number. - * Note that it is better to use the libtecla_version() function than these - * macros since the macros only tell you which version of the library your - * code was compiled against, whereas the libtecla_version() function - * tells you which version of the shared tecla library your program is - * actually linked to. - */ -#define TECLA_MAJOR_VER 1 -#define TECLA_MINOR_VER 4 -#define TECLA_MICRO_VER 1 - -/*....................................................................... - * Query the version number of the tecla library. - * - * Input: - * major int * The major version number of the library - * will be assigned to *major. This number is - * only incremented when a change to the library is - * made that breaks binary (shared library) and/or - * compilation backwards compatibility. - * minor int * The minor version number of the library - * will be assigned to *minor. This number is - * incremented whenever new functions are added to - * the public API. - * micro int * The micro version number of the library will be - * assigned to *micro. This number is incremented - * whenever internal changes are made that don't - * change the public API, such as bug fixes and - * performance enhancements. - */ -void libtecla_version(int *major, int *minor, int *micro); - -/*----------------------------------------------------------------------- - * The getline module provides interactive command-line input, recall - * and editing by users at terminals. See the gl_getline(3) man page for - * more details. - *-----------------------------------------------------------------------*/ - -/* - * Provide an opaque handle for the resource object that is defined in - * getline.h. - */ -typedef struct GetLine GetLine; - -/* - * The following two functions are used to create and delete the - * resource objects that are used by the gl_getline() function. - */ -GetLine *new_GetLine(size_t linelen, size_t histlen); -GetLine *del_GetLine(GetLine *gl); - -/* - * Read a line into an internal buffer of gl. - */ -char *gl_get_line(GetLine *gl, const char *prompt, const char *start_line, - int start_pos); - -/* - * Configure the application specific and/or user-specific behavior of - * gl_get_line(). - */ -int gl_configure_getline(GetLine *gl, const char *app_string, - const char *app_file, const char *user_file); - -/*----------------------------------------------------------------------- - * The file-expansion module provides facilities for expanding ~user/ and - * $envvar expressions, and for expanding glob-style wildcards. - * See the ef_expand_file(3) man page for more details. - *-----------------------------------------------------------------------*/ - -/* - * ExpandFile objects contain the resources needed to expand pathnames. - */ -typedef struct ExpandFile ExpandFile; - -/* - * The following functions are used to create and delete the resource - * objects that are used by the ef_expand_file() function. - */ -ExpandFile *new_ExpandFile(void); -ExpandFile *del_ExpandFile(ExpandFile *ef); - -/* - * A container of the following type is returned by ef_expand_file(). - */ -typedef struct { - int exists; /* True if the files in files[] currently exist. */ - /* This only time that this may not be true is if */ - /* the input filename didn't contain any wildcards */ - /* and thus wasn't matched against existing files. */ - /* In this case the single entry in 'nfile' may not */ - /* refer to an existing file. */ - int nfile; /* The number of files in files[] */ - char **files; /* An array of 'nfile' filenames. */ -} FileExpansion; - -/* - * The ef_expand_file() function expands a specified pathname, converting - * ~user/ and ~/ patterns at the start of the pathname to the - * corresponding home directories, replacing $envvar with the value of - * the corresponding environment variable, and then, if there are any - * wildcards, matching these against existing filenames. - * - * If no errors occur, a container is returned containing the array of - * files that resulted from the expansion. If there were no wildcards - * in the input pathname, this will contain just the original pathname - * after expansion of ~ and $ expressions. If there were any wildcards, - * then the array will contain the files that matched them. Note that - * if there were any wildcards but no existing files match them, this - * is counted as an error and NULL is returned. - * - * The supported wildcards and their meanings are: - * * - Match any sequence of zero or more characters. - * ? - Match any single character. - * [chars] - Match any single character that appears in 'chars'. - * If 'chars' contains an expression of the form a-b, - * then any character between a and b, including a and b, - * matches. The '-' character looses its special meaning - * as a range specifier when it appears at the start - * of the sequence of characters. - * [^chars] - The same as [chars] except that it matches any single - * character that doesn't appear in 'chars'. - * - * Wildcard expressions are applied to individual filename components. - * They don't match across directory separators. A '.' character at - * the beginning of a filename component must also be matched - * explicitly by a '.' character in the input pathname, since these - * are UNIX's hidden files. - * - * Input: - * fe ExpandFile * The pathname expansion resource object. - * path const char * The path name to be expanded. - * pathlen int The length of the suffix of path[] that - * constitutes the filename to be expanded, - * or -1 to specify that the whole of the - * path string should be used. - * Output: - * return FileExpansion * A pointer to a results container within the - * given ExpandFile object. This contains an - * array of the pathnames that resulted from - * expanding ~ and $ expressions and from - * matching any wildcards, sorted into lexical - * order. - * - * This container and its contents will be - * recycled on subsequent calls, so if you need - * to keep the results of two successive runs, - * you will either have to allocate a private - * copy of the array, or use two ExpandFile - * objects. - * - * On error, NULL is returned. A description - * of the error can be acquired by calling the - * ef_last_error() function. - */ -FileExpansion *ef_expand_file(ExpandFile *ef, const char *path, int pathlen); - -/*....................................................................... - * Print out an array of matching files. - * - * Input: - * result FileExpansion * The container of the sorted array of - * expansions. - * fp FILE * The output stream to write to. - * term_width int The width of the terminal. - * Output: - * return int 0 - OK. - * 1 - Error. - */ -int ef_list_expansions(FileExpansion *result, FILE *fp, int term_width); - -/* - * The ef_last_error() function returns a description of the last error - * that occurred in a call ef_expand_file(). Note that this message is - * contained in an array which is allocated as part of *ef, and its - * contents thus potentially change on every call to ef_expand_file(). - */ -const char *ef_last_error(ExpandFile *ef); - -/*----------------------------------------------------------------------- - * The WordCompletion module is used for completing incomplete words, such - * as filenames. Programs can use functions within this module to register - * their own customized completion functions. - *-----------------------------------------------------------------------*/ - -/* - * Ambiguous completion matches are recorded in objects of the - * following type. - */ -typedef struct WordCompletion WordCompletion; - -/* - * Create a new completion object. - */ -WordCompletion *new_WordCompletion(void); - -/* - * Delete a redundant completion object. - */ -WordCompletion *del_WordCompletion(WordCompletion *cpl); - -/*....................................................................... - * Callback functions declared and prototyped using the following macro - * are called upon to return an array of possible completion suffixes - * for the token that precedes a specified location in the given - * input line. It is up to this function to figure out where the token - * starts, and to call cpl_add_completion() to register each possible - * completion before returning. - * - * Input: - * cpl WordCompletion * An opaque pointer to the object that will - * contain the matches. This should be filled - * via zero or more calls to cpl_add_completion(). - * data void * The anonymous 'data' argument that was - * passed to cpl_complete_word() or - * gl_customize_completion()). - * line const char * The current input line. - * word_end int The index of the character in line[] which - * follows the end of the token that is being - * completed. - * Output - * return int 0 - OK. - * 1 - Error. - */ -#define CPL_MATCH_FN(fn) int (fn)(WordCompletion *cpl, void *data, \ - const char *line, int word_end) -typedef CPL_MATCH_FN(CplMatchFn); - -/*....................................................................... - * Optional callback functions declared and prototyped using the - * following macro are called upon to return non-zero if a given - * file, specified by its pathname, is to be included in a list of - * completions. - * - * Input: - * data void * The application specified pointer which - * was specified when this callback function - * was registered. This can be used to have - * anything you like passed to your callback. - * pathname const char * The pathname of the file to be checked to - * see if it should be included in the list - * of completions. - * Output - * return int 0 - Ignore this file. - * 1 - Do include this file in the list - * of completions. - */ -#define CPL_CHECK_FN(fn) int (fn)(void *data, const char *pathname) -typedef CPL_CHECK_FN(CplCheckFn); - -/* - * You can use the following CplCheckFn callback function to only - * have executables included in a list of completions. - */ -CPL_CHECK_FN(cpl_check_exe); - -/* - * cpl_file_completions() is the builtin filename completion callback - * function. This can also be called by your own custom CPL_MATCH_FN() - * callback functions. To do this pass on all of the arguments of your - * custom callback function to cpl_file_completions(), with the exception - * of the (void *data) argument. The data argument should either be passed - * NULL to request the default behaviour of the file-completion function, - * or be passed a pointer to a CplFileConf structure (see below). In the - * latter case the contents of the structure modify the behavior of the - * file-completer. - */ -CPL_MATCH_FN(cpl_file_completions); - -/* - * Objects of the following type can be used to change the default - * behavior of the cpl_file_completions() callback function. - */ -typedef struct CplFileConf CplFileConf; - -/* - * If you want to change the behavior of the cpl_file_completions() - * callback function, call the following function to allocate a - * configuration object, then call one or more of the subsequent - * functions to change any of the default configuration parameters - * that you don't want. This function returns NULL when there is - * insufficient memory. - */ -CplFileConf *new_CplFileConf(void); - -/* - * If backslashes in the prefix being passed to cpl_file_completions() - * should be treated as literal characters, call the following function - * with literal=1. Otherwise the default is to treat them as escape - * characters which remove the special meanings of spaces etc.. - */ -void cfc_literal_escapes(CplFileConf *cfc, int literal); - -/* - * Before calling cpl_file_completions(), call this function if you - * know the index at which the filename prefix starts in the input line. - * Otherwise by default, or if you specify start_index to be -1, the - * filename is taken to start after the first unescaped space preceding - * the cursor, or the start of the line, which ever comes first. - */ -void cfc_file_start(CplFileConf *cfc, int start_index); - -/* - * If you only want certain types of files to be included in the - * list of completions, use the following function to specify a - * callback function which will be called to ask whether a given file - * should be included. The chk_data argument is will be passed to the - * callback function whenever it is called and can be anything you want. - */ -void cfc_set_check_fn(CplFileConf *cfc, CplCheckFn *chk_fn, void *chk_data); - -/* - * The following function deletes a CplFileConf objects previously - * returned by new_CplFileConf(). It always returns NULL. - */ -CplFileConf *del_CplFileConf(CplFileConf *cfc); - -/* - * The following configuration structure is deprecated. Do not change - * its contents, since this will break any programs that still use it, - * and don't use it in new programs. Instead use opaque CplFileConf - * objects as described above. cpl_file_completions() figures out - * what type of structure you pass it, by virtue of a magic int code - * placed at the start of CplFileConf object by new_CplFileConf(). - */ -typedef struct { - int escaped; /* Opposite to the argument of cfc_literal_escapes() */ - int file_start; /* Equivalent to the argument of cfc_file_start() */ -} CplFileArgs; -/* - * This initializes the deprecated CplFileArgs structures. - */ -void cpl_init_FileArgs(CplFileArgs *cfa); - -/*....................................................................... - * When an error occurs while performing a completion, custom completion - * callback functions should register a terse description of the error - * by calling cpl_record_error(). This message will then be returned on - * the next call to cpl_last_error() and used by getline to display an - * error message to the user. - * - * Input: - * cpl WordCompletion * The string-completion resource object that was - * originally passed to the callback. - * errmsg const char * The description of the error. - */ -void cpl_record_error(WordCompletion *cpl, const char *errmsg); - -/*....................................................................... - * This function can be used to replace the builtin filename-completion - * function with one of the user's choice. The user's completion function - * has the option of calling the builtin filename-completion function - * if it believes that the token that it has been presented with is a - * filename (see cpl_file_completions() above). - * - * Input: - * gl GetLine * The resource object of the command-line input - * module. - * data void * This is passed to match_fn() whenever it is - * called. It could, for example, point to a - * symbol table that match_fn() would look up - * matches in. - * match_fn CplMatchFn * The function that will identify the prefix - * to be completed from the input line, and - * report matching symbols. - * Output: - * return int 0 - OK. - * 1 - Error. - */ -int gl_customize_completion(GetLine *gl, void *data, CplMatchFn *match_fn); - -/*....................................................................... - * Change the terminal (or stream) that getline interacts with. - * - * Input: - * gl GetLine * The resource object of the command-line input - * module. - * input_fp FILE * The stdio stream to read from. - * output_fp FILE * The stdio stream to write to. - * term const char * The terminal type. This can be NULL if - * either or both of input_fp and output_fp don't - * refer to a terminal. Otherwise it should refer - * to an entry in the terminal information database. - * Output: - * return int 0 - OK. - * 1 - Error. - */ -int gl_change_terminal(GetLine *gl, FILE *input_fp, FILE *output_fp, - const char *term); - -/*....................................................................... - * The following functions can be used to save and restore the contents - * of the history buffer. - * - * Input: - * gl GetLine * The resource object of the command-line input - * module. - * filename const char * The name of the new file to write to. - * comment const char * Extra information such as timestamps will - * be recorded on a line started with this - * string, the idea being that the file can - * double as a command file. Specify "" if - * you don't care. Be sure to specify the - * same string to both functions. - * max_lines int The maximum number of lines to save, or -1 - * to save all of the lines in the history - * list. - * Output: - * return int 0 - OK. - * 1 - Error. - */ -int gl_save_history(GetLine *gl, const char *filename, const char *comment, - int max_lines); -int gl_load_history(GetLine *gl, const char *filename, const char *comment); - -/* - * Enumerate file-descriptor events that can be waited for. - */ -typedef enum { - GLFD_READ, /* Watch for data waiting to be read from a file descriptor */ - GLFD_WRITE, /* Watch for ability to write to a file descriptor */ - GLFD_URGENT /* Watch for urgent out-of-band data on the file descriptor */ -} GlFdEvent; - -/* - * The following enumeration is used for the return status of file - * descriptor event callbacks. - */ -typedef enum { - GLFD_ABORT, /* Cause gl_get_line() to abort with an error */ - GLFD_REFRESH, /* Redraw the input line and continue waiting for input */ - GLFD_CONTINUE /* Continue to wait for input, without redrawing the line */ -} GlFdStatus; - -/*....................................................................... - * While gl_get_line() is waiting for terminal input, it can also be - * asked to listen for activity on arbitrary file descriptors. - * Callback functions of the following type can be registered to be - * called when activity is seen. If your callback needs to write to - * the terminal or use signals, please see the gl_get_line(3) man - * page. - * - * Input: - * gl GetLine * The gl_get_line() resource object. You can use - * this safely to call gl_watch_fd() or - * gl_watch_time(). The effect of calling other - * functions that take a gl argument is undefined, - * and must be avoided. - * data void * A pointer to arbitrary callback data, as originally - * registered with gl_watch_fd(). - * fd int The file descriptor that has activity. - * event GlFdEvent The activity seen on the file descriptor. The - * inclusion of this argument allows the same - * callback to be registered for multiple events. - * Output: - * return GlFdStatus GLFD_ABORT - Cause gl_get_line() to abort with - * an error (set errno if you need it). - * GLFD_REFRESH - Redraw the input line and continue - * waiting for input. Use this if you - * wrote something to the terminal. - * GLFD_CONTINUE - Continue to wait for input, without - * redrawing the line. - */ -#define GL_FD_EVENT_FN(fn) GlFdStatus (fn)(GetLine *gl, void *data, int fd, \ - GlFdEvent event) -typedef GL_FD_EVENT_FN(GlFdEventFn); - -/*....................................................................... - * Where possible, register a function and associated data to be called - * whenever a specified event is seen on a file descriptor. - * - * Input: - * gl GetLine * The resource object of the command-line input - * module. - * fd int The file descriptor to watch. - * event GlFdEvent The type of activity to watch for. - * callback GlFdEventFn * The function to call when the specified - * event occurs. Setting this to 0 removes - * any existing callback. - * data void * A pointer to arbitrary data to pass to the - * callback function. - * Output: - * return int 0 - OK. - * 1 - Either gl==NULL, or this facility isn't - * available on the the host system - * (ie. select() isn't available). No - * error message is generated in the latter - * case. - */ -int gl_watch_fd(GetLine *gl, int fd, GlFdEvent event, - GlFdEventFn *callback, void *data); - -/*....................................................................... - * Switch history streams. History streams represent separate history - * lists recorded within a single history buffer. Different streams - * are distinguished by integer identifiers chosen by the calling - * appplicaton. Initially new_GetLine() sets the stream identifier to - * 0. Whenever a new line is appended to the history list, the current - * stream identifier is recorded with it, and history lookups only - * consider lines marked with the current stream identifier. - * - * Input: - * gl GetLine * The resource object of gl_get_line(). - * id unsigned The new history stream identifier. - * Output: - * return int 0 - OK. - * 1 - Error. - */ -int gl_group_history(GetLine *gl, unsigned id); - -/*....................................................................... - * Display the contents of the history list. - * - * Input: - * gl GetLine * The resource object of gl_get_line(). - * fp FILE * The stdio output stream to write to. - * fmt const char * A format string. This containing characters to be - * written verbatim, plus any of the following - * format directives: - * %D - The date, formatted like 2001-11-20 - * %T - The time of day, formatted like 23:59:59 - * %N - The sequential entry number of the - * line in the history buffer. - * %G - The number of the history group that - * the line belongs to. - * %% - A literal % character. - * %H - The history line itself. - * Note that a '\n' newline character is not - * appended by default. - * all_groups int If true, display history lines from all - * history groups. Otherwise only display - * those of the current history group. - * max_lines int If max_lines is < 0, all available lines - * are displayed. Otherwise only the most - * recent max_lines lines will be displayed. - * Output: - * return int 0 - OK. - * 1 - Error. - */ -int gl_show_history(GetLine *gl, FILE *fp, const char *fmt, int all_groups, - int max_lines); - -/*....................................................................... - * Resize or delete the history buffer. - * - * Input: - * gl GetLine * The resource object of gl_get_line(). - * bufsize size_t The number of bytes in the history buffer, or 0 - * to delete the buffer completely. - * Output: - * return int 0 - OK. - * 1 - Insufficient memory (the previous buffer - * will have been retained). No error message - * will be displayed. - */ -int gl_resize_history(GetLine *gl, size_t bufsize); - -/*....................................................................... - * Set an upper limit to the number of lines that can be recorded in the - * history list, or remove a previously specified limit. - * - * Input: - * gl GetLine * The resource object of gl_get_line(). - * max_lines int The maximum number of lines to allow, or -1 to - * cancel a previous limit and allow as many lines - * as will fit in the current history buffer size. - */ -void gl_limit_history(GetLine *gl, int max_lines); - -/*....................................................................... - * Discard either all historical lines, or just those associated with the - * current history group. - * - * Input: - * gl GetLine * The resource object of gl_get_line(). - * all_groups int If true, clear all of the history. If false, - * clear only the stored lines associated with the - * currently selected history group. - */ -void gl_clear_history(GetLine *gl, int all_groups); - -/*....................................................................... - * Temporarily enable or disable the gl_get_line() history mechanism. - * - * Input: - * gl GetLine * The resource object of gl_get_line(). - * enable int If true, turn on the history mechanism. If - * false, disable it. - */ -void gl_toggle_history(GetLine *gl, int enable); - -/* - * Objects of the following type are returned by gl_terminal_size(). - */ -typedef struct { - int nline; /* The terminal has nline lines */ - int ncolumn; /* The terminal has ncolumn columns */ -} GlTerminalSize; - -/*....................................................................... - * Update if necessary, and return the current size of the terminal. - * - * Input: - * gl GetLine * The resource object of gl_get_line(). - * def_ncolumn int If the number of columns in the terminal - * can't be determined, substitute this number. - * def_nline int If the number of lines in the terminal can't - * be determined, substitute this number. - * Output: - * return GlTerminalSize The current terminal size. - */ -GlTerminalSize gl_terminal_size(GetLine *gl, int def_ncolumn, int def_nline); - -/* - * The gl_lookup_history() function returns information in an - * argument of the following type. - */ -typedef struct { - const char *line; /* The requested history line */ - unsigned group; /* The history group to which the */ - /* line belongs. */ - time_t timestamp; /* The date and time at which the */ - /* line was originally entered. */ -} GlHistoryLine; - -/*....................................................................... - * Lookup a history line by its sequential number of entry in the - * history buffer. - * - * Input: - * gl GetLine * The resource object of gl_get_line(). - * id unsigned long The identification number of the line to - * be returned, where 0 denotes the first line - * that was entered in the history list, and - * each subsequently added line has a number - * one greater than the previous one. For - * the range of lines currently in the list, - * see the gl_range_of_history() function. - * Input/Output: - * line GlHistoryLine * A pointer to the variable in which to - * return the details of the line. - * Output: - * return int 0 - The line is no longer in the history - * list, and *line has not been changed. - * 1 - The requested line can be found in - * *line. Note that the string in - * line->line is part of the history - * buffer and will change, so a private - * copy should be made if you wish to - * use it after subsequent calls to any - * functions that take gl as an argument. - */ -int gl_lookup_history(GetLine *gl, unsigned long id, GlHistoryLine *line); - -/* - * The gl_state_of_history() function returns information in an argument - * of the following type. - */ -typedef struct { - int enabled; /* True if history is enabled */ - unsigned group; /* The current history group */ - int max_lines; /* The current upper limit on the number of lines */ - /* in the history list, or -1 if unlimited. */ -} GlHistoryState; - -/*....................................................................... - * Query the state of the history list. Note that any of the input/output - * pointers can be specified as NULL. - * - * Input: - * gl GetLine * The resource object of gl_get_line(). - * Input/Output: - * state GlHistoryState * A pointer to the variable in which to record - * the return values. - */ -void gl_state_of_history(GetLine *gl, GlHistoryState *state); - -/* - * The gl_range_of_history() function returns information in an argument - * of the following type. - */ -typedef struct { - unsigned long oldest; /* The sequential entry number of the oldest */ - /* line in the history list. */ - unsigned long newest; /* The sequential entry number of the newest */ - /* line in the history list. */ - int nlines; /* The number of lines in the history list */ -} GlHistoryRange; - -/*....................................................................... - * Query the number and range of lines in the history buffer. - * - * Input: - * gl GetLine * The resource object of gl_get_line(). - * range GlHistoryRange * A pointer to the variable in which to record - * the return values. If range->nline=0, the - * range of lines will be given as 0-0. - */ -void gl_range_of_history(GetLine *gl, GlHistoryRange *range); - -/* - * The gl_size_of_history() function returns information in an argument - * of the following type. - */ -typedef struct { - size_t size; /* The size of the history buffer (bytes) */ - size_t used; /* The number of bytes of the history buffer */ - /* that are currently occupied. */ -} GlHistorySize; - -/*....................................................................... - * Return the size of the history buffer and the amount of the - * buffer that is currently in use. - * - * Input: - * gl GetLine * The resource object of gl_get_line(). - * Input/Output: - * GlHistorySize size * A pointer to the variable in which to return - * the results. - */ -void gl_size_of_history(GetLine *gl, GlHistorySize *size); - -/*....................................................................... - * Specify whether text that users type should be displayed or hidden. - * In the latter case, only the prompt is displayed, and the final - * input line is not archived in the history list. - * - * Input: - * gl GetLine * The input-line history maintenance object. - * enable int 0 - Disable echoing. - * 1 - Enable echoing. - * -1 - Just query the mode without changing it. - * Output: - * return int The echoing disposition that was in effect - * before this function was called: - * 0 - Echoing was disabled. - * 1 - Echoing was enabled. - */ -int gl_echo_mode(GetLine *gl, int enable); - -/*....................................................................... - * This function can be called from gl_get_line() callbacks to have - * the prompt changed when they return. It has no effect if gl_get_line() - * is not currently being invoked. - * - * Input: - * gl GetLine * The resource object of gl_get_line(). - * prompt const char * The new prompt. - */ -void gl_replace_prompt(GetLine *gl, const char *prompt); - -/* - * Enumerate the available prompt formatting styles. - */ -typedef enum { - GL_LITERAL_PROMPT, /* Display the prompt string literally */ - GL_FORMAT_PROMPT /* The prompt string can contain any of the */ - /* following formatting directives: */ - /* %B - Display subsequent characters */ - /* with a bold font. */ - /* %b - Stop displaying characters */ - /* with the bold font. */ - /* %U - Underline subsequent characters. */ - /* %u - Stop underlining characters. */ - /* %S - Highlight subsequent characters */ - /* (also known as standout mode). */ - /* %s - Stop highlighting characters */ - /* %% - Display a single % character. */ -} GlPromptStyle; - -/*....................................................................... - * Specify whether to heed text attribute directives within prompt - * strings. - * - * Input: - * gl GetLine * The resource object of gl_get_line(). - * style GlPromptStyle The style of prompt (see the definition of - * GlPromptStyle in libtecla.h for details). - */ -void gl_prompt_style(GetLine *gl, GlPromptStyle style); - -/*....................................................................... - * Remove a signal from the list of signals that gl_get_line() traps. - * - * Input: - * gl GetLine * The resource object of gl_get_line(). - * signo int The number of the signal to be ignored. - * Output: - * return int 0 - OK. - * 1 - Error. - */ -int gl_ignore_signal(GetLine *gl, int signo); - -/* - * A bitwise union of the following enumerators is passed to - * gl_trap_signal() to specify the environment in which the - * application's signal handler is to be called. - */ -typedef enum { - GLS_RESTORE_SIG=1, /* Restore the caller's signal environment */ - /* while handling the signal. */ - GLS_RESTORE_TTY=2, /* Restore the caller's terminal settings */ - /* while handling the signal. */ - GLS_RESTORE_LINE=4, /* Move the cursor to the start of the next line */ - GLS_REDRAW_LINE=8, /* Redraw the input line when the signal handler */ - /* returns. */ - GLS_UNBLOCK_SIG=16, /* Normally a signal who's delivery is found to */ - /* be blocked by the calling application is not */ - /* trapped by gl_get_line(). Including this flag */ - /* causes it to be temporarily unblocked and */ - /* trapped while gl_get_line() is executing. */ - GLS_DONT_FORWARD=32,/* Don't forward the signal to the signal handler */ - /* of the calling program. */ - GLS_RESTORE_ENV = GLS_RESTORE_SIG | GLS_RESTORE_TTY | GLS_REDRAW_LINE, - GLS_SUSPEND_INPUT = GLS_RESTORE_ENV | GLS_RESTORE_LINE -} GlSignalFlags; - -/* - * The following enumerators are passed to gl_trap_signal() to tell - * it what to do after the application's signal handler has been called. - */ -typedef enum { - GLS_RETURN, /* Return the line as though the user had pressed the */ - /* return key. */ - GLS_ABORT, /* Cause gl_get_line() to return NULL */ - GLS_CONTINUE /* After handling the signal, resume command line editing */ -} GlAfterSignal; - -/*....................................................................... - * Tell gl_get_line() how to respond to a given signal. This can be used - * both to override the default responses to signals that gl_get_line() - * normally catches and to add new signals to the list that are to be - * caught. - * - * Input: - * gl GetLine * The resource object of gl_get_line(). - * signo int The number of the signal to be caught. - * flags unsigned A bitwise union of GlSignalFlags enumerators. - * after GlAfterSignal What to do after the application's signal - * handler has been called. - * errno_value int The value to set errno to. - * Output: - * return int 0 - OK. - * 1 - Insufficient memory to record the - * new signal disposition. - */ -int gl_trap_signal(GetLine *gl, int signo, unsigned flags, - GlAfterSignal after, int errno_value); - -/*....................................................................... - * Return the last signal that was caught by the most recent call to - * gl_get_line(), or -1 if no signals were caught. This is useful if - * gl_get_line() returns errno=EINTR and you need to find out what signal - * caused it to abort. - * - * Input: - * gl GetLine * The resource object of gl_get_line(). - * Output: - * return int The last signal caught by the most recent - * call to gl_get_line(), or -1 if no signals - * were caught. - */ -int gl_last_signal(const GetLine *gl); - -/*....................................................................... - * This function is designed to be called by CPL_MATCH_FN() callback - * functions. It adds one possible completion of the token that is being - * completed to an array of completions. If the completion needs any - * special quoting to be valid when displayed in the input line, this - * quoting must be included in the string. - * - * Input: - * cpl WordCompletion * The argument of the same name that was passed - * to the calling CPL_MATCH_FN() callback function. - * line const char * The input line, as received by the callback - * function. - * word_start int The index within line[] of the start of the - * word that is being completed. If an empty - * string is being completed, set this to be - * the same as word_end. - * word_end int The index within line[] of the character which - * follows the incomplete word, as received by the - * callback function. - * suffix const char * The appropriately quoted string that could - * be appended to the incomplete token to complete - * it. A copy of this string will be allocated - * internally. - * type_suffix const char * When listing multiple completions, gl_get_line() - * appends this string to the completion to indicate - * its type to the user. If not pertinent pass "". - * Otherwise pass a literal or static string. - * cont_suffix const char * If this turns out to be the only completion, - * gl_get_line() will append this string as - * a continuation. For example, the builtin - * file-completion callback registers a directory - * separator here for directory matches, and a - * space otherwise. If the match were a function - * name you might want to append an open - * parenthesis, etc.. If not relevant pass "". - * Otherwise pass a literal or static string. - * Output: - * return int 0 - OK. - * 1 - Error. - */ -int cpl_add_completion(WordCompletion *cpl, const char *line, - int word_start, int word_end, const char *suffix, - const char *type_suffix, const char *cont_suffix); - -/* - * Each possible completion string is recorded in an array element of - * the following type. - */ -typedef struct { - char *completion; /* The matching completion string */ - char *suffix; /* The pointer into completion[] at which the */ - /* string was extended. */ - const char *type_suffix; /* A suffix to be added when listing completions */ - /* to indicate the type of the completion. */ -} CplMatch; - -/* - * Completions are returned in a container of the following form. - */ -typedef struct { - char *suffix; /* The common initial part of all of the */ - /* completion suffixes. */ - const char *cont_suffix; /* Optional continuation string to be appended to */ - /* the sole completion when nmatch==1. */ - CplMatch *matches; /* The array of possible completion strings, */ - /* sorted into lexical order. */ - int nmatch; /* The number of elements in matches[] */ -} CplMatches; - -/*....................................................................... - * Given an input line and the point at which completion is to be - * attempted, return an array of possible completions. - * - * Input: - * cpl WordCompletion * The word-completion resource object. - * line const char * The current input line. - * word_end int The index of the character in line[] which - * follows the end of the token that is being - * completed. - * data void * Anonymous 'data' to be passed to match_fn(). - * match_fn CplMatchFn * The function that will identify the prefix - * to be completed from the input line, and - * record completion suffixes. - * Output: - * return CplMatches * The container of the array of possible - * completions. The returned pointer refers - * to a container owned by the parent Completion - * object, and its contents thus potentially - * change on every call to cpl_matches(). - */ -CplMatches *cpl_complete_word(WordCompletion *cpl, const char *line, - int word_end, void *data, - CplMatchFn *match_fn); - -/*....................................................................... - * Print out an array of matching completions. - * - * Input: - * result CplMatches * The container of the sorted array of - * completions. - * fp FILE * The output stream to write to. - * term_width int The width of the terminal. - * Output: - * return int 0 - OK. - * 1 - Error. - */ -int cpl_list_completions(CplMatches *result, FILE *fp, int term_width); - -/*....................................................................... - * Return a description of the error that occurred on the last call to - * cpl_complete_word() or cpl_add_completion(). - * - * Input: - * cpl WordCompletion * The string-completion resource object. - * Output: - * return const char * The description of the last error. - */ -const char *cpl_last_error(WordCompletion *cpl); - -/* - * PathCache objects encapsulate the resources needed to record - * files of interest from comma-separated lists of directories. - */ -typedef struct PathCache PathCache; - -/*....................................................................... - * Create an object who's function is to maintain a cache of filenames - * found within a list of directories, and provide quick lookup and - * completion of selected files in this cache. - * - * Output: - * return PathCache * The new, initially empty cache, or NULL - * on error. - */ -PathCache *new_PathCache(void); - -/*....................................................................... - * Delete a given cache of files, returning the resources that it - * was using to the system. - * - * Input: - * pc PathCache * The cache to be deleted (can be NULL). - * Output: - * return PathCache * The deleted object (ie. allways NULL). - */ -PathCache *del_PathCache(PathCache *pc); - -/*....................................................................... - * Return a description of the last path-caching error that occurred. - * - * Input: - * pc PathCache * The filename cache that suffered the error. - * Output: - * return char * The description of the last error. - */ -const char *pca_last_error(PathCache *pc); - -/*....................................................................... - * Build the list of files of interest contained in a given - * colon-separated list of directories. - * - * Input: - * pc PathCache * The cache in which to store the names of - * the files that are found in the list of - * directories. - * path const char * A colon-separated list of directory - * paths. Under UNIX, when searching for - * executables, this should be the return - * value of getenv("PATH"). - * Output: - * return int 0 - OK. - * 1 - An error occurred. - */ -int pca_scan_path(PathCache *pc, const char *path); - -/*....................................................................... - * If you want subsequent calls to pca_lookup_file() and - * pca_path_completions() to only return the filenames of certain - * types of files, for example executables, or filenames ending in - * ".ps", call this function to register a file-selection callback - * function. This callback function takes the full pathname of a file, - * plus application-specific data, and returns 1 if the file is of - * interest, and zero otherwise. - * - * Input: - * pc PathCache * The filename cache. - * check_fn CplCheckFn * The function to call to see if the name of - * a given file should be included in the - * cache. This determines what type of files - * will reside in the cache. To revert to - * selecting all files, regardless of type, - * pass 0 here. - * data void * You can pass a pointer to anything you - * like here, including NULL. It will be - * passed to your check_fn() callback - * function, for its private use. - */ -void pca_set_check_fn(PathCache *pc, CplCheckFn *check_fn, void *data); - -/*....................................................................... - * Given the simple name of a file, search the cached list of files - * in the order in which they where found in the list of directories - * previously presented to pca_scan_path(), and return the pathname - * of the first file which has this name. - * - * Input: - * pc PathCache * The cached list of files. - * name const char * The name of the file to lookup. - * name_len int The length of the filename substring at the - * beginning of name[], or -1 to assume that the - * filename occupies the whole of the string. - * literal int If this argument is zero, lone backslashes - * in name[] are ignored during comparison - * with filenames in the cache, under the - * assumption that they were in the input line - * soley to escape the special significance of - * characters like spaces. To have them treated - * as normal characters, give this argument a - * non-zero value, such as 1. - * Output: - * return char * The pathname of the first matching file, - * or NULL if not found. Note that the returned - * pointer points to memory owned by *pc, and - * will become invalid on the next call. - */ -char *pca_lookup_file(PathCache *pc, const char *name, int name_len, - int literal); - -/* - * Objects of the following type can be used to change the default - * behavior of the pca_path_completions() callback function. - */ -typedef struct PcaPathConf PcaPathConf; - -/* - * pca_path_completions() is a completion callback function for use directly - * with cpl_complete_word() or gl_customize_completions(), or indirectly - * from your own completion callback function. It requires that a PcaPathConf - * object be passed via its 'void *data' argument (see below). - */ -CPL_MATCH_FN(pca_path_completions); - -/*....................................................................... - * Allocate and initialize a pca_path_completions() configuration object. - * - * Input: - * pc PathCache * The filename cache in which to look for - * file name completions. - * Output: - * return PcaPathConf * The new configuration structure, or NULL - * on error. - */ -PcaPathConf *new_PcaPathConf(PathCache *pc); - -/*....................................................................... - * Deallocate memory, previously allocated by new_PcaPathConf(). - * - * Input: - * ppc PcaPathConf * Any pointer previously returned by - * new_PcaPathConf() [NULL is allowed]. - * Output: - * return PcaPathConf * The deleted structure (always NULL). - */ -PcaPathConf *del_PcaPathConf(PcaPathConf *ppc); - -/* - * If backslashes in the prefix being passed to pca_path_completions() - * should be treated as literal characters, call the following function - * with literal=1. Otherwise the default is to treat them as escape - * characters which remove the special meanings of spaces etc.. - */ -void ppc_literal_escapes(PcaPathConf *ppc, int literal); - -/* - * Before calling pca_path_completions, call this function if you know - * the index at which the filename prefix starts in the input line. - * Otherwise by default, or if you specify start_index to be -1, the - * filename is taken to start after the first unescaped space preceding - * the cursor, or the start of the line, whichever comes first. - */ -void ppc_file_start(PcaPathConf *ppc, int start_index); - -#ifdef __cplusplus -} -#endif - -#endif diff --git a/libtecla-1.4.1/libtecla.map b/libtecla-1.4.1/libtecla.map deleted file mode 100644 index 7e774b7..0000000 --- a/libtecla-1.4.1/libtecla.map +++ /dev/null @@ -1,124 +0,0 @@ -# This mapfile (or version script) lists the public symbols that are -# publically exported by each version of the tecla library. This file -# has the format required by the Sun and Linux linkers, and also acts -# as a template from which map files for other systems can be derived -# with awk or sed. -# -# Under Solaris and Linux, this map file is used by ld during shared -# library creation. It has two purposes: -# -# 1. It specifies which symbols in the library are to be made visible -# to applications. This has the dual benefits of reducing namespace -# polution, and of preventing applications from using private -# internal library functions that might change or disappear in -# future releases. -# -# 2. The information listed in this file is recorded in the shared -# library, such that when an application is linked against it, the -# linker can record a dependency in the application which says -# which is the earliest library version which included all of the -# symbols that the application needs. This means that if the -# application is copied to another system that has an earlier -# version of the library, the linker can quickly determine whether -# the earlier version contains all of the symbols that it needs. -# -# Under Linux, mapfiles can also be used to allow multiple -# incompatible versions of a given function to exist in a library, -# thus supporting applications that were compiled against different -# incompatible versions of the library. Since this feature (and the -# inclusion of .symver directives) isn't supported by Solaris, it -# can't be included in this file. Non backwards compatibility in the -# ABI must instead be handled in the more traditional way, by -# incrementing the major version number. -# -# When a new minor release is made, a new tecla_1.x specification -# should be added which inherits the symbols of the previous release -# and lists newly added functions. For example, below you will find -# the following clause: -# -# tecla_1.3 { -# global: -# ef_list_expansions; -# } tecla_1.2; -# -# This says that ef_list_expansions is the name of a public function -# that was added in the 1.3 release, and that the symbols defined in -# the previous tecla_1.2 clause have been inherited by tecla_1.3. -# -# For more details see the following URL: -# -# http://www.usenix.org/publications/library/proceedings/als2000/browndavid.html -#------------------------------------------------------------------------------- - -tecla_1.2 { - global: - cfc_file_start; - cfc_literal_escapes; - cfc_set_check_fn; - cpl_add_completion; - cpl_check_exe; - cpl_complete_word; - cpl_file_completions; - cpl_init_FileArgs; - cpl_last_error; - cpl_list_completions; - cpl_record_error; - del_CplFileConf; - del_ExpandFile; - del_GetLine; - del_PathCache; - del_PcaPathConf; - del_WordCompletion; - ef_expand_file; - ef_last_error; - gl_change_terminal; - gl_customize_completion; - gl_get_line; - new_CplFileConf; - new_ExpandFile; - new_GetLine; - new_PathCache; - new_PcaPathConf; - new_WordCompletion; - pca_last_error; - pca_lookup_file; - pca_path_completions; - pca_scan_path; - pca_set_check_fn; - ppc_file_start; - ppc_literal_escapes; - - local: - *; -}; - -tecla_1.3 { - global: - ef_list_expansions; -} tecla_1.2; - -tecla_1.4 { - global: - gl_configure_getline; - gl_save_history; - gl_load_history; - gl_group_history; - gl_show_history; - gl_resize_history; - gl_limit_history; - gl_clear_history; - gl_toggle_history; - gl_watch_fd; - libtecla_version; - gl_terminal_size; - gl_state_of_history; - gl_range_of_history; - gl_size_of_history; - gl_lookup_history; - gl_echo_mode; - gl_replace_prompt; - gl_prompt_style; - gl_ignore_signal; - gl_trap_signal; - gl_last_signal; -} tecla_1.3; diff --git a/libtecla-1.4.1/man3/cfc_file_start.3 b/libtecla-1.4.1/man3/cfc_file_start.3 deleted file mode 100644 index 36c83a3..0000000 --- a/libtecla-1.4.1/man3/cfc_file_start.3 +++ /dev/null @@ -1 +0,0 @@ -.so man3/cpl_complete_word.3 diff --git a/libtecla-1.4.1/man3/cfc_literal_escapes.3 b/libtecla-1.4.1/man3/cfc_literal_escapes.3 deleted file mode 100644 index 36c83a3..0000000 --- a/libtecla-1.4.1/man3/cfc_literal_escapes.3 +++ /dev/null @@ -1 +0,0 @@ -.so man3/cpl_complete_word.3 diff --git a/libtecla-1.4.1/man3/cfc_set_check_fn.3 b/libtecla-1.4.1/man3/cfc_set_check_fn.3 deleted file mode 100644 index 36c83a3..0000000 --- a/libtecla-1.4.1/man3/cfc_set_check_fn.3 +++ /dev/null @@ -1 +0,0 @@ -.so man3/cpl_complete_word.3 diff --git a/libtecla-1.4.1/man3/cpl_add_completion.3 b/libtecla-1.4.1/man3/cpl_add_completion.3 deleted file mode 100644 index 36c83a3..0000000 --- a/libtecla-1.4.1/man3/cpl_add_completion.3 +++ /dev/null @@ -1 +0,0 @@ -.so man3/cpl_complete_word.3 diff --git a/libtecla-1.4.1/man3/cpl_complete_word.3 b/libtecla-1.4.1/man3/cpl_complete_word.3 deleted file mode 100644 index ae76439..0000000 --- a/libtecla-1.4.1/man3/cpl_complete_word.3 +++ /dev/null @@ -1,405 +0,0 @@ -.\" Copyright (C) 2000, 2001 by Martin C. Shepherd -.\" -.\" All rights reserved. -.\" -.\" Permission is hereby granted, free of charge, to any person obtaining a -.\" copy of this software and associated documentation files (the -.\" "Software"), to deal in the Software without restriction, including -.\" without limitation the rights to use, copy, modify, merge, publish, -.\" distribute, and/or sell copies of the Software, and to permit persons -.\" to whom the Software is furnished to do so, provided that the above -.\" copyright notice(s) and this permission notice appear in all copies of -.\" the Software and that both the above copyright notice(s) and this -.\" permission notice appear in supporting documentation. -.\" -.\" THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS -.\" OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -.\" MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT -.\" OF THIRD PARTY RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR -.\" HOLDERS INCLUDED IN THIS NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL -.\" INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING -.\" FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, -.\" NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION -.\" WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. -.\" -.\" Except as contained in this notice, the name of a copyright holder -.\" shall not be used in advertising or otherwise to promote the sale, use -.\" or other dealings in this Software without prior written authorization -.\" of the copyright holder. -.TH cpl_complete_word 3 -.SH NAME -cpl_complete_word, cfc_file_start, cfc_literal_escapes, cfc_set_check_fn, cpl_add_completion, cpl_file_completions, cpl_last_error, cpl_list_completions, cpl_record_error, del_CplFileConf, del_WordCompletion, new_CplFileConf, new_WordCompletion \- lookup possible completions for a word -.SH SYNOPSIS -.nf -#include -#include - -WordCompletion *new_WordCompletion(void); - -WordCompletion *del_WordCompletion(WordCompletion *cpl); - -#define CPL_MATCH_FN(fn) int (fn)(WordCompletion *cpl, \\ - void *data, \\ - const char *line, \\ - int word_end) -typedef CPL_MATCH_FN(CplMatchFn); - -CPL_MATCH_FN(cpl_file_completions); - -CplMatches *cpl_complete_word(WordCompletion *cpl, - const char *line, - int word_end, void *data, - CplMatchFn *match_fn); - -int cpl_list_completions(CplMatches *result, FILE *fp, - int term_width); - -int cpl_add_completion(WordCompletion *cpl, - const char *line, int word_start, - int word_end, const char *suffix, - const char *type_suffix, - const char *cont_suffix); - -void cpl_record_error(WordCompletion *cpl, - const char *errmsg); - -const char *cpl_last_error(WordCompletion *cpl); - -.fi - -.SH DESCRIPTION - -The \f3cpl_complete_word()\f1 function is part of the tecla library -(see the libtecla(3) man page). It is usually called behind the scenes -by \f3gl_get_line(3)\f1, but can also be called separately. - -Given an input line containing an incomplete word to be completed, it -calls a user-provided callback function (or the provided -file-completion callback function) to look up all possible completion -suffixes for that word. The callback function is expected to look -backward in the line, starting from the specified cursor position, to -find the start of the word to be completed, then to look up all -possible completions of that word and record them, one at a time by -calling \f3cpl_add_completion()\f1. - -.sp -Descriptions of the functions of this module are as follows: -.sp -.nf - CompleteWord *new_CompleteWord(void) -.fi -.sp -This function creates the resources used by the \f3cpl_complete_word()\f1 -function. In particular, it maintains the memory that is used to -return the results of calling \f3cpl_complete_word()\f1. -.sp -.nf - CompleteWord *del_CompleteWord(CompleteWord *cpl) -.fi -.sp -This function deletes the resources that were returned by a previous -call to \f3new_CompleteWord()\f1. It always returns \f3NULL\f1 (ie. a -deleted object). It does nothing if the \f3cpl\f1 argument is -\f3NULL\f1. -.sp -The callback functions which lookup possible completions should be -defined with the following macro (which is defined in libtecla.h). -.sp -.nf - #define CPL_MATCH_FN(fn) int (fn)(WordCompletion *cpl, \\ - void *data, \\ - const char *line, \\ - int word_end) -.fi -.sp -Functions of this type are called by \f3cpl_complete_word()\f1, and -all of the arguments of the callback are those that were passed to -said function. In particular, the \f3line\f1 argument contains the -input line containing the word to be completed, and \f3word_end\f1 is -the index of the character that follows the last character of the -incomplete word within this string. The callback is expected to look -backwards from \f3word_end\f1 for the start of the incomplete -word. What constitutes the start of a word clearly depends on the -application, so it makes sense for the callback to take on this -responsibility. For example, the builtin filename completion function -looks backwards until it hits an unescaped space, or the start of the -line. Having found the start of the word, the callback should then -lookup all possible completions of this word, and record each -completion via separate calls to \f3cpl_add_completion()\f1. If the -callback needs access to an application-specific symbol table, it can -pass it and any other data that it needs, via the \f3data\f1 -argument. This removes any need for globals. -.sp -The callback function should return 0 if no errors occur. On failure -it should return 1, and register a terse description of the error by -calling \f3cpl_record_error()\f1. -.sp -.nf - void cpl_record_error(WordCompletion *cpl, - const char *errmsg); -.fi -.sp -The last error message recorded by calling \f3cpl_record_error()\f1, -can subsequently be queried by calling \f3cpl_last_error()\f1, as -described later. -.sp -.nf - int cpl_add_completion(WordCompletion *cpl, - const char *line, int word_start, - int word_end, const char *suffix, - const char *type_suffix, - const char *cont_suffix); -.fi -.sp -The \f3cpl_add_completion()\f1 function is called zero or more times -by the completion callback function to record each possible completion -in the specified \f3WordCompletion\f1 object. These completions are -subsequently returned by \f3cpl_complete_word()\f1, as described -later. The \f3cpl\f1, \f3line\f1, and \f3word_end\f1 arguments should -be those that were passed to the callback function. The -\f3word_start\f1 argument should be the index within the input line -string of the start of the word that is being completed. This should -equal \f3word_end\f1 if a zero-length string is being completed. The -\f3suffix\f1 argument is the string that would have to be appended to -the incomplete word to complete it. If this needs any quoting -(eg. the addition of backslashes before special charaters) to be valid -within the displayed input line, this should be included. A copy of -the suffix string is allocated internally, so there is no need to -maintain your copy of the string after \f3cpl_add_completion()\f1 -returns. -.sp -Note that in the array of possible completions which the -\f3cpl_complete_word()\f1 function returns, the suffix recorded by -\f3cpl_add_completion()\f1 is listed along with the concatentation of -this suffix with the word that lies between \f3word_start\f1 and -\f3word_end\f1 in the input line. -.sp -The \f3type_suffix\f1 argument specifies an optional string to be -appended to the completion if it is displayed as part of a list of -completions by \f3cpl_list_completions()\f1. The intention is that -this indicate to the user the type of each completion. For example, -the file completion function places a directory separator after -completions that are directories, to indicate their nature to the -user. Similary, if the completion were a function, you could indicate -this to the user by setting \f3type_suffix\f1 to "()". Note that the -\f3type_suffix\f1 string isn't copied, so if the argument isn't a -literal string between speech marks, be sure that the string remains -valid for at least as long as the results of \f3cpl_complete_word()\f1 -are needed. -.sp -The \f3cont_suffix\f1 is a continuation suffix to append to the -completed word in the input line if this is the only completion. This -is something that isn't part of the completion itself, but that gives -the user an indication about how they might continue to extend the -token. For example, the file-completion callback function adds a -directory separator if the completed word is a directory. If the -completed word were a function name, you could similarly aid the user -by arranging for an open parenthesis to be appended. -.sp -.nf - CplMatches *cpl_complete_word(WordCompletion *cpl, - const char *line, - int word_end, void *data, - CplMatchFn *match_fn); -.fi -.sp -The \f3cpl_complete_word()\f1 is normally called behind the scenes by -\f3gl_get_line(3)\f1, but can also be called separately if you -separately allocate a \f3WordCompletion\f1 object. It performs word -completion, as described at the beginning of this section. Its first -argument is a resource object previously returned by -\f3new_CompleteWord()\f1. The \f3line\f1 argument is the input line -string, containing the word to be completed. The \f3word_end\f1 -argument contains the index of the character in the input line, that -just follows the last character of the word to be completed. When -called by \f3gl_get_line()\f1, this is the character over which the -user pressed \f3TAB\f1. The \f3match_fn\f3 argument is the function -pointer of the callback function which will lookup possible -completions of the word, as described above, and the \f3data\f1 -argument provides a way for the application to pass arbitrary data to -the callback function. -.sp -If no errors occur, the \f3cpl_complete_word()\f1 function returns a -pointer to a \f3CplMatches\f1 container, as defined below. This -container is allocated as part of the \f3cpl\f1 object that was passed -to \f3cpl_complete_word()\f1, and will thus change on each call which -uses the same \f3cpl\f1 argument. -.sp -.nf - typedef struct { - char *completion; /* A matching completion */ - /* string */ - char *suffix; /* The part of the */ - /* completion string which */ - /* would have to be */ - /* appended to complete the */ - /* original word. */ - const char *type_suffix; /* A suffix to be added when */ - /* listing completions, to */ - /* indicate the type of the */ - /* completion. */ - } CplMatch; - - typedef struct { - char *suffix; /* The common initial part */ - /* of all of the completion */ - /* suffixes. */ - const char *cont_suffix; /* Optional continuation */ - /* string to be appended to */ - /* the sole completion when */ - /* nmatch==1. */ - CplMatch *matches; /* The array of possible */ - /* completion strings, */ - /* sorted into lexical */ - /* order. */ - int nmatch; /* The number of elements in */ - /* the above matches[] */ - /* array. */ - } CplMatches; -.fi -.sp -If an error occurs during completion, \f3cpl_complete_word()\f1 -returns NULL. A description of the error can be acquired by calling -the \f3cpl_last_error()\f3 function. -.sp -.nf - const char *cpl_last_error(WordCompletion *cpl); -.fi -.sp -The \f3cpl_last_error()\f3 function returns a terse description of the -error which occurred on the last call to \f3cpl_complete_word()\f1 or -\f3cpl_add_completion()\f1. -.sp -.nf - int cpl_list_completions(CplMatches *result, FILE *fp, - int terminal_width); -.fi -.sp -When the \f3cpl_complete_word()\f1 function returns multiple possible -completions, the \f3cpl_list_completions()\f1 function can be called -upon to list them, suitably arranged across the available width of the -terminal. It arranges for the displayed columns of completions to all -have the same width, set by the longest completion. It also appends -the \f3type_suffix\f1 strings that were recorded with each completion, -thus indicating their types to the user. - -.SH THE BUILT-IN FILENAME-COMPLETION CALLBACK - -By default the \f3gl_get_line(3)\f1 function, passes the following -completion callback function to \f3cpl_complete_word()\f1. This -function can also be used separately, either by sending it to -\f3cpl_complete_word()\f1, or by calling it directly from your -own completion callback function. -.sp -.nf - CPL_MATCH_FN(cpl_file_completions); -.fi -.sp -Certain aspects of the behavior of this callback can be changed via -its \f3data\f1 argument. If you are happy with its default behavior -you can pass \f3NULL\f1 in this argument. Otherwise it should be a -pointer to a \f3CplFileConf\f1 object, previously allocated by calling -\f3new_CplFileConf()\f1. -.sp -.nf - CplFileConf *new_CplFileConf(void); -.fi -.sp -\f3CplFileConf\f1 objects encapsulate the configuration parameters of -\f3cpl_file_completions()\f1. These parameters, which start out with -default values, can be changed by calling the accessor functions -described below. -.sp -By default, the \f3cpl_file_completions()\f3 callback function -searches backwards for the start of the filename being completed, -looking for the first un-escaped space or the start of the input -line. If you wish to specify a different location, call -\f3cfc_file_start()\f1 with the index at which the filename starts in -the input line. Passing start_index=-1 re-enables the default -behavior. -.sp -.nf - void cfc_file_start(CplFileConf *cfc, int start_index); -.fi -.sp -By default, when \f3cpl_file_completions()\f1 looks at a filename in -the input line, each lone backslash in the input line is interpreted -as being a special character which removes any special significance of -the character which follows it, such as a space which should be taken -as part of the filename rather than delimiting the start of the -filename. These backslashes are thus ignored while looking for -completions, and subsequently added before spaces, tabs and literal -backslashes in the list of completions. To have unescaped backslashes -treated as normal characters, call \f3cfc_literal_escapes()\f1 with a -non-zero value in its \f3literal\f1 argument. -.sp -.nf - void cfc_literal_escapes(CplFileConf *cfc, int literal); -.fi -.sp -By default, \f3cpl_file_completions()\f1 reports all files who's names -start with the prefix that is being completed. If you only want a -selected subset of these files to be reported in the list of -completions, you can arrange this by providing a callback function -which takes the full pathname of a file, and returns \f30\f1 if the -file should be ignored, or \f31\f1 if the file should be included in -the list of completions. To register such a function for use by -\f3cpl_file_completions()\f1, call \f3cfc_set_check_fn()\f1, and pass -it a pointer to the function, together with a pointer to any data that -you would like passed to this callback whenever it is called. Your -callback can make its decisions based on any property of the file, -such as the filename itself, whether the file is readable, writable or -executable, or even based on what the file contains. -.sp -.nf - #define CPL_CHECK_FN(fn) int (fn)(void *data, \\ - const char *pathname) - typedef CPL_CHECK_FN(CplCheckFn); - - void cfc_set_check_fn(CplFileConf *cfc, - CplCheckFn *chk_fn, void *chk_data); -.fi -.sp -The \f3cpl_check_exe()\f1 function is a provided callback of the above -type, for use with \f3cpl_file_completions()\f1. It returns non-zero -if the filename that it is given represents a normal file that the -user has execute permission to. You could use this to have -\f3cpl_file_completions()\f1 only list completions of executable -files. -.sp -When you have finished with a \f3CplFileConf\f1 variable, you can pass -it to the \f3del_CplFileConf()\f1 destructor function to reclaim its -memory. -.sp -.nf - CplFileConf *del_CplFileConf(CplFileConf *cfc); -.fi -.sp - -.SH THREAD SAFETY - -In multi-threaded programs, you should use the \f3libtecla_r.a\f1 -version of the library. This uses POSIX reentrant functions where -available (hence the \f3_r\f1 suffix), and disables features that rely -on non-reentrant system functions. In the case of this module, the -only disabled feature is username completion in \f3~username/\f1 -expressions, in \f3cpl_file_completions()\f1. - -Using the \f3libtecla_r.a\f1 version of the library, it is safe to use -the facilities of this module in multiple threads, provided that each -thread uses a separately allocated \f3WordCompletion\f1 object. In -other words, if two threads want to do word completion, they should -each call \f3new_WordCompletion()\f1 to allocate their own completion -objects. - -.SH FILES -.nf -libtecla.a - The tecla library -libtecla.h - The tecla header file. -.fi - -.SH SEE ALSO -libtecla(3), gl_get_line(3), ef_expand_file(3), pca_lookup_file(3) - -.SH AUTHOR -Martin Shepherd (mcs@astro.caltech.edu) diff --git a/libtecla-1.4.1/man3/cpl_file_completions.3 b/libtecla-1.4.1/man3/cpl_file_completions.3 deleted file mode 100644 index 36c83a3..0000000 --- a/libtecla-1.4.1/man3/cpl_file_completions.3 +++ /dev/null @@ -1 +0,0 @@ -.so man3/cpl_complete_word.3 diff --git a/libtecla-1.4.1/man3/cpl_last_error.3 b/libtecla-1.4.1/man3/cpl_last_error.3 deleted file mode 100644 index 36c83a3..0000000 --- a/libtecla-1.4.1/man3/cpl_last_error.3 +++ /dev/null @@ -1 +0,0 @@ -.so man3/cpl_complete_word.3 diff --git a/libtecla-1.4.1/man3/cpl_list_completions.3 b/libtecla-1.4.1/man3/cpl_list_completions.3 deleted file mode 100644 index 36c83a3..0000000 --- a/libtecla-1.4.1/man3/cpl_list_completions.3 +++ /dev/null @@ -1 +0,0 @@ -.so man3/cpl_complete_word.3 diff --git a/libtecla-1.4.1/man3/cpl_record_error.3 b/libtecla-1.4.1/man3/cpl_record_error.3 deleted file mode 100644 index 36c83a3..0000000 --- a/libtecla-1.4.1/man3/cpl_record_error.3 +++ /dev/null @@ -1 +0,0 @@ -.so man3/cpl_complete_word.3 diff --git a/libtecla-1.4.1/man3/del_CplFileConf.3 b/libtecla-1.4.1/man3/del_CplFileConf.3 deleted file mode 100644 index 36c83a3..0000000 --- a/libtecla-1.4.1/man3/del_CplFileConf.3 +++ /dev/null @@ -1 +0,0 @@ -.so man3/cpl_complete_word.3 diff --git a/libtecla-1.4.1/man3/del_ExpandFile.3 b/libtecla-1.4.1/man3/del_ExpandFile.3 deleted file mode 100644 index f4299df..0000000 --- a/libtecla-1.4.1/man3/del_ExpandFile.3 +++ /dev/null @@ -1 +0,0 @@ -.so man3/ef_expand_file.3 diff --git a/libtecla-1.4.1/man3/del_GetLine.3 b/libtecla-1.4.1/man3/del_GetLine.3 deleted file mode 100644 index 6bd3d1f..0000000 --- a/libtecla-1.4.1/man3/del_GetLine.3 +++ /dev/null @@ -1 +0,0 @@ -.so man3/gl_get_line.3 diff --git a/libtecla-1.4.1/man3/del_PathCache.3 b/libtecla-1.4.1/man3/del_PathCache.3 deleted file mode 100644 index e5a136e..0000000 --- a/libtecla-1.4.1/man3/del_PathCache.3 +++ /dev/null @@ -1 +0,0 @@ -.so man3/pca_lookup_file.3 diff --git a/libtecla-1.4.1/man3/del_PcaPathConf.3 b/libtecla-1.4.1/man3/del_PcaPathConf.3 deleted file mode 100644 index e5a136e..0000000 --- a/libtecla-1.4.1/man3/del_PcaPathConf.3 +++ /dev/null @@ -1 +0,0 @@ -.so man3/pca_lookup_file.3 diff --git a/libtecla-1.4.1/man3/del_WordCompletion.3 b/libtecla-1.4.1/man3/del_WordCompletion.3 deleted file mode 100644 index 36c83a3..0000000 --- a/libtecla-1.4.1/man3/del_WordCompletion.3 +++ /dev/null @@ -1 +0,0 @@ -.so man3/cpl_complete_word.3 diff --git a/libtecla-1.4.1/man3/ef_expand_file.3 b/libtecla-1.4.1/man3/ef_expand_file.3 deleted file mode 100644 index 88c2d54..0000000 --- a/libtecla-1.4.1/man3/ef_expand_file.3 +++ /dev/null @@ -1,245 +0,0 @@ -.\" Copyright (C) 2000, 2001 by Martin C. Shepherd -.\" -.\" All rights reserved. -.\" -.\" Permission is hereby granted, free of charge, to any person obtaining a -.\" copy of this software and associated documentation files (the -.\" "Software"), to deal in the Software without restriction, including -.\" without limitation the rights to use, copy, modify, merge, publish, -.\" distribute, and/or sell copies of the Software, and to permit persons -.\" to whom the Software is furnished to do so, provided that the above -.\" copyright notice(s) and this permission notice appear in all copies of -.\" the Software and that both the above copyright notice(s) and this -.\" permission notice appear in supporting documentation. -.\" -.\" THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS -.\" OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -.\" MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT -.\" OF THIRD PARTY RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR -.\" HOLDERS INCLUDED IN THIS NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL -.\" INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING -.\" FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, -.\" NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION -.\" WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. -.\" -.\" Except as contained in this notice, the name of a copyright holder -.\" shall not be used in advertising or otherwise to promote the sale, use -.\" or other dealings in this Software without prior written authorization -.\" of the copyright holder. -.TH ef_expand_file 3 -.SH NAME -ef_expand_file, del_ExpandFile, ef_last_error, ef_list_expansions, new_ExpandFile \- expand filenames containing ~user/$envvar and wildcard expressions -.SH SYNOPSIS -.nf -#include - -ExpandFile *new_ExpandFile(void); - -ExpandFile *del_ExpandFile(ExpandFile *ef); - -FileExpansion *ef_expand_file(ExpandFile *ef, - const char *path, - int pathlen); - -int ef_list_expansions(FileExpansion *result, FILE *fp, - int term_width); - -const char *ef_last_error(ExpandFile *ef); -.fi - -.SH DESCRIPTION - -The \f3ef_expand_file()\f1 function is part of the tecla library -(see the libtecla(3) man page). It expands a specified filename, -converting \f3~user/\f1 and \f3~/\f1 expressions at the start of the -filename to the corresponding home directories, replacing -\f3$envvar\f1 with the value of the corresponding environment -variable, and then, if there are any wildcards, matching these against -existing filenames. Backslashes in the input filename are interpreted -as escaping any special meanings of the characters that follow them. -Only backslahes that are themselves preceded by backslashes are -preserved in the expanded filename. -.sp -In the presence of wildcards, the returned list of filenames only -includes the names of existing files which match the -wildcards. Otherwise, the original filename is returned after -expansion of tilde and dollar expressions, and the result is not -checked against existing files. This mimics the file-globbing behavior -of the unix \f3tcsh\f1 shell. -.sp -The supported wildcards and their meanings are: -.nf - * - Match any sequence of zero or more characters. - ? - Match any single character. - [chars] - Match any single character that appears in - 'chars'. If 'chars' contains an expression of - the form a-b, then any character between a and - b, including a and b, matches. The '-' - character looses its special meaning as a - range specifier when it appears at the start - of the sequence of characters. The ']' - character also looses its significance as the - terminator of the range expression if it - appears immediately after the opening '[', at - which point it is treated one of the - characters of the range. If you want both '-' - and ']' to be part of the range, the '-' - should come first and the ']' second. - - [^chars] - The same as [chars] except that it matches any - single character that doesn't appear in - 'chars'. -.fi - -Note that wildcards never match the initial dot in filenames that -start with '.'. The initial '.' must be explicitly specified in the -filename. This again mimics the globbing behavior of most unix shells, -and its rational is based in the fact that in unix, files with names -that start with '.' are usually hidden configuration files, which are -not listed by default by the ls command. -.sp -The following is a complete example of how to use the file expansion -function. - -.nf - #include - #include - - int main(int argc, char *argv[]) - { - ExpandFile *ef; /* The expansion resource object */ - char *filename; /* The filename being expanded */ - FileExpansion *expn; /* The results of the expansion */ - int i; - - ef = new_ExpandFile(); - if(!ef) - return 1; - - for(arg = *(argv++); arg; arg = *(argv++)) { - if((expn = ef_expand_file(ef, arg, -1)) == NULL) { - fprintf(stderr, "Error expanding %s (%s).\\n", arg, - ef_last_error(ef)); - } else { - printf("%s matches the following files:\\n", arg); - for(i=0; infile; i++) - printf(" %s\\n", expn->files[i]); - } - } - - ef = del_ExpandFile(ef); - return 0; - } -.fi -.sp -Descriptions of the functions used above are as follows: -.sp -.nf - ExpandFile *new_ExpandFile(void) -.fi -.sp -This function creates the resources used by the \f3ef_expand_file()\f1 -function. In particular, it maintains the memory that is used to record the -array of matching filenames that is returned by \f3ef_expand_file()\f1. This -array is expanded as needed, so there is no built in limit to the number of -files that can be matched. -.sp -.nf - ExpandFile *del_ExpandFile(ExpandFile *ef) -.fi -.sp -This function deletes the resources that were returned by a previous call to -\f3new_ExpandFile()\f1. It always returns \f3NULL\f1 (ie a deleted object). It -does nothing if the \f3ef\f1 argument is \f3NULL\f1. -.sp -A container of the following type is returned by \f3ef_expand_file()\f1. -.sp -.nf - typedef struct { - int exists; /* True if the files in files[] exist */ - int nfile; /* The number of files in files[] */ - char **files; /* An array of 'nfile' filenames. */ - } FileExpansion; -.fi -.sp -.nf - FileExpansion *ef_expand_file(ExpandFile *ef, - const char *path, - int pathlen) -.fi -.sp -The \f3ef_expand_file()\f1 function performs filename expansion, as documented -at the start of this section. Its first argument is a resource object returned -by \f3new_ExpandFile()\f1. A pointer to the start of the filename to be matched -is passed via the \f3path\f1 argument. This must be a normal \f3NUL\f1 -terminated string, but unless a length of -1 is passed in \f3pathlen\f1, only -the first \f3pathlen\f1 characters will be used in the filename expansion. If -the length is specified as -1, the whole of the string will be -expanded. -.sp -The function returns a pointer to a container who's contents are the -results of the expansion. If there were no wildcards in the filename, -the \f3nfile\f1 member will be 1, and the \f3exists\f1 member should -be queried if it is important to know if the expanded file currently -exists or not. If there were wildcards, then the contained -\f3files[]\f1 array will contain the names of the \f3nfile\f1 existing -files that matched the wildcarded filename, and the \f3exists\f1 -member will have the value 1. Note that the returned container belongs -to the specified \f3ef\f1 object, and its contents will change on each -call, so if you need to retain the results of more than one call to -\f3ef_expand_file()\f1, you should either make a private copy of the -returned results, or create multiple file-expansion resource objects -via multiple calls to \f3new_ExpandFile()\f1. -.sp -On error, \f3NULL\f1 is returned, and an explanation of the error can -be determined by calling \f3ef_last_error(ef)\f1. -.sp -.nf - const char *ef_last_error(ExpandFile *ef) -.fi -.sp -This function returns the message which describes the error that -occurred on the last call to \f3ef_expand_file()\f1, for the given -\f3(ExpandFile *ef)\f1 resource object. -.sp -.nf - int ef_list_expansions(FileExpansion *result, FILE *fp, - int terminal_width); -.fi -.sp -The \f3ef_list_expansions()\f1 function provides a convenient way to -list the filename expansions returned by \f3ef_expand_file()\f1. Like -the unix \f3ls\f1 command, it arranges the filenames into equal width -columns, each column having the width of the largest file. The number -of columns used is thus determined by the length of the longest -filename, and the specified terminal width. Beware that filenames that -are longer than the specified terminal width are printed without being -truncated, so output longer than the specified terminal width can -occur. The list is written to the stdio stream specified by the -\f3fp\f1 argument. - -.SH THREAD SAFETY - -In multi-threaded programs, you should use the \f3libtecla_r.a\f1 -version of the library. This uses POSIX reentrant functions where -available (hence the \f3_r\f1 suffix), and disables features that rely -on non-reentrant system functions. Currently there are no features -disabled in this module. - -Using the \f3libtecla_r.a\f1 version of the library, it is safe to use -the facilities of this module in multiple threads, provided that each -thread uses a separately allocated \f3ExpandFile\f1 object. In other -words, if two threads want to do file expansion, they should each call -\f3new_ExpandFile()\f1 to allocate their own file-expansion objects. - -.SH FILES -.nf -libtecla.a - The tecla library -libtecla.h - The tecla header file. -.fi - -.SH SEE ALSO -libtecla(3), gl_get_line(3), cpl_complete_word(3), pca_lookup_file(3) - -.SH AUTHOR -Martin Shepherd (mcs@astro.caltech.edu) diff --git a/libtecla-1.4.1/man3/ef_last_error.3 b/libtecla-1.4.1/man3/ef_last_error.3 deleted file mode 100644 index f4299df..0000000 --- a/libtecla-1.4.1/man3/ef_last_error.3 +++ /dev/null @@ -1 +0,0 @@ -.so man3/ef_expand_file.3 diff --git a/libtecla-1.4.1/man3/ef_list_expansions.3 b/libtecla-1.4.1/man3/ef_list_expansions.3 deleted file mode 100644 index f4299df..0000000 --- a/libtecla-1.4.1/man3/ef_list_expansions.3 +++ /dev/null @@ -1 +0,0 @@ -.so man3/ef_expand_file.3 diff --git a/libtecla-1.4.1/man3/enhance.3 b/libtecla-1.4.1/man3/enhance.3 deleted file mode 100644 index 648ef34..0000000 --- a/libtecla-1.4.1/man3/enhance.3 +++ /dev/null @@ -1,86 +0,0 @@ -.\" Copyright (C) 2000, 2001 by Martin C. Shepherd -.\" -.\" All rights reserved. -.\" -.\" Permission is hereby granted, free of charge, to any person obtaining a -.\" copy of this software and associated documentation files (the -.\" "Software"), to deal in the Software without restriction, including -.\" without limitation the rights to use, copy, modify, merge, publish, -.\" distribute, and/or sell copies of the Software, and to permit persons -.\" to whom the Software is furnished to do so, provided that the above -.\" copyright notice(s) and this permission notice appear in all copies of -.\" the Software and that both the above copyright notice(s) and this -.\" permission notice appear in supporting documentation. -.\" -.\" THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS -.\" OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -.\" MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT -.\" OF THIRD PARTY RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR -.\" HOLDERS INCLUDED IN THIS NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL -.\" INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING -.\" FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, -.\" NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION -.\" WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. -.\" -.\" Except as contained in this notice, the name of a copyright holder -.\" shall not be used in advertising or otherwise to promote the sale, use -.\" or other dealings in this Software without prior written authorization -.\" of the copyright holder. -.TH libtecla 3 -.SH NAME -enhance - A program that adds command-line editing to third party programs. -.SH SYNOPSIS -.nf -enhance command [ argument ... ] -.fi - -.SH DESCRIPTION - -The \f3enhance\f1 program provides enhanced command-line editing -facilities to users of third party applications, to which one doesn't -have any source code. It does this by placing a pseudo-terminal -between the application and the real terminal. It uses the tecla -command-line editing library to read input from the real terminal, -then forwards each just completed input line to the application via -the pseudo-terminal. All output from the application is forwarded -back unchanged to the real terminal. -.sp -Whenever the application stops generating output for more than a tenth -of a second, the \f3enhance\f1 program treats the latest incomplete -output line as the prompt, and redisplays any incompleted input line -that the user has typed after it. Note that the small delay, which is -imperceptible to the user, isn't necessary for correct operation of -the program. It is just an optimization, designed to stop the input -line from being redisplayed so often that it slows down output. - -.SH DEFICIENCIES - -The one major problem that hasn't been solved yet, is how to deal with -applications that change whether typed input is echo'd by their -controlling terminal. For example, programs that ask for a password, -such as ftp and telnet, temporarily tell their controlling terminal -not to echo what the user types. Since this request goes to the -application side of the psuedo terminal, the \f3enhance\f1 program has -no way of knowing that this has happened, and continues to echo typed -input to its controlling terminal, while the user types their -password. -.sp -Furthermore, before executing the host application, the \f3enhance\f1 -program initially sets the pseudo terminal to noecho mode, so that -everything that it sends to the program doesn't get redundantly -echoed. If a program that switches to noecho mode explicitly restores -echoing afterwards, rather than restoring the terminal modes that were -previously in force, then subsequently, every time that you enter a -new input line, a duplicate copy will be displayed on the next line. - -.SH FILES -.nf -libtecla.a - The tecla library. -~/.teclarc - The tecla personal customization file. -.fi - -.SH SEE ALSO -libtecla(3) - -.SH AUTHOR -Martin Shepherd (mcs@astro.caltech.edu) diff --git a/libtecla-1.4.1/man3/gl_change_terminal.3 b/libtecla-1.4.1/man3/gl_change_terminal.3 deleted file mode 100644 index 6bd3d1f..0000000 --- a/libtecla-1.4.1/man3/gl_change_terminal.3 +++ /dev/null @@ -1 +0,0 @@ -.so man3/gl_get_line.3 diff --git a/libtecla-1.4.1/man3/gl_clear_history.3 b/libtecla-1.4.1/man3/gl_clear_history.3 deleted file mode 100644 index 6bd3d1f..0000000 --- a/libtecla-1.4.1/man3/gl_clear_history.3 +++ /dev/null @@ -1 +0,0 @@ -.so man3/gl_get_line.3 diff --git a/libtecla-1.4.1/man3/gl_configure_getline.3 b/libtecla-1.4.1/man3/gl_configure_getline.3 deleted file mode 100644 index 6bd3d1f..0000000 --- a/libtecla-1.4.1/man3/gl_configure_getline.3 +++ /dev/null @@ -1 +0,0 @@ -.so man3/gl_get_line.3 diff --git a/libtecla-1.4.1/man3/gl_customize_completion.3 b/libtecla-1.4.1/man3/gl_customize_completion.3 deleted file mode 100644 index 6bd3d1f..0000000 --- a/libtecla-1.4.1/man3/gl_customize_completion.3 +++ /dev/null @@ -1 +0,0 @@ -.so man3/gl_get_line.3 diff --git a/libtecla-1.4.1/man3/gl_echo_mode.3 b/libtecla-1.4.1/man3/gl_echo_mode.3 deleted file mode 100644 index 6bd3d1f..0000000 --- a/libtecla-1.4.1/man3/gl_echo_mode.3 +++ /dev/null @@ -1 +0,0 @@ -.so man3/gl_get_line.3 diff --git a/libtecla-1.4.1/man3/gl_get_line.3 b/libtecla-1.4.1/man3/gl_get_line.3 deleted file mode 100644 index 51fc9d8..0000000 --- a/libtecla-1.4.1/man3/gl_get_line.3 +++ /dev/null @@ -1,2329 +0,0 @@ -.\" Copyright (C) 2000, 2001 by Martin C. Shepherd -.\" -.\" All rights reserved. -.\" -.\" Permission is hereby granted, free of charge, to any person obtaining a -.\" copy of this software and associated documentation files (the -.\" "Software"), to deal in the Software without restriction, including -.\" without limitation the rights to use, copy, modify, merge, publish, -.\" distribute, and/or sell copies of the Software, and to permit persons -.\" to whom the Software is furnished to do so, provided that the above -.\" copyright notice(s) and this permission notice appear in all copies of -.\" the Software and that both the above copyright notice(s) and this -.\" permission notice appear in supporting documentation. -.\" -.\" THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS -.\" OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -.\" MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT -.\" OF THIRD PARTY RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR -.\" HOLDERS INCLUDED IN THIS NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL -.\" INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING -.\" FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, -.\" NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION -.\" WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. -.\" -.\" Except as contained in this notice, the name of a copyright holder -.\" shall not be used in advertising or otherwise to promote the sale, use -.\" or other dealings in this Software without prior written authorization -.\" of the copyright holder. -.TH gl_get_line 3 -.SH NAME -gl_get_line, new_GetLine, del_GetLine, gl_customize_completion, gl_change_terminal, gl_configure_getline, gl_load_history, gl_save_history, gl_group_history, gl_show_history, gl_watch_fd, gl_terminal_size, gl_resize_history, gl_limit_history, gl_clear_history, gl_toggle_history, gl_lookup_history, gl_state_of_history, gl_range_of_history, gl_size_of_history, gl_echo_mode, gl_replace_prompt, gl_prompt_style, gl_ignore_signal, gl_trap_signal, gl_last_signal \- allow the user to compose an input line -.SH SYNOPSIS -.nf -#include -#include - -GetLine *new_GetLine(size_t linelen, size_t histlen); - -GetLine *del_GetLine(GetLine *gl); - -char *gl_get_line(GetLine *gl, const char *prompt, - const char *start_line, int start_pos); - -int gl_customize_completion(GetLine *gl, void *data, - CplMatchFn *match_fn); - -int gl_change_terminal(GetLine *gl, FILE *input_fp, - FILE *output_fp, const char *term); - -int gl_configure_getline(GetLine *gl, - const char *app_string, - const char *app_file, - const char *user_file); - -int gl_save_history(GetLine *gl, const char *filename, - const char *comment, int max_lines); - -int gl_load_history(GetLine *gl, const char *filename, - const char *comment); - -int gl_watch_fd(GetLine *gl, int fd, GlFdEvent event, - GlFdEventFn *callback, void *data); - -int gl_group_history(GetLine *gl, unsigned stream); - -int gl_show_history(GetLine *gl, FILE *fp, - const char *fmt, int all_groups, - int max_lines); - -int gl_resize_history(GetLine *gl, size_t bufsize); - -void gl_limit_history(GetLine *gl, int max_lines); - -void gl_clear_history(GetLine *gl, int all_groups); - -void gl_toggle_history(GetLine *gl, int enable); - -GlTerminalSize gl_terminal_size(GetLine *gl, - int def_ncolumn, - int def_nline); - -int gl_lookup_history(GetLine *gl, unsigned long id, - GlHistoryLine *hline); - -void gl_state_of_history(GetLine *gl, - GlHistoryState *state); - -void gl_range_of_history(GetLine *gl, - GlHistoryRange *range); - -void gl_size_of_history(GetLine *gl, GlHistorySize *size); - -void gl_echo_mode(GetLine *gl, int enable); - -void gl_replace_prompt(GetLine *gl, const char *prompt); - -void gl_prompt_style(GetLine *gl, GlPromptStyle style); - -int gl_ignore_signal(GetLine *gl, int signo); - -int gl_trap_signal(GetLine *gl, int signo, unsigned flags, - GlAfterSignal after, int errno_value); - -int gl_last_signal(const GetLine *gl); - -.fi - -.SH DESCRIPTION - -The \f3gl_get_line()\f1 function is part of the tecla library (see -the libtecla(3) man page). If the user is typing at a terminal, it -prompts them for an line of input, then provides interactive editing -facilities, similar to those of the unix \f3tcsh\f1 shell. In addition -to simple command-line editing, it supports recall of previously -entered command lines, TAB completion of file names, and in-line -wild-card expansion of filenames. -.sp -.SH AN EXAMPLE - -The following shows a complete example of how to use the -\f3gl_get_line()\f1 function to get input from the user: - -.nf - #include - #include - #include - - int main(int argc, char *argv[]) - { - char *line; /* The line that the user typed */ - GetLine *gl; /* The gl_get_line() resource object */ - - setlocale(LC_CTYPE, ""); /* Adopt the user's choice */ - /* of character set. */ - - gl = new_GetLine(1024, 2048); - if(!gl) - return 1; - - while((line=gl_get_line(gl, "$ ", NULL, -1)) != NULL && - strcmp(line, "exit\\n") != 0) - printf("You typed: %s\\n", line); - - gl = del_GetLine(gl); - return 0; - } -.fi -.sp -In the example, first the resources needed by the \f3gl_get_line()\f1 function -are created by calling \f3new_GetLine()\f1. This allocates the memory used in -subsequent calls to the \f3gl_get_line()\f1 function, including the history -buffer for recording previously entered lines. Then one or more lines are read -from the user, until either an error occurs, or the user types \f3exit\f1. Then -finally the resources that were allocated by \f3new_GetLine()\f1, are returned -to the system by calling \f3del_GetLine()\f1. Note the use of the \f3NULL\f1 -return value of \f3del_GetLine()\f1 to make \f3gl\f1 \f3NULL\f1. This is a -safety precaution. If the program subsequently attempts to pass \f3gl\f1 to -\f3gl_get_line()\f1, said function will complain, and return an error, instead of -attempting to use the deleted resource object. - -.sp -.SH THE FUNCTIONS USED IN THE EXAMPLE -The descriptions of the functions used in the example are as follows: -.sp -.nf - GetLine *new_GetLine(size_t linelen, size_t histlen) -.fi -.sp -This function creates the resources used by the \f3gl_get_line()\f1 -function and returns an opaque pointer to the object that contains -them. The maximum length of an input line is specified via the -\f3linelen\f1 argument, and the number of bytes to allocate for -storing history lines is set by the \f3histlen\f1 argument. History -lines are stored back-to-back in a single buffer of this size. Note -that this means that the number of history lines that can be stored at -any given time, depends on the lengths of the individual lines. If -you want to place an upper limit on the number of lines that can be -stored, see the \f3gl_limit_history()\f1 function described later. If -you don't want history at all, specify \f3histlen\f1 as zero, and no -history buffer will be allocated. -.sp -On error, a message is printed to \f3stderr\f1 and \f3NULL\f1 is returned. -.sp -.nf - GetLine *del_GetLine(GetLine *gl) -.fi -.sp -This function deletes the resources that were returned by a previous -call to \f3new_GetLine()\f1. It always returns \f3NULL\f1 (ie a -deleted object). It does nothing if the \f3gl\f1 argument is -\f3NULL\f1. -.sp -.nf - char *gl_get_line(GetLine *gl, const char *prompt, - const char *start_line, int start_pos); -.fi -.sp -The \f3gl_get_line()\f1 function can be called any number of -times to read input from the user. The \f3gl\f1 argument -must have been previously returned by a call to -\f3new_GetLine()\f1. The \f3prompt\f1 argument should be a -normal \f3NUL\f1 terminated string, specifying the prompt to -present the user with. By default prompts are displayed -literally, but if enabled with the \f3gl_prompt_style()\f1 -function (see later), prompts can contain directives to do -underlining, switch to and from bold fonts, or turn -highlighting on and off. - -If you want to specify the initial contents of the line, for -the user to edit, pass the desired string via the -\f3start_line\f1 argument. You can then specify which -character of this line the cursor is initially positioned -over, using the \f3start_pos\f1 argument. This should be -1 -if you want the cursor to follow the last character of the -start line. If you don't want to preload the line in this -manner, send \f3start_line\f1 as \f3NULL\f1, and set -\f3start_pos\f1 to -1. - -The \f3gl_get_line()\f1 function returns a pointer to the line entered -by the user, or \f3NULL\f1 on error or at the end of the input. The -returned pointer is part of the specified \f3gl\f1 resource object, -and thus should not be free'd by the caller, or assumed to be -unchanging from one call to the next. When reading from a user at a -terminal, there will always be a newline character at the end of the -returned line. When standard input is being taken from a pipe or a -file, there will similarly be a newline unless the input line was too -long to store in the internal buffer. In the latter case you should -call \f3gl_get_line()\f1 again to read the rest of the line. Note that -this behavior makes \f3gl_get_line()\f1 similar to \f3fgets()\f1. In -fact when \f3stdin\f1 isn't connected to a terminal,\f3gl_get_line()\f1 -just calls \f3fgets()\f1. - -.SH OPTIONAL PROMPT FORMATTING - -Whereas by default the prompt string that you specify is -displayed literally, without any special interpretation of -the characters within it, the \f3gl_prompt_style()\f1 -function can be used to enable optional formatting -directives within the prompt. -.sp -.nf - void gl_prompt_style(GetLine *gl, GlPromptStyle style); -.fi -.sp -The \f3style\f1 argument, which specifies the formatting -style, can take any of the following values: -.sp -.nf - GL_FORMAT_PROMPT - In this style, the formatting - directives described below, when - included in prompt strings, are - interpreted as follows: - - %B - Display subsequent - characters with a bold - font. - %b - Stop displaying characters - with the bold font. - %F - Make subsequent characters - flash. - %f - Turn off flashing - characters. - %U - Underline subsequent - characters. - %u - Stop underlining - characters. - %P - Switch to a pale (half - brightness) font. - %p - Stop using the pale font. - %S - Highlight subsequent - characters (also known as - standout mode). - %s - Stop highlighting - characters. - %V - Turn on reverse video. - %v - Turn off reverse video. - %% - Display a single % - character. - - For example, in this mode, a prompt - string like \f3"%UOK%u$ "\f1 would - display the prompt \f3"OK$ "\f1, - but with the \f3OK\f1 part - underlined. - - Note that although a pair of - characters that starts with a % - character, but doesn't match any of - the above directives is displayed - literally, if a new directive is - subsequently introduced which does - match, the displayed prompt will - change, so it is better to always - use %% to display a literal %. - - Also note that not all terminals - support all of these text - attributes, and that some substitute - a different attribute for missing - ones. - - GL_LITERAL_PROMPT - In this style, the prompt string is - printed literally. This is the - default style. -.fi -.sp - -.SH THE AVAILABLE KEY BINDING FUNCTIONS - -The \f3gl_get_line()\f1 function provides a number of functions which -can be bound to key sequences. The names of these functions, and what -they do, are given below. - -.nf - user-interrupt - Send a SIGINT signal to the - parent process. - abort - Send a SIGABRT signal to the - parent process. - suspend - Suspend the parent process. - stop-output - Pause terminal output. - start-output - Resume paused terminal output. - literal-next - Arrange for the next character - to be treated as a normal - character. This allows control - characters to be entered. - cursor-right - Move the cursor one character - right. - cursor-left - Move the cursor one character - left. - insert-mode - Toggle between insert mode and - overwrite mode. - beginning-of-line - Move the cursor to the - beginning of the line. - end-of-line - Move the cursor to the end of - the line. - delete-line - Delete the contents of the - current line. - kill-line - Delete everything that follows - the cursor. - backward-kill-line - Delete all characters between - the cursor and the start of the - line. - forward-word - Move to the end of the word - which follows the cursor. - forward-to-word - Move the cursor to the start of - the word that follows the - cursor. - backward-word - Move to the start of the word - which precedes the cursor. - goto-column - Move the cursor to the - 1-relative column in the line - specified by any preceding - digit-argument sequences (see - ENTERING REPEAT COUNTS below). - find-parenthesis - If the cursor is currently - over a parenthesis character, - move it to the matching - parenthesis character. If not - over a parenthesis character - move right to the next close - parenthesis. - forward-delete-char - Delete the character under the - cursor. - backward-delete-char - Delete the character which - precedes the cursor. - list-or-eof - This is intended for binding - to ^D. When invoked when the - cursor is within the line it - displays all possible - completions then redisplays - the line unchanged. When - invoked on an empty line, it - signals end-of-input (EOF) to - the caller of gl_get_line(). - del-char-or-list-or-eof - This is intended for binding - to ^D. When invoked when the - cursor is within the line it - invokes forward-delete-char. - When invoked at the end of the - line it displays all possible - completions then redisplays - the line unchanged. When - invoked on an empty line, it - signals end-of-input (EOF) to - the caller of gl_get_line(). - forward-delete-word - Delete the word which follows - the cursor. - backward-delete-word - Delete the word which precedes - the cursor. - upcase-word - Convert all of the characters - of the word which follows the - cursor, to upper case. - downcase-word - Convert all of the characters - of the word which follows the - cursor, to lower case. - capitalize-word - Capitalize the word which - follows the cursor. - change-case - If the next character is upper - case, toggle it to lower case - and vice versa. - redisplay - Redisplay the line. - clear-screen - Clear the terminal, then - redisplay the current line. - transpose-chars - Swap the character under the - cursor with the character just - before the cursor. - set-mark - Set a mark at the position of - the cursor. - exchange-point-and-mark - Move the cursor to the last - mark that was set, and move - the mark to where the cursor - used to be. - kill-region - Delete the characters that lie - between the last mark that was - set, and the cursor. - copy-region-as-kill - Copy the text between the mark - and the cursor to the cut - buffer, without deleting the - original text. - yank - Insert the text that was last - deleted, just before the - current position of the cursor. - append-yank - Paste the current contents of - the cut buffer, after the - cursor. - up-history - Recall the next oldest line - that was entered. Note that - in vi mode you are left in - command mode. - down-history - Recall the next most recent - line that was entered. If no - history recall session is - currently active, the next - line from a previous recall - session is recalled. Note that - in vi mode you are left in - command mode. - history-search-backward - Recall the next oldest line - who's prefix matches the string - which currently precedes the - cursor (in vi command-mode the - character under the cursor is - also included in the search - string). Note that in vi mode - you are left in command mode. - history-search-forward - Recall the next newest line - who's prefix matches the string - which currently precedes the - cursor (in vi command-mode the - character under the cursor is - also included in the search - string). Note that in vi mode - you are left in command mode. - history-re-search-backward -Recall the next oldest line - who's prefix matches that - established by the last - invocation of either - history-search-forward or - history-search-backward. - history-re-search-forward - Recall the next newest line - who's prefix matches that - established by the last - invocation of either - history-search-forward or - history-search-backward. - complete-word - Attempt to complete the - incomplete word which - precedes the cursor. Unless - the host program has customized - word completion, filename - completion is attempted. In vi - commmand mode the character - under the cursor is also - included in the word being - completed, and you are left in - vi insert mode. - expand-filename - Within the command line, expand - wild cards, tilde expressions - and dollar expressions in the - filename which immediately - precedes the cursor. In vi - commmand mode the character - under the cursor is also - included in the filename being - expanded, and you are left in - vi insert mode. - list-glob - List any filenames which match - the wild-card, tilde and dollar - expressions in the filename - which immediately precedes the - cursor, then redraw the input - line unchanged. - list-history - Display the contents of the - history list for the current - history group. If a repeat - count of > 1 is specified, - only that many of the most - recent lines are displayed. - See the "ENTERING REPEAT - COUNTS" section. - read-from-file - Temporarily switch to reading - input from the file who's - name precedes the cursor. - read-init-files - Re-read teclarc configuration - files. - beginning-of-history - Move to the oldest line in the - history list. Note that in vi - mode you are left in command - mode. - end-of-history - Move to the newest line in the - history list (ie. the current - line). Note that in vi mode - this leaves you in command - mode. - digit-argument - Enter a repeat count for the - next key-binding function. - For details, see the ENTERING - REPEAT COUNTS section. - newline - Terminate and return the - current contents of the - line, after appending a - newline character. The newline - character is normally '\\n', - but will be the first - character of the key-sequence - that invoked the newline - action, if this happens to be - a printable character. If the - action was invoked by the - '\\n' newline character or the - '\\r' carriage return - character, the line is - appended to the history - buffer. - repeat-history - Return the line that is being - edited, then arrange for the - next most recent entry in the - history buffer to be recalled - when \f3gl_get_line()\f1 is - next called. Repeatedly - invoking this action causes - successive historical input - lines to be re-executed. Note - that this action is equivalent - to the 'Operate' action in - ksh. - ring-bell - Ring the terminal bell, unless - the bell has been silenced via - the \f3nobeep\f1 configuration - option (see the THE TECLA - CONFIGURATION FILE section). - forward-copy-char - Copy the next character into - the cut buffer (NB. use repeat - counts to copy more than one). - backward-copy-char - Copy the previous character - into the cut buffer. - forward-copy-word - Copy the next word into the cut - buffer. - backward-copy-word - Copy the previous word into the - cut buffer. - forward-find-char - Move the cursor to the next - occurrence of the next - character that you type. - backward-find-char - Move the cursor to the last - occurrence of the next - character that you type. - forward-to-char - Move the cursor to the - character just before the next - occurrence of the next - character that the user types. - backward-to-char - Move the cursor to the - character just after the last - occurrence before the cursor - of the next character that the - user types. - repeat-find-char - Repeat the last - backward-find-char, - forward-find-char, - backward-to-char or - forward-to-char. - invert-refind-char - Repeat the last - backward-find-char, - forward-find-char, - backward-to-char, or - forward-to-char in the - opposite direction. - delete-to-column - Delete the characters from the - cursor up to the column that - is specified by the repeat - count. - delete-to-parenthesis - Delete the characters from the - cursor up to and including - the matching parenthesis, or - next close parenthesis. - forward-delete-find - Delete the characters from the - cursor up to and including the - following occurence of the - next character typed. - backward-delete-find - Delete the characters from the - cursor up to and including the - preceding occurence of the - next character typed. - forward-delete-to - Delete the characters from the - cursor up to, but not - including, the following - occurence of the next - character typed. - backward-delete-to - Delete the characters from the - cursor up to, but not - including, the preceding - occurence of the next - character typed. - delete-refind - Repeat the last *-delete-find - or *-delete-to action. - delete-invert-refind - Repeat the last *-delete-find - or *-delete-to action, in the - opposite direction. - copy-to-column - Copy the characters from the - cursor up to the column that - is specified by the repeat - count, into the cut buffer. - copy-to-parenthesis - Copy the characters from the - cursor up to and including - the matching parenthesis, or - next close parenthesis, into - the cut buffer. - forward-copy-find - Copy the characters from the - cursor up to and including the - following occurence of the - next character typed, into the - cut buffer. - backward-copy-find - Copy the characters from the - cursor up to and including the - preceding occurence of the - next character typed, into the - cut buffer. - forward-copy-to - Copy the characters from the - cursor up to, but not - including, the following - occurence of the next - character typed, into the cut - buffer. - backward-copy-to - Copy the characters from the - cursor up to, but not - including, the preceding - occurence of the next - character typed, into the cut - buffer. - copy-refind - Repeat the last *-copy-find - or *-copy-to action. - copy-invert-refind - Repeat the last *-copy-find - or *-copy-to action, in the - opposite direction. - vi-mode - Switch to vi mode from emacs - mode. - emacs-mode - Switch to emacs mode from vi - mode. - vi-insert - From vi command mode, switch to - insert mode. - vi-overwrite - From vi command mode, switch to - overwrite mode. - vi-insert-at-bol - From vi command mode, move the - cursor to the start of the line - and switch to insert mode. - vi-append-at-eol - From vi command mode, move the - cursor to the end of the line - and switch to append mode. - vi-append - From vi command mode, move the - cursor one position right, and - switch to insert mode. - vi-replace-char - From vi command mode, replace - the character under the cursor - with the the next character - entered. - vi-forward-change-char - From vi command mode, delete - the next character then enter - insert mode. - vi-backward-change-char - From vi command mode, delete - the preceding character then - enter insert mode. - vi-forward-change-word - From vi command mode, delete - the next word then enter - insert mode. - vi-backward-change-word - From vi command mode, delete - the preceding word then - enter insert mode. - vi-change-rest-of-line - From vi command mode, delete - from the cursor to the end of - the line, then enter insert - mode. - vi-change-line - From vi command mode, delete - the current line, then enter - insert mode. - vi-change-to-bol - From vi command mode, delete - all characters between the - cursor and the beginning of - the line, then enter insert - mode. - vi-change-to-column - From vi command mode, delete - the characters from the cursor - up to the column that is - specified by the repeat count, - then enter insert mode. - vi-change-to-parenthesis - Delete the characters from the - cursor up to and including - the matching parenthesis, or - next close parenthesis, then - enter vi insert mode. - vi-forward-change-find - From vi command mode, delete - the characters from the - cursor up to and including the - following occurence of the - next character typed, then - enter insert mode. - vi-backward-change-find - From vi command mode, delete - the characters from the - cursor up to and including the - preceding occurence of the - next character typed, then - enter insert mode. - vi-forward-change-to - From vi command mode, delete - the characters from the - cursor up to, but not - including, the following - occurence of the next - character typed, then enter - insert mode. - vi-backward-change-to - From vi command mode, delete - the characters from the - cursor up to, but not - including, the preceding - occurence of the next - character typed, then enter - insert mode. - vi-change-refind - Repeat the last - vi-*-change-find or - vi-*-change-to action. - vi-change-invert-refind - Repeat the last - vi-*-change-find or - vi-*-change-to action, in the - opposite direction. - vi-undo - In vi mode, undo the last - editing operation. - vi-repeat-change - In vi command mode, repeat the - last command that modified the - line. -.fi - -.SH DEFAULT KEY BINDINGS IN EMACS MODE - -The following default key bindings, which can be overriden by -the tecla configuration file, are designed to mimic most of -the bindings of the unix \f3tcsh\f1 shell, when it is in -emacs editing mode. -.sp -This is the default editing mode of the tecla library. -.sp -Note that a key sequence like \f3^A\f1 or \f3C-a\f1 means hold the control-key -down while pressing the letter \f3A\f1, and that where you see \f3\\E\f1 or -\f3M-\f1 in a binding, this represents the escape key or the Meta modifier -key. Also note that to \f3gl_get_line()\f1, pressing the escape key before a -key is equivalent to pressing the meta key at the same time as that key. Thus -the key sequence \f3M-p\f1 can be typed in two ways, by pressing the escape -key, followed by pressing \f3p\f1, or by pressing the Meta key at the same time -as \f3p\f1. -.sp -Under UNIX the terminal driver sets a number of special keys for certain -functions. The tecla library attempts to use the same keybindings to maintain -consistency. The key sequences shown for the following 6 bindings are thus just -examples of what they will probably be set to. If you have used the \f3stty\f1 -command to change these keys, then the default bindings should match. - -.nf - ^C -> user-interrupt - ^\\ -> abort - ^Z -> suspend - ^Q -> start-output - ^S -> stop-output - ^V -> literal-next -.fi - -The cursor keys are refered to by name, as follows. This is necessary -because different types of terminals generate different key sequences -when their cursor keys are pressed. - - right -> cursor-right - left -> cursor-left - up -> up-history - down -> down-history - -The remaining bindings don't depend on the terminal setttings. - -.nf - ^F -> cursor-right - ^B -> cursor-left - M-i -> insert-mode - ^A -> beginning-of-line - ^E -> end-of-line - ^U -> delete-line - ^K -> kill-line - M-f -> forward-word - M-b -> backward-word - ^D -> del-char-or-list-or-eof - ^H -> backward-delete-char - ^? -> backward-delete-char - M-d -> forward-delete-word - M-^H -> backward-delete-word - M-^? -> backward-delete-word - M-u -> upcase-word - M-l -> downcase-word - M-c -> capitalize-word - ^R -> redisplay - ^L -> clear-screen - ^T -> transpose-chars - ^@ -> set-mark - ^X^X -> exchange-point-and-mark - ^W -> kill-region - M-w -> copy-region-as-kill - ^Y -> yank - ^P -> up-history - ^N -> down-history - M-p -> history-search-backward - M-n -> history-search-forward - ^I -> complete-word - ^X* -> expand-filename - ^X^F -> read-from-file - ^X^R -> read-init-files - ^Xg -> list-glob - ^Xh -> list-history - M-< -> beginning-of-history - M-> -> end-of-history - \\n -> newline - \\r -> newline - M-o -> repeat-history - M-^V -> vi-mode - - M-0, M-1, ... M-9 -> digit-argument (see below) -.fi - -Note that ^I is what the TAB key generates, and that ^@ can be -generated not only by pressing the control key and the @ key -simultaneously, but also by pressing the control key and the space bar -at the same time. - -.SH DEFAULT KEY BINDINGS IN VI MODE - -The following default key bindings are designed to mimic the -vi style of editing as closely as possible. This means that -very few editing functions are provided in the initial -character input mode, editing functions instead being -provided by the vi command mode. Vi command mode is entered -whenever the escape character is pressed, or whenever a -key-sequence that starts with a meta character is entered. In -addition to mimicing vi, libtecla provides bindings for tab -completion, wild-card expansion of file names, and historical -line recall. -.sp -To learn how to tell the tecla library to use vi mode instead -of the default emacs editing mode, see the section entitled -THE TECLA CONFIGURATION FILE. -.sp -As already mentioned above in the emacs section, Note that a -key sequence like \f3^A\f1 or \f3C-a\f1 means hold the -control-key down while pressing the letter \f3A\f1, and that -where you see \f3\\E\f1 or \f3M-\f1 in a binding, this -represents the escape key or the Meta modifier key. Also note -that to \f3gl_get_line()\f1, pressing the escape key before a -key is equivalent to pressing the meta key at the same time -as that key. Thus the key sequence \f3M-p\f1 can be typed in -two ways, by pressing the escape key, followed by pressing -\f3p\f1, or by pressing the Meta key at the same time as -\f3p\f1. -.sp -Under UNIX the terminal driver sets a number of special keys -for certain functions. The tecla library attempts to use the -same keybindings to maintain consistency, binding them both -in input mode and in command mode. The key sequences shown -for the following 6 bindings are thus just examples of what -they will probably be set to. If you have used the \f3stty\f1 -command to change these keys, then the default bindings -should match. - -.nf - ^C -> user-interrupt - ^\\ -> abort - ^Z -> suspend - ^Q -> start-output - ^S -> stop-output - ^V -> literal-next - M-^C -> user-interrupt - M-^\\ -> abort - M-^Z -> suspend - M-^Q -> start-output - M-^S -> stop-output -.fi - -Note that above, most of the bindings are defined twice, once -as a raw control code like \f3^C\f1 and then a second time as -a meta character like \f3M-^C\f1. The former is the binding -for vi input mode, whereas the latter is the binding for vi -command mode. Once in command mode all key-sequences that the -user types that they don't explicitly start with an escape or -a meta key, have their first key secretly converted to a meta -character before the key sequence is looked up in the key -binding table. Thus, once in command mode, when you type the -letter \f3i\f1, for example, the tecla library actually looks -up the binding for \f3M-i\f1. - -The cursor keys are refered to by name, as follows. This is necessary -because different types of terminals generate different key sequences -when their cursor keys are pressed. - - right -> cursor-right - left -> cursor-left - up -> up-history - down -> down-history - -The cursor keys normally generate a keysequence that start -with an escape character, so beware that using the arrow keys -will put you into command mode (if you aren't already in -command mode). -.sp -The following are the terminal-independent key bindings for vi input -mode. - -.nf - ^D -> list-or-eof - ^G -> list-glob - ^H -> backward-delete-char - ^I -> complete-word - \\r -> newline - \\n -> newline - ^L -> clear-screen - ^N -> down-history - ^P -> up-history - ^R -> redisplay - ^U -> backward-kill-line - ^W -> backward-delete-word - ^X* -> expand-filename - ^X^F -> read-from-file - ^X^R -> read-init-files - ^? -> backward-delete-char -.fi - -The following are the key bindings that are defined in vi -command mode, this being specified by them all starting with -a meta character. As mentioned above, once in command mode -the initial meta character is optional. For example, you -might enter command mode by typing Esc, and then press h -twice to move the cursor two positions to the left. Both h -characters get quietly converted to M-h before being compared -to the key-binding table, the first one because Escape -followed by a character is always converted to the equivalent -meta character, and the second because command mode was -already active. - -.nf - M-\\ -> cursor-right (Meta-space) - M-$ -> end-of-line - M-* -> expand-filename - M-+ -> down-history - M-- -> up-history - M-< -> beginning-of-history - M-> -> end-of-history - M-^ -> beginning-of-line - M-; -> repeat-find-char - M-, -> invert-refind-char - M-| -> goto-column - M-~ -> change-case - M-. -> vi-repeat-change - M-% -> find-parenthesis - M-a -> vi-append - M-A -> vi-append-at-eol - M-b -> backward-word - M-B -> backward-word - M-C -> vi-change-rest-of-line - M-cb -> vi-backward-change-word - M-cB -> vi-backward-change-word - M-cc -> vi-change-line - M-ce -> vi-forward-change-word - M-cE -> vi-forward-change-word - M-cw -> vi-forward-change-word - M-cW -> vi-forward-change-word - M-cF -> vi-backward-change-find - M-cf -> vi-forward-change-find - M-cT -> vi-backward-change-to - M-ct -> vi-forward-change-to - M-c; -> vi-change-refind - M-c, -> vi-change-invert-refind - M-ch -> vi-backward-change-char - M-c^H -> vi-backward-change-char - M-c^? -> vi-backward-change-char - M-cl -> vi-forward-change-char - M-c\\ -> vi-forward-change-char (Meta-c-space) - M-c^ -> vi-change-to-bol - M-c0 -> vi-change-to-bol - M-c$ -> vi-change-rest-of-line - M-c| -> vi-change-to-column - M-c% -> vi-change-to-parenthesis - M-dh -> backward-delete-char - M-d^H -> backward-delete-char - M-d^? -> backward-delete-char - M-dl -> forward-delete-char - M-d -> forward-delete-char (Meta-d-space) - M-dd -> delete-line - M-db -> backward-delete-word - M-dB -> backward-delete-word - M-de -> forward-delete-word - M-dE -> forward-delete-word - M-dw -> forward-delete-word - M-dW -> forward-delete-word - M-dF -> backward-delete-find - M-df -> forward-delete-find - M-dT -> backward-delete-to - M-dt -> forward-delete-to - M-d; -> delete-refind - M-d, -> delete-invert-refind - M-d^ -> backward-kill-line - M-d0 -> backward-kill-line - M-d$ -> kill-line - M-D -> kill-line - M-d| -> delete-to-column - M-d% -> delete-to-parenthesis - M-e -> forward-word - M-E -> forward-word - M-f -> forward-find-char - M-F -> backward-find-char - M-- -> up-history - M-h -> cursor-left - M-H -> beginning-of-history - M-i -> vi-insert - M-I -> vi-insert-at-bol - M-j -> down-history - M-J -> history-search-forward - M-k -> up-history - M-K -> history-search-backward - M-l -> cursor-right - M-L -> end-of-history - M-n -> history-re-search-forward - M-N -> history-re-search-backward - M-p -> append-yank - M-P -> yank - M-r -> vi-replace-char - M-R -> vi-overwrite - M-s -> vi-forward-change-char - M-S -> vi-change-line - M-t -> forward-to-char - M-T -> backward-to-char - M-u -> vi-undo - M-w -> forward-to-word - M-W -> forward-to-word - M-x -> forward-delete-char - M-X -> backward-delete-char - M-yh -> backward-copy-char - M-y^H -> backward-copy-char - M-y^? -> backward-copy-char - M-yl -> forward-copy-char - M-y\\ -> forward-copy-char (Meta-y-space) - M-ye -> forward-copy-word - M-yE -> forward-copy-word - M-yw -> forward-copy-word - M-yW -> forward-copy-word - M-yb -> backward-copy-word - M-yB -> backward-copy-word - M-yf -> forward-copy-find - M-yF -> backward-copy-find - M-yt -> forward-copy-to - M-yT -> backward-copy-to - M-y; -> copy-refind - M-y, -> copy-invert-refind - M-y^ -> copy-to-bol - M-y0 -> copy-to-bol - M-y$ -> copy-rest-of-line - M-yy -> copy-line - M-Y -> copy-line - M-y| -> copy-to-column - M-y% -> copy-to-parenthesis - M-^E -> emacs-mode - M-^H -> cursor-left - M-^? -> cursor-left - M-^L -> clear-screen - M-^N -> down-history - M-^P -> up-history - M-^R -> redisplay - M-^D -> list-or-eof - M-^I -> complete-word - M-\\r -> newline - M-\\n -> newline - M-^X^R -> read-init-files - M-^Xh -> list-history - - M-0, M-1, ... M-9 -> digit-argument (see below) -.fi - -Note that ^I is what the TAB key generates. - -.SH ENTERING REPEAT COUNTS - -Many of the key binding functions described previously, take -an optional count, typed in before the target -keysequence. This is interpreted as a repeat count by most -bindings. A notable exception is the goto-column binding, -which interprets the count as a column number. -.sp -By default you can specify this count argument by pressing -the meta key while typing in the numeric count. This relies -on the \f3digit-argument\f1 action being bound to Meta-0, -Meta-1 etc. Once any one of these bindings has been -activated, you can optionally take your finger off the meta -key to type in the rest of the number, since every numeric -digit thereafter is treated as part of the number, unless it -is preceded by the \f3literal-next\f1 binding. As soon as a -non-digit, or literal digit key is pressed the repeat count -is terminated and either causes the just typed character to -be added to the line that many times, or causes the next -key-binding function to be given that argument. -.sp -For example, in emacs mode, typing: -.sp -.nf - M-12a -.fi -.sp -causes the letter 'a' to be added to the line 12 times, -whereas -.sp -.nf - M-4M-c -.fi -.sp -Capitalizes the next 4 words. -.sp -In vi command mode the Meta modifier is automatically added -to all characters typed in, so to enter a count in vi -command-mode, just involves typing in the number, just as at -it does in the vi editor itself. So for example, in vi -command mode, typing: -.sp -.nf - 4w2x -.fi -.sp -moves the cursor four words to the right, then deletes two characters. -.sp -You can also bind \f3digit-argument\f1 to other key sequences. If -these end in a numeric digit, that digit gets appended to the current -repeat count. If it doesn't end in a numeric digit, a new repeat count -is started with a value of zero, and can be completed by typing in the -number, after letting go of the key which triggered the digit-argument -action. - -.SH THE TECLA CONFIGURATION FILE - -By default, the first call to \f3gl_get_line()\f1 looks for a file -called \f3\&.teclarc\f1 in your home directory (ie. \f3~/.teclarc\f1). -If it finds this file, it reads it, interpreting each line as defining -a new key binding or an editing configuration option. Since the emacs -keybindings are installed by default, if you want to use the -non-default vi editing mode, the most important item to go in this -file is the following line: - -.nf - edit-mode vi -.fi - -This will re-configure the default bindings for vi-mode. The -complete set of arguments that this command accepts are: -.sp -.nf - vi - Install key-bindings like those of the vi - editor. - emacs - Install key-bindings like those of the emacs - editor. This is the default. - none - Use just the native line editing facilities - provided by the terminal driver. -.fi -.sp -To prevent the terminal bell from being rung, such as when -an unrecognized control-sequence is typed, place the -following line in the configuration file: - -.nf - nobeep -.fi - -An example of a key binding line in the configuration file is -the following. - -.nf - bind M-[2~ insert-mode -.fi - -On many keyboards, the above key sequence is generated when one -presses the \f3insert\f1 key, so with this keybinding, one can toggle -between the emacs-mode insert and overwrite modes by hitting one -key. One could also do it by typing out the above sequence of -characters one by one. As explained above, the \f3M-\f1 part of this -sequence can be typed either by pressing the escape key before the -following key, or by pressing the Meta key at the same time as the -following key. Thus if you had set the above key binding, and the -insert key on your keyboard didn't generate the above key sequence, -you could still type it in either of the following 2 ways. - -.nf - 1. Hit the escape key momentarily, then press '[', then '2', then - finally '~'. - - 2. Press the meta key at the same time as pressing the '[' key, - then press '2', then '~'. -.fi - -If you set a keybinding for a key-sequence that is already bound to a function, -the new binding overrides the old one. If in the new binding you omit the name -of the new function to bind to the key-sequence, the original binding becomes -undefined. -.sp -Starting with versions of libtecla later than 1.3.3 it is now possible -to bind keysequences that begin with a printable character. Previously -key-sequences were required to start with a control or meta character. -.sp -Note that the special keywords "up", "down", "left" and "right" refer -to the arrow keys, and are thus not treated as keysequences. So, for -example, to rebind the up and down arrow keys to use the history -search mechanism instead of the simple history recall method, you -could place the following in your configuration file: - -.nf - bind up history-search-backwards - bind down history-search-backwards -.fi -.sp -To unbind an existing binding, you can do this with the bind command -by omitting to name any action to rebind the key sequence to. For -example, by not specifying an action function, the following command -unbinds the default beginning-of-line action from the ^A key sequence: - -.nf - bind ^A -.fi - -.SH ALTERNATE CONFIGURATION SOURCES - -As mentioned above, by default users have the option of configuring -the behavior of \f3gl_get_line()\f1 via a configuration file called -\f3\&.teclarc\f1 in their home directories. The fact that all -applications share this same configuration file is both an advantage -and a disadvantage. In most cases it is an advantage, since it -encourages uniformity, and frees the user from having to configure -each application separately. In some applications, however, this -single means of configuration is a problem. This is particularly true -of embedded software, where there's no filesystem to read a -configuration file from, and also in applications where a radically -different choice of keybindings is needed to emulate a legacy keyboard -interface. To cater for such cases, the following function allows the -application to control where configuration information is read from. - -.sp -.nf - int gl_configure_getline(GetLine *gl, - const char *app_string, - const char *app_file, - const char *user_file); -.fi -.sp - -It allows the configuration commands that would normally be read from -a user's \f3~/.teclarc\f1 file, to be read from any or none of, a -string, an application specific configuration file, and/or a -user-specific configuration file. If this function is called before -the first call to \f3gl_get_line()\f1, the default behavior of -reading \f3~/.teclarc\f1 on the first call to \f3gl_get_line()\f1 is -disabled, so all configuration must be achieved using the -configuration sources specified with this function. - -If \f3app_string != NULL\f1, then it is interpreted as a string -containing one or more configuration commands, separated from each -other in the string by embedded newline characters. If \f3app_file != -NULL\f1 then it is interpreted as the full pathname of an -application-specific configuration file. If \f3user_file != NULL\f1 -then it is interpreted as the full pathname of a user-specific -configuration file, such as \f3~/.teclarc\f1. For example, in the -following call, - - gl_configure_getline(gl, "edit-mode vi \\n nobeep", - "/usr/share/myapp/teclarc", - "~/.teclarc"); - -the \f3app_string\f1 argument causes the calling application to start -in vi edit-mode, instead of the default emacs mode, and turns off the -use of the terminal bell by the library. It then attempts to read -system-wide configuration commands from an optional file called -\f3/usr/share/myapp/teclarc\f1, then finally reads user-specific -configuration commands from an optional \f3\&.teclarc\f1 file in the -user's home directory. Note that the arguments are listed in ascending -order of priority, with the contents of \f3app_string\f1 being -potentially overriden by commands in \f3app_file\f1, and commands in -\f3app_file\f1 potentially being overriden by commands in -\f3user_file\f1. -.sp -You can call this function as many times as needed, the results being -cumulative, but note that copies of any filenames specified via the -\f3app_file\f1 and \f3user_file\f1 arguments are recorded internally -for subsequent use by the \f3read-init-files\f1 key-binding function, -so if you plan to call this function multiple times, be sure that the -last call specifies the filenames that you want re-read when the user -requests that the configuration files be re-read. - -.SH FILENAME AND TILDE COMPLETION - -With the default key bindings, pressing the TAB key (aka. ^I) results -in \f3gl_get_line()\f1 attempting to complete the incomplete filename -that precedes the cursor. \f3gl_get_line()\f1 searches backwards from -the cursor, looking for the start of the filename, stopping when it -hits either a space or the start of the line. If more than one file -has the specified prefix, \f3gl_get_line()\f1 completes the filename -up to the point at which the ambiguous matches start to differ, then -lists the possible matches. -.sp -In addition to literally written filenames, \f3gl_get_line()\f1 can -complete files that start with \f3~/\f1 and \f3~user/\f1 expressions -and that contain \f3$envvar\f1 expressions. In particular, if you hit -TAB within an incomplete \f3~user\f1, expression, \f3gl_get_line()\f1 -will attempt to complete the username, listing any ambiguous matches. -.sp -The completion binding is implemented using the -\f3cpl_word_completions()\f1 function, which is also available -separately to users of this library. See the -\f3cpl_word_completions(3)\f1 man page for more details. - -.SH CUSTOMIZED WORD COMPLETION - -If in your application, you would like to have TAB completion complete -other things in addition to or instead of filenames, you can arrange -this by registering an alternate completion callback function, via a -call to the \f3gl_customize_completion()\f1 function. -.sp -.nf - int gl_customize_completion(GetLine *gl, void *data, - CplMatchFn *match_fn); -.fi -.sp -The \f3data\f1 argument provides a way for your application to pass -arbitrary, application-specific information to the callback -function. This is passed to the callback every time that it is -called. It might for example, point to the symbol table from which -possible completions are to be sought. The \f3match_fn\f1 argument -specifies the callback function to be called. The \f3CplMatchFn\f1 -function type is defined in \f3libtecla.h\f1, as is a -\f3CPL_MATCH_FN()\f1 macro that you can use to declare and prototype -callback functions. The declaration and responsibilities of callback -functions are described in depth in the \f1cpl_complete_word(3)\f1 man -page. -.sp -In brief, the callback function is responsible for looking backwards -in the input line, back from the point at which the user pressed TAB, -to find the start of the word being completed. It then must lookup -possible completions of this word, and record them one by one in the -\f3WordCompletion\f1 object that is passed to it as an argument, by -calling the \f3cpl_add_completion()\f1 function. If the callback -function wishes to provide filename completion in addition to its own -specific completions, it has the option of itself calling the builtin -file-name completion callback. This also, is documented in the -\f3cpl_complete_word(3)\f1 man page. -.sp -Note that if you would like \f3gl_get_line()\f1 to return the current -input line when a successful completion is been made, you can arrange -this when you call \f3cpl_add_completion()\f1, by making the last -character of the continuation suffix a newline character. If you do -this, the input line will be updated to display the completion, -together with any contiuation suffix up to the newline character, then -\f3gl_get_line()\f1 will return this input line. - -.SH FILENAME EXPANSION - -With the default key bindings, pressing \f3^X*\f1 causes -\f3gl_get_line()\f1 to expand the filename that precedes the cursor, -replacing \f3~/\f1 and \f3~user/\f1 expressions with the corresponding -home directories, and replacing \f3$envvar\f1 expressions with the -value of the specified environment variable, then if there are any -wildcards, replacing the so far expanded filename with a -space-separated list of the files which match the wild cards. -.sp -The expansion binding is implemented using the \f3ef_expand_file()\f1 function. -See the \f3ef_expand_file(3)\f1 man page for more details. - -.SH RECALLING PREVIOUSLY TYPED LINES - -Every time that a new line is entered by the user, it is appended to a -list of historical input lines maintained within the GetLine resource -object. You can traverse up and down this list using the up and down -arrow keys. Alternatively, you can do the same with the \f3^P\f1, and -\f3^N\f1 keys, and in vi command mode you can alternatively use the k -and j characters. Thus pressing up-arrow once, replaces the current -input line with the previously entered line. Pressing up-arrow again, -replaces this with the line that was entered before it, etc.. Having -gone back one or more lines into the history list, one can return to -newer lines by pressing down-arrow one or more times. If you do this -sufficient times, you will return to the original line that you were -entering when you first hit up-arrow. -.sp -Note that in vi mode, all of the history recall functions switch the -library into command mode. -.sp -In emacs mode the \f3M-p\f1 and \f3M-n\f1 keys work just like the -\f3^P\f1 and \f3^N\f1 keys, except that they skip all but those -historical lines which share the prefix that precedes the cursor. In -vi command mode the upper case \f3K\f1 and \f3J\f1 characters do the -same thing, except that the string that they search for includes the -character under the cursor as well as what precedes it. -.sp -Thus for example, suppose that you were in emacs mode, and you had -just entered the following list of commands in the order shown: - -.nf - ls ~/tecla/ - cd ~/tecla - ls -l getline.c - emacs ~/tecla/getline.c -.fi - -If you next typed: - -.nf - ls -.fi - -and then hit \f3M-p\f1, then rather than returning the previously -typed emacs line, which doesn't start with "ls", \f3gl_get_line()\f1 -would recall the "ls -l getline.c" line. Pressing \f3M-p\f1 again -would recall the "ls ~/tecla/" line. - -.SH HISTORY FILES - -To save the contents of the history buffer before quitting your -application, and subsequently restore them when you next start the -application, the following functions are provided. - -.sp -.nf - int gl_save_history(GetLine *gl, const char *filename, - const char *comment, int max_lines); - int gl_load_history(GetLine *gl, const char *filename, - const char *comment); -.fi -.sp - -The \f3filename\f1 argument specifies the name to give the history -file when saving, or the name of an existing history file, when -loading. This may contain home-directory and environment variable -expressions, such as "~/.myapp_history" or "$HOME/.myapp_history". -.sp -Along with each history line, extra information about it, such as when -it was entered by the user, and what its nesting level is, is recorded -as a comment preceding the line in the history file. Writing this as a -comment allows the history file to double as a command file, just in -case you wish to replay a whole session using it. Since comment -prefixes differ in different languages, the \f3comment\f1 argument is -provided for specifying the comment prefix. For example, if your -application were a unix shell, such as the bourne shell, you would -specify "#" here. Whatever you choose for the comment character, you -must specify the same prefix to \f3gl_load_history()\f1 that you used -when you called \f3gl_save_history()\f1 to write the history file. -.sp -The \f3max_lines\f1 must be either -1 to specify that all lines in the -history list be saved, or a positive number specifying a ceiling on -how many of the most recent lines should be saved. -.sp -Both fuctions return non-zero on error, after writing an error message -to stderr. Note that \f3gl_load_history()\f1 does not consider the -non-existence of a file to be an error. - -.SH MULTIPLE HISTORY LISTS - -If your application uses a single \f3GetLine\f1 object for entering -many different types of input lines, you may wish \f3gl_get_line()\f1 -to distinguish the different types of lines in the history list, and -only recall lines that match the current type of line. To support this -requirement, \f3gl_get_line()\f1 marks lines being recorded in the -history list with an integer identifier chosen by the application. -Initially this identifier is set to \f10\f3 by \f3new_GetLine()\f1, -but it can be changed subsequently by calling -\f3gl_group_history()\f1. - -.sp -.nf - int gl_group_history(GetLine *gl, unsigned id); -.fi -.sp - -The integer identifier \f3id\f1 can be any number chosen by the -application, but note that \f3gl_save_history()\f1 and -\f3gl_load_history()\f1 preserve the association between identifiers -and historical input lines between program invokations, so you should -choose fixed identifiers for the different types of input line used by -your application. -.sp -Whenever \f3gl_get_line()\f1 appends a new input line to the history -list, the current history identifier is recorded with it, and when it -is asked to recall a historical input line, it only recalls lines that -are marked with the current identifier. - -.SH DISPLAYING HISTORY - -The history list can be displayed by calling \f3gl_show_history()\f1. - -.sp -.nf - int gl_show_history(GetLine *gl, FILE *fp, - const char *fmt, - int all_groups, - int max_lines); -.fi -.sp - -This displays the current contents of the history list to the stdio -output stream \f3fp\f1. If the \f3max_lines\f1 argument is greater -than or equal to zero, then no more than this number of the most -recent lines will be displayed. If the \f3all_groups\f1 argument is -non-zero, lines from all history groups are displayed. Otherwise just -those of the currently selected history group are displayed. The -format string argument, \f3fmt\f1, determines how the line is -displayed. This can contain arbitrary characters which are written -verbatim, interleaved with any of the following format directives: - -.nf - %D - The date on which the line was originally - entered, formatted like 2001-11-20. - %T - The time of day when the line was entered, - formatted like 23:59:59. - %N - The sequential entry number of the line in - the history buffer. - %G - The number of the history group which the - line belongs to. - %% - A literal % character. - %H - The history line itself. -.fi - -Thus a format string like \f3"%D %T %H\n"\f1 would output something like: - -.nf - 2001-11-20 10:23:34 Hello world -.fi - -Note the inclusion of an explicit newline character in the format -string. - -.SH LOOKING UP HISTORY - -The \f3gl_lookup_history()\f1 function allows the calling application -to look up lines in the history list. - -.sp -.nf - typedef struct { - const char *line; /* The requested historical */ - /* line. */ - unsigned group; /* The history group to which */ - /* the line belongs. */ - time_t timestamp; /* The date and time at which */ - /* the line was originally */ - /* entered. */ - } GlHistoryLine; - - int gl_lookup_history(GetLine *gl, unsigned long id, - GlHistoryLine *hline); -.fi -.sp - -The \f3id\f1 argument indicates which line to look up, where the first -line that was entered in the history list after \f3new_GetLine()\f1 -was called, is denoted by 0, and subsequently entered lines are -denoted with successively higher numbers. Note that the range of lines -currently preserved in the history list can be queried by calling the -\f3gl_range_of_history()\f1 function, described later. If the -requested line is in the history list, the details of the line are -recorded in the variable pointed to by the \f3hline\f1 argument, and -\f31\f1 is returned. Otherwise \f30\f1 is returned, and the variable -pointed to by \f3hline\f1 is left unchanged. -.sp -Beware that the string returned in \f3hline->line\f1 is part of the -history buffer, so it must not be modified by the caller, and will be -recycled on the next call to any function that takes \f3gl\f1 as its -argument. Therefore you should make a private copy of this string if -you need to keep it around. - -.SH MISCELLANEOUS HISTORY CONFIGURATION - -If you wish to change the size of the history buffer that was -originally specified in the call to \f3new_GetLine()\f1, you can do so -with the \f3gl_resize_history()\f1 function. - -.sp -.nf - int gl_resize_history(GetLine *gl, size_t histlen); -.fi -.sp - -The \f3histlen\f1 argument specifies the new size in bytes, and if you -specify this as 0, the buffer will be deleted. -.sp -As mentioned in the discussion of \f3new_GetLine()\f1, the number of -lines that can be stored in the history buffer, depends on the lengths -of the individual lines. For example, a 1000 byte buffer could equally -store 10 lines of average length 100 bytes, or 2 lines of average -length 50 bytes. Although the buffer is never expanded when new lines -are added, a list of pointers into the buffer does get expanded when -needed to accomodate the number of lines currently stored in the -buffer. To place an upper limit on the number of lines in the buffer, -and thus a ceiling on the amount of memory used in this list, you can -call the \f3gl_limit_history()\f1 function. - -.sp -.nf - void gl_limit_history(GetLine *gl, int max_lines); -.fi -.sp - -The \f3max_lines\f1 should either be a positive number \f3>= 0\f1, -specifying an upper limit on the number of lines in the buffer, or be -\f3-1\f1 to cancel any previously specified limit. When a limit is in -effect, only the \f3max_lines\f1 most recently appended lines are kept -in the buffer. Older lines are discarded. -.sp -To discard lines from the history buffer, use the -\f3gl_clear_history()\f1 function. -.sp -.nf - void gl_clear_history(GetLine *gl, int all_groups); -.fi -.sp -The \f3all_groups\f1 argument tells the function whether to delete -just the lines associated with the current history group (see -\f3gl_group_history()\f1), or all historical lines in the buffer. -.sp -The \f3gl_toggle_history()\f1 function allows you to toggle history on -and off without losing the current contents of the history list. - -.sp -.nf - void gl_toggle_history(GetLine *gl, int enable); -.fi -.sp - -Setting the \f3enable\f1 argument to 0 turns off the history -mechanism, and setting it to 1 turns it back on. When history is -turned off, no new lines will be added to the history list, and -history lookup key-bindings will act as though there is nothing in the -history buffer. - -.SH QUERYING HISTORY INFORMATION - -The configured state of the history list can be queried with the -\f3gl_history_state()\f1 function. - -.sp -.nf - typedef struct { - int enabled; /* True if history is enabled */ - unsigned group; /* The current history group */ - int max_lines; /* The current upper limit on the */ - /* number of lines in the history */ - /* list, or -1 if unlimited. */ - } GlHistoryState; - - void gl_state_of_history(GetLine *gl, - GlHistoryState *state); -.fi -.sp -On return, the status information is recorded in the variable pointed -to by the \f3state\f1 argument. -.sp -The \f3gl_range_of_history()\f1 function returns the number and -range of lines in the history list. - -.sp -.nf -typedef struct { - unsigned long oldest; /* The sequential entry number */ - /* of the oldest line in the */ - /* history list. */ - unsigned long newest; /* The sequential entry number */ - /* of the newest line in the */ - /* history list. */ - int nlines; /* The number of lines in the */ - /* history list. */ -} GlHistoryRange; - -void gl_range_of_history(GetLine *gl, GlHistoryRange *range); -.fi -.sp -The return values are recorded in the variable pointed to by the -\f3range\f1 argument. If the \f3nlines\f1 member of this structure is -greater than zero, then the \f3oldest\f1 and \f3newest\f1 members -report the range of lines in the list, and \f3newest=oldest+nlines-1\f1. -Otherwise they are both zero. -.sp -The \f3gl_size_of_history()\f1 function returns the total size of the -history buffer and the amount of the buffer that is currently -occupied. -.sp -.nf - typedef struct { - size_t size; /* The size of the history buffer */ - /* (bytes). */ - size_t used; /* The number of bytes of the */ - /* history buffer that are */ - /* currently occupied. */ - } GlHistorySize; - - void gl_size_of_history(GetLine *gl, GlHistorySize *size); -.fi -.sp -On return, the size information is recorded in the variable pointed to -by the \f3size\f1 argument. - -.SH CHANGING TERMINALS - -The \f3new_GetLine()\f1 constructor function assumes that input is to -be read from \f3stdin\f1, and output written to \f3stdout\f1. The -following function allows you to switch to different input and output -streams. -.sp -.nf - int gl_change_terminal(GetLine *gl, FILE *input_fp, - FILE *output_fp, const char *term); -.fi -.sp -The \f3gl\f1 argument is the object that was returned by -\f3new_GetLine()\f1. The \f3input_fp\f1 argument specifies the stream -to read from, and \f3output_fp\f1 specifies the stream to be written -to. Only if both of these refer to a terminal, will interactive -terminal input be enabled. Otherwise \f3gl_get_line()\f1 will simply -call \f3fgets()\f1 to read command input. If both streams refer to a -terminal, then they must refer to the same terminal, and the type of -this terminal must be specified via the \f3term\f1 argument. The value -of the \f3term\f1 argument is looked up in the terminal information -database (terminfo or termcap), in order to determine which special -control sequences are needed to control various aspects of the -terminal. \f3new_GetLine()\f1 for example, passes the return value of -\f3getenv("TERM")\f1 in this argument. Note that if one or both of -\f3input_fp\f1 and \f3output_fp\f1 don't refer to a terminal, then it -is legal to pass \f3NULL\f1 instead of a terminal type. -.sp -Note that if you want to pass file descriptors to -\f3gl_change_terminal()\f1, you can do this by creating stdio stream -wrappers using the POSIX \f3fdopen()\f1 function. - -.SH EXTERNAL EVENT HANDLING - -While \f3gl_get_line()\f1 is waiting for keyboard input from the user, -you can ask it to also watch for activity on arbitrary file -descriptors, such as network sockets, pipes etc, and have it call -functions of your choosing when activity is seen. This works on any -system that has the \f3select()\f1 system call, which is most, if not -all flavors of unix. Registering a file descriptor to be watched by -\f3gl_get_line()\f1 involves calling the \f3gl_watch_fd()\f1 function. - -.sp -.nf - int gl_watch_fd(GetLine *gl, int fd, GlFdEvent event, - GlFdEventFn *callback, void *data); -.fi -.sp - -If this returns non-zero, then it means that either your arguments are -invalid, or that this facility isn't supported on the host system. -.sp -The \f3fd\f1 argument is the file descriptor to be watched. The -\f3event\f1 argument specifies what type of activity is of interest, -chosen from the following enumerated values: - -.sp -.nf - GLFD_READ - Watch for the arrival of data to be read. - GLFD_WRITE - Watch for the ability to write to the file - descriptor without blocking. - GLFD_URGENT - Watch for the arrival of urgent - out-of-band data on the file descriptor. -.fi -.sp - -The \f3callback\f1 argument is the function to call when the selected -activity is seen. It should be defined with the following macro, which -is defined in libtecla.h. - -.sp -.nf - #define GL_FD_EVENT_FN(fn) GlFdStatus (fn)(GetLine *gl, \\ - void *data, int fd, \\ - GlFdEvent event) -.fi -.sp -The \f3data\f1 argument of the \f3gl_watch_fd()\f1 function is passed -to the callback function for its own use, and can point to anything -you like, including \f3NULL\f1. The file descriptor and the event -argument are also passed to the callback function, and this -potentially allows the same callback function to be registered to more -than one type of event and/or more than one file descriptor. The -return value of the callback function should be one of the following -values. - -.sp -.nf - GLFD_ABORT - Tell gl_get_line() to abort with an - error (errno won't be set, so set it - appropriately yourself if you need it). - GLFD_REFRESH - Redraw the input line then continue - waiting for input. Return this if - your callback wrote to the terminal. - GLFD_CONTINUE - Continue to wait for input, without - redrawing the line. -.fi -.sp -Note that before calling the callback, \f3gl_get_line()\f1 blocks most -signals, and leaves its own signal handlers installed, so if you need -to catch a particular signal you will need to both temporarily install -your own signal handler, and unblock the signal. Be sure to re-block -the signal (if it was originally blocked) and reinstate the original -signal handler, if any, before returning. -.sp -Your callback shouldn't try to read from the terminal, which is left -in raw mode as far as input is concerned. You can however write to the -terminal as usual, since features like conversion of newline to -carriage-return/linefeed are re-enabled while the callback is -running. If your callback function does write to the terminal, be sure -to output a newline first, and when your callback returns, tell -\f3gl_get_line()\f1 that the input line needs to be redrawn, by -returning the \f3GLFD_REFRESH\f1 status code. -.sp -To remove a callback function that you previously registered for a -given file descriptor and event, simply call \f3gl_watch_fd()\f1 with -the same file descriptor and \f3event\f1 arguments, but with a -\f3callback\f1 argument of \f30\f1. The \f3data\f1 argument is ignored -in this case. - -.SH SIGNAL HANDLING DEFAULTS - -By default, the \f3gl_get_line()\f1 function intercepts a -number of signals. This is particularly important for -signals which would by default terminate the process, since -the terminal needs to be restored to a usable state before -this happens. In this section, the signals that are trapped -by default, and how gl_get_line() responds to them, is -described. Changing these defaults is the topic of the -following section. -.sp -When the following subset of signals are caught, \f3gl_get_line()\f1 -first restores the terminal settings and signal handling to how they -were before \f3gl_get_line()\f1 was called, resends the signal, to -allow the calling application's signal handlers to handle it, then if -the process still exists, \f3gl_get_line()\f1 returns \f3NULL\f1 and -sets \f3errno\f1 as specified below. - -.sp -.nf - SIGINT - This signal is generated both by the keyboard - interrupt key (usually ^C), and the keyboard - break key. - - errno=EINTR - - SIGHUP - This signal is generated when the controlling - terminal exits. - - errno=ENOTTY - - SIGPIPE - This signal is generated when a program attempts - to write to a pipe who's remote end isn't being - read by any process. This can happen for example - if you have called \f3gl_change_terminal()\f1 to - redirect output to a pipe hidden under a pseudo - terminal. - - errno=EPIPE - - SIGQUIT - This signal is generated by the keyboard quit - key (usually ^\\). - - errno=EINTR - - SIGABRT - This signal is generated by the standard C, - abort() function. By default it both - terminates the process and generates a core - dump. - - errno=EINTR - - SIGTERM - This is the default signal that the UN*X - kill command sends to processes. - - errno=EINTR -.fi -.sp -Note that in the case of all of the above signals, POSIX mandates that -by default the process is terminated, with the addition of a core dump -in the case of the \f3SIGQUIT\f1 signal. In other words, if the -calling application doesn't override the default handler by supplying -its own signal handler, receipt of the corresponding signal will -terminate the application before \f3gl_get_line()\f1 returns. -.sp -If gl_get_line() aborts with errno set to EINTR, you can find out what -signal caused it to abort, by calling the following function. -.sp -.nf - int gl_last_signal(const GetLine *gl); -.fi -.sp -This returns the numeric code (eg. \f3SIGINT\f1) of the last signal -that was received during the most recent call to \f3gl_get_line()\f1, -or \f3-1\f1 if no signals were received. -.sp -On systems that support it, when a SIGWINCH (window change) signal is -received, \f3gl_get_line()\f1 queries the terminal to find out its new -size, redraws the current input line to accomodate the new size, then -returns to waiting for keyboard input from the user. Unlike other -signals, this signal isn't resent to the application. -.sp -Finally, the following signals cause \f3gl_get_line()\f1 to first -restore the terminal and signal environment to that which prevailed -before \f3gl_get_line()\f1 was called, then resend the signal to the -application. If the process still exists after the signal has been -delivered, then \f3gl_get_line()\f1 then re-establishes its own signal -handlers, switches the terminal back to raw mode, redisplays the input -line, and goes back to awaiting terminal input from the user. -.sp -.nf - SIGCONT - This signal is generated when a suspended - process is resumed. - - SIGPWR - This signal is generated when a power failure - occurs (presumably when the system is on a - UPS). - - SIGALRM - This signal is generated when a timer - expires. - - SIGUSR1 - An application specific signal. - - SIGUSR2 - Another application specific signal. - - SIGVTALRM - This signal is generated when a virtual - timer expires (see man setitimer(2)). - - SIGXCPU - This signal is generated when a process - exceeds its soft CPU time limit. - - SIGTSTP - This signal is generated by the terminal - suspend key, which is usually ^Z, or the - delayed terminal suspend key, which is - usually ^Y. - - SIGTTIN - This signal is generated if the program - attempts to read from the terminal while the - program is running in the background. - - SIGTTOU - This signal is generated if the program - attempts to write to the terminal while the - program is running in the background. -.fi -.sp - -Obviously not all of the above signals are supported on all systems, -so code to support them is conditionally compiled into the tecla -library. -.sp -Note that if \f3SIGKILL\f1, which by definition can't be caught, or -any of the hardware generated exception signals, such as -\f3SIGSEGV\f1, \f3SIGBUS\f1 and \f3SIGFPE\f1, are received and -unhandled while \f3gl_get_line()\f1 has the terminal in raw mode, the -program will be terminated without the terminal having been restored -to a usable state. In practice, job-control shells usually reset the -terminal settings when a process relinquishes the controlling -terminal, so this is only a problem with older shells. - -.SH CUSTOMIZED SIGNAL HANDLING - -The previous section listed the signals that -\f3gl_get_line()\f1 traps by default, and described how it -responds to them. This section describes how to both add and -remove signals from the list of trapped signals, and how to -specify how \f3gl_get_line()\f1 should respond to a given -signal. -.sp -If you don't need \f3gl_get_line()\f1 to do anything in -response to a signal that it normally traps, you can tell to -\f3gl_get_line()\f1 to ignore that signal by calling -\f3gl_ignore_signal()\f1. -.sp -.nf - int gl_ignore_signal(GetLine *gl, int signo); -.fi -.sp -The \f3signo\f1 argument is the number of the signal -(eg. \f3SIGINT\f1) that you want to have ignored. If the -specified signal isn't currently one of those being trapped, -this function does nothing. -.sp -The \f3gl_trap_signal()\f1 function allows you to either add -a new signal to the list that \f3gl_get_line()\f1 traps, or -modify how it responds to a signal that it already traps. -.sp -.nf - int gl_trap_signal(GetLine *gl, int signo, unsigned flags, - GlAfterSignal after, int errno_value); -.fi -.sp -The \f3signo\f1 argument is the number of the signal that -you wish to have trapped. The \f3flags\f1 argument is a set -of flags which determine the environment in which the -application's signal handler is invoked, the \f3after\f1 -argument tells \f3gl_get_line()\f1 what to do after the -application's signal handler returns, and \f3errno_value\f1 -tells \f3gl_get_line()\f1 what to set \f3errno\f1 to if told -to abort. -.sp -The \f3flags\f1 argument is a bitwise OR of zero or more of -the following enumerators: -.sp -.nf - GLS_RESTORE_SIG - Restore the caller's signal - environment while handling the - signal. - - GLS_RESTORE_TTY - Restore the caller's terminal settings - while handling the signal. - - GLS_RESTORE_LINE - Move the cursor to the start of the - line following the input line before - invoking the application's signal - handler. - - GLS_REDRAW_LINE - Redraw the input line when the - application's signal handler returns. - - GLS_UNBLOCK_SIG - Normally, if the calling program has - a signal blocked (man sigprocmask), - gl_get_line() does not trap that - signal. This flag tells gl_get_line() - to trap the signal and unblock it for - the duration of the call to - gl_get_line(). - - GLS_DONT_FORWARD - If this flag is included, the signal - will not be forwarded to the signal - handler of the calling program. -.fi -.sp -Two commonly useful flag combinations are also enumerated as -follows: -.sp -.nf - GLS_RESTORE_ENV = GLS_RESTORE_SIG | GLS_RESTORE_TTY | - GLS_REDRAW_LINE - - GLS_SUSPEND_INPUT = GLS_RESTORE_ENV | GLS_RESTORE_LINE -.fi -.sp - -If your signal handler, or the default system signal -handler for this signal, if you haven't overriden it, never -either writes to the terminal, nor suspends or terminates -the calling program, then you can safely set the \f3flags\f1 -argument to \f30\f1. -.sp -If your signal handler always writes to the terminal, reads -from it, or suspends or terminates the program, you should -specify the \f3flags\f1 argument as \f3GL_SUSPEND_INPUT\f1, -so that: -.sp -.nf -1. The cursor doesn't get left in the middle of the input - line. -2. So that the user can type in input and have it echoed. -3. So that you don't need to end each output line with - \f3\\r\\n\f1, instead of just \f3\\n\f1. -.fi -.sp -The \f3GL_RESTORE_ENV\f1 combination is the same as -\f3GL_SUSPEND_INPUT\f1, except that it doesn't move the -cursor, and if your signal handler doesn't read or write -anything to the terminal, the user won't see any visible -indication that a signal was caught. This can be useful if -you have a signal handler that only occasionally writes to -the terminal, where using \f3GL_SUSPEND_LINE\f1 would cause -the input line to be unnecessarily duplicated when nothing -had been written to the terminal. Such a signal handler, -when it does write to the terminal, should be sure to start -a new line at the start of its first write, by writing a -'\\n' character, and should be sure to leave the cursor on a -new line before returning. If the signal arrives while the -user is entering a line that only occupies a signal terminal -line, or if the cursor is on the last terminal line of a -longer input line, this will have the same effect as -\f3GL_SUSPEND_INPUT\f1. Otherwise it will start writing on a -line that already contains part of the displayed input line. -This doesn't do any harm, but it looks a bit ugly, which is -why the \f3GL_SUSPEND_INPUT\f1 combination is better if you -know that you are always going to be writting to the -terminal. -.sp -The \f3after\f1 argument, which determines what -\f3gl_get_line()\f1 does after the application's signal -handler returns (if it returns), can take any one of the -following values: -.sp -.nf - GLS_RETURN - Return the completed input line, just as - though the user had pressed the return - key. - - GLS_ABORT - Cause gl_get_line() to return \f3NULL\f1. - - GLS_CONTINUE - Resume command line editing. -.fi -.sp -The \f3errno_value\f1 argument is intended to be combined -with the \f3GLS_ABORT\f1 option, telling \f3gl_get_line()\f1 -what to set the standard \f3errno\f1 variable to before -returning \f3NULL\f1 to the calling program. It can also, -however, be used with the \f3GL_RETURN\f1 option, in case -you wish to have a way to distinguish between an input line -that was entered using the return key, and one that was -entered by the receipt of a signal. - -.SH THE TERMINAL SIZE - -On most systems the combination of the \f3TIOCGWINSZ\f1 ioctl and the -\f3SIGWINCH\f1 signal is used to maintain an accurate idea of the -terminal size. The terminal size is newly queried every time that -\f3gl_get_line()\f1 is called and whenever a \f3SIGWINCH\f1 signal is -received. -.sp -On the few systems where this mechanism isn't available, at -startup \f3new_GetLine()\f1 first looks for the \f3LINES\f1 -and \f3COLUMNS\f1 environment variables. If these aren't -found, or they contain unusable values, then if a terminal -information database like terminfo or termcap is available, -the default size of the terminal is looked up in this -database. If this too fails to provide the terminal size, a -default size of 80 columns by 24 lines is used. If this -default isn't appropriate for your system, -\f3gl_terminal_size()\f1 can be used to supply a different -fallback. -.sp -The \f3gl_terminal_size()\f1 function allows you to query -the current size of the terminal, and install an alternate -fallback size for cases where the size isn't available. -Beware that the terminal size won't be available if reading -from a pipe or a file, so the default values can be -important even on systems that do support ways of finding -out the terminal size. -.sp -.nf - typedef struct { - int nline; /* The terminal has nline lines */ - int ncolumn; /* The terminal has ncolumn columns */ - } GlTerminalSize; - - GlTerminalSize gl_terminal_size(GetLine *gl, - int def_ncolumn, - int def_nline); -.fi -.sp -This function first updates \f3gl_get_line()\f1's idea of -the terminal size, then records its findings in the return -value. -.sp -The \f3def_ncolumn\f1 and \f3def_nline\f1 specify the -default number of terminal columns and lines to use if the -terminal size can't be determined. - -.SH HIDING WHAT YOU TYPE - -When entering sensitive information, such as passwords, it is best not -to have the text that you are entering echoed on the terminal. -Furthermore, such text should not be recorded in the history list, -since somebody finding your terminal unattended could then recall it, -or somebody snooping through your directories could see it in your -history file. With this in mind, the \f3gl_echo_mode()\f1 -function allows you to toggle on and off the display and archival of -any text that is subsequently entered in calls to \f3gl_get_line()\f1. - -.sp -.nf - int gl_echo_mode(GetLine *gl, int enable); -.fi -.sp - -The \f3enable\f1 argument specifies whether entered text -should be visible or not. If it is \f30\f1, then -subsequently entered lines will not be visible on the -terminal, and will not be recorded in the history list. If -it is \f31\f1, then subsequent input lines will be displayed -as they are entered, and provided that history hasn't been -turned off via a call to \f3gl_toggle_history()\f1, then -they will also be archived in the history list. Finally, if -the \f3enable\f1 argument is \f3-1\f1, then the echoing mode -is left unchanged, which allows you to non-destructively -query the current setting via the return value. In all -cases, the return value of the function is \f30\f1 if -echoing was disabled before the function was called, and -\f31\f1 if it was enabled. -.sp -When echoing is turned off, note that although tab -completion will invisibly complete your prefix as far as -possible, ambiguous completions will not be displayed. - -.SH CALLBACK FUNCTION FACILITIES - -Unless otherwise stated, callback functions, such as tab -completion callbacks and event callbacks should not call any -functions in this module. The following functions, however, -are designed specifically to be used by callback functions. -.sp -Calling the \f3gl_replace_prompt()\f1 function from a -callback tells \f3gl_get_line() to display a different -prompt when the callback returns. It has no effect if called -when \f3gl_get_line()\f1 is not being called. -.sp -.nf - void gl_replace_prompt(GetLine *gl, const char *prompt); -.fi -.sp - -.SH INTERNATIONAL CHARACTER SETS - -Since libtecla version 1.4.0, \f3gl_get_line()\f1 has been 8-bit -clean. This means that all 8-bit characters that are printable in the -user's current locale are now displayed verbatim and included in the -returned input line. Assuming that the calling program correctly -contains a call like the following, -.sp -.nf - setlocale(LC_CTYPE, ""); -.fi -.sp -then the current locale is determined by the first of the environment -variables \f3LC_CTYPE\f1, \f3LC_ALL\f1, and \f3LANG\f1, that is found -to contain a valid locale name. If none of these variables are -defined, or the program neglects to call setlocale, then the default -\f3C\f1 locale is used, which is US 7-bit ASCII. On most unix-like -platforms, you can get a list of valid locales by typing the command: -.sp -.nf - locale -a -.fi -.sp -at the shell prompt. -.sp -.SS "Meta keys and locales" - -Beware that in most locales other than the default C locale, meta -characters become printable, and they are then no longer considered to -match \f3M-c\f1 style key bindings. This allows international -characters to be entered with the compose key without unexpectedly -triggering meta key bindings. You can still invoke meta bindings, -since there are actually two ways to do this. For example the binding -\f3M-c\f1 can also be invoked by pressing the escape key momentarily, -then pressing the \f3c\f1 key, and this will work regardless of -locale. Moreover, many modern terminal emulators, such as gnome's -gnome-terminal's and KDE's konsole terminals, already generate escape -pairs like this when you use the meta key, rather than a real meta -character, and other emulators usually have a way to request this -behavior, so you can continue to use the meta key on most systems. -.sp -For example, although xterm terminal emulators generate real 8-bit -meta characters by default when you use the meta key, they can be -configured to output the equivalent escape pair by setting their -\f3EightBitInput\f1 X resource to \f3False\f1. You can either do this -by placing a line like the following in your \f3~/.Xdefaults\f1 file, -.sp -.nf - XTerm*EightBitInput: False -.sp -.fi -or by starting an xterm with an \f3-xrm '*EightBitInput: False'\f1 -command-line argument. In recent versions of xterm you can toggle this -feature on and off with the \f3"Meta Sends Escape"\f1 option in the -menu that is displayed when you press the left mouse button and the -control key within an xterm window. In CDE, dtterms can be similarly -coerced to generate escape pairs in place of meta characters, by -setting the \f3Dtterm*KshMode\f1 resource to \f3True\f1. -.sp -.SS "Entering international characters" - -If you don't have a keyboard that generates all of the international -characters that you need, there is usually a compose key that will -allow you to enter special characters, or a way to create one. For -example, under X windows on unix-like systems, if your keyboard -doesn't have a compose key, you can designate a redundant key to serve -this purpose with the xmodmap command. For example, on many PC -keyboards there is a microsoft-windows key, which is otherwise useless -under Linux. On my PC the \f3xev\f1 program reports that pressing this -key generates keycode 115, so to turn this key into a compose key, I -do the following: -.sp -.nf - xmodmap -e 'keycode 115 = Multi_key' -.fi -.sp -I can then enter an i with a umlaut over it by typing this key, -followed by \f3"\f1, followed by i. - -.SH THREAD SAFETY - -In a multi-threaded program, you should use the libtecla_r.a version -of the library. This uses reentrant versions of system functions, -where available. Unfortunately neither terminfo nor termcap were -designed to be reentrant, so you can't safely use the functions of the -getline module in multiple threads (you can use the separate -file-expansion and word-completion modules in multiple threads, see -the corresponding man pages for details). However due to the use of -POSIX reentrant functions for looking up home directories etc, it is -safe to use this module from a single thread of a multi-threaded -program, provided that your other threads don't use any termcap or -terminfo functions. - -.SH FILES -.nf -libtecla.a - The tecla library -libtecla.h - The tecla header file. -~/.teclarc - The personal tecla customization file. -.fi - -.SH SEE ALSO -libtecla(3), ef_expand_file(3), cpl_complete_word(3), pca_lookup_file(3) - -.SH AUTHOR -Martin Shepherd (mcs@astro.caltech.edu) diff --git a/libtecla-1.4.1/man3/gl_group_history.3 b/libtecla-1.4.1/man3/gl_group_history.3 deleted file mode 100644 index 6bd3d1f..0000000 --- a/libtecla-1.4.1/man3/gl_group_history.3 +++ /dev/null @@ -1 +0,0 @@ -.so man3/gl_get_line.3 diff --git a/libtecla-1.4.1/man3/gl_ignore_signal.3 b/libtecla-1.4.1/man3/gl_ignore_signal.3 deleted file mode 100644 index 6bd3d1f..0000000 --- a/libtecla-1.4.1/man3/gl_ignore_signal.3 +++ /dev/null @@ -1 +0,0 @@ -.so man3/gl_get_line.3 diff --git a/libtecla-1.4.1/man3/gl_last_signal.3 b/libtecla-1.4.1/man3/gl_last_signal.3 deleted file mode 100644 index 6bd3d1f..0000000 --- a/libtecla-1.4.1/man3/gl_last_signal.3 +++ /dev/null @@ -1 +0,0 @@ -.so man3/gl_get_line.3 diff --git a/libtecla-1.4.1/man3/gl_limit_history.3 b/libtecla-1.4.1/man3/gl_limit_history.3 deleted file mode 100644 index 6bd3d1f..0000000 --- a/libtecla-1.4.1/man3/gl_limit_history.3 +++ /dev/null @@ -1 +0,0 @@ -.so man3/gl_get_line.3 diff --git a/libtecla-1.4.1/man3/gl_load_history.3 b/libtecla-1.4.1/man3/gl_load_history.3 deleted file mode 100644 index 6bd3d1f..0000000 --- a/libtecla-1.4.1/man3/gl_load_history.3 +++ /dev/null @@ -1 +0,0 @@ -.so man3/gl_get_line.3 diff --git a/libtecla-1.4.1/man3/gl_lookup_history.3 b/libtecla-1.4.1/man3/gl_lookup_history.3 deleted file mode 100644 index 6bd3d1f..0000000 --- a/libtecla-1.4.1/man3/gl_lookup_history.3 +++ /dev/null @@ -1 +0,0 @@ -.so man3/gl_get_line.3 diff --git a/libtecla-1.4.1/man3/gl_prompt_style.3 b/libtecla-1.4.1/man3/gl_prompt_style.3 deleted file mode 100644 index 6bd3d1f..0000000 --- a/libtecla-1.4.1/man3/gl_prompt_style.3 +++ /dev/null @@ -1 +0,0 @@ -.so man3/gl_get_line.3 diff --git a/libtecla-1.4.1/man3/gl_range_of_history.3 b/libtecla-1.4.1/man3/gl_range_of_history.3 deleted file mode 100644 index 6bd3d1f..0000000 --- a/libtecla-1.4.1/man3/gl_range_of_history.3 +++ /dev/null @@ -1 +0,0 @@ -.so man3/gl_get_line.3 diff --git a/libtecla-1.4.1/man3/gl_resize_history.3 b/libtecla-1.4.1/man3/gl_resize_history.3 deleted file mode 100644 index 6bd3d1f..0000000 --- a/libtecla-1.4.1/man3/gl_resize_history.3 +++ /dev/null @@ -1 +0,0 @@ -.so man3/gl_get_line.3 diff --git a/libtecla-1.4.1/man3/gl_save_history.3 b/libtecla-1.4.1/man3/gl_save_history.3 deleted file mode 100644 index 6bd3d1f..0000000 --- a/libtecla-1.4.1/man3/gl_save_history.3 +++ /dev/null @@ -1 +0,0 @@ -.so man3/gl_get_line.3 diff --git a/libtecla-1.4.1/man3/gl_show_history.3 b/libtecla-1.4.1/man3/gl_show_history.3 deleted file mode 100644 index 6bd3d1f..0000000 --- a/libtecla-1.4.1/man3/gl_show_history.3 +++ /dev/null @@ -1 +0,0 @@ -.so man3/gl_get_line.3 diff --git a/libtecla-1.4.1/man3/gl_size_of_history.3 b/libtecla-1.4.1/man3/gl_size_of_history.3 deleted file mode 100644 index 6bd3d1f..0000000 --- a/libtecla-1.4.1/man3/gl_size_of_history.3 +++ /dev/null @@ -1 +0,0 @@ -.so man3/gl_get_line.3 diff --git a/libtecla-1.4.1/man3/gl_state_of_history.3 b/libtecla-1.4.1/man3/gl_state_of_history.3 deleted file mode 100644 index 6bd3d1f..0000000 --- a/libtecla-1.4.1/man3/gl_state_of_history.3 +++ /dev/null @@ -1 +0,0 @@ -.so man3/gl_get_line.3 diff --git a/libtecla-1.4.1/man3/gl_terminal_size.3 b/libtecla-1.4.1/man3/gl_terminal_size.3 deleted file mode 100644 index 6bd3d1f..0000000 --- a/libtecla-1.4.1/man3/gl_terminal_size.3 +++ /dev/null @@ -1 +0,0 @@ -.so man3/gl_get_line.3 diff --git a/libtecla-1.4.1/man3/gl_toggle_history.3 b/libtecla-1.4.1/man3/gl_toggle_history.3 deleted file mode 100644 index 6bd3d1f..0000000 --- a/libtecla-1.4.1/man3/gl_toggle_history.3 +++ /dev/null @@ -1 +0,0 @@ -.so man3/gl_get_line.3 diff --git a/libtecla-1.4.1/man3/gl_trap_signal.3 b/libtecla-1.4.1/man3/gl_trap_signal.3 deleted file mode 100644 index 6bd3d1f..0000000 --- a/libtecla-1.4.1/man3/gl_trap_signal.3 +++ /dev/null @@ -1 +0,0 @@ -.so man3/gl_get_line.3 diff --git a/libtecla-1.4.1/man3/gl_watch_fd.3 b/libtecla-1.4.1/man3/gl_watch_fd.3 deleted file mode 100644 index 6bd3d1f..0000000 --- a/libtecla-1.4.1/man3/gl_watch_fd.3 +++ /dev/null @@ -1 +0,0 @@ -.so man3/gl_get_line.3 diff --git a/libtecla-1.4.1/man3/libtecla.3 b/libtecla-1.4.1/man3/libtecla.3 deleted file mode 100644 index 611eb57..0000000 --- a/libtecla-1.4.1/man3/libtecla.3 +++ /dev/null @@ -1,160 +0,0 @@ -.\" Copyright (C) 2000, 2001 by Martin C. Shepherd -.\" -.\" All rights reserved. -.\" -.\" Permission is hereby granted, free of charge, to any person obtaining a -.\" copy of this software and associated documentation files (the -.\" "Software"), to deal in the Software without restriction, including -.\" without limitation the rights to use, copy, modify, merge, publish, -.\" distribute, and/or sell copies of the Software, and to permit persons -.\" to whom the Software is furnished to do so, provided that the above -.\" copyright notice(s) and this permission notice appear in all copies of -.\" the Software and that both the above copyright notice(s) and this -.\" permission notice appear in supporting documentation. -.\" -.\" THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS -.\" OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -.\" MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT -.\" OF THIRD PARTY RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR -.\" HOLDERS INCLUDED IN THIS NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL -.\" INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING -.\" FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, -.\" NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION -.\" WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. -.\" -.\" Except as contained in this notice, the name of a copyright holder -.\" shall not be used in advertising or otherwise to promote the sale, use -.\" or other dealings in this Software without prior written authorization -.\" of the copyright holder. -.TH libtecla 3 -.SH NAME -libtecla - An interactive command-line input library. -.SH SYNOPSIS -.nf -gcc ... -ltecla -lcurses -.fi - -.SH DESCRIPTION - -The \f3tecla\f1 library provides programs with interactive command -line editing facilities, similar to those of the unix \f3tcsh\f1 -shell. In addition to simple command-line editing, it supports recall -of previously entered command lines, TAB completion of file names or -other tokens, and in-line wild-card expansion of filenames. The -internal functions which perform file-name completion and wild-card -expansion are also available externally for optional use by the -calling program. -.sp -The various parts of the library are documented in the following man -pages: - -.nf - gl_get_line(3) - The interactive line-input module. - cpl_complete_word(3) - The word completion module. - ef_expand_file(3) - The filename expansion module. - pca_lookup_file(3) - A directory-list based filename - lookup and completion module. -.fi - -In addition there is one optional application distributed -with the library: - -.nf - enhance(3) - Add command-line editing to third - party applications. -.fi - -.SH THREAD SAFETY - -If the library is compiled with -D_POSIX_C_SOURCE=199506L, reentrant -versions of as many functions as possible are used. This includes -using getpwuid_r() and getpwnam_r() instead of getpwuid() and -getpwnam() when looking up the home directories of specific users in -the password file (for ~user/ expansion), and readdir_r() instead of -readdir() for reading directory entries when doing filename -completion. The reentrant version of the library is usually called -libtecla_r.a instead of libtecla.a, so if only the latter is -available, it probably isn't the correct version to link with -threaded programs. - -Reentrant functions for iterating through the password file aren't -available, so when the library is compiled to be reentrant, TAB -completion of incomplete usernames in \f3~username/\f1 expressions is -disabled. This doesn't disable expansion of complete \f3~username\f1 -expressions, which can be done reentrantly, or expansion of the parts -of filenames that follow them, so this doesn't remove much -functionality. - -The terminfo functions setupterm(), tigetstr(), tigetnum() and tputs() -also aren't reentrant, but very few programs will want to interact -with multiple terminals, so this shouldn't prevent this library from -being used in threaded programs. - -.SH LIBRARY VERSION NUMBER - -The version number of the library can be queried using the following -function. -.sp -.nf - void libtecla_version(int *major, int *minor, int *micro); -.fi -.sp - -On return, this function records the three components of the libtecla -version number in \f3*major\f1, \f3*minor\f1, \f3*micro\f1. The formal -meaning of the three components is as follows. - -.sp -.nf - major - Incrementing this number implies that a change has - been made to the library's public interface, which - makes it binary incompatible with programs that - were linked with previous shared versions of the - tecla library. - - minor - This number is incremented by one whenever - additional functionality, such as new functions or - modules, are added to the library. - - micro - This is incremented whenever modifications to the - library are made which make no changes to the - public interface, but which fix bugs and/or improve - the behind-the-scenes implementation. -.fi -.sp - -.SH TRIVIA - -In Spanish, a "tecla" is the key of a keyboard. Since this library -centers on keyboard input, and given that I wrote much of the library -while working in Chile, this seemed like a suitable name. - -.SH FILES -.nf -libtecla.a - The tecla library. -libtecla.h - The tecla header file. -~/.teclarc - The tecla personal customization file. -.fi - -.SH SEE ALSO -gl_get_line(3), ef_expand_file(3), cpl_complete_word(3), -pca_lookup_file(3), enhance(3) - -.SH AUTHOR -Martin Shepherd (mcs@astro.caltech.edu) - -.SH ACKNOWLEDGMENTS - -.nf -Markus Gyger - Lots of assistance, including help with - shared libraries, configuration information, - particularly for Solaris; modifications to - support C++ compilers, improvements for ksh - users, faster cursor motion, output - buffering, and changes to make gl_get_line() - 8-bit clean. -Mike MacFaden - Suggestions, feedback and testing that led - to many of the major new functions that were - added in version 1.4.0. -Tim Eliseo - Many vi-mode bindings and fixes. -.fi diff --git a/libtecla-1.4.1/man3/libtecla_version.3 b/libtecla-1.4.1/man3/libtecla_version.3 deleted file mode 100644 index 2c9c2c7..0000000 --- a/libtecla-1.4.1/man3/libtecla_version.3 +++ /dev/null @@ -1 +0,0 @@ -.so man3/libtecla.3 diff --git a/libtecla-1.4.1/man3/new_CplFileConf.3 b/libtecla-1.4.1/man3/new_CplFileConf.3 deleted file mode 100644 index 36c83a3..0000000 --- a/libtecla-1.4.1/man3/new_CplFileConf.3 +++ /dev/null @@ -1 +0,0 @@ -.so man3/cpl_complete_word.3 diff --git a/libtecla-1.4.1/man3/new_ExpandFile.3 b/libtecla-1.4.1/man3/new_ExpandFile.3 deleted file mode 100644 index f4299df..0000000 --- a/libtecla-1.4.1/man3/new_ExpandFile.3 +++ /dev/null @@ -1 +0,0 @@ -.so man3/ef_expand_file.3 diff --git a/libtecla-1.4.1/man3/new_GetLine.3 b/libtecla-1.4.1/man3/new_GetLine.3 deleted file mode 100644 index 6bd3d1f..0000000 --- a/libtecla-1.4.1/man3/new_GetLine.3 +++ /dev/null @@ -1 +0,0 @@ -.so man3/gl_get_line.3 diff --git a/libtecla-1.4.1/man3/new_PathCache.3 b/libtecla-1.4.1/man3/new_PathCache.3 deleted file mode 100644 index e5a136e..0000000 --- a/libtecla-1.4.1/man3/new_PathCache.3 +++ /dev/null @@ -1 +0,0 @@ -.so man3/pca_lookup_file.3 diff --git a/libtecla-1.4.1/man3/new_PcaPathConf.3 b/libtecla-1.4.1/man3/new_PcaPathConf.3 deleted file mode 100644 index e5a136e..0000000 --- a/libtecla-1.4.1/man3/new_PcaPathConf.3 +++ /dev/null @@ -1 +0,0 @@ -.so man3/pca_lookup_file.3 diff --git a/libtecla-1.4.1/man3/new_WordCompletion.3 b/libtecla-1.4.1/man3/new_WordCompletion.3 deleted file mode 100644 index 36c83a3..0000000 --- a/libtecla-1.4.1/man3/new_WordCompletion.3 +++ /dev/null @@ -1 +0,0 @@ -.so man3/cpl_complete_word.3 diff --git a/libtecla-1.4.1/man3/pca_last_error.3 b/libtecla-1.4.1/man3/pca_last_error.3 deleted file mode 100644 index e5a136e..0000000 --- a/libtecla-1.4.1/man3/pca_last_error.3 +++ /dev/null @@ -1 +0,0 @@ -.so man3/pca_lookup_file.3 diff --git a/libtecla-1.4.1/man3/pca_lookup_file.3 b/libtecla-1.4.1/man3/pca_lookup_file.3 deleted file mode 100644 index 131394a..0000000 --- a/libtecla-1.4.1/man3/pca_lookup_file.3 +++ /dev/null @@ -1,361 +0,0 @@ -.\" Copyright (C) 2001 by Martin C. Shepherd -.\" -.\" All rights reserved. -.\" -.\" Permission is hereby granted, free of charge, to any person obtaining a -.\" copy of this software and associated documentation files (the -.\" "Software"), to deal in the Software without restriction, including -.\" without limitation the rights to use, copy, modify, merge, publish, -.\" distribute, and/or sell copies of the Software, and to permit persons -.\" to whom the Software is furnished to do so, provided that the above -.\" copyright notice(s) and this permission notice appear in all copies of -.\" the Software and that both the above copyright notice(s) and this -.\" permission notice appear in supporting documentation. -.\" -.\" THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS -.\" OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -.\" MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT -.\" OF THIRD PARTY RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR -.\" HOLDERS INCLUDED IN THIS NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL -.\" INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING -.\" FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, -.\" NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION -.\" WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. -.\" -.\" Except as contained in this notice, the name of a copyright holder -.\" shall not be used in advertising or otherwise to promote the sale, use -.\" or other dealings in this Software without prior written authorization -.\" of the copyright holder. -.TH pca_lookup_file 3 -.SH NAME -pca_lookup_file, del_PathCache, del_PcaPathConf, new_PathCache, new_PcaPathConf, pca_last_error, pca_path_completions, pca_scan_path, pca_set_check_fn, ppc_file_start, ppc_literal_escapes \- lookup a file in a list of directories -.SH SYNOPSIS -.nf -#include - -PathCache *new_PathCache(void); - -PathCache *del_PathCache(PathCache *pc); - -int pca_scan_path(PathCache *pc, const char *path); - -void pca_set_check_fn(PathCache *pc, CplCheckFn *check_fn, - void *data); - -char *pca_lookup_file(PathCache *pc, const char *name, - int name_len, int literal); - -const char *pca_last_error(PathCache *pc); - -CPL_MATCH_FN(pca_path_completions); - -.fi - -.SH DESCRIPTION - -The \f3PathCache\f1 object is part of the tecla library (see the -libtecla(3) man page). -.sp -\f3PathCache\f1 objects allow an application to search for files in -any colon separated list of directories, such as the unix execution -PATH environment variable. Files in absolute directories are cached in -a \f3PathCache\f1 object, whereas relative directories are scanned as -needed. Using a \f3PathCache\f1 object, you can look up the full -pathname of a simple filename, or you can obtain a list of the -possible completions of a given filename prefix. By default all files -in the list of directories are targets for lookup and completion, but -a versatile mechanism is provided for only selecting specific types of -files. The obvious application of this facility is to provide -Tab-completion and lookup of executable commands in the unix PATH, so -an optional callback which rejects all but executable files, is -provided. -.sp -.SH AN EXAMPLE - -Under UNIX, the following example program looks up and displays the -full pathnames of each of the command names on the command line. -.sp -.nf - #include - #include - #include - - int main(int argc, char *argv[]) - { - int i; - /* - * Create a cache for executable files. - */ - PathCache *pc = new_PathCache(); - if(!pc) - exit(1); - /* - * Scan the user's PATH for executables. - */ - if(pca_scan_path(pc, getenv("PATH"))) { - fprintf(stderr, "%s\\n", pca_last_error(pc)); - exit(1); - } - /* - * Arrange to only report executable files. - */ - pca_set_check_fn(pc, cpl_check_exe, NULL); - /* - * Lookup and display the full pathname of each of the - * commands listed on the command line. - */ - for(i=1; i -#include -#include -#include -#include - -#include -#include -#include - -#include "pathutil.h" - -/*....................................................................... - * Create a new PathName object. - * - * Output: - * return PathName * The new object, or NULL on error. - */ -PathName *_new_PathName(void) -{ - PathName *path; /* The object to be returned */ -/* - * Allocate the container. - */ - path = (PathName *)malloc(sizeof(PathName)); - if(!path) { - fprintf(stderr, "_new_PathName: Insufficient memory.\n"); - return NULL; - }; -/* - * Before attempting any operation that might fail, initialize the - * container at least up to the point at which it can safely be passed - * to _del_PathName(). - */ - path->name = NULL; - path->dim = 0; -/* - * Figure out the maximum length of an expanded pathname. - */ - path->dim = _pu_pathname_dim(); - if(path->dim == 0) - return _del_PathName(path); -/* - * Allocate the pathname buffer. - */ - path->name = (char *)malloc(path->dim * sizeof(char)); - if(!path->name) { - fprintf(stderr, - "_new_PathName: Insufficient memory to allocate pathname buffer.\n"); - return _del_PathName(path); - }; - return path; -} - -/*....................................................................... - * Delete a PathName object. - * - * Input: - * path PathName * The object to be deleted. - * Output: - * return PathName * The deleted object (always NULL). - */ -PathName *_del_PathName(PathName *path) -{ - if(path) { - if(path->name) - free(path->name); - free(path); - }; - return NULL; -} - -/*....................................................................... - * Return the pathname to a zero-length string. - * - * Input: - * path PathName * The pathname container. - * Output: - * return char * The cleared pathname buffer, or NULL on error. - */ -char *_pn_clear_path(PathName *path) -{ -/* - * Check the arguments. - */ - if(!path) { - fprintf(stderr, "_pn_clear_path: NULL argument.\n"); - return NULL; - }; - path->name[0] = '\0'; - return path->name; -} - -/*....................................................................... - * Append a string to a pathname, increasing the size of the pathname - * buffer if needed. - * - * Input: - * path PathName * The pathname container. - * string const char * The string to be appended to the pathname. - * Note that regardless of the slen argument, - * this should be a '\0' terminated string. - * slen int The maximum number of characters to append - * from string[], or -1 to append the whole - * string. - * remove_escapes int If true, remove the backslashes that escape - * spaces, tabs, backslashes etc.. - * Output: - * return char * The pathname string path->name[], which may - * have been reallocated, or NULL if there was - * insufficient memory to extend the pathname. - */ -char *_pn_append_to_path(PathName *path, const char *string, int slen, - int remove_escapes) -{ - int pathlen; /* The length of the pathname */ - int i; -/* - * Check the arguments. - */ - if(!path || !string) { - fprintf(stderr, "_pn_append_to_path: NULL argument(s).\n"); - return NULL; - }; -/* - * Get the current length of the pathname. - */ - pathlen = strlen(path->name); -/* - * How many characters should be appended? - */ - if(slen < 0 || slen > strlen(string)) - slen = strlen(string); -/* - * Resize the pathname if needed. - */ - if(!_pn_resize_path(path, pathlen + slen)) - return NULL; -/* - * Append the string to the output pathname, removing any escape - * characters found therein. - */ - if(remove_escapes) { - int is_escape = 0; - for(i=0; iname[pathlen++] = string[i]; - }; -/* - * Terminate the string. - */ - path->name[pathlen] = '\0'; - } else { -/* - * Append the string directly to the pathname. - */ - memcpy(path->name + pathlen, string, slen); - path->name[pathlen + slen] = '\0'; - }; - return path->name; -} - -/*....................................................................... - * Prepend a string to a pathname, increasing the size of the pathname - * buffer if needed. - * - * Input: - * path PathName * The pathname container. - * string const char * The string to be prepended to the pathname. - * Note that regardless of the slen argument, - * this should be a '\0' terminated string. - * slen int The maximum number of characters to prepend - * from string[], or -1 to append the whole - * string. - * remove_escapes int If true, remove the backslashes that escape - * spaces, tabs, backslashes etc.. - * Output: - * return char * The pathname string path->name[], which may - * have been reallocated, or NULL if there was - * insufficient memory to extend the pathname. - */ -char *_pn_prepend_to_path(PathName *path, const char *string, int slen, - int remove_escapes) -{ - int pathlen; /* The length of the pathname */ - int shift; /* The number of characters to shift the suffix by */ - int i,j; -/* - * Check the arguments. - */ - if(!path || !string) { - fprintf(stderr, "_pn_prepend_to_path: NULL argument(s).\n"); - return NULL; - }; -/* - * Get the current length of the pathname. - */ - pathlen = strlen(path->name); -/* - * How many characters should be appended? - */ - if(slen < 0 || slen > strlen(string)) - slen = strlen(string); -/* - * Work out how far we need to shift the original path string to make - * way for the new prefix. When removing escape characters, we need - * final length of the new prefix, after unescaped backslashes have - * been removed. - */ - if(remove_escapes) { - int is_escape = 0; - for(shift=0,i=0; iname + shift, path->name, pathlen+1); -/* - * Copy the new prefix into the vacated space at the beginning of the - * output pathname, removing any escape characters if needed. - */ - if(remove_escapes) { - int is_escape = 0; - for(i=j=0; iname[j++] = string[i]; - }; - } else { - memcpy(path->name, string, slen); - }; - return path->name; -} - -/*....................................................................... - * If needed reallocate a given pathname buffer to allow a string of - * a given length to be stored in it. - * - * Input: - * path PathName * The pathname container object. - * length size_t The required length of the pathname buffer, - * not including the terminating '\0'. - * Output: - * return char * The pathname buffer, or NULL if there was - * insufficient memory (this isn't reported - * to stderr). - */ -char *_pn_resize_path(PathName *path, size_t length) -{ -/* - * Check the arguments. - */ - if(!path) { - fprintf(stderr, "_pn_resize_path: NULL argument(s).\n"); - return NULL; - }; -/* - * If the pathname buffer isn't large enough to accomodate a string - * of the specified length, attempt to reallocate it with the new - * size, plus space for a terminating '\0'. Also add a bit of - * head room to prevent too many reallocations if the initial length - * turned out to be very optimistic. - */ - if(length + 1 > path->dim) { - size_t dim = length + 1 + PN_PATHNAME_INC; - char *name = (char *) realloc(path->name, dim); - if(!name) - return NULL; - path->name = name; - path->dim = dim; - }; - return path->name; -} - -/*....................................................................... - * Estimate the largest amount of space needed to store a pathname. - * - * Output: - * return size_t The number of bytes needed, including space for the - * terminating '\0'. - */ -size_t _pu_pathname_dim(void) -{ - int maxlen; /* The return value excluding space for the '\0' */ -/* - * If the POSIX PATH_MAX macro is defined in limits.h, use it. - */ -#ifdef PATH_MAX - maxlen = PATH_MAX; -/* - * If we have pathconf, use it. - */ -#elif defined(_PC_PATH_MAX) - errno = 0; - maxlen = pathconf(FS_ROOT_DIR, _PC_PATH_MAX); - if(maxlen <= 0 || errno) - maxlen = MAX_PATHLEN_FALLBACK; -/* - * None of the above approaches worked, so substitute our fallback - * guess. - */ -#else - maxlen = MAX_PATHLEN_FALLBACK; -#endif -/* - * Return the amount of space needed to accomodate a pathname plus - * a terminating '\0'. - */ - return maxlen + 1; -} - -/*....................................................................... - * Return non-zero if the specified path name refers to a directory. - * - * Input: - * pathname const char * The path to test. - * Output: - * return int 0 - Not a directory. - * 1 - pathname[] refers to a directory. - */ -int _pu_path_is_dir(const char *pathname) -{ - struct stat statbuf; /* The file-statistics return buffer */ -/* - * Look up the file attributes. - */ - if(stat(pathname, &statbuf) < 0) - return 0; -/* - * Is the file a directory? - */ - return S_ISDIR(statbuf.st_mode) != 0; -} - -/*....................................................................... - * Return non-zero if the specified path name refers to a regular file. - * - * Input: - * pathname const char * The path to test. - * Output: - * return int 0 - Not a regular file. - * 1 - pathname[] refers to a regular file. - */ -int _pu_path_is_file(const char *pathname) -{ - struct stat statbuf; /* The file-statistics return buffer */ -/* - * Look up the file attributes. - */ - if(stat(pathname, &statbuf) < 0) - return 0; -/* - * Is the file a regular file? - */ - return S_ISREG(statbuf.st_mode) != 0; -} - -/*....................................................................... - * Return non-zero if the specified path name refers to an executable. - * - * Input: - * pathname const char * The path to test. - * Output: - * return int 0 - Not an executable file. - * 1 - pathname[] refers to an executable file. - */ -int _pu_path_is_exe(const char *pathname) -{ - struct stat statbuf; /* The file-statistics return buffer */ -/* - * Look up the file attributes. - */ - if(stat(pathname, &statbuf) < 0) - return 0; -/* - * Is the file a regular file which is executable by the current user. - */ - return S_ISREG(statbuf.st_mode) != 0 && - (statbuf.st_mode & (S_IXOTH | S_IXGRP | S_IXUSR)) && - access(pathname, X_OK) == 0; -} - -/*....................................................................... - * Search backwards for the potential start of a filename. This - * looks backwards from the specified index in a given string, - * stopping at the first unescaped space or the start of the line. - * - * Input: - * string const char * The string to search backwards in. - * back_from int The index of the first character in string[] - * that follows the pathname. - * Output: - * return char * The pointer to the first character of - * the potential pathname, or NULL on error. - */ -char *_pu_start_of_path(const char *string, int back_from) -{ - int i, j; -/* - * Check the arguments. - */ - if(!string || back_from < 0) { - fprintf(stderr, "_pu_start_path: Invalid argument(s).\n"); - return NULL; - }; -/* - * Search backwards from the specified index. - */ - for(i=back_from-1; i>=0; i--) { - int c = string[i]; -/* - * Stop on unescaped spaces. - */ - if(isspace((int)(unsigned char)c)) { -/* - * The space can't be escaped if we are at the start of the line. - */ - if(i==0) - break; -/* - * Find the extent of the escape characters which precedes the space. - */ - for(j=i-1; j>=0 && string[j]=='\\'; j--) - ; -/* - * If there isn't an odd number of escape characters before the space, - * then the space isn't escaped. - */ - if((i - 1 - j) % 2 == 0) - break; - }; - }; - return (char *)string + i + 1; -} - -/*....................................................................... - * Find the length of a potential filename starting from a given - * point. This looks forwards from the specified index in a given string, - * stopping at the first unescaped space or the end of the line. - * - * Input: - * string const char * The string to search backwards in. - * start_from int The index of the first character of the pathname - * in string[]. - * Output: - * return char * The pointer to the character that follows - * the potential pathname, or NULL on error. - */ -char *_pu_end_of_path(const char *string, int start_from) -{ - int c; /* The character being examined */ - int escaped = 0; /* True when the next character is escaped */ - int i; -/* - * Check the arguments. - */ - if(!string || start_from < 0) { - fprintf(stderr, "_pu_end_path: Invalid argument(s).\n"); - return NULL; - }; -/* - * Search forwards from the specified index. - */ - for(i=start_from; (c=string[i]) != '\0'; i++) { - if(escaped) { - escaped = 0; - } else if(isspace(c)) { - break; - } else if(c == '\\') { - escaped = 1; - }; - }; - return (char *)string + i; -} - -/*....................................................................... - * Return non-zero if the specified path name refers to an existing file. - * - * Input: - * pathname const char * The path to test. - * Output: - * return int 0 - The file doesn't exist. - * 1 - The file does exist. - */ -int _pu_file_exists(const char *pathname) -{ - struct stat statbuf; - return stat(pathname, &statbuf) == 0; -} diff --git a/libtecla-1.4.1/pathutil.h b/libtecla-1.4.1/pathutil.h deleted file mode 100644 index 2f4ece5..0000000 --- a/libtecla-1.4.1/pathutil.h +++ /dev/null @@ -1,122 +0,0 @@ -#ifndef pathutil_h -#define pathutil_h - -/* - * Copyright (c) 2000, 2001 by Martin C. Shepherd. - * - * All rights reserved. - * - * Permission is hereby granted, free of charge, to any person obtaining a - * copy of this software and associated documentation files (the - * "Software"), to deal in the Software without restriction, including - * without limitation the rights to use, copy, modify, merge, publish, - * distribute, and/or sell copies of the Software, and to permit persons - * to whom the Software is furnished to do so, provided that the above - * copyright notice(s) and this permission notice appear in all copies of - * the Software and that both the above copyright notice(s) and this - * permission notice appear in supporting documentation. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS - * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT - * OF THIRD PARTY RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR - * HOLDERS INCLUDED IN THIS NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL - * INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING - * FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, - * NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION - * WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - * - * Except as contained in this notice, the name of a copyright holder - * shall not be used in advertising or otherwise to promote the sale, use - * or other dealings in this Software without prior written authorization - * of the copyright holder. - */ - -/* - * The following object encapsulates a buffer designed to be used to - * store pathnames. The pathname member of the object is initially - * allocated with the size that _pu_pathname_dim() returns, and then - * if this turns out to be pessimistic, the pathname can be reallocated - * via calls to pb_append_to_path() and/or pb_resize_path(). - */ -typedef struct { - char *name; /* The path buffer */ - size_t dim; /* The current allocated size of buffer[] */ -} PathName; - -PathName *_new_PathName(void); -PathName *_del_PathName(PathName *path); - -char *_pn_clear_path(PathName *path); -char *_pn_append_to_path(PathName *path, const char *string, int slen, - int remove_escapes); -char *_pn_prepend_to_path(PathName *path, const char *string, int slen, - int remove_escapes); -char *_pn_resize_path(PathName *path, size_t length); - -/* - * Search backwards for the potential start of a filename. This - * looks backwards from the specified index in a given string, - * stopping at the first unescaped space or the start of the line. - */ -char *_pu_start_of_path(const char *string, int back_from); - -/* - * Find the end of a potential filename, starting from a given index - * in the string. This looks forwards from the specified index in a - * given string, stopping at the first unescaped space or the end - * of the line. - */ -char *_pu_end_of_path(const char *string, int start_from); - - -/* - * Return an estimate of the the length of the longest pathname - * on the local system. - */ -size_t _pu_pathname_dim(void); - -/* - * Return non-zero if the specified path name refers to a directory. - */ -int _pu_path_is_dir(const char *pathname); - -/* - * Return non-zero if the specified path name refers to a regular file. - */ -int _pu_path_is_file(const char *pathname); - -/* - * Return non-zero if the specified path name refers to an executable. - */ -int _pu_path_is_exe(const char *pathname); - -/* - * Return non-zero if a file exists with the specified pathname. - */ -int _pu_file_exists(const char *pathname); - -/* - * If neither the POSIX PATH_MAX macro nor the pathconf() function - * can be used to find out the maximum pathlength on the target - * system, the following fallback maximum length is used. - */ -#define MAX_PATHLEN_FALLBACK 1024 - -/* - * If the pathname buffer turns out to be too small, it will be extended - * in chunks of the following amount (plus whatever is needed at the time). - */ -#define PN_PATHNAME_INC 100 - -/* - * Define the special character-sequences of the filesystem. - */ -#define FS_ROOT_DIR "/" /* The root directory */ -#define FS_ROOT_DIR_LEN (sizeof(FS_ROOT_DIR) - 1) -#define FS_PWD "." /* The current working directory */ -#define FS_PWD_LEN (sizeof(FS_PWD_LEN) - 1) -#define FS_DIR_SEP "/" /* The directory separator string */ -#define FS_DIR_SEP_LEN (sizeof(FS_DIR_SEP) - 1) - -#endif diff --git a/libtecla-1.4.1/pcache.c b/libtecla-1.4.1/pcache.c deleted file mode 100644 index bbea916..0000000 --- a/libtecla-1.4.1/pcache.c +++ /dev/null @@ -1,1688 +0,0 @@ -/* - * Copyright (c) 2000, 2001 by Martin C. Shepherd. - * - * All rights reserved. - * - * Permission is hereby granted, free of charge, to any person obtaining a - * copy of this software and associated documentation files (the - * "Software"), to deal in the Software without restriction, including - * without limitation the rights to use, copy, modify, merge, publish, - * distribute, and/or sell copies of the Software, and to permit persons - * to whom the Software is furnished to do so, provided that the above - * copyright notice(s) and this permission notice appear in all copies of - * the Software and that both the above copyright notice(s) and this - * permission notice appear in supporting documentation. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS - * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT - * OF THIRD PARTY RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR - * HOLDERS INCLUDED IN THIS NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL - * INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING - * FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, - * NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION - * WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - * - * Except as contained in this notice, the name of a copyright holder - * shall not be used in advertising or otherwise to promote the sale, use - * or other dealings in this Software without prior written authorization - * of the copyright holder. - */ - -#include -#include -#include - -#include "libtecla.h" -#include "pathutil.h" -#include "homedir.h" -#include "freelist.h" -#include "direader.h" -#include "stringrp.h" - -/* - * The new_PcaPathConf() constructor sets the integer first member of - * the returned object to the following magic number. This is then - * checked for by pca_path_completions() as a sanity check. - */ -#define PPC_ID_CODE 4567 - -/* - * A pointer to a structure of the following type can be passed to - * the builtin path-completion callback function to modify its behavior. - */ -struct PcaPathConf { - int id; /* This is set to PPC_ID_CODE by new_PcaPathConf() */ - PathCache *pc; /* The path-list cache in which to look up the executables */ - int escaped; /* If non-zero, backslashes in the input line are */ - /* interpreted as escaping special characters and */ - /* spaces, and any special characters and spaces in */ - /* the listed completions will also be escaped with */ - /* added backslashes. This is the default behaviour. */ - /* If zero, backslashes are interpreted as being */ - /* literal parts of the file name, and none are added */ - /* to the completion suffixes. */ - int file_start; /* The index in the input line of the first character */ - /* of the file name. If you specify -1 here, */ - /* pca_path_completions() identifies the */ - /* the start of the file by looking backwards for */ - /* an unescaped space, or the beginning of the line. */ -}; - -/* - * Prepended to each chached filename is a character which contains - * one of the following status codes. When a given filename (minus - * this byte) is passed to the application's check_fn(), the result - * is recorded in this byte, such that the next time it is looked - * up, we don't have to call check_fn() again. These codes are cleared - * whenever the path is scanned and whenever the check_fn() callback - * is changed. - */ -typedef enum { - PCA_F_ENIGMA='?', /* The file remains to be checked */ - PCA_F_WANTED='+', /* The file has been selected by the caller's callback */ - PCA_F_IGNORE='-' /* The file has been rejected by the caller's callback */ -} PcaFileStatus; - -/* - * Encapsulate the memory management objects which supply memoy for - * the arrays of filenames. - */ -typedef struct { - StringGroup *sg; /* The memory used to record the names of files */ - size_t files_dim; /* The allocated size of files[] */ - char **files; /* Memory for 'files_dim' pointers to files */ - size_t nfiles; /* The number of filenames currently in files[] */ -} CacheMem; - -static CacheMem *new_CacheMem(void); -static CacheMem *del_CacheMem(CacheMem *cm); -static void rst_CacheMem(CacheMem *cm); - -/* - * Lists of nodes of the following type are used to record the - * names and contents of individual directories. - */ -typedef struct PathNode PathNode; -struct PathNode { - PathNode *next; /* The next directory in the path */ - int relative; /* True if the directory is a relative pathname */ - CacheMem *mem; /* The memory used to store dir[] and files[] */ - char *dir; /* The directory pathname (stored in pc->sg) */ - int nfile; /* The number of filenames stored in 'files' */ - char **files; /* Files of interest in the current directory, */ - /* or NULL if dir[] is a relative pathname */ - /* who's contents can't be cached. This array */ - /* and its contents are taken from pc->abs_mem */ - /* or pc->rel_mem */ -}; - -/* - * Append a new node to the list of directories in the path. - */ -static int add_PathNode(PathCache *pc, const char *dirname); - -/* - * Set the max length of the error-reporting string. There is no point - * in this being longer than the width of a typical terminal window. - * In composing error messages, I have assumed that this number is - * at least 80, so you don't decrease it below this number. - */ -#define ERRLEN 200 - -/* - * Set the maximum length allowed for usernames. - * names. - */ -#define USR_LEN 100 - -/* - * PathCache objects encapsulate the resources needed to record - * files of interest from comma-separated lists of directories. - */ -struct PathCache { - FreeList *node_mem; /* A free-list of PathNode objects */ - CacheMem *abs_mem; /* Memory for the filenames of absolute paths */ - CacheMem *rel_mem; /* Memory for the filenames of relative paths */ - PathNode *head; /* The head of the list of directories in the */ - /* path, or NULL if no path has been scanned yet. */ - PathNode *tail; /* The tail of the list of directories in the */ - /* path, or NULL if no path has been scanned yet. */ - PathName *path; /* The fully qualified name of a file */ - HomeDir *home; /* Home-directory lookup object */ - DirReader *dr; /* A portable directory reader */ - CplFileConf *cfc; /* Configuration parameters to pass to */ - /* cpl_file_completions() */ - CplCheckFn *check_fn; /* The callback used to determine if a given */ - /* filename should be recorded in the cache. */ - void *data; /* Annonymous data to be passed to pc->check_fn() */ - char usrnam[USR_LEN+1];/* The buffer used when reading the names of */ - /* users. */ - char errmsg[ERRLEN+1]; /* Error-report buffer */ -}; - -/* - * Empty the cache. - */ -static void pca_clear_cache(PathCache *pc); - -/* - * Read a username from string[] and record it in pc->usrnam[]. - */ -static int pca_read_username(PathCache *pc, const char *string, int slen, - int literal, const char **nextp); - -/* - * Extract the next component of a colon separated list of directory - * paths. - */ -static int pca_extract_dir(PathCache *pc, const char *path, - const char **nextp); - -/* - * Scan absolute directories for files of interest, recording their names - * in mem->sg and recording pointers to these names in mem->files[]. - */ -static int pca_scan_dir(PathCache *pc, const char *dirname, CacheMem *mem); - -/* - * A qsort() comparison function for comparing the cached filename - * strings pointed to by two (char **) array elements. Note that - * this ignores the initial cache-status byte of each filename. - */ -static int pca_cmp_matches(const void *v1, const void *v2); - -/* - * A qsort() comparison function for comparing a filename - * against an element of an array of pointers to filename cache - * entries. - */ -static int pca_cmp_file(const void *v1, const void *v2); - -/* - * Initialize a PcaPathConf configuration objects with the default - * options. - */ -static int pca_init_PcaPathConf(PcaPathConf *ppc, PathCache *pc); - -/* - * Make a copy of a completion suffix, suitable for passing to - * cpl_add_completion(). - */ -static int pca_prepare_suffix(PathCache *pc, const char *suffix, - int add_escapes); - -/* - * Return non-zero if the specified string appears to start with a pathname. - */ -static int cpa_cmd_contains_path(const char *prefix, int prefix_len); - -/* - * Return a given prefix with escapes optionally removed. - */ -static const char *pca_prepare_prefix(PathCache *pc, const char *prefix, - size_t prefix_len, int escaped); - -/* - * If there is a tilde expression at the beginning of the specified path, - * place the corresponding home directory into pc->path. Otherwise - * just clear pc->path. - */ -static int pca_expand_tilde(PathCache *pc, const char *path, int pathlen, - int literal, const char **endp); - -/* - * Clear the filename status codes that are recorded before each filename - * in the cache. - */ -static void pca_remove_marks(PathCache *pc); - -/* - * Specify how many PathNode's to allocate at a time. - */ -#define PATH_NODE_BLK 30 - -/* - * Specify the amount by which the files[] arrays are to be extended - * whenever they are found to be too small. - */ -#define FILES_BLK_FACT 256 - -/*....................................................................... - * Create a new object who's function is to maintain a cache of - * filenames found within a list of directories, and provide quick - * lookup and completion of selected files in this cache. - * - * Output: - * return PathCache * The new, initially empty cache, or NULL - * on error. - */ -PathCache *new_PathCache(void) -{ - PathCache *pc; /* The object to be returned */ -/* - * Allocate the container. - */ - pc = (PathCache *)malloc(sizeof(PathCache)); - if(!pc) { - fprintf(stderr, "new_PathCache: Insufficient memory.\n"); - return NULL; - }; -/* - * Before attempting any operation that might fail, initialize the - * container at least up to the point at which it can safely be passed - * to del_PathCache(). - */ - pc->node_mem = NULL; - pc->abs_mem = NULL; - pc->rel_mem = NULL; - pc->head = NULL; - pc->tail = NULL; - pc->path = NULL; - pc->home = NULL; - pc->dr = NULL; - pc->cfc = NULL; - pc->check_fn = 0; - pc->data = NULL; - pc->usrnam[0] = '\0'; - pc->errmsg[0] = '\0'; -/* - * Allocate the freelist of directory list nodes. - */ - pc->node_mem = _new_FreeList("new_PathCache", sizeof(PathNode), - PATH_NODE_BLK); - if(!pc->node_mem) - return del_PathCache(pc); -/* - * Allocate memory for recording names of files in absolute paths. - */ - pc->abs_mem = new_CacheMem(); - if(!pc->abs_mem) - return del_PathCache(pc); -/* - * Allocate memory for recording names of files in relative paths. - */ - pc->rel_mem = new_CacheMem(); - if(!pc->rel_mem) - return del_PathCache(pc); -/* - * Allocate a pathname buffer. - */ - pc->path = _new_PathName(); - if(!pc->path) - return del_PathCache(pc); -/* - * Allocate an object for looking up home-directories. - */ - pc->home = _new_HomeDir(); - if(!pc->home) - return del_PathCache(pc); -/* - * Allocate an object for reading directories. - */ - pc->dr = _new_DirReader(); - if(!pc->dr) - return del_PathCache(pc); -/* - * Allocate a cpl_file_completions() configuration object. - */ - pc->cfc = new_CplFileConf(); - if(!pc->cfc) - return del_PathCache(pc); -/* - * Configure cpl_file_completions() to use check_fn() to select - * files of interest. - */ - cfc_set_check_fn(pc->cfc, pc->check_fn, pc->data); -/* - * Return the cache, ready for use. - */ - return pc; -} - -/*....................................................................... - * Delete a given cache of files, returning the resources that it - * was using to the system. - * - * Input: - * pc PathCache * The cache to be deleted (can be NULL). - * Output: - * return PathCache * The deleted object (ie. allways NULL). - */ -PathCache *del_PathCache(PathCache *pc) -{ - if(pc) { -/* - * Delete the memory of the list of path nodes. - */ - pc->node_mem = _del_FreeList(NULL, pc->node_mem, 1); -/* - * Delete the memory used to record filenames. - */ - pc->abs_mem = del_CacheMem(pc->abs_mem); - pc->rel_mem = del_CacheMem(pc->rel_mem); -/* - * The list of PathNode's was already deleted when node_mem was - * deleted. - */ - pc->head = NULL; - pc->tail = NULL; -/* - * Delete the pathname buffer. - */ - pc->path = _del_PathName(pc->path); -/* - * Delete the home-directory lookup object. - */ - pc->home = _del_HomeDir(pc->home); -/* - * Delete the directory reader. - */ - pc->dr = _del_DirReader(pc->dr); -/* - * Delete the cpl_file_completions() config object. - */ - pc->cfc = del_CplFileConf(pc->cfc); -/* - * Delete the container. - */ - free(pc); - }; - return NULL; -} - -/*....................................................................... - * If you want subsequent calls to pca_lookup_file() and - * pca_path_completions() to only return the filenames of certain - * types of files, for example executables, or filenames ending in - * ".ps", call this function to register a file-selection callback - * function. This callback function takes the full pathname of a file, - * plus application-specific data, and returns 1 if the file is of - * interest, and zero otherwise. - * - * Input: - * pc PathCache * The filename cache. - * check_fn CplCheckFn * The function to call to see if the name of - * a given file should be included in the - * cache. This determines what type of files - * will reside in the cache. To revert to - * selecting all files, regardless of type, - * pass 0 here. - * data void * You can pass a pointer to anything you - * like here, including NULL. It will be - * passed to your check_fn() callback - * function, for its private use. - */ -void pca_set_check_fn(PathCache *pc, CplCheckFn *check_fn, void *data) -{ - if(pc) { -/* - * If the callback or its data pointer have changed, clear the cached - * statuses of files that were accepted or rejected by the previous - * calback. - */ - if(check_fn != pc->check_fn || data != pc->data) - pca_remove_marks(pc); -/* - * Record the new callback locally. - */ - pc->check_fn = check_fn; - pc->data = data; -/* - * Configure cpl_file_completions() to use the same callback to - * select files of interest. - */ - cfc_set_check_fn(pc->cfc, check_fn, data); - }; - return; -} - -/*....................................................................... - * Return a description of the last path-caching error that occurred. - * - * Input: - * pc PathCache * The filename cache that suffered the error. - * Output: - * return char * The description of the last error. - */ -const char *pca_last_error(PathCache *pc) -{ - return pc ? pc->errmsg : "NULL PathCache argument"; -} - -/*....................................................................... - * Discard all cached filenames. - * - * Input: - * pc PathCache * The cache to be cleared. - */ -static void pca_clear_cache(PathCache *pc) -{ - if(pc) { -/* - * Return all path-nodes to the freelist. - */ - _rst_FreeList(pc->node_mem); - pc->head = pc->tail = NULL; -/* - * Delete all filename strings. - */ - rst_CacheMem(pc->abs_mem); - rst_CacheMem(pc->rel_mem); - }; - return; -} - -/*....................................................................... - * Build the list of files of interest contained in a given - * colon-separated list of directories. - * - * Input: - * pc PathCache * The cache in which to store the names of - * the files that are found in the list of - * directories. - * path const char * A colon-separated list of directory - * paths. Under UNIX, when searching for - * executables, this should be the return - * value of getenv("PATH"). - * Output: - * return int 0 - OK. - * 1 - An error occurred. A description of - * the error can be acquired by calling - * pca_last_error(pc). - */ -int pca_scan_path(PathCache *pc, const char *path) -{ - const char *pptr; /* A pointer to the next unprocessed character in path[] */ - PathNode *node; /* A node in the list of directory paths */ - char **fptr; /* A pointer into pc->abs_mem->files[] */ -/* - * Check the arguments. - */ - if(!pc) - return 1; -/* - * Clear the outdated contents of the cache. - */ - pca_clear_cache(pc); -/* - * If no path list was provided, there is nothing to be added to the - * cache. - */ - if(!path) - return 0; -/* - * Extract directories from the path list, expanding tilde expressions - * on the fly into pc->pathname, then add them to the list of path - * nodes, along with a sorted list of the filenames of interest that - * the directories hold. - */ - pptr = path; - while(*pptr) { -/* - * Extract the next pathname component into pc->path->name. - */ - if(pca_extract_dir(pc, pptr, &pptr)) - return 1; -/* - * Add a new node to the list of paths, containing both the - * directory name and, if not a relative pathname, the list of - * files of interest in the directory. - */ - if(add_PathNode(pc, pc->path->name)) - return 1; - }; -/* - * The file arrays in each absolute directory node are sections of - * pc->abs_mem->files[]. Record pointers to the starts of each - * of these sections in each directory node. Note that this couldn't - * be done in add_PathNode(), because pc->abs_mem->files[] may - * get reallocated in subsequent calls to add_PathNode(), thus - * invalidating any pointers to it. - */ - fptr = pc->abs_mem->files; - for(node=pc->head; node; node=node->next) { - node->files = fptr; - fptr += node->nfile; - }; - return 0; -} - -/*....................................................................... - * Extract the next directory path from a colon-separated list of - * directories, expanding tilde home-directory expressions where needed. - * - * Input: - * pc PathCache * The cache of filenames. - * path const char * A pointer to the start of the next component - * in the path list. - * Input/Output: - * nextp const char ** A pointer to the next unprocessed character - * in path[] will be assigned to *nextp. - * Output: - * return int 0 - OK. The extracted path is in pc->path->name. - * 1 - Error. A description of the error will - * have been left in pc->errmsg. - */ -static int pca_extract_dir(PathCache *pc, const char *path, const char **nextp) -{ - const char *pptr; /* A pointer into path[] */ - const char *sptr; /* The path following tilde expansion */ - int escaped = 0; /* True if the last character was a backslash */ -/* - * If there is a tilde expression at the beginning of the specified path, - * place the corresponding home directory into pc->path. Otherwise - * just clear pc->path. - */ - if(pca_expand_tilde(pc, path, strlen(path), 0, &pptr)) - return 1; -/* - * Keep a record of the current location in the path. - */ - sptr = pptr; -/* - * Locate the end of the directory name in the pathname string, stopping - * when either the end of the string is reached, or an un-escaped colon - * separator is seen. - */ - while(*pptr && (escaped || *pptr != ':')) - escaped = !escaped && *pptr++ == '\\'; -/* - * Append the rest of the directory path to the pathname buffer. - */ - if(_pn_append_to_path(pc->path, sptr, pptr - sptr, 1) == NULL) { - strcpy(pc->errmsg, "Insufficient memory to record directory name"); - return 1; - }; -/* - * To facilitate subsequently appending filenames to the directory - * path name, make sure that the recorded directory name ends in a - * directory separator. - */ - { - int dirlen = strlen(pc->path->name); - if(dirlen < FS_DIR_SEP_LEN || - strncmp(pc->path->name + dirlen - FS_DIR_SEP_LEN, FS_DIR_SEP, - FS_DIR_SEP_LEN) != 0) { - if(_pn_append_to_path(pc->path, FS_DIR_SEP, FS_DIR_SEP_LEN, 0) == NULL) { - strcpy(pc->errmsg, "Insufficient memory to record directory name"); - return 1; - }; - }; - }; -/* - * Skip the separator unless we have reached the end of the path. - */ - if(*pptr==':') - pptr++; -/* - * Return the unprocessed tail of the path-list string. - */ - *nextp = pptr; - return 0; -} - -/*....................................................................... - * Read a username, stopping when a directory separator is seen, a colon - * separator is seen, the end of the string is reached, or the username - * buffer overflows. - * - * Input: - * pc PathCache * The cache of filenames. - * string char * The string who's prefix contains the name. - * slen int The max number of characters to read from string[]. - * literal int If true, treat backslashes as literal characters - * instead of escapes. - * Input/Output: - * nextp char ** A pointer to the next unprocessed character - * in string[] will be assigned to *nextp. - * Output: - * return int 0 - OK. The username can be found in pc->usrnam. - * 1 - Error. A description of the error message - * can be found in pc->errmsg. - */ -static int pca_read_username(PathCache *pc, const char *string, int slen, - int literal, const char **nextp) -{ - int usrlen; /* The number of characters in pc->usrnam[] */ - const char *sptr; /* A pointer into string[] */ - int escaped = 0; /* True if the last character was a backslash */ -/* - * Extract the username. - */ - for(sptr=string,usrlen=0; usrlen < USR_LEN && (sptr-string) < slen; sptr++) { -/* - * Stop if the end of the string is reached, or a directory separator - * or un-escaped colon separator is seen. - */ - if(!*sptr || strncmp(sptr, FS_DIR_SEP, FS_DIR_SEP_LEN)==0 || - (!escaped && *sptr == ':')) - break; -/* - * Escape the next character? - */ - if(!literal && !escaped && *sptr == '\\') { - escaped = 1; - } else { - escaped = 0; - pc->usrnam[usrlen++] = *sptr; - }; - }; -/* - * Did the username overflow the buffer? - */ - if(usrlen >= USR_LEN) { - strcpy(pc->errmsg, "Username too long"); - return 1; - }; -/* - * Terminate the string. - */ - pc->usrnam[usrlen] = '\0'; -/* - * Indicate where processing of the input string should continue. - */ - *nextp = sptr; - return 0; -} - - -/*....................................................................... - * Create a new CacheMem object. - * - * Output: - * return CacheMem * The new object, or NULL on error. - */ -static CacheMem *new_CacheMem(void) -{ - CacheMem *cm; /* The object to be returned */ -/* - * Allocate the container. - */ - cm = (CacheMem *)malloc(sizeof(CacheMem)); - if(!cm) { - fprintf(stderr, "new_CacheMem: Insufficient memory.\n"); - return NULL; - }; -/* - * Before attempting any operation that might fail, initialize the - * container at least up to the point at which it can safely be passed - * to del_CacheMem(). - */ - cm->sg = NULL; - cm->files_dim = 0; - cm->files = NULL; - cm->nfiles = 0; -/* - * Allocate a list of string segments for storing filenames. - */ - cm->sg = _new_StringGroup(_pu_pathname_dim()); - if(!cm->sg) - return del_CacheMem(cm); -/* - * Allocate an array of pointers to filenames. - * This will be extended later if needed. - */ - cm->files_dim = FILES_BLK_FACT; - cm->files = (char **) malloc(sizeof(*cm->files) * cm->files_dim); - if(!cm->files) { - fprintf(stderr, - "new_CacheMem: Insufficient memory to allocate array of files.\n"); - return del_CacheMem(cm); - }; - return cm; -} - -/*....................................................................... - * Delete a CacheMem object. - * - * Input: - * cm CacheMem * The object to be deleted. - * Output: - * return CacheMem * The deleted object (always NULL). - */ -static CacheMem *del_CacheMem(CacheMem *cm) -{ - if(cm) { -/* - * Delete the memory that was used to record filename strings. - */ - cm->sg = _del_StringGroup(cm->sg); -/* - * Delete the array of pointers to filenames. - */ - cm->files_dim = 0; - if(cm->files) { - free(cm->files); - cm->files = NULL; - }; -/* - * Delete the container. - */ - free(cm); - }; - return NULL; -} - -/*....................................................................... - * Re-initialize the memory used to allocate filename strings. - * - * Input: - * cm CacheMem * The memory cache to be cleared. - */ -static void rst_CacheMem(CacheMem *cm) -{ - _clr_StringGroup(cm->sg); - cm->nfiles = 0; - return; -} - -/*....................................................................... - * Append a new directory node to the list of directories read from the - * path. - * - * Input: - * pc PathCache * The filename cache. - * dirname const char * The name of the new directory. - * Output: - * return int 0 - OK. - * 1 - Error. - */ -static int add_PathNode(PathCache *pc, const char *dirname) -{ - PathNode *node; /* The new directory list node */ - int relative; /* True if dirname[] is a relative pathname */ -/* - * Have we been passed a relative pathname or an absolute pathname? - */ - relative = strncmp(dirname, FS_ROOT_DIR, FS_ROOT_DIR_LEN) != 0; -/* - * If it's an absolute pathname, ignore it if the corresponding - * directory doesn't exist. - */ - if(!relative && !_pu_path_is_dir(dirname)) - return 0; -/* - * Allocate a new list node to record the specifics of the new directory. - */ - node = (PathNode *) _new_FreeListNode(pc->node_mem); - if(!node) { - sprintf(pc->errmsg, "Insufficient memory to cache new directory."); - return 1; - }; -/* - * Initialize the node. - */ - node->next = NULL; - node->relative = relative; - node->mem = relative ? pc->rel_mem : pc->abs_mem; - node->dir = NULL; - node->nfile = 0; - node->files = NULL; -/* - * Make a copy of the directory pathname. - */ - node->dir = _sg_store_string(pc->abs_mem->sg, dirname, 0); - if(!node->dir) { - strcpy(pc->errmsg, "Insufficient memory to store directory name."); - return 1; - }; -/* - * Scan absolute directories for files of interest, recording their names - * in node->mem->sg and appending pointers to these names to the - * node->mem->files[] array. - */ - if(!node->relative) { - int nfile = node->nfile = pca_scan_dir(pc, node->dir, node->mem); - if(nfile < 1) { /* No files matched or an error occurred */ - node = (PathNode *) _del_FreeListNode(pc->node_mem, node); - return nfile < 0; - }; - }; -/* - * Append the new node to the list. - */ - if(pc->head) { - pc->tail->next = node; - pc->tail = node; - } else { - pc->head = pc->tail = node; - }; - return 0; -} - -/*....................................................................... - * Scan a given directory for files of interest, record their names - * in mem->sg and append pointers to them to the mem->files[] array. - * - * Input: - * pc PathCache * The filename cache. - * dirname const char * The pathname of the directory to be scanned. - * mem CacheMem * The memory in which to store filenames of - * interest. - * Output: - * return int The number of files recorded, or -1 if a - * memory error occurs. Note that the - * inability to read the contents of the - * directory is not counted as an error. - */ -static int pca_scan_dir(PathCache *pc, const char *dirname, CacheMem *mem) -{ - int nfile = 0; /* The number of filenames recorded */ - const char *filename; /* The name of the file being looked at */ -/* - * Attempt to open the directory. If the directory can't be read then - * there are no accessible files of interest in the directory. - */ - if(_dr_open_dir(pc->dr, dirname, NULL)) - return 0; -/* - * Record the names of all files in the directory in the cache. - */ - while((filename = _dr_next_file(pc->dr))) { - char *copy; /* A copy of the filename */ -/* - * Make a temporary copy of the filename with an extra byte prepended. - */ - _pn_clear_path(pc->path); - if(_pn_append_to_path(pc->path, " ", 1, 0) == NULL || - _pn_append_to_path(pc->path, filename, -1, 1) == NULL) { - strcpy(pc->errmsg, "Insufficient memory to record filename"); - return -1; - }; -/* - * Store the filename. - */ - copy = _sg_store_string(mem->sg, pc->path->name, 0); - if(!copy) { - strcpy(pc->errmsg, "Insufficient memory to cache file name."); - return -1; - }; -/* - * Mark the filename as unchecked. - */ - copy[0] = PCA_F_ENIGMA; -/* - * Make room to store a pointer to the copy in mem->files[]. - */ - if(mem->nfiles + 1 > mem->files_dim) { - int needed = mem->files_dim + FILES_BLK_FACT; - char **files = (char **) realloc(mem->files, sizeof(*mem->files)*needed); - if(!files) { - strcpy(pc->errmsg, "Insufficient memory to extend filename cache."); - return 1; - }; - mem->files = files; - mem->files_dim = needed; - }; -/* - * Record a pointer to the copy of the filename at the end of the files[] - * array. - */ - mem->files[mem->nfiles++] = copy; -/* - * Keep a record of the number of files matched so far. - */ - nfile++; - }; -/* - * Sort the list of files into lexical order. - */ - qsort(mem->files + mem->nfiles - nfile, nfile, sizeof(*mem->files), - pca_cmp_matches); -/* - * Return the number of files recorded in mem->files[]. - */ - return nfile; -} - -/*....................................................................... - * A qsort() comparison function for comparing the cached filename - * strings pointed to by two (char **) array elements. Note that - * this ignores the initial cache-status byte of each filename. - * - * Input: - * v1, v2 void * Pointers to the pointers of two strings to be compared. - * Output: - * return int -1 -> v1 < v2. - * 0 -> v1 == v2 - * 1 -> v1 > v2 - */ -static int pca_cmp_matches(const void *v1, const void *v2) -{ - const char **s1 = (const char **) v1; - const char **s2 = (const char **) v2; - return strcmp(*s1+1, *s2+1); -} - -/*....................................................................... - * Given the simple name of a file, search the cached list of files - * in the order in which they where found in the list of directories - * previously presented to pca_scan_path(), and return the pathname - * of the first file which has this name. If a pathname to a file is - * given instead of a simple filename, this is returned without being - * looked up in the cache, but with any initial ~username expression - * expanded, and optionally, unescaped backslashes removed. - * - * Input: - * pc PathCache * The cached list of files. - * name const char * The name of the file to lookup. - * name_len int The length of the filename string at the - * beginning of name[], or -1 to indicate that - * the filename occupies the whole of the - * string. - * literal int If this argument is zero, lone backslashes - * in name[] are ignored during comparison - * with filenames in the cache, under the - * assumption that they were in the input line - * soley to escape the special significance of - * characters like spaces. To have them treated - * as normal characters, give this argument a - * non-zero value, such as 1. - * Output: - * return char * The pathname of the first matching file, - * or NULL if not found. Note that the returned - * pointer points to memory owned by *pc, and - * will become invalid on the next call to any - * function in the PathCache module. - */ -char *pca_lookup_file(PathCache *pc, const char *name, int name_len, - int literal) -{ - PathNode *node; /* A node in the list of directories in the path */ - char **match; /* A pointer to a matching filename string in the cache */ -/* - * Check the arguments. - */ - if(!pc || !name || name_len==0) - return NULL; -/* - * If no length was specified, determine the length of the string to - * be looked up. - */ - if(name_len < 0) - name_len = strlen(name); -/* - * If the word starts with a ~username expression, the root directory, - * of it contains any directory separators, then treat it isn't a simple - * filename that can be looked up in the cache, but rather appears to - * be the pathname of a file. If so, return a copy of this pathname with - * escapes removed, if requested, and any initial ~username expression - * expanded. - */ - if(cpa_cmd_contains_path(name, name_len)) { - const char *nptr; - if(pca_expand_tilde(pc, name, name_len, literal, &nptr) || - _pn_append_to_path(pc->path, nptr, name_len - (nptr-name), - !literal) == NULL) - return NULL; - return pc->path->name; - }; -/* - * Look up the specified filename in each of the directories of the path, - * in the same order that they were listed in the path, and stop as soon - * as an instance of the file is found. - */ - for(node=pc->head; node; node=node->next) { -/* - * If the directory of the latest node is a relative pathname, - * scan it for files of interest. - */ - if(node->relative) { - rst_CacheMem(node->mem); - if(pca_scan_dir(pc, node->dir, node->mem) < 1) - continue; - node->files = node->mem->files; - node->nfile = node->mem->nfiles; - }; -/* - * Copy the filename into a temporary buffer, while interpretting - * escape characters if needed. - */ - _pn_clear_path(pc->path); - if(_pn_append_to_path(pc->path, name, name_len, !literal) == NULL) - return NULL; -/* - * Perform a binary search for the requested filename. - */ - match = (char **)bsearch(pc->path->name, node->files, node->nfile, - sizeof(*node->files), pca_cmp_file); - if(match) { -/* - * Prepend the pathname in which the directory was found, which we have - * guaranteed to end in a directory separator, to the located filename. - */ - if(_pn_prepend_to_path(pc->path, node->dir, -1, 0) == NULL) - return NULL; -/* - * Return the matching pathname unless it is rejected by the application. - */ - if(!pc->check_fn || (*match)[0] == PCA_F_WANTED || - ((*match)[0]==PCA_F_ENIGMA && pc->check_fn(pc->data, pc->path->name))){ - (*match)[0] = PCA_F_WANTED; - return pc->path->name; - } else { - *(match)[0] = PCA_F_IGNORE; - }; - }; - }; -/* - * File not found. - */ - return NULL; -} - -/*....................................................................... - * A qsort() comparison function for comparing a filename string to - * a cached filename string pointed to by a (char **) array element. - * This ignores the initial code byte at the start of the cached filename - * string. - * - * Input: - * v1, v2 void * Pointers to the pointers of two strings to be compared. - * Output: - * return int -1 -> v1 < v2. - * 0 -> v1 == v2 - * 1 -> v1 > v2 - */ -static int pca_cmp_file(const void *v1, const void *v2) -{ - const char *file_name = (const char *) v1; - const char **cache_name = (const char **) v2; - return strcmp(file_name, *cache_name + 1); -} - -/*....................................................................... - * The PcaPathConf structure may have options added to it in the future. - * To allow your application to be linked against a shared version of the - * tecla library, without these additions causing your application to - * crash, you should use new_PcaPathConf() to allocate such structures. - * This will set all of the configuration options to their default values, - * which you can then change before passing the structure to - * pca_path_completions(). - * - * Input: - * pc PathCache * The filename cache in which to look for - * file name completions. - * Output: - * return PcaPathConf * The new configuration structure, or NULL - * on error. A descripition of the error - * can be found by calling pca_last_error(pc). - */ -PcaPathConf *new_PcaPathConf(PathCache *pc) -{ - PcaPathConf *ppc; /* The object to be returned */ -/* - * Check the arguments. - */ - if(!pc) - return NULL; -/* - * Allocate the container. - */ - ppc = (PcaPathConf *)malloc(sizeof(PcaPathConf)); - if(!ppc) { - strcpy(pc->errmsg, "Insufficient memory."); - return NULL; - }; -/* - * Before attempting any operation that might fail, initialize the - * container at least up to the point at which it can safely be passed - * to del_PcaPathConf(). - */ - if(pca_init_PcaPathConf(ppc, pc)) - return del_PcaPathConf(ppc); - return ppc; -} - -/*....................................................................... - * Initialize a PcaPathConf configuration structure with defaults. - * - * Input: - * ppc PcaPathConf * The structre to be initialized. - * pc PathCache * The cache in which completions will be looked up. - * Output: - * return int 0 - OK. - * 1 - Error. A description of the error can be - * obtained by calling pca_last_error(pc). - */ -static int pca_init_PcaPathConf(PcaPathConf *ppc, PathCache *pc) -{ -/* - * Check the arguments. - */ - if(!pc) - return 1; -/* - * Set the default options. - */ - ppc->id = PPC_ID_CODE; - ppc->pc = pc; - ppc->escaped = 1; - ppc->file_start = -1; - return 0; -} - -/*....................................................................... - * Delete a PcaPathConf object. - * - * Input: - * ppc PcaPathConf * The object to be deleted. - * Output: - * return PcaPathConf * The deleted object (always NULL). - */ -PcaPathConf *del_PcaPathConf(PcaPathConf *ppc) -{ - if(ppc) { - ppc->pc = NULL; /* It is up to the caller to delete the cache */ -/* - * Delete the container. - */ - free(ppc); - }; - return NULL; -} - -/*....................................................................... - * pca_path_completions() is a completion callback function for use - * directly with cpl_complete_word() or gl_customize_completions(), or - * indirectly from your own completion callback function. It requires - * that a CpaPathArgs object be passed via its 'void *data' argument. - */ -CPL_MATCH_FN(pca_path_completions) -{ - PcaPathConf *ppc; /* The configuration arguments */ - PathCache *pc; /* The cache in which to look for completions */ - PathNode *node; /* A node in the list of directories in the path */ - const char *filename; /* The name of the file being looked at */ - const char *start_path; /* The pointer to the start of the pathname */ - /* in line[]. */ - int word_start; /* The index in line[] corresponding to start_path */ - const char *prefix; /* The file-name prefix being searched for */ - size_t prefix_len; /* The length of the prefix being completed */ - int bot; /* The lowest index of the array not searched yet */ - int top; /* The highest index of the array not searched yet */ -/* - * Check the arguments. - */ - if(!cpl) - return 1; - if(!line || word_end < 0 || !data) { - cpl_record_error(cpl, "pca_path_completions: Invalid arguments."); - return 1; - }; -/* - * Get the configuration arguments. - */ - ppc = (PcaPathConf *) data; -/* - * Check that the callback data is a PcaPathConf structure returned - * by new_PcaPathConf(). - */ - if(ppc->id != PPC_ID_CODE) { - cpl_record_error(cpl, - "Invalid callback data passed to pca_path_completions()"); - return 1; - }; -/* - * Get the filename cache. - */ - pc = ppc->pc; -/* - * Get the start of the file name. If not specified by the caller, - * identify it by searching backwards in the input line for an - * unescaped space or the start of the line. - */ - if(ppc->file_start < 0) { - start_path = _pu_start_of_path(line, word_end); - if(!start_path) { - cpl_record_error(cpl, "Unable to find the start of the file name."); - return 1; - }; - } else { - start_path = line + ppc->file_start; - }; -/* - * Get the index of the start of the word being completed. - */ - word_start = start_path - line; -/* - * Work out the length of the prefix that is bein completed. - */ - prefix_len = word_end - word_start; -/* - * If the word starts with a ~username expression or the root directory, - * of it contains any directory separators, then completion must be - * delegated to cpl_file_completions(). - */ - if(cpa_cmd_contains_path(start_path, prefix_len)) { - cfc_file_start(pc->cfc, word_start); - return cpl_file_completions(cpl, pc->cfc, line, word_end); - }; -/* - * Look up the specified file name in each of the directories of the path, - * in the same order that they were listed in the path, and stop as soon - * as an instance of the file is found. - */ - for(node=pc->head; node; node=node->next) { -/* - * If the directory of the latest node is a relative pathname, - * scan it for files of interest. - */ - if(node->relative) { - rst_CacheMem(node->mem); - if(pca_scan_dir(pc, node->dir, node->mem) < 1) - continue; - node->files = node->mem->files; - node->nfile = node->mem->nfiles; - }; -/* - * If needed, make a copy of the file-name being matched, with - * escapes removed. Note that we need to do this anew every loop - * iteration, because the above call to pca_scan_dir() uses - * pc->path. - */ - prefix = pca_prepare_prefix(pc, start_path, prefix_len, ppc->escaped); - if(!prefix) - return 1; -/* - * The directory entries are sorted, so we can perform a binary - * search for an instance of the prefix being searched for. - */ - bot = 0; - top = node->nfile - 1; - while(top >= bot) { - int mid = (top + bot)/2; - int test = strncmp(node->files[mid]+1, prefix, prefix_len); - if(test > 0) - top = mid - 1; - else if(test < 0) - bot = mid + 1; - else { - top = bot = mid; - break; - }; - }; -/* - * If we found a match, look to see if any of its neigbors also match. - */ - if(top == bot) { - while(--bot >= 0 && strncmp(node->files[bot]+1, prefix, prefix_len) == 0) - ; - while(++top < node->nfile && - strncmp(node->files[top]+1, prefix, prefix_len) == 0) - ; -/* - * We will have gone one too far in each direction. - */ - bot++; - top--; -/* - * Add the completions to the list after checking them against the - * callers requirements. - */ - for( ; bot<=top; bot++) { - char *match = node->files[bot]; -/* - * Form the full pathname of the file. - */ - _pn_clear_path(pc->path); - if(_pn_append_to_path(pc->path, node->dir, -1, 0) == NULL || - _pn_append_to_path(pc->path, match+1, -1, 0) == NULL) { - strcpy(pc->errmsg, "Insufficient memory to complete file name"); - return 1; - }; -/* - * Should the file be included in the list of completions? - */ - if(!pc->check_fn || match[0] == PCA_F_WANTED || - (match[0]==PCA_F_ENIGMA && pc->check_fn(pc->data, pc->path->name))) { - match[0] = PCA_F_WANTED; -/* - * Copy the completion suffix into the work pathname pc->path->name, - * adding backslash escapes if needed. - */ - if(pca_prepare_suffix(pc, match + 1 + prefix_len, - ppc->escaped)) - return 1; -/* - * Record the completion. - */ - if(cpl_add_completion(cpl, line, word_start, word_end, pc->path->name, - "", " ")) - return 1; -/* - * The file was rejected by the application. - */ - } else { - match[0] = PCA_F_IGNORE; - }; - }; - }; - }; -/* - * We now need to search for subdirectories of the current directory which - * have matching prefixes. First, if needed, make a copy of the word being - * matched, with escapes removed. - */ - prefix = pca_prepare_prefix(pc, start_path, prefix_len, ppc->escaped); - if(!prefix) - return 1; -/* - * Now open the current directory. - */ - if(_dr_open_dir(pc->dr, FS_PWD, NULL)) - return 0; -/* - * Scan the current directory for sub-directories whos names start with - * the prefix that we are completing. - */ - while((filename = _dr_next_file(pc->dr))) { -/* - * Does the latest filename match the prefix, and is it a directory? - */ - if(strncmp(filename, prefix, prefix_len) == 0 && _pu_path_is_dir(filename)){ -/* - * Record the completion. - */ - if(pca_prepare_suffix(pc, filename + prefix_len, ppc->escaped) || - cpl_add_completion(cpl, line, word_start, word_end, pc->path->name, - FS_DIR_SEP, FS_DIR_SEP)) - return 1; -/* - * The prefix in pc->path->name will have been overwritten by - * pca_prepare_suffix(). Restore it here. - */ - prefix = pca_prepare_prefix(pc, start_path, prefix_len, ppc->escaped); - if(!prefix) - return 1; - }; - }; - _dr_close_dir(pc->dr); - return 0; -} - -/*....................................................................... - * Using the work buffer pc->path, make a suitably escaped copy of a - * given completion suffix, ready to be passed to cpl_add_completion(). - * - * Input: - * pc PathCache * The filename cache resource object. - * suffix char * The suffix to be copied. - * add_escapes int If true, escape special characters. - * Output: - * return int 0 - OK. - * 1 - Error. - */ -static int pca_prepare_suffix(PathCache *pc, const char *suffix, - int add_escapes) -{ - const char *sptr; /* A pointer into suffix[] */ - int nbsl; /* The number of backslashes to add to the suffix */ - int i; -/* - * How long is the suffix? - */ - int suffix_len = strlen(suffix); -/* - * Clear the work buffer. - */ - _pn_clear_path(pc->path); -/* - * Count the number of backslashes that will have to be added to - * escape spaces, tabs, backslashes and wildcard characters. - */ - nbsl = 0; - if(add_escapes) { - for(sptr = suffix; *sptr; sptr++) { - switch(*sptr) { - case ' ': case '\t': case '\\': case '*': case '?': case '[': - nbsl++; - break; - }; - }; - }; -/* - * Arrange for the output path buffer to have sufficient room for the - * both the suffix and any backslashes that have to be inserted. - */ - if(_pn_resize_path(pc->path, suffix_len + nbsl) == NULL) { - strcpy(pc->errmsg, "Insufficient memory to complete file name"); - return 1; - }; -/* - * If the suffix doesn't need any escapes, copy it directly into the - * work buffer. - */ - if(nbsl==0) { - strcpy(pc->path->name, suffix); - } else { -/* - * Make a copy with special characters escaped? - */ - if(nbsl > 0) { - const char *src = suffix; - char *dst = pc->path->name; - for(i=0; i= FS_ROOT_DIR_LEN && - strncmp(prefix, FS_ROOT_DIR, FS_ROOT_DIR_LEN) == 0) - return 1; -/* - * Search the prefix for directory separators, returning as soon as - * any are found, since their presence indicates that the filename - * starts with a pathname specification (valid or otherwise). - */ - for(i=0; i= FS_DIR_SEP_LEN && - strncmp(prefix + i, FS_DIR_SEP, FS_DIR_SEP_LEN) == 0) - return 1; - }; -/* - * The file name doesn't appear to start with a pathname specification. - */ - return 0; -} - -/*....................................................................... - * If needed make a new copy of the prefix being matched, in pc->path->name, - * but with escapes removed. If no escapes are to be removed, simply return - * the original prefix string. - * - * Input: - * pc PathCache * The cache being searched. - * prefix const char * The prefix to be processed. - * prefix_len size_t The length of the prefix. - * escaped int If true, return a copy with escapes removed. - * Output: - * return const char * The prepared prefix, or NULL on error, in - * which case an error message will have been - * left in pc->errmsg. - */ -static const char *pca_prepare_prefix(PathCache *pc, const char *prefix, - size_t prefix_len, int escaped) -{ -/* - * Make a copy with escapes removed? - */ - if(escaped) { - _pn_clear_path(pc->path); - if(_pn_append_to_path(pc->path, prefix, prefix_len, 1) == NULL) { - strcpy(pc->errmsg, "Insufficient memory to complete filename"); - return NULL; - }; - return pc->path->name; - }; - return prefix; -} - -/*....................................................................... - * If backslashes in the filename should be treated as literal - * characters, call the following function with literal=1. Otherwise - * the default is to treat them as escape characters, used for escaping - * spaces etc.. - * - * Input: - * ppc PcaPathConf * The pca_path_completions() configuration object - * to be configured. - * literal int Pass non-zero here to enable literal interpretation - * of backslashes. Pass 0 to turn off literal - * interpretation. - */ -void ppc_literal_escapes(PcaPathConf *ppc, int literal) -{ - if(ppc) - ppc->escaped = !literal; -} - -/*....................................................................... - * Call this function if you know where the index at which the - * filename prefix starts in the input line. Otherwise by default, - * or if you specify start_index to be -1, the filename is taken - * to start after the first unescaped space preceding the cursor, - * or the start of the line, which ever comes first. - * - * Input: - * ppc PcaPathConf * The pca_path_completions() configuration object - * to be configured. - * start_index int The index of the start of the filename in - * the input line, or -1 to select the default. - */ -void ppc_file_start(PcaPathConf *ppc, int start_index) -{ - if(ppc) - ppc->file_start = start_index; -} - -/*....................................................................... - * Expand any ~user expression found at the start of a path, leaving - * either an empty string in pc->path if there is no ~user expression, - * or the corresponding home directory. - * - * Input: - * pc PathCache * The filename cache. - * path const char * The path to expand. - * pathlen int The max number of characters to look at in path[]. - * literal int If true, treat backslashes as literal characters - * instead of escapes. - * Input/Output: - * endp const char * A pointer to the next unprocessed character in - * path[] will be assigned to *endp. - * Output: - * return int 0 - OK - * 1 - Error (a description will have been placed - * in pc->errmsg[]). - */ -static int pca_expand_tilde(PathCache *pc, const char *path, int pathlen, - int literal, const char **endp) -{ - const char *pptr = path; /* A pointer into path[] */ - const char *homedir=NULL; /* A home directory */ -/* - * Clear the pathname buffer. - */ - _pn_clear_path(pc->path); -/* - * If the first character is a tilde, then perform home-directory - * interpolation. - */ - if(*pptr == '~') { -/* - * Skip the tilde character and attempt to read the username that follows - * it, into pc->usrnam[]. - */ - if(pca_read_username(pc, ++pptr, pathlen-1, literal, &pptr)) - return 1; -/* - * Attempt to lookup the home directory of the user. - */ - homedir = _hd_lookup_home_dir(pc->home, pc->usrnam); - if(!homedir) { - strncpy(pc->errmsg, _hd_last_home_dir_error(pc->home), ERRLEN); - pc->errmsg[ERRLEN] = '\0'; - return 1; - }; -/* - * Append the home directory to the pathname string. - */ - if(_pn_append_to_path(pc->path, homedir, -1, 0) == NULL) { - strcpy(pc->errmsg, "Insufficient memory for home directory expansion"); - return 1; - }; - }; -/* - * ~user and ~ are usually followed by a directory separator to - * separate them from the file contained in the home directory. - * If the home directory is the root directory, then we don't want - * to follow the home directory by a directory separator, so we should - * skip over it so that it doesn't get copied into the output pathname - */ - if(homedir && strcmp(homedir, FS_ROOT_DIR) == 0 && - (pptr-path) + FS_DIR_SEP_LEN < pathlen && - strncmp(pptr, FS_DIR_SEP, FS_DIR_SEP_LEN) == 0) { - pptr += FS_DIR_SEP_LEN; - }; -/* - * Return a pointer to the next unprocessed character. - */ - *endp = pptr; - return 0; -} - -/*....................................................................... - * Clear the filename status codes that are recorded before each filename - * in the cache. - * - * Input: - * pc PathCache * The filename cache. - */ -static void pca_remove_marks(PathCache *pc) -{ - PathNode *node; /* A node in the list of directories in the path */ - int i; -/* - * Traverse the absolute directories of the path, clearing the - * filename status marks that precede each filename. - */ - for(node=pc->head; node; node=node->next) { - if(!node->relative) { - for(i=0; infile; i++) - *node->files[i] = PCA_F_ENIGMA; - }; - }; - return; -} diff --git a/libtecla-1.4.1/stringrp.c b/libtecla-1.4.1/stringrp.c deleted file mode 100644 index fe7d875..0000000 --- a/libtecla-1.4.1/stringrp.c +++ /dev/null @@ -1,285 +0,0 @@ -/* - * Copyright (c) 2000, 2001 by Martin C. Shepherd. - * - * All rights reserved. - * - * Permission is hereby granted, free of charge, to any person obtaining a - * copy of this software and associated documentation files (the - * "Software"), to deal in the Software without restriction, including - * without limitation the rights to use, copy, modify, merge, publish, - * distribute, and/or sell copies of the Software, and to permit persons - * to whom the Software is furnished to do so, provided that the above - * copyright notice(s) and this permission notice appear in all copies of - * the Software and that both the above copyright notice(s) and this - * permission notice appear in supporting documentation. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS - * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT - * OF THIRD PARTY RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR - * HOLDERS INCLUDED IN THIS NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL - * INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING - * FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, - * NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION - * WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - * - * Except as contained in this notice, the name of a copyright holder - * shall not be used in advertising or otherwise to promote the sale, use - * or other dealings in this Software without prior written authorization - * of the copyright holder. - */ -#include -#include -#include - -#include "freelist.h" -#include "stringrp.h" - -/* - * StringSegment objects store lots of small strings in larger - * character arrays. Since the total length of all of the strings can't - * be known in advance, an extensible list of large character arrays, - * called string-segments are used. - */ -typedef struct StringSegment StringSegment; -struct StringSegment { - StringSegment *next; /* A pointer to the next segment in the list */ - char *block; /* An array of characters to be shared between strings */ - int unused; /* The amount of unused space at the end of block[] */ -}; - -/* - * StringGroup is typedef'd in stringrp.h. - */ -struct StringGroup { - FreeList *node_mem; /* The StringSegment free-list */ - int block_size; /* The dimension of each character array block */ - StringSegment *head; /* The list of character arrays */ -}; - -/* - * Specify how many StringSegment's to allocate at a time. - */ -#define STR_SEG_BLK 20 - -/*....................................................................... - * Create a new StringGroup object. - * - * Input: - * segment_size int The length of each of the large character - * arrays in which multiple strings will be - * stored. This sets the length of longest - * string that can be stored, and for efficiency - * should be at least 10 times as large as - * the average string that will be stored. - * Output: - * return StringGroup * The new object, or NULL on error. - */ -StringGroup *_new_StringGroup(int segment_size) -{ - StringGroup *sg; /* The object to be returned */ -/* - * Check the arguments. - */ - if(segment_size < 1) { - fprintf(stderr, "_new_StringGroup: Invalid segment_size argument.\n"); - return NULL; - }; -/* - * Allocate the container. - */ - sg = (StringGroup *) malloc(sizeof(StringGroup)); - if(!sg) { - fprintf(stderr, "_new_StringGroup: Insufficient memory.\n"); - return NULL; - }; -/* - * Before attempting any operation that might fail, initialize the - * container at least up to the point at which it can safely be passed - * to _del_StringGroup(). - */ - sg->node_mem = NULL; - sg->head = NULL; - sg->block_size = segment_size; -/* - * Allocate the free list that is used to allocate list nodes. - */ - sg->node_mem = _new_FreeList("_new_StringGroup", sizeof(StringSegment), - STR_SEG_BLK); - if(!sg->node_mem) - return _del_StringGroup(sg); - return sg; -} - -/*....................................................................... - * Delete a StringGroup object. - * - * Input: - * sg StringGroup * The object to be deleted. - * Output: - * return StringGroup * The deleted object (always NULL). - */ -StringGroup *_del_StringGroup(StringGroup *sg) -{ - if(sg) { - StringSegment *node; -/* - * Delete the character arrays. - */ - for(node=sg->head; node; node=node->next) { - if(node->block) - free(node->block); - node->block = NULL; - }; -/* - * Delete the list nodes that contained the string segments. - */ - sg->node_mem = _del_FreeList("_del_StringGroup", sg->node_mem, 1); - sg->head = NULL; /* Already deleted by deleting sg->node_mem */ -/* - * Delete the container. - */ - free(sg); - }; - return NULL; -} - -/*....................................................................... - * Make a copy of a string in the specified string group, and return - * a pointer to the copy. - * - * Input: - * sg StringGroup * The group to store the string in. - * string const char * The string to be recorded. - * remove_escapes int If true, omit backslashes which escape - * other characters when making the copy. - * Output: - * return char * The pointer to the copy of the string, - * or NULL if there was insufficient memory. - */ -char *_sg_store_string(StringGroup *sg, const char *string, int remove_escapes) -{ - char *copy; /* The recorded copy of string[] */ -/* - * Check the arguments. - */ - if(!sg || !string) - return NULL; -/* - * Get memory for the string. - */ - copy = _sg_alloc_string(sg, strlen(string)); - if(copy) { -/* - * If needed, remove backslash escapes while copying the input string - * into the cache string. - */ - if(remove_escapes) { - int escaped = 0; /* True if the next character should be */ - /* escaped. */ - const char *src = string; /* A pointer into the input string */ - char *dst = copy; /* A pointer into the cached copy of the */ - /* string. */ - while(*src) { - if(!escaped && *src == '\\') { - escaped = 1; - src++; - } else { - escaped = 0; - *dst++ = *src++; - }; - }; - *dst = '\0'; -/* - * If escapes have already been removed, copy the input string directly - * into the cache. - */ - } else { - strcpy(copy, string); - }; - }; -/* - * Return a pointer to the copy of the string (or NULL if the allocation - * failed). - */ - return copy; -} - -/*....................................................................... - * Allocate memory for a string of a given length. - * - * Input: - * sg StringGroup * The group to store the string in. - * length int The required length of the string. - * Output: - * return char * The pointer to the copy of the string, - * or NULL if there was insufficient memory. - */ -char *_sg_alloc_string(StringGroup *sg, int length) -{ - StringSegment *node; /* A node of the list of string segments */ - char *copy; /* The allocated string */ -/* - * If the string is longer than block_size, then we can't record it. - */ - if(length > sg->block_size || length < 0) - return NULL; -/* - * See if there is room to record the string in one of the existing - * string segments. - */ - for(node=sg->head; node && node->unused <= length; node=node->next) - ; -/* - * If there wasn't room, allocate a new string segment. - */ - if(!node) { - node = (StringSegment *) _new_FreeListNode(sg->node_mem); - if(!node) - return NULL; -/* - * Initialize the segment. - */ - node->next = NULL; - node->block = NULL; - node->unused = sg->block_size; -/* - * Attempt to allocate the string segment character array. - */ - node->block = (char *) malloc(sg->block_size); - if(!node->block) - return NULL; -/* - * Prepend the node to the list. - */ - node->next = sg->head; - sg->head = node; - }; -/* - * Get memory for the string. - */ - copy = node->block + sg->block_size - node->unused; - node->unused -= length + 1; -/* - * Return a pointer to the string memory. - */ - return copy; -} - -/*....................................................................... - * Delete all of the strings that are currently stored by a specified - * StringGroup object. - * - * Input: - * sg StringGroup * The group of strings to clear. - */ -void _clr_StringGroup(StringGroup *sg) -{ - StringSegment *node; /* A node in the list of string segments */ -/* - * Mark all of the string segments as unoccupied. - */ - for(node=sg->head; node; node=node->next) - node->unused = sg->block_size; - return; -} diff --git a/libtecla-1.4.1/stringrp.h b/libtecla-1.4.1/stringrp.h deleted file mode 100644 index 8b15ce9..0000000 --- a/libtecla-1.4.1/stringrp.h +++ /dev/null @@ -1,84 +0,0 @@ -#ifndef stringrp_h -#define stringrp_h -/* - * Copyright (c) 2000, 2001 by Martin C. Shepherd. - * - * All rights reserved. - * - * Permission is hereby granted, free of charge, to any person obtaining a - * copy of this software and associated documentation files (the - * "Software"), to deal in the Software without restriction, including - * without limitation the rights to use, copy, modify, merge, publish, - * distribute, and/or sell copies of the Software, and to permit persons - * to whom the Software is furnished to do so, provided that the above - * copyright notice(s) and this permission notice appear in all copies of - * the Software and that both the above copyright notice(s) and this - * permission notice appear in supporting documentation. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS - * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT - * OF THIRD PARTY RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR - * HOLDERS INCLUDED IN THIS NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL - * INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING - * FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, - * NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION - * WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - * - * Except as contained in this notice, the name of a copyright holder - * shall not be used in advertising or otherwise to promote the sale, use - * or other dealings in this Software without prior written authorization - * of the copyright holder. - */ - -/* - * StringGroup objects provide memory for modules that need to - * allocate lots of small strings without needing to free any of them - * individually, but rather is happy to free them all at the same - * time. Taking advantage of these properties, StringGroup objects - * avoid the heap fragmentation that tends to occur when lots of small - * strings are allocated directly from the heap and later free'd. They - * do this by allocating a list of large character arrays in each of - * which multiple strings are stored. Thus instead of allocating lots - * of small strings, a few large character arrays are allocated. When - * the strings are free'd on mass, this list of character arrays is - * maintained, ready for subsequent use in recording another set of - * strings. - */ -typedef struct StringGroup StringGroup; - -/* - * The following constructor allocates a string-allocation object. - * The segment_size argument specifies how long each string segment - * array should be. This should be at least 10 times the length of - * the average string to be recorded in the string group, and - * sets the length of the longest string that can be stored. - */ -StringGroup *_new_StringGroup(int segment_size); - -/* - * Delete all of the strings that are currently stored by a specified - * StringGroup object. - */ -void _clr_StringGroup(StringGroup *sg); - -/* - * Make a copy of the specified string, returning a pointer to - * the copy, or NULL if there was insufficient memory. If the - * remove_escapes argument is non-zero, backslashes that escape - * other characters will be removed. - */ -char *_sg_store_string(StringGroup *sg, const char *string, int remove_escapes); - -/* - * Allocate memory for a string of a given length. - */ -char *_sg_alloc_string(StringGroup *sg, int length); - -/* - * Delete a StringGroup object (and all of the strings that it - * contains). - */ -StringGroup *_del_StringGroup(StringGroup *sg); - -#endif diff --git a/libtecla-1.4.1/strngmem.c b/libtecla-1.4.1/strngmem.c deleted file mode 100644 index fedf4ff..0000000 --- a/libtecla-1.4.1/strngmem.c +++ /dev/null @@ -1,226 +0,0 @@ -/* - * Copyright (c) 2000, 2001 by Martin C. Shepherd. - * - * All rights reserved. - * - * Permission is hereby granted, free of charge, to any person obtaining a - * copy of this software and associated documentation files (the - * "Software"), to deal in the Software without restriction, including - * without limitation the rights to use, copy, modify, merge, publish, - * distribute, and/or sell copies of the Software, and to permit persons - * to whom the Software is furnished to do so, provided that the above - * copyright notice(s) and this permission notice appear in all copies of - * the Software and that both the above copyright notice(s) and this - * permission notice appear in supporting documentation. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS - * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT - * OF THIRD PARTY RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR - * HOLDERS INCLUDED IN THIS NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL - * INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING - * FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, - * NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION - * WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - * - * Except as contained in this notice, the name of a copyright holder - * shall not be used in advertising or otherwise to promote the sale, use - * or other dealings in this Software without prior written authorization - * of the copyright holder. - */ - -#include -#include - -#include "strngmem.h" -#include "freelist.h" - -struct StringMem { - unsigned long nmalloc; /* The number of strings allocated with malloc */ - FreeList *fl; /* The free-list */ -}; - -/*....................................................................... - * Create a string free-list container and the first block of its free-list. - * - * Input: - * caller const char * The name of the calling function, or NULL - * to not report errors to stderr. - * blocking_factor int The blocking_factor argument specifies how - * many strings of length SM_STRLEN - * bytes (see stringmem.h) are allocated in each - * free-list block. - * For example if blocking_factor=64 and - * SM_STRLEN=16, then each new - * free-list block will take 1K of memory. - * Output: - * return StringMem * The new free-list container, or NULL on - * error. - */ -StringMem *_new_StringMem(const char *caller, unsigned blocking_factor) -{ - StringMem *sm; /* The container to be returned. */ -/* - * Check arguments. - */ - if(blocking_factor < 1) { - if(caller) { - fprintf(stderr, "_new_StringMem (%s): Bad blocking factor (%d).\n", - caller, blocking_factor); - }; - return NULL; - }; -/* - * Allocate the container. - */ - sm = (StringMem *) malloc(sizeof(StringMem)); - if(!sm) { - if(caller) - fprintf(stderr, "_new_StringMem (%s): Insufficient memory.\n", caller); - return NULL; - }; -/* - * Before attempting any operation that might fail, initialize - * the container at least up to the point at which it can safely - * be passed to _del_StringMem(). - */ - sm->nmalloc = 0; - sm->fl = NULL; -/* - * Allocate the free-list. - */ - sm->fl = _new_FreeList(caller, SM_STRLEN, blocking_factor); - if(!sm->fl) - return _del_StringMem(caller, sm, 1); -/* - * Return the free-list container. - */ - return sm; -} - -/*....................................................................... - * Delete a string free-list. - * - * Input: - * caller const char * The name of the calling function, or NULL to - * not report errors to stderr. - * sm StringMem * The string free-list to be deleted, or NULL. - * force int If force==0 then _del_StringMem() will complain - * and refuse to delete the free-list if any - * of nodes have not been returned to the free-list. - * If force!=0 then _del_StringMem() will not check - * whether any nodes are still in use and will - * always delete the list. - * Output: - * return StringMem * Always NULL (even if the list couldn't be - * deleted). - */ -StringMem *_del_StringMem(const char *caller, StringMem *sm, int force) -{ - if(sm) { -/* - * Check whether any strings have not been returned to the free-list. - */ - if(!force && (sm->nmalloc > 0 || _busy_FreeListNodes(sm->fl) > 0)) { - if(caller) - fprintf(stderr, "_del_StringMem (%s): Free-list in use.\n", caller); - return NULL; - }; -/* - * Delete the free-list. - */ - sm->fl = _del_FreeList(caller, sm->fl, force); -/* - * Delete the container. - */ - free(sm); - }; - return NULL; -} - -/*....................................................................... - * Allocate an array of 'length' chars. - * - * Input: - * sm StringMem * The string free-list to allocate from. - * length size_t The length of the new string (including '\0'). - * Output: - * return char * The new string or NULL on error. - */ -char *_new_StringMemString(StringMem *sm, size_t length) -{ - char *string; /* The string to be returned */ - int was_malloc; /* True if malloc was used to allocate the string */ -/* - * Check arguments. - */ - if(!sm) - return NULL; - if(length < 1) - length = 1; -/* - * Allocate the new node from the free list if possible. - */ - if(length < SM_STRLEN) { - string = (char *)_new_FreeListNode(sm->fl); - if(!string) - return NULL; - was_malloc = 0; - } else { - string = (char *) malloc(length+1); /* Leave room for the flag byte */ - if(!string) - return NULL; -/* - * Count malloc allocations. - */ - was_malloc = 1; - sm->nmalloc++; - }; -/* - * Use the first byte of the string to record whether the string was - * allocated with malloc or from the free-list. Then return the rest - * of the string for use by the user. - */ - string[0] = (char) was_malloc; - return string + 1; -} - -/*....................................................................... - * Free a string that was previously returned by _new_StringMemString(). - * - * Input: - * sm StringMem * The free-list from which the string was originally - * allocated. - * s char * The string to be returned to the free-list, or NULL. - * Output: - * return char * Always NULL. - */ -char *_del_StringMemString(StringMem *sm, char *s) -{ - int was_malloc; /* True if the string originally came from malloc() */ -/* - * Is there anything to be deleted? - */ - if(s && sm) { -/* - * Retrieve the true string pointer. This is one less than the one - * returned by _new_StringMemString() because the first byte of the - * allocated memory is reserved by _new_StringMemString as a flag byte - * to say whether the memory was allocated from the free-list or directly - * from malloc(). - */ - s--; -/* - * Get the origination flag. - */ - was_malloc = s[0]; - if(was_malloc) { - free(s); - s = NULL; - sm->nmalloc--; - } else { - s = (char *) _del_FreeListNode(sm->fl, s); - }; - }; - return NULL; -} diff --git a/libtecla-1.4.1/strngmem.h b/libtecla-1.4.1/strngmem.h deleted file mode 100644 index 5737ae0..0000000 --- a/libtecla-1.4.1/strngmem.h +++ /dev/null @@ -1,80 +0,0 @@ -#ifndef stringmem_h -#define stringmem_h -/* - * Copyright (c) 2000, 2001 by Martin C. Shepherd. - * - * All rights reserved. - * - * Permission is hereby granted, free of charge, to any person obtaining a - * copy of this software and associated documentation files (the - * "Software"), to deal in the Software without restriction, including - * without limitation the rights to use, copy, modify, merge, publish, - * distribute, and/or sell copies of the Software, and to permit persons - * to whom the Software is furnished to do so, provided that the above - * copyright notice(s) and this permission notice appear in all copies of - * the Software and that both the above copyright notice(s) and this - * permission notice appear in supporting documentation. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS - * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT - * OF THIRD PARTY RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR - * HOLDERS INCLUDED IN THIS NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL - * INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING - * FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, - * NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION - * WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - * - * Except as contained in this notice, the name of a copyright holder - * shall not be used in advertising or otherwise to promote the sale, use - * or other dealings in this Software without prior written authorization - * of the copyright holder. - */ - -typedef struct StringMem StringMem; - -/* - * Applications that dynamically allocate lots of small strings - * run the risk of significantly fragmenting the heap. This module - * aims to reduce this risk by allocating large arrays of small fixed - * length strings, arranging them as a free-list and allowing - * callers to allocate from the list. Strings that are too long - * to be allocated from the free-list are allocated from the heap. - * Since typical implementations of malloc() eat up a minimum of - * 16 bytes per call to malloc() [because of alignment and space - * management constraints] it makes sense to set the free-list - * string size to 16 bytes. Note that unlike malloc() which typically - * keeps 8 bytes per allocation for its own use, our allocator will - * return all but one of the 16 bytes for use. One hidden byte of overhead - * is reserved for flagging whether the string was allocated directly - * from malloc or from the free-list. - */ - -/* - * Set the length of each free-list string. The longest string that - * will be returned without calling malloc() will be one less than - * this number. - */ -#define SM_STRLEN 16 - -/* - * Create a string free-list container and the first block of its free-list. - */ -StringMem *_new_StringMem(const char *caller, unsigned blocking_factor); - -/* - * Delete a string free-list. - */ -StringMem *_del_StringMem(const char *caller, StringMem *sm, int force); - -/* - * Allocate an array of 'length' chars. - */ -char *_new_StringMemString(StringMem *sm, size_t size); - -/* - * Free a string that was previously returned by _new_StringMemString(). - */ -char *_del_StringMemString(StringMem *sm, char *s); - -#endif diff --git a/libtecla-1.4.1/update_html b/libtecla-1.4.1/update_html deleted file mode 100755 index 35625a7..0000000 --- a/libtecla-1.4.1/update_html +++ /dev/null @@ -1,35 +0,0 @@ -#!/bin/sh - -# Convert man pages to html files. - -cd man3 -for page in *.3;do - if [ "`wc -l $page | awk '{print $1}'`" -gt 1 ]; then - html="../html/`echo $page | sed -e 's/.3$/.html/'`" - man2html $page > $html - for ref in "libtecla(3)" "cpl_complete_word(3)" "ef_expand_file(3)" "gl_get_line(3)" "pca_lookup_file(3)" "enhance(3)"; do - link="`echo $ref | sed 's/(3)/.html/'`" - ed -s $html << EOF - 1,\$s|$ref|&|g - w - q -EOF - done - fi -done - -# Convert the change log into a web page. - -cd ../html -echo 'The tecla library change log' > changes.html -echo '
' >> changes.html
-cat ../CHANGES >> changes.html
-echo '
' >> changes.html - -# Do the same to the release-notes file. - -cd ../html -echo 'The tecla library release notes' > release.html -echo '
' >> release.html
-cat ../RELEASE.NOTES >> release.html
-echo '
' >> release.html diff --git a/libtecla-1.4.1/update_version b/libtecla-1.4.1/update_version deleted file mode 100755 index c18f714..0000000 --- a/libtecla-1.4.1/update_version +++ /dev/null @@ -1,82 +0,0 @@ -#!/bin/sh -#----------------------------------------------------------------------- -# Change the version number of the library. This changes the number in -# every file that it is known to appear in. -# -# Usage: -# update_version major minor micro -#----------------------------------------------------------------------- - -usage="$0 major minor micro" - -if [ $# -ne 3 ]; then - echo $usage - exit 1 -fi - -# Get the three components of the version number. - -major="$1" -minor="$2" -micro="$3" - -# Everything will need to be reconfigured after this change, so -# discard any existing configuration. - -make distclean 2>/dev/null - -# Check that the version components are all positive integers. - -for c in $major $minor $micro; do - if echo "$c" | awk '{exit $1 ~ /^[0-9]+$/}'; then - echo 'Version number components must all be positive integers.' - exit 1 - fi -done - -# -# Update the version number in the configure.in script. -# -ed -s configure.in << EOF -/^MAJOR_VER=\"[0-9][0-9]*\"/ s/^.*$/MAJOR_VER=\"$major\"/ -/^MINOR_VER=\"[0-9][0-9]*\"/ s/^.*$/MINOR_VER=\"$minor\"/ -/^MICRO_VER=\"[0-9][0-9]*\"/ s/^.*$/MICRO_VER=\"$micro\"/ -w -q -EOF - -if which autoconf 1>/dev/null 2>&1; then - autoconf -else - echo 'Note that autoconf needs to be run.' -fi - -# -# Update the version number in the libtecla header file script. -# -ed -s libtecla.h << EOF -/^#define TECLA_MAJOR_VER [0-9][0-9]*/ s/^.*$/#define TECLA_MAJOR_VER $major/ -/^#define TECLA_MINOR_VER [0-9][0-9]*/ s/^.*$/#define TECLA_MINOR_VER $minor/ -/^#define TECLA_MICRO_VER [0-9][0-9]*/ s/^.*$/#define TECLA_MICRO_VER $micro/ -w -q -EOF - -# -# Update the version number in the README file. -# -ed -s README << EOF -/version [0-9][0-9]*\.[0-9][0-9]*\.[0-9][0-9]* / s/version [0-9][0-9]*\.[0-9][0-9]*\.[0-9][0-9]*/version $major.$minor.$micro/ -w -q -EOF - -# -# Update the version number in the html index file. -# -ed -s html/index.html << EOF -/version [0-9][0-9]*\.[0-9][0-9]*\.[0-9][0-9]*\./ s/version [0-9][0-9]*\.[0-9][0-9]*\.[0-9][0-9]*/version $major.$minor.$micro/g -/libtecla-[0-9][0-9]*\.[0-9][0-9]*\.[0-9][0-9]*\./ s/libtecla-[0-9][0-9]*\.[0-9][0-9]*\.[0-9][0-9]*\./libtecla-$major.$minor.$micro./g -w -q -EOF diff --git a/libtecla-1.4.1/version.c b/libtecla-1.4.1/version.c deleted file mode 100644 index 9e1275e..0000000 --- a/libtecla-1.4.1/version.c +++ /dev/null @@ -1,30 +0,0 @@ -#include "libtecla.h" - -/*....................................................................... - * Return the version number of the tecla library. - * - * Input: - * major int * The major version number of the library - * will be assigned to *major. This number is - * only incremented when a change to the library is - * made that breaks binary (shared library) and/or - * compilation backwards compatibility. - * minor int * The minor version number of the library - * will be assigned to *minor. This number is - * incremented whenever new functions are added to - * the public API. - * micro int * The micro version number of the library will be - * assigned to *micro. This number is incremented - * whenever internal changes are made that don't - * change the public API, such as bug fixes and - * performance enhancements. - */ -void libtecla_version(int *major, int *minor, int *micro) -{ - if(major) - *major = TECLA_MAJOR_VER; - if(minor) - *minor = TECLA_MINOR_VER; - if(micro) - *micro = TECLA_MICRO_VER; -} diff --git a/libtecla-1.6.1/CHANGES b/libtecla-1.6.1/CHANGES new file mode 100644 index 0000000..b02c224 --- /dev/null +++ b/libtecla-1.6.1/CHANGES @@ -0,0 +1,2758 @@ +In the following log, modification dates are listed using the European +convention in which the day comes before the month (ie. DD/MM/YYYY). +The most recent modifications are listed first. + +31/10/2004 mcs@astro.caltech.edu (problem reported by Godfrey van der Linden) + getline.c + The gl_event_handler() function had the endif of a + conditional compilation clause in the wrong place. This + only upset the compiler on unusual systems that don't + have select(). The problem was seen under Mac OS X, due + to the configuration problem in 1.6.0 that caused the + configure script to mistakenly report that select wasn't + available. + +31/10/2004 mcs@astro.caltech.edu (info provided by Ivan Rayner) + configure.in configure Makefile.in + Ivan reported that under IRIX 6.5 it is necessary to add + -D_XOPEN_SOURCE=500 to the compiler flags, when compiling + the reentrant version of the library. Thus, whereas + previously I hardwired the value of DEFINES_R in + Makefile.in, I have now made this a variable in the + configure script, which is augmented with the above + addition, within an IRIX-specific switch clause. + + Also apparently configure leaves the RANLIB variable + blank, instead of setting it to ":", so I have now + explicitly set this to ":", within the new IRIX clause of + the configure script. + +31/10/2004 mcs@astro.caltech.edu (info provided by Ivan Rayner) + getline.c + Under IRIX, the compiler warned that gl_read_unmasked() + was returning an int, which was then being assigned to an + enumeration type. This is techically fine, but it + highlighted the fact that I had meant to declare + gl_read_unmasked() to directly return the enumerated + type. I have now done so. + +26/09/2004 mcs@astro.caltech.edu + getline.c + Users can now turn off interactive command-line editing + by setting the TERM environment variable to the word "dumb". + +18/07/2004 mcs@astro.caltech.edu (problem noted by Michael MacFaden) + getline.c + Calling gl_terminal_size() on a system without support + for SIGWINCH caused a divide-by-zero error in an unintended + call to gl_erase_line(), because gl_update_size() was + incorrectly being called to query the terminal size, + instead of gl_query_size(). + +18/07/2004 Padraig Brady (documented here by mcs@astro.caltech.edu) + getline.c + The suspend and termination signal-handlers installed by + gl_tty_signals(), were being installed swapped. + +03/06/2004 Mike Meaney (documented here by mcs@astro.caltech.edu) + getline.c + Mike pointed out the fact that the curses setupterm() + function is actually documented to exit the application + if an error occurs while its optional errret argument is + NULL. I hadn't noticed this, and because I didn't need + the extra information returned in the errret argument, I + was passing it a NULL. As suggested by Mike, I now pass + this argument a pointer to a dummy errret variable. + +23/05/2004 mcs@astro.caltech.edu (problem noted by John Beck) + man/func/cpl_complete_word.in + Some of the prototypes of functions and types documented + by the cpl_complete_word man page, weren't listed in the + Synopsis section of this man page. They are now listed + there. + +23/05/2004 mcs@astro.caltech.edu + getline.c man/func/gl_get_line.in + I have now added support for calling gl_normal_io() from + any callback functions that the application installs by + calling either gl_inactivity_timeout(), or gl_watch_fd(). + Previously, if one of these callback functions called + gl_normal_io(), then after returning to gl_get_line(), + gl_get_line() would incorrectly assume that the terminal + was still in raw I/O mode. Now, gl_get_line() checks to + see if gl_normal_io() was called by the callback, and + if so, calls _gl_raw_io() to reinstate raw I/O mode. + +21/05/2004 mcs@astro.caltech.edu + configure.in configure + On Mac OS X the code that the configure script used to + check for select() failed due to missing symbols in + sys/select.h. Moving the inclusion of sys/select.h to + after the inclusion of sys/time.h, sys/types.h and + sys/unistd.h fixed this. + +11/05/2004 mcs@astro.caltech.edu + getline.c man/func/gl_get_line.in + If the line buffer returned by one call to gl_get_line() + was passed as the start_line argument of the next call to + gl_get_line(), then instead of the just-entered line + being presented back to the user for further editing, the + start_line argument was effectively ignored, because the + line buffer whose pointer was being passed back, was + being cleared before the start_line pointer was examined. + This appears to have been a case of me incorrectly + thinking that I had forgotten to initialize gl->line[] + and gl->ntotal in the gl_reset_input_line() function, and + then "fixing" this supposed omission. Removing this + erroneous fix, restored things to how they were meant to + be. To make it unlikely that I will make the same mistake + again, I have renamed the function from + gl_reset_input_line() to gl_reset_editor(), to stop it + looking as though it is meant to reset the contents of + the input line (that is what gl_truncate_buffer() is + for), explicitly stated that it doesn't clear the input + line, in the header comments of the function, and added a + prominent warning comment in the body of the function. + + Also, since support for passing back the returned line + pointer via the start_line argument of the next call to + gl_get_line(), wasn't documented in the man page, but was + meant to be supported, and definitely used to work, I + have now amended the man page documentation of + gl_get_line() to explicitly state that this feature is + officially supported. + +2?/04/2004 Released 1.6.0 + +22/04/2004 mcs@astro.caltech.edu (Fixed a bug reported by John Beck) + getline.c + When an error, signal, or other abnormal event aborted + gl_get_line(), the cleanup code that restored the + terminal to a sane state, also overwrote the value of + errno that was associated with the aborting event. An + I/O error occurring in the cleanup code would have also + overwritten the value to be returned by + gl_return_status(), and thus remove any possibility of + the caller finding out what really caused gl_get_line() + to abort. I have now written a new internal function + called, gl_record_status(), which records the completion + status to be returned by gl_return_status(), and the + value to assign to errno just before gl_get_line() + returns. This is called wherever code detects conditions + that require gl_get_line() to return early. The function + ensures that once an abnormal completion status has been + recorded for return, subsequent completions statuses + aren't recorded. This ensures that the caller sees the + original cause of the abnormal return, rather than any + error that occurs during cleaning up from this before + return. + +17/04/2004 mcs@astro.caltech.edu + getline.c + If an application's callback called gl_read_char() after + calling gl_normal_io(), it would inappropriately + redisplay the input line, when it called _gl_raw_io() to + temporarily switch the terminal back into raw mode. + + To fix this, _gl_raw_io() now takes a new 'redisplay' + argument, which specifies whether or not to queue a + redisplay of the input line. I also created a new + gl->postpone flag, which is set by gl_normal_io(), and + cleared by _gl_raw_io() (when its redisplay argument is + true). When this flag is set, gl_flush_output() ignores + queued redisplays, as it generally should between calls + to gl_normal_io() and gl_raw_io(). Thus its effect is to + postpone redisplays while line editing is suspended. + +11/04/2004 mcs@astro.caltech.edu + history.c man/misc/tecla.in + History searches can now include the globbing operators + *, ?, []. When a search prefix is found to have at least + one of these characters, then only history lines that + completely match that pattern are returned. + +11/04/2004 mcs@astro.caltech.edu (issue raised by Mark Coiley) + getline.c ioutil.c + There appears to be a bug in Solaris's terminal I/O. + When the terminal file descriptor is placed in + non-blocking I/O mode, and the terminal is switched from + canonical to raw mode, characters that were previously + entered in canonical I/O mode don't become available to + be read until the user types one character more. Select() + incorrectly says that there are no characters available, + and read() returns EAGAIN. This is only a problem for + gl_get_line() when gl_get_line() is in non-blocking + server I/O mode, so most users won't have experienced any + problems with this. + + The only way that I have found to get read() to return + the characters, without the user first having to type + another character, is to turn off non-blocking I/O before + calling read(). Select() still claims that there are no + characters available to be read, but read happily returns + them anyway. Fortunately, one can perform non-blocking + terminal reads without setting the non-blocking I/O flag + of the file descriptor, simply by setting the VTIME + terminal attribute to zero (which I already was + doing). Thus, when in non-blocking server I/O, I now turn + off the non-blocking I/O flag, attempt to read one + character and only if this fails, do I then call the + select() based event handler to implement any configured + non-zero timeout, before attempting the read again. Of + course the non-blocking I/O flag is still needed for + writing, so I only turn it off temporarily while reading. + +25/03/2004 mcs@astro.caltech.edu (bug reported by Gregory Harris) + Makefile.in + It appears that when in February, I patched Makefile.in + to add abolute paths to the install-sh shell-script, + I accidentally replaced install-sh with install.sh. I + corrected the name in the Makefile. + +25/03/2004 Gregory Harris (documented here by mcs) + configure.in configure + Greg added the configuration parameters needed to build + the shared version of the libtecla library under FreeBSD. + +25/03/2004 mcs@astro.caltech.edu + getline.c libtecla.h libtecla.map man/func/gl_get_line.in + man/func/gl_read_char.in + I wrote a public function called gl_read_char(). Unlike + gl_query_char(), this function neither prompts the user + for input, nor displays the character that was entered. + In fact it doesn't write anything to the terminal, and + takes pains not to disturb any incompletely entered + input line, and can safely be called from application + callback functions. + +21/03/2004 mcs@astro.caltech.edu + getline.c libtecla.h libtecla.map man/func/gl_get_line.in + man/func/gl_query_char.in + I wrote a public function called gl_query_char(), which + prompts the user and awaits a single-character reply, + without the user having to hit return. + +23/02/2004 mcs@astro.caltech.edu (bug reported by Gregory Harris) + configure.in configure getline.c enhance.c demo3.c + The configure script now checks for the sys/select.h + header file, and arranges for a C macro called + HAVE_SYS_SELECT_H to be set if it exists. Thus the files + that use select() now use this macro to conditionally + include sys/select.h where available. Apparently this + header is required under FreeBSD 5.1. + +23/02/2004 mcs@astro.caltech.edu + getline.c libtecla.h man/func/gl_get_line.in + I wrote two new public functions, gl_append_history() and + gl_automatic_history(). Together these allow the + application to take over the responsibility of adding + lines to the history list from gl_get_line(). I then + documented their functionality in the gl_get_line man + page. + Version 1.6.0 + I incremented the minor version number of the library, to + comply with the requirement to do so when additions are + made to the public interface. See libtecla.map for + details. + libtecla.map + I added a new 1.6.0 group for the new minor version, and + added the above pair of functions to it. + +15/02/2004 mcs@astro.caltech.edu (fixes a bug reported by Satya Sahoo) + history.c + Calling gl_load_history() multiple times, eventually led + to a segmentation fault. This was due to the head of the + list of unused history string segments not getting + reset when the history buffer was cleared. While + debugging this problem I also noticed that the history + resizing function was way too complicated to verify, so + after fixing the above bug, I heavily simplified the + history resizing function, trading off a small reduction + in memory efficiency, for greatly improved clarity, and + thus made it much more verifiable and maintainable. + +14/02/2004 mcs@astro.caltech.edu (fixes a bug reported by Tim Burress). + getline.c + If gl_change_terminal() was first used to tell + gl_get_line to read input from a file, then called later + to tell it to read subsequent input from a terminal, no + prompt would be displayed for the first line of + interactive input. The problem was that on reaching the + end of the input file, gl_get_line() should have called + gl_abandon_line(), to tell the next call to gl_get_line() + to start inputting a new line from scratch. I have added + this now. + +14/02/2004 Krister Walfridsson (documented here by mcs@astro.caltech.edu) + Makefile.in + Krister noticed that I had failed to put $(srcdir)/ in front + of some invokations of install.sh. I have remedied this. + config.guess config.sub + I hadn't updated these for a long time, so apparently they + didn't recognise the BSD system that Krister was using. + I have now updated them to the versions that come with + autoconf-2.59. + +22/01/2004 mcs@astro.caltech.edu + keytab.c + When parsing key-binding specifications, backslash escaped + characters following ^ characters were not being expanded. + Thus ^\\ got interpretted as a control-\ character followed + by a \ character, rather than simply as a control-\ + character. + +12/01/2004 mcs@astro.caltech.edu + cplfile.c cplmatch.c demo2.c demo3.c demo.c direader.c + expand.c getline.c history.c homedir.c pathutil.c pcache.c + configure.in configure INSTALL + The configuration script now takes a + "--without-file-system" argument. This is primarily for + intended for embedded systems that either don't have + filesystems, or where the file-system code in libtecla is + unwanted bloat. It sets the WITHOUT_FILE_SYSTEM + macro. This removes all code related to filesystem + access, including the entire public file-expansion, + file-completion and path-lookup facilities. Note that the + general word completion facility is still included, but + without the normally bundled file completion + callback. Actually the callback is still there, but it + reports no completions, regardless of what string you ask + it to complete. + + This option is described in the INSTALL document. + +12/01/2004 mcs@astro.caltech.edu + getline.c configure.in configure INSTALL + The configuration script now takes a + "--without-file-actions" argument. This allows an + application author/installer to prevent users of + gl_get_line() from accessing the filesystem from the + builtin actions of gl_get_line(). It defines a macro + called HIDE_FILE_SYSTEM. This causes the + "expand-filename", "read-from-file", "read-init-files", + and "list-glob" action functions to be completely + removed. It also changes the default behavior of actions + such as "complete-word" and "list-or-eof" to show no + completions, instead of the normal default of showing + filename completions. + + This option is described in the INSTALL document. + +11/01/2004 mcs@astro.caltech.edu + getline.c man/func/gl_get_line.in + In case an application's customized completion handler + needs to write to the terminal for some unforseen reason, + there needs to be a way for the it to cleanly suspend raw + line editing, before writing to the terminal, and the + caller then needs to be aware that it may need to + resurrect the input line when the callback returns. I + have now arranged that the completion callback functions + can call the gl_normal_io() function for this purpose, + and documented this in the gl_get_line() man page. + +11/01/2004 mcs@astro.caltech.edu (In response to a bug report by Satya Sahoo) + getline.c + The gl_configure_getline() function makes a malloc'd copy + of the names of the configuration files that it is asked + to read. Before the bug fix, if the application made one + or more calls to this function, the memory allocated by + the final call that it made before calling del_GetLine(), + wasn't being freed. Note that memory allocated in all but + the final call was being correctly freed, so the maximum + extent of the memory leak was the length of the file + name(s) passed in the final call to + gl_configure_getline(), and an application that didn't + call gl_configure_getline() didn't suffer any leak. + +20/12/2003 mcs@astro.caltech.edu + history.c + Ellen tested the history fix that I reported below, and + pointed out that it still had a problem. This turned out + to be because getline.c was making some incorrect + assumptions about the new behavior of history.c. This + problem and the previous one both revolved around how + search prefixes were stored and discarded, so I have now + re-written this part of the code. Previously the search + prefix was retained by looking for a line with that + prefix, and keeping a pointer to that line. This saved + memory, compared to storing a separate copy of the + prefix, but it led to all kinds of hairy + interdependencies, so I have now changed the code to keep + a separate copy of search prefixes. To keep the memory + requirements constant, the search prefix is stored in the + history buffer, like normal history lines, but not + referenced by the time-ordered history list. The prefix + can now be kept around indefinitely, until a new search + prefix is specified, regardless of changes to the + archived lines in the history buffer. This is actually + necessary to make the vi-mode re-search actions work + correctly. In particular, I no longer discard the search + prefix whenever a history search session ends. Also, + rather than have getline.c keep its own record of when a + history session is in progress, it now consults + history.c, so that failed assumptions can't cause the + kind of discrepancy that occurred before. For this to + work, getline.c now explicitly tells history.c to cancel + search sessions whenever it executes any non-history + action. + +14/12/2003 mcs@astro.caltech.edu (bug reported by Ellen Oschmann) + history.c + If one searched backwards for a prefix, then returned to + the original line, changed that line, then started + another backwards prefix search, getline incorrectly + discarded the new search prefix in the process of + throwing away its cached copy of the previous pre-search + input line. In other words getline was belatedly + cancelling a previous search, after a new search had + already partially begun, and thus messed up the new + search. The obvious fix was to arrange for the current + search to be cancelled whenever the history pointer + returns to its starting point, rather than waiting for + the next search to begin from there. + +14/12/2003 mcs@astro.caltech.edu + history.c + _glh_recall_line() was returning the last line in the + history buffer instead of the line requested by the + caller. This only affected the obscure "repeat-history" + action-function, which probably isn't used by anybody. + +09/12/2003 Version 1.5.0 released. + +28/09/2003 mcs@astro.caltech.edu + homedir.c + When the home directory of the login user is requested, + see if the HOME environment variable exists, and if so + return its value, rather than looking up the user's home + directory in the password file. This seems to be the + convention adopted by other unix programs that perform + tilde expansion, and it works around a strange problem, + where a third-party libtecla program, statically compiled + under an old version of RedHat, unexpectedly complained + that getpwd() returned an error when the program was run + under RedHat 9. + +01/09/2003 mcs@astro.caltech.edu + getline.c libtecla.h libtecla.map man/func/gl_get_line.in + man/func/gl_register_action.in. + It is now possible for an application to register + external functions as action functions. These actions are + initially bound to specified key-sequences, but if they + are registered before the user's configuration file is + loaded, they can also be re-bound by the user to + different key-sequences. The function used to register a + new action, is called gl_register_action(). Action + functions are passed a readonly copy of the input line + and the cursor position. They can display text to the + terminal, or perform other operations on the application + environment. Currently, they can't edit the input line or + move the cursor. This will require the future addition of + functions to queue the invokation of the built-in action + functions. + +26/08/2003 mcs@astro.caltech.edu + getline.c + I modified gl_update_buffer() to ensure that the cursor + stays within the input line after external line + modifications, and to queue a redisplay of the + potentially modified input line. + +21/07/2003 mcs@astro.caltech.edu + configure.in configure Makefile.in Makefile.stub INSTALL + By specifying --without-man-pages or --with-man-pages=no + as command-line arguments to the configure script, it is + now possible to have the configure script skip the + man-page preprocessing step, and arrange for the man-page + installation targets in the Makefile to do nothing. This + option is designed for people who embed libtecla within + other packages. It is also used by Makefile.stub when + the distclean target is specified. + +21/07/2003 mcs@astro.caltech.edu + configure.in configure + The previous workaround for recent versions of gcc + placing /usr/local/include at the start of the system + inlcude-file search path, broke something else. The fix + placed /usr/include before gcc's include area, which + meant that gcc's modified version of stdarg.h was being + ignored in deference to the version in /usr/include. I + have changed the fix to have gcc report the search path, + then have awk add options to CFLAGS to reorder this path, + plaing /usr/local/include at the end. + + Also, under Solaris 9, including term.h without first + including curses.h results in complaints about undefined + symbols, such as bool. As a result the configure script's + test for term.h was failing. I have now modified it to + include curses.h in the test code that it uses to check + for term.h. In the process I also improved the tests for + curses.h and term.h to prevent an ncurses version of + term.h from being used with the system-default version of + curses.h. + +29/06/2003 mcs@astro.caltech.edu + Makefile.in direader.c homedir.c + On some systems (eg. linux) the _POSIX_C_SOURCE + feature-test macro is set by system headers, rather than + being an option set by a project's Makefile at + compilation time. In software, such as tecla, where the + definition of this macro is used as an indication of + whether to use the non-reentrant or reentrant versions of + system functions, this means that the reentrant functions + are always used, regardless of whether this macro is set + or not by the project Makefile. Thus, on such systems the + reentrant and non-reentrant versions of the tecla library + are essentially identical. This has a couple of + drawbacks. First, since thread-safe functions for + traversing the password file don't exist, the supposedly + non-reentrant version of the tecla library can't support + ambiguous tab-completion of usernames in ~username/ + constructions. Secondly, on some systems the use of + reentrant system functions dictates the use of a shared + library that isn't needed for the non-reentrant + functions, thus making it more difficult to distribute + binary versions of the library. + + To remedy this situation I have modified the DEFINES_R + variable in Makefile.in to arrange for the compiler to + define a C macro called PREFER_REENTRANT when it is + compiling the reentrant version of the tecla library. + This macro is now used in the source code to determine + when to require reentrant code. Whithin the source code, + wherever a potentially non-reentrant interface is used, + the existance of both this macro and a suitably valued + _POSIX_C_SOURCE macro, are tested for to see if a + reentrant alternative to the problem code should be used. + +22/06/2003 mcs@astro.caltech.edu + getline.c + I changed the way that redisplays are requested and + performed. Redisplays are now queued by calling + gl_queue_redisplay(), and subsequently performed by + gl_flush_output(), when the queue of already pending + output has been completely dispatched. This was necessary + to prevent event handlers from filling up the output + queue with redisplays, and it also simplifies a number of + things. In the process I removed the gl_queue_display() + function. I also wrote a gl_line_erased() function, which + is now called by all functions that erase the input + line. I also split the gl_abandon_line() function into + public and private callable parts, and used the private + version internally to arrange to discard the input line + after errors. + + The raw_mode flag was not being initialized by new_GetLine(). + It is now initialized to zero. + + I removed the zapline flag, since using the endline flag to + communicate the desire to terminate the line, did the same + thing. + + gl_terminal_move_cursor() now does nothing when the input + line isn't displayed. + +18/03/2003 mcs@astro.caltech.edu + getline.c + Fixed bug which was causing newlines not to be output + at the end of each newly entered line. I was + interpreting the gl->endline flag in conflicting ways in + two places. To fix this I have created a gl->displayed + flag. This flags whether an input line is currently + displayed. + +17/03/2003 mcs@astro.caltech.edu + getline.c libtecla.h man/func/gl_get_line.in + man/func/gl_erase_terminal.in libtecla.map + I added a new function that programs can call to clear + the terminal between calls to gl_get_line(). + +11/03/2003 mcs@astro.caltech.edu + configure.in configure + Under linux when _POSIX_C_SOURCE is defined, getpwent() + and associated functions become undefined, because + _SVID_SOURCE and _BSD_SOURCE become undefined. Adding + these feature macros back to CFLAGS resolves this. + +06/03/2003 mcs@astro.caltech.edu + getline.c libtecla.map man/func/gl_get_line.in + Following the lead of Edward Chien, I wrote a function + called gl_bind_keyseq(), which binds a specified + key-sequence to a given action, or unbinds the + key-sequence. + +24/02/2003 mcs@astro.caltech.edu + getline.c libtecla.map man/func/cpl_complete_word.in + I implemented a simple function called + cpl_recall_matches(). This recalls the return value of + the last call to cpl_complete_word(). + +19/01/2003 mcs@astro.caltech.edu + getline.c + The documented signal handling, fd event-handling, + inactivity timeout handling, and server-mode non-blocking + I/O features are now implemented for non-interactive + input streams, such as pipes and files. + +19/01/2003 mcs@astro.caltech.edu + getline.c libtecla.h man/func/gl_get_line.in demo3.c + I added a new return status enumerator to report + when an end-of-file condition causes gl_get_line() + to return NULL. + +13/01/2003 mcs@astro.caltech.edu + history.c + I rewrote the history facility. The previous + circular buffer implementation was a nightmare to change, + and it couldn't efficiently support certain newly + requested features. The new implementation stores history + lines in linked lists of fixed sized string segments, + taken from the buffer, with each line being reference + counted and recorded in a hash table. If the user enters + a line multiple times, only one copy of the line is now + stored. Not only does this make better use of the + available buffer space, but it also makes it easy to + ensure that a line whose prefix matches the current + search prefix, isn't returned more than once in sequence, + since we can simply see if the latest search result has + the same hash-table pointer as the previous one, rather + than having to compare strings. Another plus is that due + to the use of linked lists of nodes of fixed size line + segments, there is no longer any need to continually + shuffle the contents of the buffer in order to defragment + it. As far as the user is concerned, the visible + differences are as follows: + + 1. If the user enters a given line multiple times in a + row, each one will be recorded in the history list, + and will thus be listed by gl_show_history(), and + saved in the history file. Previously only one line + was recorded when consecutive duplicates were entered. + This was a kludge to prevent history recall from + recalling the same line multiple times in a row. This + only achieved the desired result when not recalling by + prefix. + + 2. Not only simple recall, but prefix-based history line + recalls now don't return the same line multiple times + in a row. As mentioned in (1) above, previously this + only worked when performing a simple recall, without a + search prefix. + +28/12/2002 mcs@astro.caltech.edu + getline.c + The one-line function, gl_buff_curpos_to_term_curpos() + was only being used by gl_place_cursor(), so I inlined it + in that function, and removed it. + +28/12/2002 mcs@astro.caltech.edu + getline.c + gl_suspend_process() was calling the application-level + gl_normal_io() and gl_raw_io() functions, where it should + have been calling the internal versions _gl_normal_io() + and _gl_raw_io(). + Also gl_handle_signal() was masking and unmasking just + the signals of the first element of the gl[] array + argument. It now masks and unmasks all trappable signals. + +28/12/2002 mcs@astro.caltech.edu + getline.c + Now that the number of terminal characters used to + display the current input line, is recorded, the relative + line on which the last character of the input line + resides can be determined without having to call + gl_buff_curpos_to_term_curpos(). This is now used by + gl_normal_io() via gl_start_newline(), so there is now no + need for gl_buff_curpos_to_term_curpos() to be + async-signal safe. I have thus removed the annoying + gl->cwidth[] array, and gl_buff_curpos_to_term_curpos() + now calls gl_width_of_char() directly again. There is + also now no need for the gl_line_of_char_start() and + gl_line_of_char_end() functions, so I have removed them. + +28/12/2002 mcs@astro.caltech.edu + getline.c + Unfortunately it turns out that the terminfo/termcap + control sequence which is defined to delete everything + from the current position to the end of the terminal, is + only defined to work when at the start of a terminal + line. In gnome terminals in RedHat 8.0, if it is used + within a terminal line, it erases the whole terminal + line, rather than just what follows the cursor. Thus to + portably truncate the displayed input line it is + necessary to first use the control sequence which deletes + from the cursor position to the end of the line, then if + there are more terminal lines, move to the start of the + next line, and use the delete to end-of-terminal control + sequence, then restore the cursor position. This requires + that one know how many physical terminal lines are used + by the current input line, so I now keep a record of the + number of characters so far displayed to the terminal + following the start of the prompt, and the new + gl_truncate_display() function uses this information to + truncate the displayed input line from the current cursor + position. + +28/12/2002 mcs@astro.caltech.edu + getline.c + gl_start_newline() now moves to an empty line following + the input line, rather than just to the next line. It + also arranges for the input line to be redisplayed before + editing resumes. A major user of this is gl_print_info(), + which now need not be followed by an explicit call to + gl_redisplay(), since the terminal input loop in + gl_get_input_line() ensures that gl_redisplay() is called + after any action function that asserts gl->redisplay. + Also, all functions that erase the displayed input line + can now call the gl_erase_line() function, which is + designed to work correctly even when a terminal resize + invalidates the horizontal cursor position. Finally, the + new gl_queue_display() function is now used by functions + that need to arrange for the input line to be displayed + from scratch after the displayed line has been erased or + invalidated by other text being written to the terminal. + All of these changes are aimed at reducing the number of + places that directly modify gl->term_curpos and + gl->redisplay. + +22/12/2002 Markus Gyger (logged here by mcs) + Makefile.in update_html + In places where echo and sed were being used to extract + the base names of files, Markus substituted the basename + command. He also replaced explicit cp and chmod commands + with invokations of the install-sh script. + configure.in + Use $target_os and $target_cpu, where appropriate, + instead of $target. + configure.in + The Solaris man function and library man pages should + be in sections 3lib and 3tecla respectively, only in + Solaris version 2.8 and above. + configure.in + Markus provided values for the man page configuration + variables for HPUX. + man/*/*.in + I had missed parameterizing man page section numbers in + the man page titles, Markus corrected this. + man/func/libtecla_version.in + Fixed incorrect section number in the link to the + libtecla man page. + homedir.c + When compiled to be reentrant, although one can't use the + non-reentrant getpwent() function to scan the password + file for username completions, one can at least see if + the prefix being completed is a valid username, and if + the username of the current user minimally matches the + prefix, and if so list them. I simplified Markus' + modification by adding a prefix argument to the + _hd_scan_user_home_dirs() function, and redefining the + function description accordingly, such that now it + reports only those password file entries who's usernames + minimally match the specified prefix. Without this, it + would have been necessary to peak inside the private data + argument passed in by cf_complete_username(). + Markus also provided code which under Solaris uses the + non-reentrant interfaces if the reentrant version of the + library isn't linked with the threads library. + +19/12/2002 mcs@astro.caltech.edu + Makefile.in + Markus pointed out that LDFLAGS was being picked up by + the configure script, but not then being interpolated + into te Makefile. I have thus added the necessary + assignment to Makefile.in and arranged for the value of + LDFLAGS to be passed on to recursive make's. I also did + the same for CPPFLAGS, which had also been omitted. + +18/12/2002 mcs@astro.caltech.edu + man/* man/*/* configure.in configure Makefile.in + update_html + It turns out that the assignment of man page sections to + topics differs somewhat from system to system, so this is + another thing that needs to be configured by the main + configuration script, rather than being hardwired. All + man pages have now been moved into suitably named + topic-specific sub-directories of the top-level man + directory, and instead of having a numeric suffix, now + have the .in suffix, since they are now preprocessed by + the configure script, in the same fashion as Makefile.in. + Whithin these *.in versions of the man pages, and within + Makefile.in, the installation subdirectory (eg. man1) and + the file-name suffix (eg. 1), are written using + configuration macros, so that they get expanded to the + appropriate tokens when the configure script is run. In + principle, the man pages could also take advantage of + other configuration macros, such as the one which expands + to the library installation directory, to include full + path names to installed files in the documentation, so in + the future this feature could have more uses than just + that of parameterizing man page sections. + +18/12/2002 mcs@astro.caltech.edu + man3 man3/* Makefile.in html/index.html update_html + Markus suggested splitting the gl_get_line(3) man page + into user and developer sections, and also pointed out + that the enhance man page should be in section 1, not + section 3. I have thus created a top-level man + directory in which to place the various sections, and + moved the man3 directory into it. The enhance.3 man page + is now in man/man1/enhance.1. I have extracted all + user-oriented sections from the gl_get_line(3) man page + and placed them in a new man7/tecla.7 man page. + +18/12/2002 mcs@astro.caltech.edu + getline.c + Terminal resizing was broken in normal mode, due to + me forcing the terminal cursor position to zero in the + wrong place in gl_check_caught_signal(). + +14/12/2002 Markus Gyger (logged here by mcs) + configure.in configure + Under Solaris, recent versions of gcc search + /usr/local/include for header files before the system + directories. This caused a problem if ncurses was + installed under Solaris, since the termcap.h include file + in /usr/local/include ended up being used at compile + time, whereas the system default version of the curses + library was used at link time. Since the two libraries + declare tputs() differently, this evoked a complaint from + gcc. Markus came up with a way to force Gnu cpp to move + /usr/local/include to the end of the system-include-file + search path, where it belongs. + +13/12/2002 mcs@astro.caltech.edu + man3/gl_io_mode.3 + I rewrote the man page which documents the new non-blocking + server I/O mode. + +12/12/2002 mcs@astro.caltech.edu + demo3.c + I wrote a new version of demo3.c, using signal handlers + that call gl_handle_signal() and gl_abandon_line(), where + previously in this demo, these functions were called from + the application code. + +05/12/2002 mcs@astro.caltech.edu + getline.c + gl_normal_io(), gl_raw_io() and gl_handle_signal() and + gl_abandon_line() are now signal safe, provided that + signal handlers that call them are installed with sa_mask's + that block all other signals who's handlers call them. + This is the case if gl_tty_signals() is used to install + signal handlers that call any of these functions. + + A major stumbling block that had to be overcome was that + gl_displayed_char_width() calls isprint(), which can't + safely be called from a signal handler (eg. under linux, + the is*() functions all use thread-specific data + facilities to support per-thread locales, and the + thread-specific data facilities aren't signal safe). To + work around this, all functions that modify the + input-line buffer, now do so via accessor functions which + also maintain a parallel array of character widths, for + use by gl_buff_curpos_to_term_curpos() in place of + gl_displayed_char_width(). Other minor problems were the + need to avoid tputs(), who's signal safety isn't defined. + +05/12/2002 Eric Norum (logged here by mcs@astro.caltech.edu) + configure.in + Eric provided the configuration information needed + to build shared libraries under Darwin (Max OS X). + +05/12/2002 Richard Mlynarik (logged here by mcs@astro.caltech.edu) + configure.in + AC_PROG_RANLIB gets the wrong version of ranlib when + cross compiling, so has now been replaced by an + invokation of AC_CHECK_TOOL. In addition, AC_CHECK_TOOL + is also now used to find an appropriate version of LD. + +05/12/2002 mcs@astro.caltech.edu (based on patch by Pankaj Rathore) + getline.c libtecla.h libtecla.map man3/gl_get_line.3 + The new gl_set_term_size() function provides a way + to tell gl_get_line() about changes in the size of + the terminal in cases where the values returned by + ioctl(TIOCGWINSZ) isn't correct. + +05/12/2002 mcs@astro.caltech.edu + getline.c + Rather than calling sprintf() to see how much space would + be needed to print a given number in octal, I wrote a + gl_octal_width() function, for use by + gl_displayed_char_width(). This makes the latter + function async signal safe. + +05/12/2002 mcs@astro.caltech.edu + chrqueue.c + Whenever the buffer is exhausted, and getting a new + buffer node would require a call to malloc(), attempt + to flush the buffer to the terminal. In blocking I/O + mode this means that the buffer never grows. In + non-blocking I/O mode, it just helps keep the buffer + size down. + +05/12/2002 mcs@astro.caltech.edu + freelist.h freelist.c + The new _idle_FreeListNodes() function queries the + number of nodes in the freelist which aren't currently + in use. + +05/12/2002 mcs@astro.caltech.edu + Makefile.stub + This now accepts all of the targets that the configured + makefile does, and after configuring the latter makefile, + it invokes it with the same options. + +03/12/2002 mcs@astro.caltech.edu + mans3/gl_io_mode.3 + I completed the man page for all of the new functions + related to non-blocking I/O. + +01/12/2002 mcs@astro.caltech.edu + man3/gl_get_line.3 + I wrote a long section on reliable signal handling, + explaining how gl_get_line() does this, how to make + use of this in a program, and how to handle signals + reliably when faced with other blocking functions. + This basically documents what I have learnt about + signal handling while working on this library. + +01/12/2002 mcs@astro.caltech.edu + getline.c man3/gl_get_line.3 + In non-blocking server mode, the gl_replace_prompt() + function can now be used between calls to gl_get_line() + if the application wants to change the prompt of the + line that is being edited. + +01/12/2002 mcs@astro.caltech.edu + man3/gl_get_line.3 + I documented the new gl_return_status() and + gl_error_message() functions. + +01/12/2002 mcs@astro.caltech.edu + getline.c man3/gl_get_line.3 + Added SIGPOLL and SIGXFSZ to the list of signals that + are trapped by default. These are process termination + signals, so the terminal needs to be restored to a + usable state before they terminate the process. + +27/11/2002 mcs@astro.caltech.edu + getline.c libtecla.h + Completed the essential changes needed to support + non-blocking server-I/O mode. + + The new gl_io_mode() function allows one to switch to + and from non-blocking server-I/O mode. + + The new gl_raw_io() function is used in non-blocking + server-I/O mode to switch the terminal into non-blocking + raw I/O mode. + + The new gl_normal_io() function is used in non-blocking + server-I/O mode to switch the restore the terminal to + a normal, blocking state. This is used to suspend line + input before suspending the process or writing messages + to the terminal. + + The new gl_tty_signals() function installs specified + signals handlers for all signals that suspend, terminate + or resume processes, and also for signals that indicate + that the terminal has been resized. This not only saves + the application from having to keep its own ifdef'd list + of such signals, of which there are many, but it also + makes sure that these signal handlers are registered + correctly. This includes using the sa_mask member of each + sigaction structure to ensure that only one of these + handlers runs at a time. This is essential to avoid the + signal handlers all trying to simultaneously modify + shared global data. + + The new gl_handle_signal() function is provided for + responding (from application level) to signals caught by + the application. It handles process suspension, process + termination and terminal resize signals. + + The new gl_pending_io() function tells the application + what direction of I/O gl_get_line() is currently waiting + for. + + In non-blocking server I/O mode, the new + gl_abandon_line() function can be called between calls to + gl_get_line() to discard an input line and force the next + call to gl_get_line() to start the input of a new line. + + Also, in non-blocking server-I/O gl_get_line() doesn't + attempt to do anything but return when one of the signals + that it is configured to catch is caught. This is + necessary because when in this mode, the application is + required to handle these signals when gl_get_line() is + running, and the default configuration of most of these + signals in gl_get_line() is to restore the terminal then + call the application signal handlers. This would be a + case of too many cooks spoiling the broth, so in this + mode, gl_get_line() always defers to the application's + signal handlers. + +26/11/2002 mcs@astro.caltech.edu + getline.c libtecla.h + I implemented a couple of new functions to support + reliable signal handling, as now documented + (see above) in the gl_get_line(3) man page. + + The new gl_catch_blocked() function tells gl_get_line() + to unblock all configured signals around calls to + long-running functions, not only those that aren't + blocked when gl_get_line() is called. This allows + the caller to implement reliable signal handling, + since the unblocking is only done from within code + protected by sigsetjmp(), which avoids race conditions. + + The new gl_list_signals() function fills a provided + sigset_t with the set of signals that gl_get_line() is + currently configured to catch. This allows callers to + block said signals, such that they are only unblocked by + gl_get_line() when it is waiting for I/O. When used in + conjunction with the gl_catch_blocked() function, this + removes the potential for race conditions. + + Also, when gl_get_line() installs its signal handler, + it uses the sa_mask member of the sigaction structure + to ensure that only one instance of this signal handler + will ever be executing at a time. + +25/11/2002 mcs@astro.caltech.edu (bug reported by Pankaj Rathore) + getline.c + When any history recall action was invoked when the + input line buffer was full, an error message would be + displayed complaining about the length of the string + in the line input buffer being inconsistent with the + specified allocated size. This was because instead of + sending the allocated size of the input line, I was + sending the length excluding the element that is + reserved for the '\0' terminator. Sending it the + correct size corrected the problem. + +24/11/2002 mcs@astro.caltech.edu + getline.c + All public functions which take GetLine objects as + arguments now block signals on entry and restore the + signal mask on return. This was an attempt to make it + safe to call getline functions from signal handlers, but + the fact is that the functions that I really wanted this + to apply to, potentially call malloc(), so this currently + isn't the case. + +23/11/2002 mcs@astro.caltech.edu + getline.c libtecla.h + The new gl_return_status() function returns an enumerated + return status which can be used to query what caused + gl_get_line() to return. + +22/11/2002 mcs@astro.caltech.edu + Most existing .c and .h files, plus errmsg.c errmsg.h + Makefile.rules + Until now, many library functions would report error + messages to stderr. This isn't appropriate for library + functions, so in place of this behavior, error messages + are now recorded in internal ErrMsg objects, and passed + between modules via new module-specific error querying + functions. In addition, errno is now set appropriately. + Thus when gl_get_line() and related functions return an + error, strerror() can be used to look up system errors, + and gl_error_message() can be used to recover a higher level + error message. Note that error messages that are + responses to user actions continue to be reported to the + terminal, as before. + +21/11/2002 mcs@astro.caltech.edu + getline.c keytab.h keytab.c Makefile.rules + I wrote a new version of _kt_lookup_binding() that didn't + require the caller to have access to the innards of a + KeyTab object. This then enabled me to move the definition + of KeyTab objects into keytab.c and make the typedef in + keytab.h opaque. Many nested includes were also moved from + keytab.h into keytab.c. + +05/11/2002 mcs@astro.caltech.edu + getline.c libtecla.map libtecla.h demo3.c + I split the old gl_resize_terminal() function into + two parts, gl_query_size() and gl_update_size(), with + the latter calling the former to get the new terminal + size. + +05/11/2002 mcs@astro.caltech.edu + getline.c + I fixed a long time bug in the terminal resizing code. + When the cursor wasn't on the last terminal line of the + input line, the resizing code would redisplay the + the line one or more lines above where it should be + restored. This was due to an error in the calculation of + the number of lines above the cursor position. + +04/11/2002 mcs@astro.caltech.edu + demo.c demo2.c demo3.c + I used the new gl_display_text() function to display + introductory text at the startup of each of the demo + programs. The text is enclosed within a box of asterixes, + drawn dynamically to fit within the confines of the + available terminal width. + +04/11/2002 mcs@astro.caltech.edu + libtecla.h getline.c ioutil.c ioutil.h Makefile.rules + libtecla.map man3/gl_get_line.3 man3/gl_display_text.3 + Needing a way to display introductory text intelligently + in the demo programs, I wrote and documented the + gl_display_text() function. This justifies arbitrary + length text within the bounds of the terminal width, + with or without optional indentation, prefixes and + suffixes. + +03/11/2002 mcs@astro.caltech.edu + demo3.c Makefile.rules + I wrote a new demonstration program. This program acts + exactly like the main demonstration program, except that + it uses an external event loop instead of using the + gl_get_line() internal event loop. This is thus an example + of the new non-blocking server I/O facility. + +02/11/2002 mcs@astro.caltech.edu + getline.c keytab.c keytab.h libtecla.h man3/gl_get_line.3 + man3/gl_completion_action.3 + I added the ability to register additional word + completion actions via the new function + gl_completion_action(). All action functions now take a + new (void *data) argument, which is stored with the + function in the symbol table of actions. The new + gl_completion_action() function uses this feature to + record dynamically allocated objects containing the + specified completion function and callback data along + with either the gl_complete_word() action function, or + the gl_list_completions() action function. These two + actions continue to use the builtin completion functions + when their data pointer is NULL. + +20/10/2002 mcs@astro.caltech.edu + The following are changes merged from the non-blocking + gl_get_line() development branch. + + getline.c + I wrote a gl_start_newline() function, to replace all of + the explicit calls to output \r\n to stdout. + + Informational messages are now written to the terminal + using a new variadic function called gl_print_info(). + This starts a newline, writes string arguments until a + special argument, GL_END_INFO, is seen, then starts + another newline. + + Changed _output_ to _print_ in the following function + names gl_output_control_sequence(), gl_output_char(), + gl_output_string() and gl_output_raw_string(). + + gl_print_raw_string() now has a length argument, so that + strings that aren't terminated with '\0' can be printed. + + The display of the initial contents of a new line to be + edited has been moved into a new function called + gl_present_line(). + + The gl_get_input_line() function now takes the prompt + string as an argument so that gl_replace_prompt() can be + called from within this function instead of from + gl_get_line(). + + Keyboard input is now buffered in a persistent buffer in + the parent GetLine object. gl_read_character() checks + this for unprocessed characters in preference to calling + gl_read_terminal() to append characters to it. A new + function, gl_discard_chars(), removes processed + characters from this buffer. This change is in + preparation for a non-blocking version of gl_get_line(), + where partially input key-sequences must be stored + between calls to gl_get_line(). + + getline.c getline.h history.c history.h cplmatch.c \ + cplmatch.h expand.c expand.h + All terminal output from gl_get_line() is now routed + through a GL_WRITE_FN() callback function called + gl_write_fn. Internal functions in cplmatch.c, + expand.c and history.c have been created which take + such callbacks to write output. These are used both + by functions in getline.c, to display file completions, + expansions, history etc, and as the internals of existing + public functions in these files that print to stdio + streams. In the latter case an internal stdio + GL_WRITE_FN() callback is substituted, so that the + functions behave as before. + + getline.c chrqueue.c chrqueue.h + The gl_write_fn() callback used by gl_get_line() now + writes to a queue, implemented in chrqueue.c. This queue + is implemented as a list of blocks of buffer segments, + the number of which shrink and grow as + needed. The contents of the queue are flushed to the + terminal via another GL_WRITE_FN() callback passed to the + queue object. Currently gl_get_line() passes an internal + function assigned to gl->flush_fn, called + gl_flush_terminal(), which writes the contents of the + queue to the terminal, and knows how to handle both + blocking and non-blocking I/O. The output queue is + designed to be flushed to the terminal incrementally, and + thereby also facilitates non-blocking I/O. + + getline.c getline.h + gl_get_line() now reads all input via the GL_READ_FN() + callback, assigned to gl->read_fn. Currently this is + set to an internal function called gl_read_terminal(), + which knows how to handle both blocking and + non-blocking I/O. + + getline.c libtecla.h + The new gl_set_nonblocking() function can be used to + enable or disable non-blocking I/O. The default is still + blocking I/O. In non-blocking mode, the terminal is told + not to wait when either reading or writing would block. + gl_get_line() then returns, with a return value of NULL, + but with the terminal left in raw mode, so that the + caller's event loop can detect key presses. The caller + should call gl_return_status() to check whether the NULL + return value was due to an error, lack of input, or + inability to write to the terminal without waiting. If + either reading or writing was said to have blocked, the + user then should check for I/O readiness in the specified + direction before calling gl_get_line() again to + incrementally build up the input line. + +05/08/2002 mcs@astro.caltech.edu + man3/gl_get_line.3 man3/gl_inactivity_timeout.3 + I documented the new gl_inactivity_timeout() function. + +08/07/2002 mcs@astro.caltech.edu + libtecla.h getline.c libtecla.map + I added a new gl_inactivity_timeout() function. On + systems that have the select system call, this provides + the option of registering a function that is then called + whenever no I/O activity has been seen for more than a + specified period of time. Like the gl_watch_fd() + facility, timeout callbacks return a code which tells + gl_get_line() how to proceed after the timeout has been + handled. + +04/07/2002 mcs@astro.caltech.edu (based on a bug report from Michael MacFaden) + getline.c + The internal event handler wasn't responding to write + events on client file descriptors, due to a typo which + resulted in read events being checked for twice, and + writes not checked for at all. + pathutil.c + The amount of space to allocate for pathnames is supposed + to come from PATH_MAX in limits.h, but I had neglected to + include limits.h. This went unnoticed because on most + systems the equivalent number is deduced by calling + pathconf(). Apparently under NetBSD this function doesn't + work correctly over NFS mounts. + +30/05/2002 Version 1.4.1 released. + +25/05/2002 mcs@astro.caltech.edu (based on suggestions by Paul Smith) + pathutil.c + Apparently, under QNX pathconf("/",_PC_PATH_MAX) returns + EINVAL. At Paul's suggestion I have modified the code to + silently substitute the existing MAX_PATHLEN_FALLBACK + value if pathconf() returns an error of any kind. + homedir.c + Under QNX, sysconf(_SC_GETPW_R_SIZE_MAX) also apparently + returns EINVAL, so as with pathconf() I modified the code + to substitute a fallback default, rather than + complaining and failing. + enhance.c + Paul told me that the inclusion of sys/termios.h was + causing compilation of enhance.c to fail under QNX. This + line is a bug. The correct thing to do is include + termios.h without a sub-directory prefix, as I was + already doing futher up in the file, so I have just + removed the errant include line. + +07/05/2002 mcs@astro.caltech.edu (async development branch only) + getline.c + gl_read_character() now caches and reads unprocessed + characters from a key-press lookahead buffer. Whenever + gl_intepret_char() receives a new character which makes + an initially promising key-sequence no longer match the + prefix of any binding, it now simply discards the first + character from the key-press buffer and resets the buffer + pointer so that the next call to gl_read_character() + returns the character that followed it, from the buffer. + getline.c + The part of gl_get_input_line() which preloads, displays + and prepares to edit a new input line, has now been moved + into a function called gl_present_line(). + +12/02/2002 mcs@astro.caltech.edu + getline.c configure.in configure + Mac OS X doesn't have a term.h or termcap.h, but it does + define prototypes for tputs() and setupterm(), so the + default prototypes that I was including if no headers + where available, upset it. I've removed these prototypes. + I also now conditionally include whichever is found of + curses.h and ncurses/curses.h for both termcap and + terminfo (before I wasn't including curses.h when + termcap was selected). + +12/02/2002 mcs@astro.caltech.edu + Updated version number to 1.4.1, ready for a micro + release. + +12/02/2002 mcs@astro.caltech.edu + html/index.html + Added Mac OS X and Cygwin to the list of systems that + can compile libtecla. + +12/02/2002 mcs@astro.caltech.edu + getline.c + Under Mac OS X, the tputs() callback function returns + void, instead of the int return value used by other + systems. This declaration is now used if both __MACH__ + and __APPLE__ are defined. Hopefully these are the + correct system macros to check. Thanks for Stephan + Fiedler for providing information on Mac OS X. + +11/02/2002 mcs@astro.caltech.edu + configure.in configure getline.c + Some systems don't have term.h, and others have it hidden + in an ncurses sub-directory of the standard system include + directory. If term.h can't be found, simply don't include + it. If it is in an ncurses sub-directory, include + ncurses/term.h instead of term.h. + +04/02/2002 mcs@astro.caltech.edu + configure.in configure Makefile.in Makefile.rules + Use ranlib on systems that need it (Mac OS X). Also, + make all components of the installation directories where + needed, instead of assuming that they exist. + +04/02/2002 mcs@astro.caltech.edu + getline.c + When the tab completion binding was unbound from the tab + key, hitting the tab key caused gl_get_line() to ring the + bell instead of inserting a tab character. This is + problematic when using the 'enhance' program with + Jython, since tabs are important in Python. I have + corrected this. + +10/12/2001 Version 1.4.0 released. + +10/12/2001 mcs@astro.caltech.edu + getline.c + If the TIOCGWINSZ ioctl doesn't work, as is the case when + running in an emacs shell, leave the size unchanged, rather + than returning a fatal error. + +07/12/2001 mcs@astro.caltech.edu + configure.in configure + Now that the configure version of CFLAGS is included in + the makefile, I noticed that the optimization flags -g + and -O2 had been added. It turns out that if CFLAGS isn't + already set, the autoconf AC_PROG_CC macro initializes it + with these two optimization flags. Since this would break + backwards compatibility in embedded distributions that + already use the OPT= makefile argument, and because + turning debugging on needlessly bloats the library, I now + make sure that CFLAGS is set before calling this macro. + +07/12/2001 mcs@astro.caltech.edu + enhance.c + Use argv[0] in error reports instead of using a + hardcoded macro. + +07/12/2001 mcs@astro.caltech.edu + getline.c + The cut buffer wasn't being cleared after being + used as a work buffer by gl_load_history(). + +06/12/2001 mcs@astro.caltech.edu + configure.in configure + I removed my now redundant definition of SUN_TPUTS from + CFLAGS. I also added "-I/usr/include" to CFLAGS under + Solaris to prevent gcc from seeing conflicting versions + of system header files in /usr/local/include. + +06/12/2001 Markus Gyger (logged here by mcs) + Lots of files. + Lots of corrections to misspellings and typos in the + comments. + getline.c + Markus reverted a supposed fix that I added a day or two + ago. I had incorrectly thought that in Solaris 8, Sun had + finally brought their declaration of the callback + function of tputs() into line with other systems, but it + turned out that gcc was pulling in a GNU version of + term.h from /usr/local/include, and this was what + confused me. + +05/12/2001 mcs@astro.caltech.edu + Makefile.in + I added @CFLAGS@ to the CFLAGS assignment, so that + if CFLAGS is set as an environment variable when + configure is run, the corresponding make variable + includes its values in the output makefile. + +05/12/2001 mcs@astro.caltech.edu + getline.c libtecla.h libtecla.map man3/gl_get_line.3 + man3/gl_last_signal.3 + I added a function that programs can use to find out + which signal caused gl_get_line() to return EINTR. + +05/12/2001 mcs@astro.caltech.edu + getline.c + When the newline action was triggered by a printable + character, it failed to display that character. It now + does. Also, extra control codes that I had added, to + clear to the end of the display after the carriage return, + but before displaying the prompt, were confusing expect + scripts, so I have removed them. This step is now done + instead in gl_redisplay() after displaying the full input + line. + +05/12/2001 mcs@astro.caltech.edu + getline.c man3/gl_get_line.3 + A user convinced me that continuing to invoke meta + keybindings for meta characters that are printable is a + bad idea, as is allowing users to ask to have setlocale() + called behind the application's back. I have thus changed + this. The setlocale configuration option has gone, and + gl_get_line() is now completely 8-bit clean, by default. + This means that if a meta character is printable, it is + treated as a literal character, rather than a potential + M-c binding. Meta bindings can still be invoked via + their Esc-c equivalents, and indeed most terminal + emulators either output such escape pairs by default when + the meta character is pressed, or can be configured to do + so. I have documented how to configure xterm to do this, + in the man page. + +03/12/2001 mcs@astro.caltech.edu + getline.c man3/gl_get_line.3 + gl_get_line() by default now prints any 8-bit printable + characters that don't match keybindings. Previously + characters > 127 were only printed if preceded by the + literal-next action. Alternatively, by placing the + command literal_if_printable in the tecla configuration + file, all printable characters are treated as literal + characters, even if they are bound to action functions. + + For international users of programs written by + programmers that weren't aware of the need to call + setlocale() to support alternate character sets, the + configuration file can now also contain the single-word + command "setlocale", which tells gl_get_line() to remedy + this. + +27/11/2001 mcs@astro.caltech.edu + demo.c demo2.c enhance man3/gl_get_line.3 + All demos and programs now call setlocale(LC_CTYPE,""). + This makes them support character sets of different + locales, where specified with the LC_CTYPE, LC_ALL, or + LANG environment variables. I also added this to the demo + in the man page, and documented its effect. + +27/11/2001 mcs@astro.caltech.edu + getline.c + When displaying unsigned characters with values over + 127 literally, previously it was assumed that they would + all be displayable. Now isprint() is consulted, and if it + says that a character isn't printable, the character code + is displayed in octal like \307. In non-C locales, some + characters with values > 127 are displayable, and + isprint() tells gl_get_line() which are and which aren't. + +27/11/2001 mcs@astro.caltech.edu + getline.c pathutil.c history.c enhance.c demo2.c + All arguments of the ctype.h character class functions + are now cast to (int)(unsigned char). Previously they + were cast to (int), which doesn't correctly conform to + the requirements of the C standard, and could cause + problems for characters with values > 127 on systems + with signed char's. + +26/11/2001 mcs@astro.caltech.edu + man3/enhance.3 man3/libtecla.3 + I started writing a man page for the enhance program. + +26/11/2001 mcs@astro.caltech.edu + Makefile.in Makefile.rules INSTALL + It is now possible to specify whether the demos and other + programs are to be built, by overriding the default + values of the DEMOS, PROGRAMS and PROGRAMS_R variables. + I have also documented the BINDIR variable and the + install_bin makefile target. + +22/11/2001 mcs@astro.caltech.edu + getline.c libtecla.h libtecla.map man3/gl_get_line.3 + man3/gl_ignore_signal.3 man3/gl_trap_signal.3 + Signal handling has now been modified to be customizable. + Signals that are trapped by default can be removed from + the list of trapped signals, and signals that aren't + currently trapped, can be added to the list. Applications + can also specify the signal and terminal environments in + which an application's signal handler is invoked, and + what gl_get_line() does after the signal handler returns. + +13/11/2001 mcs@astro.caltech.edu + getline.c man3/gl_get_line.3 + Added half-bright, reverse-video and blinking text to the + available prompt formatting options. + getline.c + Removed ^O from the default VT100 sgr0 capability + string. Apparently it can cause problems with some + terminal emulators, and we don't need it, since it turns + off the alternative character set mode, which we don't + use. + getline.c + gl_tigetstr() and gl_tgetstr() didn't guard against the + error returns of tigetstr() and tgetstr() respectively. + They now do. + +11/11/2001 mcs@astro.caltech.edu + getline.c libtecla.h libtecla.map man3/gl_get_line.3 + man3/gl_prompt_style.3 + Although the default remains to display the prompt string + literally, the new gl_prompt_style() function can be used + to enable text attribute formatting directives in prompt + strings, such as underlining, bold font, and highlighting + directives. + +09/11/2001 mcs@astro.caltech.edu + enhance.c Makefile.rules configure.in configure + I added a new program to the distribution that allows one + to run most third party programs with the tecla library + providing command-line editing. + +08/11/2001 mcs@astro.caltech.edu + libtecla.h getline.c man3/gl_get_line.3 history.c history.h + I added a max_lines argument to gl_show_history() and + _glh_show_history(). This can optionally be used to + set a limit on the number of history lines displayed. + libtecla.h getline.c man3/gl_get_line.3 + I added a new function called gl_replace_prompt(). This + can be used by gl_get_line() callback functions to + request that a new prompt be use when they return. + +06/11/2001 mcs@astro.caltech.edu + getline.c man3/gl_get_line.3 + I implemented, bound and documented the list-history + action, used for listing historical lines of the current + history group. + getline.c man3/gl_get_line.3 man3/gl_echo_mode.3 + I wrote functions to specify and query whether subsequent + lines will be visible as they are being typed. + +28/10/2001 mcs@astro.caltech.edu + getline.c man3/gl_get_line.3 + For those cases where a terminal provides its own + high-level terminal editing facilities, you can now + specify an edit-mode argument of 'none'. This disables + all tecla key bindings, and by using canonical terminal + input mode instead of raw input mode, editing is left up + to the terminal driver. + +21/10/2001 mcs@astro.caltech.edu + libtecla.h getline.c history.c history.h + man3/gl_get_line.3 man3/gl_history_info.3 + I added the new gl_state_of_history(), + gl_range_of_history() and gl_size_of_history() + functions for querying information about the + history list. + history.c + While testing the new gl_size_of_history() + function, I noticed that when the history buffer + wrapped, any location nodes of old lines between + the most recent line and the end of the buffer + weren't being removed. This could result in bogus + entries appearing at the start of the history list. + Now fixed. + +20/10/2001 mcs@astro.caltech.edu + + libtecla.h getline.c history.c history.h + man3/gl_get_line.3 man3/gl_lookup_history.3 + I added a function called gl_lookup_history(), that + the application can use to lookup lines in the history + list. + libtecla.h getline.c history.c history.h man3/gl_get_line.3 + gl_show_history() now takes a format string argument + to control how the line is displayed, and with what + information. It also now provides the option of either + displaying all history lines or just those of the + current history group. + getline.c man3/gl_get_line.3 + gl_get_line() only archives lines in the history buffer + if the newline action was invoked by a newline or + carriage return character. + +16/10/2001 mcs@astro.caltech.edu + + history.c history.h getline.c libtecla.h libtecla.map + man3/gl_get_line.3 man3/gl_resize_history.3 + man3/gl_limit_history.3 man3/gl_clear_history.3 + man3/gl_toggle_history.3 + I added a number of miscellaneous history configuration + functions. You can now resize or delete the history + buffer, limit the number of lines that are allowed in the + buffer, clear either all history or just the history of + the current history group, and temporarily enable and + disable the history mechanism. + +13/10/2001 mcs@astro.caltech.edu + + getline.c + tputs_fp is now only declared if using termcap or + terminfo. + getline.c libtecla.map man3/gl_get_line.3 + man3/gl_terminal_size.3 + I added a public gl_terminal_size() function for + updating and querying the current size of the terminal. + update_version configure.in libtecla.h + A user noted that on systems where the configure script + couldn't be used, it was inconvenient to have the version + number macros set by the configure script, so they are + now specified in libtecla.h. To reduce the likelihood + that the various files where the version number now + appears might get out of sync, I have written the + update_version script, which changes the version number + in all of these files to a given value. + +01/10/2001 mcs@astro.caltech.edu + + getline.c history.c history.h man3/gl_get_line.3 + I added a max_lines argument to gl_save_history(), to + allow people to optionally place a ceiling on the number + of history lines saved. Specifying this as -1 sets the + ceiling to infinity. + +01/10/2001 mcs@astro.caltech.edu + + configure.in configure + Under digital unix, getline wouldn't compile with + _POSIX_C_SOURCE set, due to type definitions needed by + select being excluded by this flag. Defining the + _OSF_SOURCE macro as well on this system, resolved this. + +30/09/2001 mcs@astro.caltech.edu + + getline.c libtecla.h history.c history.h man3/gl_get_line.3 + man3/gl_group_history.3 + I implemented history streams. History streams + effectively allow multiple history lists to be stored in + a single history buffer. Lines in the buffer are tagged + with the current stream identification number, and + lookups only consider lines that are marked with the + current stream identifier. + getline.c libtecla.h history.c history.h man3/gl_get_line.3 + man3/gl_show_history.3 + The new gl_show_history function displays the current + history to a given stdio output stream. + +29/09/2001 mcs@astro.caltech.edu + + getline.c + Previously new_GetLine() installed a persistent signal + handler to be sure to catch the SIGWINCH (terminal size + change) signal between calls to gl_get_line(). This had + the drawback that if multiple GetLine objects were + created, only the first GetLine object used after the + signal was received, would see the signal and adapt to + the new terminal size. Instead of this, a signal handler + for sigwinch is only installed while gl_get_line() is + running, and just after installing this handler, + gl_get_line() checks for terminal size changes that + might have occurred while the signal handler wasn't + installed. + getline.c + Dynamically allocated copies of capability strings looked + up in the terminfo or termcap databases are now made, so + that calls to setupterm() etc for one GetLine object + don't get trashed when another GetLine object calls + setupterm() etc. It is now safe to allocate and use + multiple GetLine objects, albeit only within a single + thread. + +28/09/2001 mcs@astro.caltech.edu + + version.c Makefile.rules + I added a function for querying the version number of + the library. + +26/09/2001 mcs@astro.caltech.edu + + getline.c man3/gl_get_line.3 + I added the new gl_watch_fd() function, which allows + applications to register callback functions to be invoked + when activity is seen on arbitrary file descriptors while + gl_get_line() is awaiting keyboard input from the user. + + keytab.c + If a request is received to delete a non-existent + binding, which happens to be an ambiguous prefix of other + bindings no complaint is now generated about it being + ambiguous. + +23/09/2001 mcs@astro.caltech.edu + + getline.c history.c history.h man3/gl_get_line.3 + libtecla.map demo.c + I added new public functions for saving and restoring the + contents of the history list. The demo program now uses + these functions to load and save history in ~/.demo_history. + +23/09/2001 mcs@astro.caltech.edu + + getline.c + On trying the demo for the first time on a KDE konsole + terminal, I discovered that the default M-O binding + to repeat history was hiding the arrow keys, which are + M-OA etc. I have removed this binding. The M-o (ie the + lower case version of this), is still bound. + +18/09/2001 mcs@astro.caltech.edu + + getline.c man3/gl_get_line.3 libtecla.map + Automatic reading of ~/.teclarc is now postponed until + the first call to gl_get_line(), to give the application + the chance to specify alternative configuration sources + with the new function gl_configure_getline(). The latter + function allows configuration to be done with a string, a + specified application-specific file, and/or a specified + user-specific file. I also added a read-init-files action + function, for re-reading the configuration files, if any. + This is by default bound to ^X^R. This is all documented + in gl_get_line.3. + +08/09/2001 mcs@astro.caltech.edu + + getline.c man3/gl_get_line.3 + It is now possible to bind actions to key-sequences + that start with printable characters. Previously + keysequences were required to start with meta or control + characters. This is documented in gl_get_line.3. + + getline.c man3/gl_get_line.3 + A customized completion function can now arrange for + gl_get_line() to return the current input line whenever a + successful completion has been made. This is signalled by + setting the last character of the optional continuation + suffix to a newline character. This is documented in + gl_get_line.3. + +05/07/2001 Bug reported by Mike MacFaden, fixed by mcs + + configure.in + There was a bug in the configure script that only + revealed itself on systems without termcap but not + terminfo (eg. NetBSD). I traced the bug back to a lack of + sufficient quoting of multi-line m4 macro arguments in + configure.in, and have now fixed this and recreated the + configure script. + +05/07/2001 Bug reported and patched by Mike MacFaden (patch modified + by mcs to match original intentions). + + getline.c + getline.c wouldn't compile when termcap was selected as + the terminal information database. setupterm() was being + passed a non-existent variable, in place of the term[] + argument of gl_control_strings(). Also if + gl_change_terminal() is called with term==NULL, "ansi" + is now substituted. + +02/07/2001 Version 1.3.3 released. + +27/06/2001 mcs@astro.caltech.edu + + getline.c expand.c cplmatch.c + Added checks to fprintf() statements that write to the + terminal. + getline.c + Move the cursor to the end of the line before suspending, + so that the cursor doesn't get left in the middle of the + input line. + Makefile.in + On systems that don't support shared libraries, the + distclean target of make deleted libtecla.h. This has + now been fixed. + getline.c + gl_change_terminal() was being called by gl_change_editor(), + with the unwanted side effect that raw terminal modes were + stored as those to be restored later, if called by an + action function. gl_change_terminal() was being called in + this case to re-establish terminal-specific key bindings, + so I have just split this part of the function out into + a separate function for both gl_change_editor() and + gl_change_terminal() to call. + +12/06/2001 mcs@astro.caltech.edu + + getline.c + Signal handling has been improved. Many more signals are + now trapped, and instead of using a simple flag set by a + signal handler, race conditions are avoided by blocking + signals during most of the gl_get_line() code, and + unblocking them via calls to sigsetjmp(), just before + attempting to read each new character from the user. + The matching use of siglongjmp() in the signal + handlers ensures that signals are reblocked correctly + before they are handled. In most cases, signals cause + gl_get_line() to restore the terminal modes and signal + handlers of the calling application, then resend the + signal to the application. In the case of SIGINT, SIGHUP, + SIGPIPE, and SIGQUIT, if the process still exists after + the signals are resent, gl_get_line() immediately returns + with appropriate values assigned to errno. If SIGTSTP, + SIGTTIN or SIGTTOU signals are received, the process is + suspended. If any other signal is received, and the + process continues to exist after the signal is resent to + the calling application, line input is resumed after the + terminal is put back into raw mode, the gl_get_line() + signal handling is restored, and the input line redrawn. + man/gl_get_line(3) + I added a SIGNAL HANDLING section to the gl_get_line() + man page, describing the new signal handling features. + +21/05/2001 Version 1.3.2 released. + +21/05/2001 mcs@astro.caltech.edu + + getline.c + When vi-replace-char was used to replace the character at + the end of the line, it left the cursor one character to + its right instead of on top of it. Now rememdied. + getline.c + When undoing, to properly emulate vi, the cursor is now + left at the leftmost of the saved and current cursor + positions. + getline.c man3/gl_get_line.3 + Implemented find-parenthesis (%), delete-to-paren (M-d%), + vi-change-to-paren (M-c%), copy-to-paren (M-y%). + cplfile.c pcache.c + In three places I was comparing the last argument of + strncmp() to zero instead of the return value of + strncmp(). + +20/05/2001 mcs@astro.caltech.edu + + getline.c man3/gl_get_line.3 + Implemented and documented the vi-repeat-change action, + bound to the period key. This repeats the last action + that modified the input line. + +19/05/2001 mcs@astro.caltech.edu + + man3/gl_get_line.3 + I documented the new action functions and bindings + provided by Tim Eliseo, plus the ring-bell action and + the new "nobeep" configuration option. + getline.c + I modified gl_change_editor() to remove and reinstate the + terminal settings as well as the default bindings, since + these have editor-specific differences. I also modified + it to not abort if a key-sequence can't be bound for some + reason. This allows the new vi-mode and emacs-mode + bindings to be used safely. + getline.c + When the line was re-displayed on receipt of a SIGWINCH + signal, the result wasn't visible until the next + character was typed, since a call to fflush() was needed. + gl_redisplay_line() now calls gl_flush_output() to remedy + this. + +17/05/2001 mcs@astro.catlech.edu + + getline.c + Under Linux, calling fflush(gl->output_fd) hangs if + terminal output has been suspended with ^S. With the + tecla library taking responsability for reading the stop + and start characters this was a problem, because once + hung in fflush(), the keyboard input loop wasn't entered, + so the user couldn't type the start character to resume + output. To remedy this, I now have the terminal process + these characters, rather than the library. + +12/05/2001 mcs@astro.caltech.edu + + getline.c + The literal-next action is now implemented as a single + function which reads the next character itself. + Previously it just set a flag which effected the + interpretation of the next character read by the input + loop. + getline.c + Added a ring-bell action function. This is currently + unbound to any key by default, but it is used internally, + and can be used by users that want to disable any of the + default key-bindings. + +12/05/2001 Tim Eliseo (logged here by mcs) + + getline.c + Don't reset gl->number until after calling an action + function. By looking at whether gl->number is <0 or + not, action functions can then tell whether the count + that they were passed was explicitly specified by the + user, as opposed to being defaulted to 1. + getline.c + In vi, the position at which input mode is entered + acts as a barrier to backward motion for the few + backward moving actions that are enabled in input mode. + Tim added this barrier to getline. + getline.c + In gl_get_line() after reading an input line, or + having the read aborted by a signal, the sig_atomic_t + gl_pending_signal was being compared to zero instead + of -1 to see if no signals had been received. + gl_get_line() will thus have been calling raise(-1), + which luckily didn't seem to do anything. Tim also + arranged for errno to be set to EINTR when a signal + aborts gl_get_line(). + getline.c + The test in gl_add_char_to_line() for detecting + when overwriting a character with a wider character, + had a < where it needed a >. Overwriting with a wider + character thus overwrote trailing characters. Tim also + removed a redundant copy of the character into the + line buffer. + getline.c + gl_cursor_left() and gl->cursor_right() were executing + a lot of redundant code, when the existing call to the + recently added gl_place_cursor() function, does all that + is necessary. + getline.c + Remove redundant code from backward_kill_line() by + re-implimenting in terms of gl_place_cursor() and + gl_delete_chars(). + getline.c + gl_forward_delete_char() now records characters in cut + buffer when in vi command mode. + getline.c + In vi mode gl_backward_delete_char() now only deletes + up to the point at which input mode was entered. Also + gl_delete_chars() restores from the undo buffer when + deleting in vi insert mode. + getline.c + Added action functions, vi-delete-goto-column, + vi-change-to-bol, vi-change-line, emacs-mode, vi-mode, + vi-forward-change-find, vi-backward-change-find, + vi-forward-change-to, vi-backward-change-to, + vi-change-goto-col, forward-delete-find, backward-delete-find, + forward-delete-to, backward-delete-to, + delete-refind, delete-invert-refind, forward-copy-find, + backward-copy-find, forward-copy-to, backward-copy-to + copy-goto-column, copy-rest-of-line, copy-to-bol, copy-line, + history-re-search-forward, history-re-search-backward. + +06/05/2001 Version 1.3.1 released. + +03/05/2001 mcs@astro.caltech.edu + + configure.in + Old versions of GNU ld don't accept version scripts. + Under Linux I thus added a test to try out ld with + the --version-script argument to see if it works. + If not, version scripts aren't used. + configure.in + My test for versions of Solaris earlier than 7 + failed when confronted by a three figure version + number (2.5.1). Fixed. + +30/04/2001 mcs@astro.caltech.edu + + getline.c + In vi mode, history-search-backward and + history-search-forward weren't doing anything when + invoked at the start of an empty line, whereas + they should have acted like up-history and down-history. + Makefile.in Makefile.rules + When shared libraries are being created, the build + procedure now arranges for any alternate library + links to be created as well, before linking the + demos. Without this the demos always linked to the + static libraries (which was perfectly ok, but wasn't a + good example). + Makefile.in Makefile.rules + On systems on which shared libraries were being created, + if there were no alternate list of names, make would + abort due to a Bourne shell 'for' statement that didn't + have any arguments. Currently there are no systems who's + shared library configurations would trigger this + problem. + Makefile.rules + The demos now relink to take account of changes to the + library. + configure.in configure + When determining whether the reentrant version of the + library should be compiled by default, the configure + script now attempts to compile a dummy program that + includes all of the appropriate system headers and + defines _POSIX_C_SOURCE. This should now be a robust test + on systems which use C macros to alias these function + names to other internal functions. + configure.in + Under Solaris 2.6 and earlier, the curses library is in + /usr/ccs/lib. Gcc wasn't finding this. In addition to + remedying this, I had to remove "-z text" from + LINK_SHARED under Solaris to get it to successfully + compile the shared library against the static curses + library. + configure.in + Under Linux the -soname directive was being used + incorrectly, citing the fully qualified name of the + library instead of its major version alias. This will + unfortunately mean that binaries linked with the 1.2.3 + and 1.2.4 versions of the shared library won't use + later versions of the library unless relinked. + +30/04/2001 mcs@astro.caltech.edu + + getline.c + In gl_get_input_line(), don't redundantly copy the + start_line if start_line == gl->line. + +30/04/2001 Version 1.3.0 released. + +28/04/2001 mcs@astro.caltech.edu + + configure.in + I removed the --no-undefined directive from the Linux + LINK_SHARED command. After recent patches to our RedHat + 7.0 systems ld started reporting some internal symbols of + libc as being undefined. Using nm on libc indicated that + the offending symbols are indeed defined, albeit as + "common" symbols, so there appears to be a bug in + RedHat's ld. Removing this flag allows the tecla shared + library to compile, and programs appear to function fine. + man3/gl_get_line.3 + The default key-sequence used to invoke the + read-from-file action was incorrectly cited as ^Xi + instead of ^X^F. + +26/04/2001 mcs@astro.caltech.edu + + getline.c man3/gl_get_line.3 + A new vi-style editing mode was added. This involved + adding many new action functions, adding support for + specifying editing modes in users' ~/.teclarc files, + writing a higher level cursor motion function to support + the different line-end bounds required in vi command + mode, and a few small changes to support the fact that vi + has two modes, input mode and command mode with different + bindings. + + When vi editing mode is enabled, any binding that starts + with an escape or a meta character, is interpreted as a + command-mode binding, and switches the library to vi + command mode if not already in that mode. Once in command + mode the first character of all keysequences entered + until input mode is re-enabled, are quietly coerced to + meta characters before being looked up in the key-binding + table. So, for example, in the key-binding table, the + standard vi command-mode 'w' key, which moves the cursor + one word to the right, is represented by M-w. This + emulates vi's dual sets of bindings in a natural way + without needing large changes to the library, or new + binding syntaxes. Since cursor keys normally emit + keysequences which start with escape, it also does + something sensible when a cursor key is pressed during + input mode (unlike true vi, which gets upset). + + I also added a ^Xg binding for the new list-glob action + to both the emacs and vi key-binding tables. This lists + the files that match the wild-card expression that + precedes it on the command line. + + The function that reads in ~/.teclarc used to tell + new_GetLine() to abort if it encountered anything that it + didn't understand in this file. It now just reports an + error and continues onto the next line. + Makefile.in: + When passing LIBS=$(LIBS) to recursive invokations of + make, quotes weren't included around the $(LIBS) part. + This would cause problems if LIBS ever contained more + than one word (with the supplied configure script this + doesn't happen currently). I added these quotes. + expand.c man3/ef_expand_file.3: + I wrote a new public function called ef_list_expansions(), + to list the matching filenames returned by + ef_expand_file(). + + I also fixed the example in the man page, which cited + exp->file instead of exp->files, and changed the + dangerous name 'exp' with 'expn'. + keytab.c: + Key-binding tables start with 100 elements, and are + supposedly incremented in size by 100 elements whenever + the a table runs out of space. The realloc arguments to + do this were wrong. This would have caused problems if + anybody added a lot of personal bindings in their + ~/.teclarc file. I only noticed it because the number of + key bindings needed by the new vi mode exceeded this + number. + libtecla.map + ef_expand_file() is now reported as having been added in + the upcoming 1.3.0 release. + +25/03/2001 Markus Gyger (logged here by mcs) + + Makefile.in: + Make symbolic links to alternative shared library names + relative instead of absolute. + Makefile.rules: + The HP-UX libtecla.map.opt file should be made in the + compilation directory, to allow the source code directory + to be on a readonly filesystem. + cplmatch.c demo2.c history.c pcache.c + To allow the library to be compiled with a C++ compiler, + without generating warnings, a few casts were added where + void* return values were being assigned directly to + none void* pointer variables. + +25/03/2001 mcs@astro.caltech.edu + + libtecla.map: + Added comment header to explain the purpose of the file. + Also added cpl_init_FileArgs to the list of exported + symbols. This symbol is deprecated, and no longer + documented, but for backwards compatibility, it should + still be exported. + configure: + I had forgotten to run autoconf before releasing version + 1.2.4, so I have just belatedly done so. This enables + Markus' changes to "configure.in" documented previously, + (see 17/03/2001). + +20/03/2001 John Levon (logged here by mcs) + + libtecla.h + A couple of the function prototypes in libtecla.h have + (FILE *) argument declarations, which means that stdio.h + needs to be included. The header file should be self + contained, so libtecla.h now includes stdio.h. + +18/03/2001 Version 1.2.4 released. + + README html/index.html configure.in + Incremented minor version from 3 to 4. + +18/03/2001 mcs@astro.caltech.edu + + getline.c + The fix for the end-of-line problem that I released a + couple of weeks ago, only worked for the first line, + because I was handling this case when the cursor position + was equal to the last column, rather than when the cursor + position modulo ncolumn was zero. + Makefile.in Makefile.rules + The demos are now made by default, their rules now being + int Makefile.rules instead of Makefile.in. + INSTALL + I documented how to compile the library in a different + directory than the distribution directory. + I also documented features designed to facilitate + configuring and building the library as part of another + package. + +17/03/2001 Markus Gyger (logged here by mcs) + + getline.c + Until now cursor motions were done one at a time. Markus + has added code to make use the of the terminfo capability + that moves the cursor by more than one position at a + time. This greatly improves performance when editing near + the start of long lines. + getline.c + To further improve performance, Markus switched from + writing one character at a time to the terminal, using + the write() system call, to using C buffered output + streams. The output buffer is only flushed when + necessary. + Makefile.rules Makefile.in configure.in + Added support for compiling for different architectures + in different directories. Simply create another directory + and run the configure script located in the original + directory. + Makefile.in configure.in libtecla.map + Under Solaris, Linux and HP-UX, symbols that are to be + exported by tecla shared libraries are explicitly specified + via symbol map files. Only publicly documented functions + are thus visible to applications. + configure.in + When linking shared libraries under Solaris SPARC, + registers that are reserved for applications are marked + as off limits to the library, using -xregs=no%appl when + compiling with Sun cc, or -mno-app-regs when compiling + with gcc. Also removed -z redlocsym for Solaris, which + caused problems under some releases of ld. + homedir.c (after minor changes by mcs) + Under ksh, ~+ expands to the current value of the ksh + PWD environment variable, which contains the path of + the current working directory, including any symbolic + links that were traversed to get there. The special + username "+" is now treated equally by tecla, except + that it substitutes the return value of getcwd() if PWD + either isn't set, or if it points at a different + directory than that reported by getcwd(). + +08/03/2001 Version 1.2.3 released. + +08/03/2001 mcs@astro.caltech.edu + + getline.c + On compiling the library under HP-UX for the first time + I encountered and fixed a couple of bugs: + + 1. On all systems except Solaris, the callback function + required by tputs() takes an int argument for the + character that is to be printed. Under Solaris it + takes a char argument. The callback function was + passing this argument, regardless of type, to write(), + which wrote the first byte of the argument. This was + fine under Solaris and under little-endian systems, + because the first byte contained the character to be + written, but on big-endian systems, it always wrote + the zero byte at the other end of the word. As a + result, no control characters were being written to + the terminal. + 2. While attempting to start a newline after the user hit + enter, the library was outputting the control sequence + for moving the cursor down, instead of the newline + character. On many systems the control sequence for + moving the cursor down happends to be a newline + character, but under HP-UX it isn't. The result was + that no new line was being started under HP-UX. + +04/03/2001 mcs@astro.caltech.edu + + configure.in Makefile.in Makefile.stub configure config.guess + config.sub Makefile.rules install-sh PORTING README INSTALL + Configuration and compilation of the library is now + performed with the help of an autoconf configure + script. In addition to relieving the user of the need to + edit the Makefile, this also allows automatic compilation + of the reentrant version of the library on platforms that + can handle it, along with the creation of shared + libraries where configured. On systems that aren't known + to the configure script, just the static tecla library is + compiled. This is currently the case on all systems + except Linux, Solaris and HP-UX. In the hope that + installers will provide specific conigurations for other + systems, the configure.in script is heavily commented, + and instructions on how to use are included in a new + PORTING file. + +24/02/2001 Version 1.2b released. + +22/02/2001 mcs@astro.caltech.edu + + getline.c + It turns out that most terminals, but not all, on writing + a character in the rightmost column, don't wrap the + cursor onto the next line until the next character is + output. This library wasn't aware of this and thus if one + tried to reposition the cursor from the last column, + gl_get_line() thought that it was moving relative to a + point on the next line, and thus moved the cursor up a + line. The fix was to write one extra character when in + the last column to force the cursor onto the next line, + then backup the cursor to the start of the new line. + getline.c + On terminal initialization, the dynamic LINES and COLUMNS + environment variables were ignored unless + terminfo/termcap didn't return sensible dimensions. In + practice, when present they should override the static + versions in the terminfo/termcap databases. This is the + new behavior. In reality this probably won't have caused + many problems, because a SIGWINCH signal which informs of + terminal size changes is sent when the terminal is + opened, so the dimensions established during + initialization quickly get updated on most systems. + +18/02/2001 Version 1.2a released. + +18/02/2001 mcs@astro.caltech.edu + + getline.c + Three months ago I moved the point at which termios.h + was included in getline.c. Unfortunately, I didn't notice + that this moved it to after the test for TIOCGWINSZ being + defined. This resulted in SIGWINCH signals not being + trapped for, and thus terminal size changes went + unnoticed. I have now moved the test to after the + inclusion of termios.h. + +12/02/2001 Markus Gyger (described here by mcs) + + man3/pca_lookup_file.3 man3/gl_get_line.3 + man3/ef_expand_file.3 man3/cpl_complete_word.3 + In the 1.2 release of the library, all functions in the + library were given man pages. Most of these simply + include one of the above 4 man pages, which describe the + functions while describing the modules that they are in. + Markus added all of these function names to the lists in + the "NAME" headers of the respective man pages. + Previously only the primary function of each module was + named there. + +11/02/2001 mcs@astro.caltech.edu + + getline.c + On entering a line that wrapped over two or more + terminal, if the user pressed enter when the cursor + wasn't on the last of the wrapped lines, the text of the + wrapped lines that followed it got mixed up with the next + line written by the application, or the next input + line. Somehow this slipped through the cracks and wasn't + noticed until now. Anyway, it is fixed now. + +09/02/2001 Version 1.2 released. + +04/02/2001 mcs@astro.caltech.edu + + pcache.c libtecla.h + With all filesystems local, demo2 was very fast to start + up, but on a Sun system with one of the target + directories being on a remote nfs mounted filesystem, the + startup time was many seconds. This was due to the + executable selection callback being applied to all files + in the path at startup. To avoid this, all files are now + included in the cache, and the application specified + file-selection callback is only called on files as they + are matched. Whether the callback rejected or accepted + them is then cached so that the next time an already + checked file is looked at, the callback doesn't have to + be called. As a result, startup is now fast on all + systems, and since usually there are only a few matching + file completions at a time, the delay during completion + is also usually small. The only exception is if the user + tries to complete an empty string, at which point all + files have to be checked. Having done this once, however, + doing it again is fast. + man3/pca_lookup_file.3 + I added a man page documenting the new PathCache module. + man3/.3 + I have added man pages for all of the functions in each + of the modules. These 1-line pages use the .so directive + to redirect nroff to the man page of the parent module. + man Makefile update_html + I renamed man to man3 to make it easier to test man page + rediction, and updated Makefile and update_html + accordingly. I also instructed update_html to ignore + 1-line man pages when making html equivalents of the man + pages. + cplmatch.c + In cpl_list_completions() the size_t return value of + strlen() was being used as the length argument of a "%*s" + printf directive. This ought to be an int, so the return + value of strlen() is now cast to int. This would have + caused problems on architectures where the size of a + size_t is not equal to the size of an int. + +02/02/2001 mcs@astro.caltech.edu + + getline.c + Under UNIX, certain terminal bindings are set using the + stty command. This, for example, specifies which control + key generates a user-interrupt (usually ^C or ^Y). What I + hadn't realized was that ASCII NUL is used as the way to + specify that one of these bindings is unset. I have now + modified the code to skip unset bindings, leaving the + corresponding action bound to the built-in default, or a + user provided binding. + +28/01/2001 mcs@astro.caltech.edu + + pcache.c libtecla.h + A new module was added which supports searching for files + in any colon separated list of directories, such as the + unix execution PATH environment variable. Files in these + directories, after being individually okayed for + inclusion via an application provided callback, are + cached in a PathCache object. You can then look up the + full pathname of a given filename, or you can use the + provided completion callback to list possible completions + in the path-list. The contents of relative directories, + such as ".", obviously can't be cached, so these + directories are read on the fly during lookups and + completions. The obvious application of this facility is + to provide Tab-completion of commands, and thus a + callback to place executable files in the cache, is + provided. + demo2.c + This new program demonstrates the new PathCache + module. It reads and processes lines of input until the + word 'exit' is entered, or C-d is pressed. The default + tab-completion callback is replaced with one which at the + start of a line, looks up completions of commands in the + user's execution path, and when invoked in other parts of + the line, reverts to normal filename completion. Whenever + a new line is entered, it extracts the first word on the + line, looks it up in the user's execution path to see if + it corresponds to a known command file, and if so, + displays the full pathname of the file, along with the + remaining arguments. + cplfile.c + I added an optional pair of callback function/data + members to the new cpl_file_completions() configuration + structure. Where provided, this callback is asked + on a file-by-file basis, which files should be included + in the list of file completions. For example, a callback + is provided for listing only completions of executable + files. + cplmatch.c + When listing completions, the length of the type suffix + of each completion wasn't being taken into account + correctly when computing the column widths. Thus the + listing appeared ragged sometimes. This is now fixed. + pathutil.c + I added a function for prepending a string to a path, + and another for testing whether a pathname referred to + an executable file. + +28/01/2001 mcs@astro.caltech.edu + + libtecla.h cplmatch.c man/cpl_complete_word.3 + The use of a publically defined structure to configure + the cpl_file_completions() callback was flawed, so a new + approach has been designed, and the old method, albeit + still supported, is no longer documented in the man + pages. The definition of the CplFileArgs structure in + libtecla.h is now accompanied by comments warning people + not to modify it, since modifications could break + applications linked to shared versions of the tecla + library. The new method involves an opaque CplFileConf + object, instances of which are returned by a provided + constructor function, configured with provided accessor + functions, and when no longer needed, deleted with a + provided destructor function. This is documented in the + cpl_complete_word man page. The cpl_file_completions() + callback distinguishes what type of configuration + structure it has been sent by virtue of a code placed at + the beginning of the CplFileConf argument by its + constructor. + +04/01/2001 mcs@astro.caltech.edu (Release of version 1.1j) + + getline.c + I added upper-case bindings for the default meta-letter + keysequences such as M-b. They thus continue to work + when the user has caps-lock on. + Makefile + I re-implemented the "install" target in terms of new + install_lib, install_inc and install_man targets. When + distributing the library with other packages, these new + targets allows for finer grained control of the + installation process. + +30/12/2000 mcs@astro.caltech.edu + + getline.c man/gl_get_line.3 + I realized that the recall-history action that I + implemented wasn't what Markus had asked me for. What he + actually wanted was for down-history to continue going + forwards through a previous history recall session if no + history recall session had been started while entering + the current line. I have thus removed the recall-history + action and modified the down-history action function + accordingly. + +24/12/2000 mcs@astro.caltech.edu + + getline.c + I modified gl_get_line() to allow the previously returned + line to be passed in the start_line argument. + getline.c man/gl_get_line.3 + I added a recall-history action function, bound to M^P. + This recalls the last recalled history line, regardless + of whether it was from the current or previous line. + +13/12/2000 mcs@astro.caltech.edu (Release of version 1.1i) + + getline.c history.h history.c man/gl_get_line.3 + I implemented the equivalent of the ksh Operate action. I + have named the tecla equivalent "repeat-history". This + causes the line that is to be edited to returned, and + arranges for the next most recent history line to be + preloaded on the next call to gl_get_line(). Repeated + invocations of this action thus result in successive + history lines being repeated - hence the + name. Implementing the ksh Operate action was suggested + by Markus Gyger. In ksh it is bound to ^O, but since ^O + is traditionally bound by the default terminal settings, + to stop-output, I have bound the tecla equivalent to M-o. + +01/12/2000 mcs@astro.caltech.edu (Release of version 1.1h) + + getline.c keytab.c keytab.h man/gl_get_line.3 + I added a digit-argument action, to allow repeat + counts for actions to be entered. As in both tcsh + and readline, this is bound by default to each of + M-0, M-1 through to M-9, the number being appended + to the current repeat count. Once one of these has been + pressed, the subsequent digits of the repeat count can be + typed with or without the meta key pressed. It is also + possible to bind digit-argument to other keys, with or + without a numeric final keystroke. See man page for + details. + + getline.c man/gl_get_line.3 + Markus noted that my choice of M-< for the default + binding of read-from-file, could be confusing, since + readline binds this to beginning-of-history. I have + thus rebound it to ^X^F (ie. like find-file in emacs). + + getline.c history.c history.h man/gl_get_line.3 + I have now implemented equivalents of the readline + beginning-of-history and end-of-history actions. + These are bound to M-< and M-> respectively. + + history.c history.h + I Moved the definition of the GlHistory type, and + its subordinate types from history.h to history.c. + There is no good reason for any other module to + have access to the innards of this structure. + +27/11/2000 mcs@astro.caltech.edu (Release of version 1.1g) + + getline.c man/gl_get_line.3 + I added a "read-from-file" action function and bound it + by default to M-<. This causes gl_get_line() to + temporarily return input from the file who's name + precedes the cursor. + +26/11/2000 mcs@astro.caltech.edu + + getline.c keytab.c keytab.h man/gl_get_line.3 + I have reworked some of the keybinding code again. + + Now, within key binding strings, in addition to the + previously existing notation, you can now use M-a to + denote meta-a, and C-a to denote control-a. For example, + a key binding which triggers when the user presses the + meta key, the control key and the letter [ + simultaneously, can now be denoted by M-C-[, or M-^[ or + \EC-[ or \E^[. + + I also updated the man page to use M- instead of \E in + the list of default bindings, since this looks cleaner. + + getline.c man/gl_get_line.3 + I added a copy-region-as-kill action function and + gave it a default binding to M-w. + +22/11/2000 mcs@astro.caltech.edu + + *.c + Markus Gyger sent me a copy of a previous version of + the library, with const qualifiers added in appropriate + places. I have done the same for the latest version. + Among other things, this gets rid of the warnings + that are generated if one tells the compiler to + const qualify literal strings. + + getline.c getline.h glconf.c + I have moved the contents of glconf.c and the declaration + of the GetLine structure into getline.c. This is cleaner, + since now only functions in getline.c can mess with the + innards of GetLine objects. It also clears up some problems + with system header inclusion order under Solaris, and also + the possibility that this might result in inconsistent + system macro definitions, which in turn could cause different + declarations of the structure to be seen in different files. + + hash.c + I wrote a wrapper function to go around strcmp(), such that + when hash.c is compiled with a C++ compiler, the pointer + to the wrapper function is a C++ function pointer. + This makes it compatible with comparison function pointer + recorded in the hash table. + + cplmatch.c getline.c libtecla.h + Markus noted that the Sun C++ compiler wasn't able to + match up the declaration of cpl_complete_word() in + libtecla.h, where it is surrounded by a extern "C" {} + wrapper, with the definition of this function in + cplmatch.c. My suspicion is that the compiler looks not + only at the function name, but also at the function + arguments to see if two functions match, and that the + match_fn() argument, being a fully blown function pointer + declaration, got interpetted as that of a C function in + one case, and a C++ function in the other, thus + preventing a match. + + To fix this I now define a CplMatchFn typedef in libtecla.h, + and use this to declare the match_fn callback. + +20/11/2000 (Changes suggested by Markus Gyger to support C++ compilers): + expand.c + Renamed a variable called "explicit" to "xplicit", to + avoid conflicts when compiling with C++ compilers. + *.c + Added explicit casts when converting from (void *) to + other pointer types. This isn't needed in C but it is + in C++. + getline.c + tputs() has a strange declaration under Solaris. I was + enabling this declaration when the SPARC feature-test + macro was set. Markus changed the test to hinge on the + __sun and __SVR4 macros. + direader.c glconf.c stringrp.c + I had omitted to include string.h in these two files. + + Markus also suggested some other changes, which are still + under discussion. With the just above changes however, the + library compiles without complaint using g++. + +19/11/2000 mcs@astro.caltech.edu + getline.h getline.c keytab.c keytab.h glconf.c + man/gl_get_line.3 + I added support for backslash escapes (include \e + for the keyboard escape key) and literal binary + characters to the characters allowed within key sequences + of key bindings. + + getline.h getline.c keytab.c keytab.h glconf.c + man/gl_get_line.3 + I introduced symbolic names for the arrow keys, and + modified the library to use the cursor key sequences + reported by terminfo/termcap in addition to the default + ANSI ones. Anything bound to the symbolically named arrow + keys also gets bound to the default and terminfo/termcap + cursor key sequences. Note that under Solaris + terminfo/termcap report the properties of hardware X + terminals when TERM is xterm instead of the terminal + emulator properties, and the cursor keys on these two + systems generate different key sequences. This is an + example of why extra default sequences are needed. + + getline.h getline.c keytab.c + For some reason I was using \e to represent the escape + character. This is supported by gcc, which thus doesn't + emit a warning except with the -pedantic flag, but isn't + part of standard C. I now use a macro to define escape + as \033 in getline.h, and this is now used wherever the + escape character is needed. + +17/11/2000 mcs@astro.caltech.edu (Release of version 1.1d) + + getline.c, man/gl_get_line(3), html/gl_get_line.html + In tcsh ^D is bound to a function which does different + things depending on where the cursor is within the input + line. I have implemented its equivalent in the tecla + library. When invoked at the end of the line this action + function displays possible completions. When invoked on + an empty line it causes gl_get_line() to return NULL, + thus signalling end of input. When invoked within a line + it invokes forward-delete-char, as before. The new action + function is called del-char-or-list-or-eof. + + getline.c, man/gl_get_line(3), html/gl_get_line.html + I found that the complete-word and expand-file actions + had underscores in their names instead of hyphens. This + made them different from all other action functions, so I + have changed the underscores to hyphens. + + homedir.c + On SCO UnixWare while getpwuid_r() is available, the + associated _SC_GETPW_R_SIZE_MAX macro used by sysconf() + to find out how big to make the buffer to pass to this + function to cater for any password entry, doesn't + exist. I also hadn't catered for the case where sysconf() + reports that this limit is indeterminate. I have thus + change the code to substitute a default limit of 1024 if + either the above macro isn't defined or if sysconf() says + that the associated limit is indeterminate. + +17/11/2000 mcs@astro.caltech.edu (Release of version 1.1c) + + getline.c, getline.h, history.c, history.h + I have modified the way that the history recall functions + operate, to make them better emulate the behavior of + tcsh. Previously the history search bindings always + searched for the prefix that preceded the cursor, then + left the cursor at the same point in the line, so that a + following search would search using the same prefix. This + isn't how tcsh operates. On finding a matching line, tcsh + puts the cursor at the end of the line, but arranges for + the followup search to continue with the same prefix, + unless the user does any cursor motion or character + insertion operations in between, in which case it changes + the search prefix to the new set of characters that are + before the cursor. There are other complications as well, + which I have attempted to emulate. As far as I can + tell, the tecla history recall facilities now fully + emulate those of tcsh. + +16/11/2000 mcs@astro.caltech.edu (Release of version 1.1b) + + demo.c: + One can now quit from the demo by typing exit. + + keytab.c: + The first entry of the table was getting deleted + by _kt_clear_bindings() regardless of the source + of the binding. This deleted the up-arrow binding. + Symptoms noted by gazelle@yin.interaccess.com. + + getline.h: + Depending on which system include files were include + before the inclusion of getline.h, SIGWINCH and + TIOCGWINSZ might or might not be defined. This resulted + in different definitions of the GetLine object in + different files, and thus some very strange bugs! I have + now added #includes for the necessary system header files + in getline.h itself. The symptom was that on creating a + ~/.teclarc file, the demo program complained of a NULL + argument to kt_set_keybinding() for the first line of the + file. + +15/11/2000 mcs@astro.caltech.edu (Release of version 1.1a) + + demo.c: + I had neglected to check the return value of + new_GetLine() in the demo program. Oops. + + getline.c libtecla.h: + I wrote gl_change_terminal(). This allows one to change to + a different terminal or I/O stream, by specifying the + stdio streams to use for input and output, along with the + type of terminal that they are connected to. + + getline.c libtecla.h: + Renamed GetLine::isterm to GetLine::is_term. Standard + C reserves names that start with "is" followed by + alphanumeric characters, so this avoids potential + clashes in the future. + + keytab.c keytab.h + Each key-sequence can now have different binding + functions from different sources, with the user provided + binding having the highest precedence, followed by the + default binding, followed by any terminal specific + binding. This allows gl_change_terminal() to redefine the + terminal-specific bindings each time that + gl_change_terminal() is called, without overwriting the + user specified or default bindings. In the future, it will + also allow for reconfiguration of user specified + bindings after the call to new_GetLine(). Ie. deleting a + user specified binding should reinstate any default or + terminal specific binding. + + man/cpl_complete_word.3 html/cpl_complete_word.html + man/ef_expand_file.3 html/ef_expand_file.html + man/gl_get_line.3 html/gl_get_line.html + I added sections on thread safety to the man pages of the + individual modules. + + man/gl_get_line.3 html/gl_get_line.html + I documented the new gl_change_terminal() function. + + man/gl_get_line.3 html/gl_get_line.html + In the description of the ~/.teclarc configuration file, + I had omitted the 'bind' command word in the example + entry. I have now remedied this. diff --git a/libtecla-1.6.1/INSTALL b/libtecla-1.6.1/INSTALL new file mode 100644 index 0000000..14fc62d --- /dev/null +++ b/libtecla-1.6.1/INSTALL @@ -0,0 +1,213 @@ +To compile and optionally install the library, it is first necessary +to create a makefile for your system, by typing: + + ./configure + +The Makefile that this generates is designed to install the files of +the library in subdirectories of /usr/local/. If you would prefer to +install them under a different directory, you can type: + + ./configure --prefix /wherever + +Where you would replace /wherever with your chosen directory. Other +command-line options are available, and can be listed by typing: + + ./configure --help + +Having run the configure script, you are then ready to make the +library. To do this, just type: + + make + +What 'make' does depends on whether the configure script knows about +your system. If the configure script doesn't know anything specific +about your system, it will arrange for 'make' to produce the static +tecla library, called libtecla.a, and if possible, the reentrant +version of this called libtecla_r.a. If it does know about your +system, it will also create shared libraries if possible. If you are +on a system that isn't known, and you would like shared libraries to +be compiled, please read the file called PORTING to see how this can +be achieved. + +To install the library, its include file and it manual pages, type: + + make install + +Note that this will also compile the library if you haven't already +done so. + +Having compiled the library, if you wish, you can test it by running +the demo programs. After building the library, you should find two +programs, called demo and demo2, in the current directory. + +The first of the demos programs reads input lines from the user, and +writes what was typed back to the screen. While typing a line of +input, you can experiment with line editing, tab completion, history +recall etc.. For details about these line editing features, see the +man page gl_get_line(3). If you haven't installed this yet, you can +see it anyway by typing: + + nroff -man man3/gl_get_line.3 | more + +The second demo program, called demo2, demonstrates command-completion +with the UNIX PATH. If you type in a partial command name, and press +TAB, the command name will be completed if possible, and possible +completions will be listed if it is ambiguous. When you then enter the +line, the demo program then prints out the full pathname of the +command that you typed. If you type anything after the command name, +filename completion with the tab key reverts to its default behavior +of completing filenames in the current directory. + +COMPILING IN A DIFFERENT DIRECTORY +---------------------------------- +If you unpack the distribution in a directory which is visible from +multiple hosts which have different architectures, you have the option +of compiling the library for the different architectures in different +directories. You might for example create a sub-directory for each +architecture, under the top level directory of the distribution. You +would then log in to a host of one of these architectures, cd to the +sub-directory that you created for it, and type: + + ../configure + +The configure script then creates a makefile in the current directory +which is designed to build the library, object files, demos etc for +the architecture of the current host, in the current directory, using +the original source code in ../. You then repeat this procedure on +hosts of other architectures. + +The compilation directories don't have to be sub-directories of the +top level directory of the distribution. That was just described as an +example. They can be anywhere you like. + +Every rule in the makefiles that are generated by the configure +script, cites the paths of the target and source files explicitly, so +this procedure should work on any system, without the need for vpath +makefile support. + +EMBEDDING IN OTHER PACKAGE DISTRIBUTIONS +---------------------------------------- + +If you distribute the library with another package, which has its own +heirarchy and configuration procedures, the following installation +options may be of interest to you. At first glance, the use of a GNU +configure script by the tecla library, may appear to reduce your +options for controlling what gets made, and where it gets installed, +but this isn't the case, because many of the parameters configured by +the configure script are assigned to makefile variables which can be +overriden when you run make. + +Configure script options: + + If the users of your package won't benefit from having access to the + tecla man pages, you can shorten the length of time taken to run the + configure script by telling this script not to preprocess the man + pages. This is done as follows. + + ./configure --without-man-pages + + Note that this option also causes the makefile man-page installation + targets to do nothing. + + Similarly, if you don't want your users to have access to the + filesystem while they are editing input lines using gl_get_line(), + then use the following configuration argument. + + ./configure --without-file-actions + + This will completely remove the "expand-filename", "read-from-file", + "read-init-files", and "list-glob" action functions. It will also + arrange that the default behavior of actions such as "complete-word" + and "list-or-eof" be changed to show no completions, instead of the + normal default of showing filename completions. + + If you are using a system that doesn't have a file-system, such as an + embedded system, then libtecla can be built with all code that + accesses the filesystem removed. This will make the library a bit + smaller, and potentially avoid running into problems of missing system + functions related to file-system access. This is done with the + following configuration option. + + ./configure --without-file-system + + Beware that this not only prevents the user from accessing the + filesystem while editing an input line in gl_get_line(), but also + removes all public file-related functions, such as the pathname + expansion module. When using gl_get_line() in this configuration, + the functions that load and save history from and to files, are + stubs that report an error if called upon to read files. The + gl_configure_getline() function can still be called upon to + configure gl_get_line() via its app_string argument, but passing it + a filename in either the app_file or user_file arguments will result + in an error being reported. + +Now lets say that you have your own configuration script in the parent +directory of the libtecla top-level directory, and that you don't want +tecla's man pages to be generated. In your configuration script, you +would first need to have a line similar to the following: + + (cd libtecla; ./configure --without-man-pages) + +Now, from your makefile or whatever script you use to build your +application, you would need to make the tecla library. Assuming that +your makefile or build script is in the parent directory of the +libtecla distribution, then the following line tells make to just +build the non-reentrant, static version of the tecla library, and then +to install it and the tecla include file in sub-directories called lib +and include in your current directory. + + (cd libtecla; make LIBDIR=../lib INCDIR=../include TARGETS=normal TARGET_LIBS="static" install_lib install_inc) + +In this statement the LIBDIR=../lib component means that on installing +the library, the make command should place the library in the +directory libtecla/../lib. Similarly INCDIR tells make where to place +the include files. The install_lib and install_inc targets tell make +to install the libraries and the include file. Because the install_man +and install_bin targets have been omitted in this example, the man +pages and programs aren't installed. If you were to include these +additional targets then you could use the MANDIR and BINDIR variables, +respectively to control where they were installed. + +The TARGETS variable is used to specify which of the normal and +reentrant versions of the library are compiled. This can contain one +or both of the words "normal" and "reentrant". If you don't specify +this when you invoke make, the default value generated by the +configure script will be used. Depending on whether reentrant POSIX +functions are available for compilation of the reentrant version, this +will be either "normal" or "normal reentrant". + +The TARGET_LIBS variable is used to specify which of the static and +shared libraries are to be built. This can contain one or both of the +words "static" and "shared". If you don't specify this when you invoke +make, the default value generated by the configure script will be +used. Depending on whether the configure script knows how to create +shared libraries on the target system, this will be either "static" or +"static shared". Beware that shared libraries aren't supported on many +systems, so requiring "shared" will limit which systems you can +compile your package on. Also note that unless your package installs +the tecla library in a directory which all users of your program will +have access to, you should only compile the static version. +Instructions for adding shared-library creation rules for new systems +are included in the PORTING file. + +The OPT variable can be used to change the default optimization from +the default of "-O" to something else. + +The DEMOS variable controls whether the demo programs are built. +Normally this has the value "demos", which tells the makefile to build +the demo programs. Setting it to an empty string stops the demos from +being built. + +The PROGRAMS variable is used to specify which programs are to be +built and subsequently installed. All available programs are built by +default. Currently there is only one such program, selected by +specifying the word "enhance". This program uses tecla-enhanced +pseudo-terminals to layer command line editing on top of third party +programs. + +The PROGRAMS_R variable serves the same purpose as the PROGRAMS +variable, except that programs listed here are linked with the +reentrant version of the library, and should be specified with a _r +suffix. Currently this variable is empty by default. + +Martin Shepherd (mcs@astro.caltech.edu) diff --git a/libtecla-1.6.1/LICENSE.TERMS b/libtecla-1.6.1/LICENSE.TERMS new file mode 100644 index 0000000..e5d9454 --- /dev/null +++ b/libtecla-1.6.1/LICENSE.TERMS @@ -0,0 +1,28 @@ +Copyright (c) 2000, 2001, 2002, 2003, 2004 by Martin C. Shepherd. + +All rights reserved. + +Permission is hereby granted, free of charge, to any person obtaining a +copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, and/or sell copies of the Software, and to permit persons +to whom the Software is furnished to do so, provided that the above +copyright notice(s) and this permission notice appear in all copies of +the Software and that both the above copyright notice(s) and this +permission notice appear in supporting documentation. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT +OF THIRD PARTY RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR +HOLDERS INCLUDED IN THIS NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL +INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING +FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, +NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION +WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + +Except as contained in this notice, the name of a copyright holder +shall not be used in advertising or otherwise to promote the sale, use +or other dealings in this Software without prior written authorization +of the copyright holder. diff --git a/libtecla-1.6.1/Makefile b/libtecla-1.6.1/Makefile new file mode 100644 index 0000000..cf89638 --- /dev/null +++ b/libtecla-1.6.1/Makefile @@ -0,0 +1,12 @@ +default: + ./configure + $(MAKE) + +distclean: + ./configure --without-man-pages + $(MAKE) $@ + +normal reentrant demos demos_r clean install_lib install_bin install_inc \ + install_man install: + ./configure + $(MAKE) $@ diff --git a/libtecla-1.6.1/Makefile.in b/libtecla-1.6.1/Makefile.in new file mode 100644 index 0000000..4b7c9c3 --- /dev/null +++ b/libtecla-1.6.1/Makefile.in @@ -0,0 +1,272 @@ +#----------------------------------------------------------------------- +# This is the template that the libtecla configure script uses to create +# the libtecla Makefile. It does this by replacing all instances of +# @name@ with the value of the correspondingly named configuration +# variable. You should find another file in the same directory as this +# one, called "configure.in". The latter file contains extensive comments +# explaining how this all works. +#----------------------------------------------------------------------- + +# Where is the source code? + +srcdir = @srcdir@ + +# Where do you want to install the library, its header file, and the man pages? + +prefix=@prefix@ +exec_prefix=@exec_prefix@ +LIBDIR=@libdir@ +INCDIR=@includedir@ +MANDIR=@mandir@ +BINDIR=@bindir@ + +# Which C compiler do you want to use? + +CC = @CC@ + +# If 'make' doesn't define the MAKE variable, define it here. + +@SET_MAKE@ + +# To use RANLIB set the RANLIB variable to ranlib. Otherwise set it to +# :, which is the bourne shell do-nothing command. + +RANLIB = @RANLIB@ + +# Optional flags to pass to the linker. + +LDFLAGS = @LDFLAGS@ + +# Optional C pre-processor flags. + +CPPFLAGS = @CPPFLAGS@ + +# The following optional defines change the characteristics of the library. +# +# USE_TERMINFO +# Use the terminfo terminal information database when looking up +# terminal characteristics. Most modern UNIX and UNIX-like operating +# systems support terminfo, so this define should normally be included. +# If in doubt leave it in, and see if the library compiles. +# USE_TERMCAP +# If you don't have terminfo but do have the termcap database, replace +# the -DUSE_TERMINFO with -DUSE_TERMCAP. If there is a termcap.h in +# /usr/include/, also add -DHAVE_TERMCAP_H. +# +# If neither USE_TERMINFO nor USE_TERMCAP are included, ANSI VT100 control +# sequences will be used to control all terminal types. +# +# For Solaris and Linux, use: +# +# DEFINES = -DUSE_TERMINFO +# + +DEFINES = @DEFS@ + +# +# The following defines are used in addition to the above when compiling +# the reentrant version of the library. Note that the definition of +# _POSIX_C_SOURCE to request reentrant functions, has the unfortunate +# side-effect on some systems of stopping the TIOCGWINSZ ioctl macro from +# getting defined. This in turn stops the library from being +# able to respond to terminal size changes. Under Solaris this can be +# remedied by adding -D__EXTENSIONS__. On linux this isn't necessary. +# If you don't get this right, the library will still work, but +# it will get confused if the terminal size gets changed and you try to +# edit a line that exceeds the terminal width. +# +# Thus on Solaris you should use: +# +# DEFINES_R = -D_POSIX_C_SOURCE=199506L -D__EXTENSIONS__ +# +# and on linux you should use: +# +# DEFINES_R = -D_POSIX_C_SOURCE=199506L +# + +DEFINES_R = @DEFS_R@ + +# +# The compiler optimization flags. I like to keep this separate so +# that I can set it to -g from the 'make' command line without having +# to edit this file when debugging the library. If you aren't working +# on modifying the library, leave this set to -O. +# + +OPT = -O + +# +# These are paranoid gcc warning flags to use when compiling new code. +# Simply invoke make with WARNING_FLAGS='$(PEDANTIC_FLAGS)'. +# +PEDANTIC_FLAGS=-Wall -Wshadow -Wpointer-arith -Wwrite-strings -Wstrict-prototypes -Wmissing-prototypes -Wmissing-declarations -Wredundant-decls + +# +# Specify any extra compiler warning options that you want to use. +# Leave this blank unless you are porting the library to a new system, +# or modifying the library. +# + +WARNING_FLAGS= + +# +# If you want to compile the demo program, specify any system +# libraries that are needed for the terminal I/O functions. +# +# If you are using terminfo, you will probably only need -lcurses. +# For termcap you may need -ltermcap or -ltermlib. +# +# For Solaris, use: +# +# LIBS = -lcurses +# +# For linux, use: +# +# LIBS = -lncurses +# + +LIBS = @LIBS@ + +# +# List the default target libraries. This should be one or +# both of the words "normal" and "reentrant". +# +TARGETS = @TARGETS@ + +# +# List which types of the above libraries are required. +# This should be one or both of the words "static" and "shared". +# +TARGET_LIBS = @TARGET_LIBS@ + +# +# If you want the demo programs to be built, the following variable +# should be assigned the single word: demos. If it isn't assigned +# anything, the demo programs won't be built. +# +DEMOS = demos + +# +# List the programs that are to be made by default. +# +PROGRAMS = enhance + +# +# List programs for which reentrant versions are to be built by default. +# +PROGRAMS_R = + +#----------------------------------------------------------------------- +# You shouldn't need to change anything below this line. +#----------------------------------------------------------------------- + +CFLAGS = $(OPT) $(WARNING_FLAGS) $(DEFINES) @CFLAGS@ @SHARED_CFLAGS@ + +default: $(TARGETS) + +normal: + @$(MAKE) -f $(srcdir)/Makefile.rules TARGETS="$(TARGET_LIBS)" SUFFIX="" CFLAGS="$(CFLAGS)" CC="$(CC)" OBJDIR=normal_obj LINK_SHARED='@LINK_SHARED@' SHARED_EXT='@SHARED_EXT@' SHARED_ALT='@SHARED_ALT@' LIBS='$(LIBS)' srcdir='$(srcdir)' LIBDIR='$(LIBDIR)' LN_S='@LN_S@' DEMOS="$(DEMOS)" PROGRAMS='$(PROGRAMS)' RANLIB='$(RANLIB)' LDFLAGS=$(LDFLAGS) CPPFLAGS=$(CPPFLAGS) + +reentrant: + @$(MAKE) -f $(srcdir)/Makefile.rules TARGETS="$(TARGET_LIBS)" SUFFIX="_r" CFLAGS="$(CFLAGS) $(DEFINES_R)" CC="$(CC)" OBJDIR=reentrant_obj LINK_SHARED='@LINK_SHARED@' SHARED_EXT='@SHARED_EXT@' SHARED_ALT='@SHARED_ALT@' LIBS='$(LIBS)' srcdir='$(srcdir)' LIBDIR='$(LIBDIR)' LN_S='@LN_S@' DEMOS="$(DEMOS)" PROGRAMS='$(PROGRAMS_R)' RANLIB='$(RANLIB)' LDFLAGS=$(LDFLAGS) CPPFLAGS=$(CPPFLAGS) + +demos: normal + +demos_r: reentrant + +clean: + rm -rf *.o normal_obj reentrant_obj libtecla*.a demo demo[0-9] demo_r demo[0-9]_r enhance *~ man/*~ man/*/*~ html/*~ compile_reentrant compile_normal `/bin/ls -1 man/*/*.in | sed 's/\.in$$//'` + @endings="@SHARED_EXT@ @SHARED_ALT@" ; \ + for alt in $$endings ; do \ + lib="libtecla*$$alt" ; \ + rm -f $$lib; echo rm -f $$lib ; \ + done + +distclean: clean + rm -rf config.cache config.status config.log Makefile libtecla.map.opt \ + autom*.cache + cp $(srcdir)/Makefile.stub Makefile + +install_lib: $(TARGETS) $(LIBDIR) + @for lib in libtecla.a libtecla_r.a ; do \ + if [ -f $$lib ] ; then \ + cp $$lib $(LIBDIR)/ ; chmod ugo+r $(LIBDIR)/$$lib; \ + echo "cp $$lib $(LIBDIR)/ ; chmod ugo+r $(LIBDIR)/$$lib"; \ + fi ; \ + done + @for lib in libtecla libtecla_r ; do \ + src="$$lib@SHARED_EXT@"; \ + if [ -f $$src ] ; then \ + dst="$(LIBDIR)/$$src"; \ + cp -f $$src $$dst; chmod a=rX $$dst; \ + echo "cp -f $$src $$dst ; chmod a=rX $$dst"; \ + endings="@SHARED_ALT@" ; \ + for alt in $$endings ; do \ + lnk="$$lib$$alt"; \ + (cd $(LIBDIR); rm -f $$lnk; @LN_S@ $$src $$lnk); \ + echo "(cd $(LIBDIR); rm -f $$lnk; @LN_S@ $$src $$lnk)"; \ + done ; \ + fi ; \ + done + +install_inc: $(INCDIR) + @if [ -f $(srcdir)/libtecla.h ]; then \ + cp $(srcdir)/libtecla.h $(INCDIR)/ ; chmod ugo+r $(INCDIR)/libtecla.h; \ + echo "cp $(srcdir)/libtecla.h $(INCDIR)/ ; chmod ugo+r $(INCDIR)/libtecla.h"; \ + fi + +install_man: $(MANDIR) libr_man func_man prog_man misc_man file_man + +libr_man: + if test "@MAKE_MAN_PAGES@"_ = "yes"_; then \ + for template in man/libr/*.in; do \ + page=`basename "$$template" .in`; \ + $(srcdir)/install-sh -c -m 644 man/libr/$$page ${MANDIR}/@LIBR_MANDIR@/$$page.@LIBR_MANEXT@; \ + done ; \ + fi + +func_man: + if test "@MAKE_MAN_PAGES@"_ = "yes"_; then \ + for template in man/func/*.in; do \ + page=`basename "$$template" .in`; \ + $(srcdir)/install-sh -c -m 644 man/func/$$page ${MANDIR}/@FUNC_MANDIR@/$$page.@FUNC_MANEXT@; \ + done ; \ + fi + +prog_man: + if test "@MAKE_MAN_PAGES@"_ = "yes"_; then \ + for template in man/prog/*.in; do \ + page=`basename "$$template" .in`; \ + $(srcdir)/install-sh -c -m 644 man/prog/$$page ${MANDIR}/@PROG_MANDIR@/$$page.@PROG_MANEXT@; \ + done ; \ + fi + +misc_man: + if test "@MAKE_MAN_PAGES@"_ = "yes"_; then \ + for template in man/misc/*.in; do \ + page=`basename "$$template" .in`; \ + $(srcdir)/install-sh -c -m 644 man/misc/$$page ${MANDIR}/@MISC_MANDIR@/$$page.@MISC_MANEXT@; \ + done ; \ + fi + +file_man: + if test "@MAKE_MAN_PAGES@"_ = "yes"_; then \ + for template in man/file/*.in; do \ + page=`basename "$$template" .in`; \ + $(srcdir)/install-sh -c -m 644 man/file/$$page ${MANDIR}/@FILE_MANDIR@/$$page.@FILE_MANEXT@; \ + done ; \ + fi + +install_bin: $(BINDIR) $(PROGRAMS) $(PROGRAMS_R) + progs="$(PROGRAMS) $(PROGRAMS_R)"; \ + for prog in $$progs; do \ + $(srcdir)/install-sh -c -m 755 -s $$prog $(BINDIR)/; \ + done + +install: install_lib install_inc install_man install_bin + +# Make any missing installation directories. + +$(MANDIR) $(LIBDIR) $(INCDIR) $(BINDIR): + $(srcdir)/install-sh -d $@ + chmod ugo+rx $@ diff --git a/libtecla-1.6.1/Makefile.rules b/libtecla-1.6.1/Makefile.rules new file mode 100644 index 0000000..22508bf --- /dev/null +++ b/libtecla-1.6.1/Makefile.rules @@ -0,0 +1,169 @@ +default: $(OBJDIR) $(TARGETS) $(DEMOS) $(PROGRAMS) + +#----------------------------------------------------------------------- +# You shouldn't need to change anything in this file. +#----------------------------------------------------------------------- + +# Create the directory in which the object files will be created. + +$(OBJDIR): + mkdir $(OBJDIR) + +# Construct the compilation command. + +COMPILE = $(CC) -c $(CFLAGS) -o $@ + +LIB_OBJECTS = $(OBJDIR)/getline.o $(OBJDIR)/keytab.o $(OBJDIR)/freelist.o \ + $(OBJDIR)/strngmem.o $(OBJDIR)/hash.o $(OBJDIR)/history.o \ + $(OBJDIR)/direader.o $(OBJDIR)/homedir.o $(OBJDIR)/pathutil.o \ + $(OBJDIR)/expand.o $(OBJDIR)/stringrp.o $(OBJDIR)/cplfile.o \ + $(OBJDIR)/cplmatch.o $(OBJDIR)/pcache.o $(OBJDIR)/version.o \ + $(OBJDIR)/chrqueue.o $(OBJDIR)/ioutil.o $(OBJDIR)/errmsg.o + +# List the available demonstration programs. + +DEMO_PROGS = demo$(SUFFIX) demo2$(SUFFIX) demo3$(SUFFIX) + +# List all of the programs that this makefile can build. + +PROGS = $(DEMO_PROGS) enhance$(SUFFIX) + +static: libtecla$(SUFFIX).a + +libtecla$(SUFFIX).a: $(LIB_OBJECTS) + ar -ru $@ $(LIB_OBJECTS); \ + $(RANLIB) $@; \ + rm -f $(PROGS) + +shared: libtecla$(SUFFIX)$(SHARED_EXT) + +libtecla$(SUFFIX)$(SHARED_EXT): $(LIB_OBJECTS) $(srcdir)/libtecla.map \ + libtecla.map.opt + $(LINK_SHARED) + @endings="$(SHARED_ALT)" ; \ + for alt in $$endings ; do \ + lnk="libtecla$(SUFFIX)$$alt"; \ + echo "rm -f $$lnk; $(LN_S) $@ $$lnk"; \ + rm -f $$lnk; $(LN_S) $@ $$lnk; \ + done; \ + rm -f $(PROGS) + +libtecla.map.opt: $(srcdir)/libtecla.map + sed -n 's/^[ ]*\([_a-zA-Z0-9]*\)[ ]*;.*/+e \1/p' $? >$@ + +demos: $(DEMO_PROGS) + +demo$(SUFFIX): $(OBJDIR)/demo.o + LD_RUN_PATH="$(LIBDIR):$$LD_RUN_PATH:`pwd`" $(CC) $(CFLAGS) -o $@ \ + $(OBJDIR)/demo.o -L. -ltecla$(SUFFIX) $(LIBS) + +demo2$(SUFFIX): $(OBJDIR)/demo2.o + LD_RUN_PATH="$(LIBDIR):$$LD_RUN_PATH:`pwd`" $(CC) $(CFLAGS) -o $@ \ + $(OBJDIR)/demo2.o -L. -ltecla$(SUFFIX) $(LIBS) + +demo3$(SUFFIX): $(OBJDIR)/demo3.o + LD_RUN_PATH="$(LIBDIR):$$LD_RUN_PATH:`pwd`" $(CC) $(CFLAGS) -o $@ \ + $(OBJDIR)/demo3.o -L. -ltecla$(SUFFIX) $(LIBS) + +enhance$(SUFFIX): $(OBJDIR)/enhance.o + LD_RUN_PATH="$(LIBDIR):$$LD_RUN_PATH:`pwd`" $(CC) $(CFLAGS) -o $@ \ + $(OBJDIR)/enhance.o -L. -ltecla$(SUFFIX) $(LIBS) + +#----------------------------------------------------------------------- +# Object file dependencies. +#----------------------------------------------------------------------- + +$(OBJDIR)/getline.o: $(srcdir)/getline.c $(srcdir)/pathutil.h \ + $(srcdir)/libtecla.h $(OBJDIR)/keytab.h $(srcdir)/history.h \ + $(srcdir)/freelist.h $(srcdir)/stringrp.h $(srcdir)/getline.h \ + $(srcdir)/ioutil.h $(srcdir)/chrqueue.h $(srcdir)/cplmatch.h \ + $(srcdir)/expand.h $(srcdir)/errmsg.h + $(COMPILE) $(srcdir)/getline.c + +$(OBJDIR)/keytab.o: $(srcdir)/keytab.c $(OBJDIR)/keytab.h \ + $(srcdir)/strngmem.h $(srcdir)/getline.h $(srcdir)/errmsg.h \ + $(srcdir)/hash.h + $(COMPILE) $(srcdir)/keytab.c + +$(OBJDIR)/strngmem.o: $(srcdir)/strngmem.c $(srcdir)/strngmem.h \ + $(srcdir)/freelist.h + $(COMPILE) $(srcdir)/strngmem.c + +$(OBJDIR)/freelist.o: $(srcdir)/freelist.c $(srcdir)/freelist.h + $(COMPILE) $(srcdir)/freelist.c + +$(OBJDIR)/hash.o: $(srcdir)/hash.c $(srcdir)/hash.h $(srcdir)/strngmem.h \ + $(srcdir)/freelist.h + $(COMPILE) $(srcdir)/hash.c + +$(OBJDIR)/history.o: $(srcdir)/history.c $(srcdir)/ioutil.h \ + $(srcdir)/history.h $(srcdir)/freelist.h $(srcdir)/errmsg.h + $(COMPILE) $(srcdir)/history.c + +$(OBJDIR)/expand.o: $(srcdir)/expand.c $(srcdir)/freelist.h \ + $(srcdir)/direader.h $(srcdir)/pathutil.h $(srcdir)/homedir.h \ + $(srcdir)/stringrp.h $(srcdir)/libtecla.h $(srcdir)/ioutil.h \ + $(srcdir)/expand.h $(srcdir)/errmsg.h + $(COMPILE) $(srcdir)/expand.c + +$(OBJDIR)/direader.o: $(srcdir)/direader.c $(srcdir)/direader.h \ + $(srcdir)/errmsg.h + $(COMPILE) $(srcdir)/direader.c + +$(OBJDIR)/homedir.o: $(srcdir)/homedir.c $(srcdir)/pathutil.h \ + $(srcdir)/homedir.h $(srcdir)/errmsg.h + $(COMPILE) $(srcdir)/homedir.c + +$(OBJDIR)/pathutil.o: $(srcdir)/pathutil.c $(srcdir)/pathutil.h + $(COMPILE) $(srcdir)/pathutil.c + +$(OBJDIR)/stringrp.o: $(srcdir)/stringrp.c $(srcdir)/freelist.h \ + $(srcdir)/stringrp.h + $(COMPILE) $(srcdir)/stringrp.c + +$(OBJDIR)/cplfile.o: $(srcdir)/cplfile.c $(srcdir)/libtecla.h \ + $(srcdir)/direader.h $(srcdir)/homedir.h $(srcdir)/pathutil.h \ + $(srcdir)/cplfile.h $(srcdir)/errmsg.h + $(COMPILE) $(srcdir)/cplfile.c + +$(OBJDIR)/cplmatch.o: $(srcdir)/cplmatch.c $(srcdir)/libtecla.h \ + $(srcdir)/ioutil.h $(srcdir)/stringrp.h $(srcdir)/pathutil.h \ + $(srcdir)/cplfile.h $(srcdir)/cplmatch.h $(srcdir)/errmsg.h + $(COMPILE) $(srcdir)/cplmatch.c + +$(OBJDIR)/pcache.o: $(srcdir)/pcache.c $(srcdir)/libtecla.h \ + $(srcdir)/pathutil.h $(srcdir)/homedir.h $(srcdir)/freelist.h \ + $(srcdir)/direader.h $(srcdir)/stringrp.h $(errmsg.h) + $(COMPILE) $(srcdir)/pcache.c + +$(OBJDIR)/demo.o: $(srcdir)/demo.c $(srcdir)/libtecla.h + $(COMPILE) $(srcdir)/demo.c + +$(OBJDIR)/demo2.o: $(srcdir)/demo2.c $(srcdir)/libtecla.h + $(COMPILE) $(srcdir)/demo2.c + +$(OBJDIR)/demo3.o: $(srcdir)/demo3.c $(srcdir)/libtecla.h + $(COMPILE) $(srcdir)/demo3.c + +$(OBJDIR)/version.o: $(srcdir)/version.c $(srcdir)/libtecla.h + $(COMPILE) $(srcdir)/version.c + +$(OBJDIR)/enhance.o: $(srcdir)/enhance.c $(srcdir)/libtecla.h + $(COMPILE) $(srcdir)/enhance.c + +$(OBJDIR)/chrqueue.o: $(srcdir)/chrqueue.c $(srcdir)/ioutil.h \ + $(srcdir)/chrqueue.h $(srcdir)/freelist.h $(srcdir)/errmsg.h + $(COMPILE) $(srcdir)/chrqueue.c + +$(OBJDIR)/ioutil.o: $(srcdir)/ioutil.c $(srcdir)/ioutil.h + $(COMPILE) $(srcdir)/ioutil.c + +$(OBJDIR)/errmsg.o: $(srcdir)/errmsg.c $(srcdir)/errmsg.h + $(COMPILE) $(srcdir)/errmsg.c + +#----------------------------------------------------------------------- +# Include file dependencies. +#----------------------------------------------------------------------- + +$(OBJDIR)/keytab.h: $(srcdir)/keytab.h $(srcdir)/libtecla.h + cp $(srcdir)/keytab.h $@ diff --git a/libtecla-1.6.1/Makefile.stub b/libtecla-1.6.1/Makefile.stub new file mode 100644 index 0000000..cf89638 --- /dev/null +++ b/libtecla-1.6.1/Makefile.stub @@ -0,0 +1,12 @@ +default: + ./configure + $(MAKE) + +distclean: + ./configure --without-man-pages + $(MAKE) $@ + +normal reentrant demos demos_r clean install_lib install_bin install_inc \ + install_man install: + ./configure + $(MAKE) $@ diff --git a/libtecla-1.6.1/PORTING b/libtecla-1.6.1/PORTING new file mode 100644 index 0000000..db39818 --- /dev/null +++ b/libtecla-1.6.1/PORTING @@ -0,0 +1,38 @@ +The Tecla library was written with portability in mind, so no +modifications to the source code should be needed on UNIX or LINUX +platforms. The default compilation and linking arrangements should +also work unchanged on these systems, but if no specific configuration +has been provided for your system, shared libraries won't be compiled. +Configuring these requires modifications to be made to the file: + + configure.in + +This file is heavily commented (comments start with the word dnl) and +is relatively simple, so the instructions and suggestions that you +find in this file should be sufficient to help you figure out how to +add a configuration for your system. This file is an input file to the +GNU autoconf program, which uses it as a template for generating the +distributed configure script. If autoconf is installed on your system, +creating a new configure script is a simple matter of typing. + + autoconf + +To avoid confusion with the leftovers of the previous configuration, +you should then do the following: + + rm -f config.cache + ./configure + make clean + ./configure + make + +The first ./configure creates a new makefile for your system, allowing +you to type 'make clean' to discard any files that were compiled with +the previous configuration. Since 'make clean' also deletes the new +makefile, a second invokation of the configure script is then +performed to re-create the makefile. The final make then creates the +tecla library from scratch. + +Once you have confirmed that the new configuration works, please send +the modified "configure.in" template to mcs@astro.caltech.edu, so that +your changes can be included in subsequent releases. diff --git a/libtecla-1.6.1/README b/libtecla-1.6.1/README new file mode 100644 index 0000000..72541b3 --- /dev/null +++ b/libtecla-1.6.1/README @@ -0,0 +1,53 @@ +This is version 1.6.1 of the tecla command-line editing library. + +For the current official release, please direct your browser to: + + http://www.astro.caltech.edu/~mcs/tecla/index.html + +The tecla library provides UNIX and LINUX programs with interactive +command line editing facilities, similar to those of the unix tcsh +shell. In addition to simple command-line editing, it supports recall +of previously entered command lines, TAB completion of file names, and +in-line wild-card expansion of filenames. The internal functions +which perform file-name completion and wild-card expansion are also +available externally for optional use by programs, along with a module +for tab-completion and lookup of filenames in a list of directories. + +Note that special care has been taken to allow the use of this library +in threaded programs. The option to enable this is discussed in the +Makefile, and specific discussions of thread safety are presented in +the included man pages. + +For instructions on how to compile and install the library, please see +the INSTALL file, which should be in the same directory as this file. + +Copyright and Disclaimer +------------------------ +Copyright (c) 2000, 2001, 2002, 2003, 2004 by Martin C. Shepherd. + +All rights reserved. + +Permission is hereby granted, free of charge, to any person obtaining a +copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, and/or sell copies of the Software, and to permit persons +to whom the Software is furnished to do so, provided that the above +copyright notice(s) and this permission notice appear in all copies of +the Software and that both the above copyright notice(s) and this +permission notice appear in supporting documentation. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT +OF THIRD PARTY RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR +HOLDERS INCLUDED IN THIS NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL +INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING +FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, +NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION +WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + +Except as contained in this notice, the name of a copyright holder +shall not be used in advertising or otherwise to promote the sale, use +or other dealings in this Software without prior written authorization +of the copyright holder. diff --git a/libtecla-1.6.1/RELEASE.NOTES b/libtecla-1.6.1/RELEASE.NOTES new file mode 100644 index 0000000..64f941f --- /dev/null +++ b/libtecla-1.6.1/RELEASE.NOTES @@ -0,0 +1,587 @@ +This file lists major changes which accompany each new release. + +Version 1.6.1: + + This is primarily a minor bug-fix release. + + One added feature is the ability to call gl_normal_io() from + callbacks registered by gl_watch_fd() and + gl_inactivity_timeout(). This allows these callbacks to cleanly + suspend line editing before either reading from the terminal, or + writing to the terminal; and then subsequently causes the input line + to be automatically redisplayed, and line-editing to be resumed by + gl_get_line(), as soon as the callback returns. + + Another minor change is that if the terminal type specified in the + TERM environment variable is set to "dumb", gl_get_line() now treats + the terminal as though it were a non-interactive stream, rather than + treating it as a VT100-compatible terminal. This means that it + doesn't either prompt for input, or perform any command-line + editing, even when it really is interacting with a terminal. This is + aimed at the rare situation where a third-pary program that connects + to libtecla through an embedded pseudo-terminal, needs to be forced + to behave as though it weren't talking to a terminal, in order that + it be useable in non-interactive scripts. + + Note that in the previous release, the optional configuration + function, gl_tty_signals(), was incorrectly swapping the suspend and + terminal signal handlers before installing them. + + A configuration problem that prevented select() from being used + under MacOS X, has been fixed. + + Although not documented in the man page, it was meant to be possible + to take the input line that one call to gl_get_line() returned, and + ask the next call to gl_get_line() to present it back to the user + for re-editing, simply by passing the pointer returned by one call + to gl_get_line() as the start_line argument of the next call to + gl_get_line(). This feature unfortunately stopped working in 1.6.0, + so this release restores it, and officially documents it in the man + page documentation of gl_get_line(). + + In the previous version of the library, calling gl_terminal_size() + on a system without SIGWINCH support, would crash the + application. This has been fixed. + + Libtecla now apparently compiles cleanly under IRIX. + +Version 1.6.0: + + This release is primarily a bug-fix release. However there are also + four new functions, so the minor version number has been + incremented to reflect this. + + Two of the new functions are gl_automatic_history() and + gl_append_history(). The former of these functions allows the + application to tell gl_get_line() not to automatically archive + entered lines in the history list. The second of these functions + allows the application to explicitly append a line to the history + list. Thus together, these two functions allow the calling + application to take over control of what is placed in the history + list. + + The third new function is gl_query_char(), which prompts the user + for a single character reply, which the user can then type without + having to hit return to enter it. Unless echoing is disabled, the + character that is entered is then displayed after the prompt, + and a newline is started. + + Finally, the 4th new function is gl_read_char(), which also reads + a single character from the user, but doesn't prompt the user, write + anything to the terminal, or disturb any partially entered input + line. It is thus safe to call this function not only from between + calls to gl_get_line(), but also from application callback + functions, even if gl_normal_io() hasn't been called. + + When using the history-search-backwards or history-search-forwards + actions, if the search prefix that the user typed, contains any of + the *,? or [ globbing characters, it is now treated as a glob + pattern to be matched against historical lines, instead of a simple + prefix. + + I have added a --without-file-system option to the configure + script. This is intended for use in embedded systems that either + don't have filesystems, or where the file-system code in libtecla is + seen as unwanted bloat. See the INSTALL document for details. + + Similarly, I also added a --without-file-actions option to the + configure script. This allows the application author/installer to + prevent users of gl_get_line() from accessing the filesystem with + the builtin actions of gl_get_line(). It does this by removing a + number of action functions, such as expand-filename, and list-glob, + and by changing the default behavior of other actions, such as + complete-word and list-or-eof, to show no completions. + + Now to the bugs that have been fixed. Version 1.5.0 had a lot of big + internal changes, so there are a number of bugs that needed to be + fixed. There was a bug which caused a crash if gl_load_history() + was called multiple times. There was another bug which caused a + prompt not to be displayed on the next line after switching from + reading input from a file to reading from the terminal. Also, in + tecla configuration files, backslash escaped characters within + key-binding key-sequences weren't being escaped. Thus ^\\ got + interpretted as a control-\ followed by a \ character instead of as + a control-\. There was a bug in the history recall mechanism which + caused the search prefix to be forgotten in certain complicated + usage scenarios. There was a minor memory leak in the + gl_configure_getline() function. Finally, if gl_get_line() was + aborted by a signal, or any other abnormal event, the value of errno + which originally indicated what had happened, got zeroed by the + code that restored the terminal to a usable state. Thus the + application couldn't figure out what had caused the error, apart + from by looking at gl_return_status(). All of these bugs have been + fixed. + + In the Makefile, there were a number of places where install-sh was + invoked without a path prefix. This has now been remedied. + + A fully functional workaround for a bug in Solaris' terminal I/O + code has also been implemented. This bug, which only manifested + itself in libtecla's uncommonly used non-blocking server I/O mode, + caused characters entered while in normal I/O mode, between calls to + gl_get_line() to be invisible to the next call to gl_get_line(), + until the user typed at least one more key after raw terminal mode + was restored. + + The Gnu autoconf config.guess and config.sub scripts have been + updated to their latest versions. Apparently the old versions that I + was previously using were too old to know about certain BSD ports. + +Version 1.5.0: + + This release includes several major new features for those using + gl_get_line(), shared library support in Darwin, better cross + compilation support, and various minor bug fixes. + + The biggest new feature is the option of a non-blocking I/O mode, in + which gl_get_line() can safely be called from an application's + external event-loop to incrementally read input lines from the user. + This feature is documented in the gl_io_mode(3) man page. + + In addition, there is now support for the definition of additional + word-completion action functions, which can then be bound to + different keys. See the documentation of the gl_completion_action() + function in the gl_get_line(3) man page. + + Externally defined action functions can also be defined, although + presently they don't have write access to the input line, so they + are restricted to operations that display information text to the + terminal, or modify the environment of the calling application in + some way. See the documentation of the gl_register_action() function + in the gl_get_line(3) man page. + + Some of the non-blocking I/O support functions can also be used for + improved signal handling in the normal blocking mode. In particular, + the gl_list_signals() and gl_catch_blocked() functions make it + easier to write reliable signal handling around gl_get_line(). The + new "RELIABLE SIGNAL HANDLING" section of the gl_get_line(3) man + page is intended as an introduction to this subject. + + Programs can now clear the terminal between calls to gl_get_line(), + by calling the new gl_erase_terminal() function. + + The gl_display_text() function, now used in the demos to display + introductory banners, is provided for formatting text according to + the width of the terminal. + + It is now possible to install inactivity timeout callbacks in + gl_get_line(), using the new gl_inactivity_timeout() function. + + The new gl_set_term_size() function allows the application to + explicitly set the terminal size, for cases, such as when one is + using a terminal at the end of a serial lineq, where the terminal + driver doesn't send the process a SIGWINCH when the terminal size + changes. + + The new gl_bind_keyseq() function provides a convenient + alternative to gl_configure_getline(), for binding or unbinding + one key-sequence at a time. + + gl_get_line()s signal handling, file-descriptor event-handling, + inactivity-timeout handling and server-mode non-blocking I/O + features now not only work when input is coming from a terminal, but + now also work when input is coming from non-interactive streams, + such as files and pipes. + + The history implementation has been re-written to make it more + efficient and easier to modify. The biggest user-level change is + that when recalling history lines using a search prefix, the same + line is no longer returned more than once in a row. Previously this + duplicate elimination only worked when one was recalling a line + without specifying a search prefix, and this was naively performed + by preventing neighboring duplicates from existing in the history + list, rather than by skipping duplicates at search time. + + In previous versions of the library, when gl_get_line() and its + associated public functions detected invalid arguments, or couldn't + allocate memory, etc, error messages were written to stderr. This + isn't appropriate for library functions, so instead of writing such + messages to stderr, these messages are now recorded in buffers + within the affected GetLine object. The latest error message can + then subsequently be queried by calling gl_error_message(). The use + of errno has also been expanded, and a new function called + gl_return_status() has been provided to expand on the cause of the + last return from gl_get_line(). + + User level usage and configuration information has now been split + out of the gl_get_line(3) man page into a separate tecla(7) man + page. The enhance(3) man page has also been renamed to enhance(1). + + When expanding "~/", gl_get_line() now checks for, and returns the + value of the HOME environment variable, if it exists, in preference + to looking up the directory of the current user in the password + file. + + When the terminal was resized to a narrower width, previous versions + of gl_get_line() would redraw the line higher up the terminal. This + bug has been fixed. A bug in history recall has also been fixed, in + which an error message was being generated if one attempted to + recall a line while the cursor was at the end of the longest + possible input line. A more serious bug, in which callbacks + registered by gl_watch_fd() weren't being called for write-events, + has also been fixed. Finally, a few minor fixes have been made to + improve support under QNX and Mac OS X. + + Beware that in this release, much of the underlying code has + undergone some radical re-work, so although backwards compatibility + of all documented features has been preserved, there may be some + lingering bugs that could break existing programs. So, if you plan + to use this version in production code, please test it as far as + possible within your application before releasing it to your + clients, and as always, please report any unexpected behavior. + +Version 1.4.1: + + This is a maintenance release. It includes minor changes to support + Mac OS X (Darwin), the QNX real-time operating system, and Cygwin + under Windows. It also fixes an oversight that was preventing the + tab key from inserting tab characters when users unbound the + complete-word action from it. + +Version 1.4.0: + + The contents of the history list can now be saved and restored with + the new gl_save_history() and gl_load_history() functions. + + Event handlers can now be registered to watch for and respond to I/O + on arbitrary file descriptors while gl_get_line() is waiting for + terminal input from the user. See the gl_get_line(3) man page + for details on gl_watch_fd(). + + As an optional alternative to getting configuration information only + from ~/.teclarc, the new gl_configure_getline() function allows + configuration commands to be taken from any of, a string, a + specified application-specific file, and/or a specified + user-specific file. See the gl_get_line(3) man page for details. + + The version number of the library can now be queried using the + libtecla_version() function. See the libtecla(3) man page. + + The new gl_group_history() function allows applications to group + different types of input line in the history buffer, and arrange for + only members of the appropriate group to be recalled on a given call + to gl_get_line(). See the gl_get_line(3) man page. + + The new gl_show_history() function displays the current history list + to a given stdio output stream. See the gl_get_line(3) man page. + + new_GetLine() now allows you to specify a history buffer size of + zero, thus requesting that no history buffer be allocated. You can + subsequently resize or delete the history buffer at any time, by + calling gl_resize_history(), limit the number of lines that are + allowed in the buffer by calling gl_limit_history(), clear either + all history lines from the history list, or just the history lines + that are associated with the current history group, by calling + gl_clear_history, and toggle the history mechanism on and off by + calling gl_toggle_history(). + + The new gl_terminal_size() function can be used to query the + current terminal size. It can also be used to supply a default + terminal size on systems where no mechanism is available for + looking up the size. + + The contents and configuration of the history list can now be + obtained by the calling application, by calling the new + gl_lookup_history(), gl_state_of_history(), gl_range_of_history() + and gl_size_of_history() functions. See the gl_get_line(3) man page. + + Echoing of the input line as it is typed, can now be turned on and + off via the new gl_echo_mode() function. While echoing is disabled, + newly entered input lines are omitted from the history list. See + the gl_get_line(3) man page. + + While the default remains to display the prompt string literally, + the new gl_prompt_style() function can be used to enable text + attribute formatting directives in prompt strings, such as + underlining, bold font, and highlighting directives. + + Signal handling in gl_get_line() is now customizable. The default + signal handling behavior remains essentially the same, except that + the SIGTSTP, SIGTTIN and SIGTTOU are now forwarded to the + corresponding signal handler of the calling program, instead of + causing a SIGSTOP to be sent to the application. It is now possible + to remove signals from the list that are trapped by gl_get_line(), + as well as add new signals to this list. The signal and terminal + environments in which the signal handler of the calling program is + invoked, and what gl_get_line() does after the signal handler + returns, is now customizable on a per signal basis. You can now also + query the last signal that was caught by gl_get_line(). This is + useful when gl_get_line() aborts with errno=EINTR, and you need to + know which signal caused it to abort. + + Key-sequences bound to action functions can now start with printable + characters. Previously only keysequences starting with control or + meta characters were permitted. + + gl_get_line() is now 8-bit clean. If the calling program has + correctly called setlocale(LC_CTYPE,""), then the user can select an + alternate locale by setting the standard LC_CTYPE, LC_ALL, or LANG + environment variables, and international characters can then be + entered directly, either by using a non-US keyboard, or by using a + compose key on a standard US keyboard. Note that in locales in which + meta characters become printable, meta characters no longer match + M-c bindings, which then have to be entered using their escape-c + equivalents. Fortunately most modern terminal emulators either + output the escape-c version by default when the meta key is used, or + can be configured to do so (see the gl_get_line(3) man page), so in + most cases you can continue to use the meta key. + + Completion callback functions can now tell gl_get_line() to return + the input line immediately after a successful tab completion, simply + by setting the last character of the optional continuation suffix to + a newline character (ie. in the call to cpl_add_completion()). + + It is now safe to create and use multiple GetLine objects, albeit + still only from a single thread. In conjunction with the new + gl_configure_getline() function, this optionally allows multiple + GetLine objects with different bindings to be used to implement + different input modes. + + The edit-mode configuration command now accepts the argument, + none. This tells gl_get_line() to revert to using just the native + line editing facilities provided by the terminal driver. This could + be used if the termcap or terminfo entry of the host terminal were + badly corrupted. + + Application callback functions invoked by gl_get_line() can now + change the displayed prompt using the gl_replace_prompt() function. + + Their is now an optional program distributed with the library. This + is a beta release of a program which adds tecla command-line editing + to virtually any third party application without the application + needing to be linked to the library. See the enhance(3) man page for + further details. Although built and installed by default, the + INSTALL document explains how to prevent this. + + The INSTALL document now explains how you can stop the demo programs + from being built and installed. + + NetBSD/termcap fixes. Mike MacFaden reported two problems that he + saw when compiling libtecla under NetBSD. Both cases were related to + the use of termcap. Most systems use terminfo, so this problem has + gone unnoticed until now, and won't have affected the grand majority + of users. The configure script had a bug which prevented the check + for CPP working properly, and getline.c wouldn't compile due to an + undeclared variable when USE_TERMCAP was defined. Both problems have + now been fixed. Note that if you successfully compiled version + 1.3.3, this problem didn't affect you. + + An unfortunate and undocumented binding of the key-sequence M-O was + shadowing the arrow-key bindings on systems that use ^[OA etc. I + have removed this binding (the documented lower case M-o binding + remains bound). Under the KDE konsole terminal this was causing the + arrow keys to do something other than expected. + + There was a bug in the history list code which could result in + strange entries appearing at the start of the history list once + enough history lines had been added to the list to cause the + circular history buffer to wrap. This is now fixed. + +Version 1.3.3: + + Signal handling has been re-written, and documentation of its + behaviour has been added to the gl_get_line(3) man page. In addition + to eliminating race conditions, and appropriately setting errno for + those signals that abort gl_get_line(), many more signals are now + intercepted, making it less likely that the terminal will be left in + raw mode by a signal that isn't trapped by gl_get_line(). + + A bug was also fixed that was leaving the terminal in raw mode if + the editing mode was changed interactively between vi and emacs. + This was only noticeable when running programs from old shells that + don't reset terminal modes. + +Version 1.3.2: + + Tim Eliseo contributed a number of improvements to vi mode, + including a fuller set of vi key-bindings, implementation of the vi + constraint that the cursor can't backup past the point at which + input mode was entered, and restoration of overwritten characters + when backspacing in overwrite mode. There are also now new bindings + to allow users to toggle between vi and emacs modes interactively. + The terminal bell is now used in some circumstances, such as when an + unrecognized key sequence is entered. This can be turned off by the + new nobeep option in the tecla configuration file. + + Unrelated to the above, a problem under Linux which prevented ^Q + from being used to resume terminal output after the user had pressed + ^S, has been fixed. + +Version 1.3.1: + + In vi mode a bug was preventing the history-search-backward and + history-search-forward actions from doing anything when invoked on + empty lines. On empty lines they now act like up-history and + down-history respectively, as in emacs mode. + + When creating shared libraries under Linux, the -soname directive + was being used incorrectly. The result is that Linux binaries linked + with the 1.2.3, 1.2.4 and 1.3.0 versions of the tecla shared + libraries, will refuse to see other versions of the shared library + until relinked with version 1.3.1 or higher. + + The configure script can now handle the fact that under Solaris-2.6 + and earlier, the only curses library is a static one that hides in + /usr/ccs/lib. Under Linux it now also caters for old versions of GNU + ld which don't accept version scripts. + + The demos are now linked against the shared version of the library + if possible. Previously they were always linked with the static + version. + +Version 1.3.0: + + The major change in this release is the addition of an optional vi + command-line editing mode in gl_get_line(), along with lots of new + action functions to support its bindings. To enable this, first + create a ~/.teclarc file if you don't already have one, then add the + following line to it. + + edit-mode vi + + The default vi bindings, which are designed to mimic those of the vi + editor as closely as possible, are described in the gl_get_line(3) + man page. + + A new convenience function called ef_list_expansions() has been + added for listing filename expansions. See the ef_list_expansions(3) + man page for details. This is used in a new list-glob binding, bound + to ^Xg in emacs mode, and ^G in vi input mode. + + A bug has been fixed in the key-binding table expansion code. This + bug would have caused problems to anybody who defined more than + about 18 personalized key-bindings in their ~/.teclarc file. + +Version 1.2.4: + + Buffered I/O is now used for writing to terminals, and where + supported, cursor motion is done with move-n-positions terminfo + capabilities instead of doing lots of move-1-position requests. This + greatly improves how the library feels over slow links. + + You can now optionally compile different architectures in different + directories, without having to make multiple copies of the + distribution. This is documented in the INSTALL file. + + The ksh ~+ directive is now supported. + + Thanks to Markus Gyger for the above improvements. + + Documentation has been added to the INSTALL file describing features + designed to facilitate configuration and installation of the library + as part of larger packages. These features are intended to remove + the need to modify the tecla distribution's configuration and build + procedures when embedding the libtecla distribution in other package + distributions. + + A previous fix to stop the cursor from warping when the last + character of the input line was in the last column of the terminal, + was only being used for the first terminal line of the input line. + It is now used for all subsequent lines as well, as originally + intended. + +Version 1.2.3: + + The installation procedure has been better automated with the + addition of an autoconf configure script. This means that installers + can now compile and install the library by typing: + + ./configure + make + make install + + On all systems this makes at least the normal static version of the + tecla library. It also makes the reentrant version if reentrant + POSIX functions are detected. Under Solaris, Linux and HP-UX the + configuration script arranges for shared libraries to be compiled in + addition to the static libraries. It is hoped that installers will + return information about how to compile shared libraries on other + systems, for inclusion in future releases, and to this end, a new + PORTING guide has been provided. + + The versioning number scheme has been changed. This release would + have been 1.2c, but instead will be refered to as 1.2.3. The + versioning scheme, based on conventions used by Sun Microsystems, is + described in configure.in. + + The library was also tested under HP-UX, and this revealed two + serious bugs, both of which have now been fixed. + + The first bug prevented the library from writing control codes to + terminals on big-endian machines, with the exception of those + running under Solaris. This was due to an int variable being used + where a char was needed. + + The second bug had the symptom that on systems that don't use the + newline character as the control code for moving the cursor down a + line, a newline wasn't started when the user hit enter. + +Version 1.2b: + + Two more minor bug fixes: + + Many terminals don't wrap the cursor to the next line when a + character is written to the rightmost terminal column. Instead, they + delay starting a new line until one more character is written, at + which point they move the cursor two positions. gl_get_line() + wasn't aware of this, so cursor repositionings just after writing + the last character of a column, caused it to erroneously go up a + line. This has now been remedied, using a method that should work + regardless of whether a terminal exhibits this behavior or not. + + Some systems dynamically record the current terminal dimensions in + environment variables called LINES and COLUMNS. On such systems, + during the initial terminal setup, these values should override the + static values read from the terminal information databases, and now + do. Previously they were only used if the dimensions returned by + terminfo/termcap looked bogus. + +Version 1.2a: + + This minor release fixes the following two bugs: + + The initial terminal size and subsequent changes thereto, weren't + being noticed by gl_get_line(). This was because the test for the + existence of TIOCWINSZ was erroneously placed before the inclusion + of termios.h. One of the results was that on input lines that + spanned more than one terminal line, the cursor occasionally jumped + unexpectedly to the previous terminal line. + + On entering a line that wrapped over multiple terminal lines, + gl_get_line() simply output a carriage-return line-feed at the point + at which the user pressed return. Thus if one typed in such a line, + then moved back onto one of the earlier terminal lines before + hitting return, the cursor was left on a line containing part of the + line that had just been entered. This didn't do any harm, but it + looked a mess. + +Version 1.2: + + A new facility for looking up and completing filenames in UNIX-style + paths has now been added (eg. you can search for, or complete + commands using the UNIX PATH environment variable). See the + pca_lookup_file(3) man page. + + The already existing filename completion callback can now be made + selective in what types of files it lists. See the + cpl_complete_word(3) man page. + + Due to its potential to break applications when changed, the use of + the publically defined CplFileArgs structure to configure the + cpl_file_completions() callback is now deprecated. The definition + of this structure has been frozen, and its documentation has been + removed from the man pages. It will remain supported, but if you + have used it, you are recommended to switch to the new method, which + involves a new opaque configuration object, allocated via a provided + constructor function, configured via accessor functions, and + eventually deleted with a provided destructor function. The + cpl_file_completions() callback distinguishes which structure type + it has been sent by virtue of a code placed at the start of the new + structure by the constructor. It is assumed that no existing + applications set the boolean 'escaped' member of the CplFileArgs + structure to 4568. The new method is documented in the + cpl_complete_word(3) man page. + +Version 1.1j + + This was the initial public release on freshmeat.org. diff --git a/libtecla-1.6.1/chrqueue.c b/libtecla-1.6.1/chrqueue.c new file mode 100644 index 0000000..346e8f6 --- /dev/null +++ b/libtecla-1.6.1/chrqueue.c @@ -0,0 +1,432 @@ +/* + * Copyright (c) 2000, 2001, 2002, 2003, 2004 by Martin C. Shepherd. + * + * All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, and/or sell copies of the Software, and to permit persons + * to whom the Software is furnished to do so, provided that the above + * copyright notice(s) and this permission notice appear in all copies of + * the Software and that both the above copyright notice(s) and this + * permission notice appear in supporting documentation. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT + * OF THIRD PARTY RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR + * HOLDERS INCLUDED IN THIS NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL + * INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING + * FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, + * NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION + * WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + * Except as contained in this notice, the name of a copyright holder + * shall not be used in advertising or otherwise to promote the sale, use + * or other dealings in this Software without prior written authorization + * of the copyright holder. + */ +#include +#include +#include +#include + +#include "ioutil.h" +#include "chrqueue.h" +#include "freelist.h" +#include "errmsg.h" + +/* + * Set the number of bytes allocated to each node of the list of + * character buffers. This facility is designed principally as + * an expandible I/O output buffer, so use the stdio buffer size + * where available. + */ +#ifdef BUFSIZ +#define GL_CQ_SIZE BUFSIZ +#else +#define GL_CQ_SIZE 512 +#endif + +/* + * The queue is contained in a list of fixed sized buffers. New nodes + * are appended to this list as needed to accomodate newly added bytes. + * Old nodes at the head of the list are removed as they are emptied. + */ +typedef struct CqCharBuff CqCharBuff; +struct CqCharBuff { + CqCharBuff *next; /* The next node in the list of buffers */ + char bytes[GL_CQ_SIZE]; /* The fixed size buffer of this node */ +}; + +/* + * Define the structure that is used to contain a list of character + * buffers. + */ +struct GlCharQueue { + ErrMsg *err; /* A buffer in which to record error messages */ + FreeList *bufmem; /* A free-list of CqCharBuff structures */ + struct { + CqCharBuff *head; /* The head of the list of output buffers */ + CqCharBuff *tail; /* The tail of the list of output buffers */ + } buffers; + int nflush; /* The total number of characters that have been */ + /* flushed from the start of the queue since */ + /* _glq_empty_queue() was last called. */ + int ntotal; /* The total number of characters that have been */ + /* appended to the queue since _glq_empty_queue() */ + /* was last called. */ +}; + +/*....................................................................... + * Create a new GlCharQueue object. + * + * Output: + * return GlCharQueue * The new object, or NULL on error. + */ +GlCharQueue *_new_GlCharQueue(void) +{ + GlCharQueue *cq; /* The object to be returned */ +/* + * Allocate the container. + */ + cq = malloc(sizeof(GlCharQueue)); + if(!cq) { + errno = ENOMEM; + return NULL; + }; +/* + * Before attempting any operation that might fail, initialize the + * container at least up to the point at which it can safely be passed + * to del_GlCharQueue(). + */ + cq->err = NULL; + cq->bufmem = NULL; + cq->buffers.head = NULL; + cq->buffers.tail = NULL; + cq->nflush = cq->ntotal = 0; +/* + * Allocate a place to record error messages. + */ + cq->err = _new_ErrMsg(); + if(!cq->err) + return _del_GlCharQueue(cq); +/* + * Allocate the freelist of CqCharBuff structures. + */ + cq->bufmem = _new_FreeList(sizeof(CqCharBuff), 1); + if(!cq->bufmem) + return _del_GlCharQueue(cq); + return cq; +} + +/*....................................................................... + * Delete a GlCharQueue object. + * + * Input: + * cq GlCharQueue * The object to be deleted. + * Output: + * return GlCharQueue * The deleted object (always NULL). + */ +GlCharQueue *_del_GlCharQueue(GlCharQueue *cq) +{ + if(cq) { + cq->err = _del_ErrMsg(cq->err); + cq->bufmem = _del_FreeList(cq->bufmem, 1); + free(cq); + }; + return NULL; +} + +/*....................................................................... + * Append an array of n characters to a character queue. + * + * Input: + * cq GlCharQueue * The queue to append to. + * chars const char * The array of n characters to be appended. + * n int The number of characters in chars[]. + * write_fn GL_WRITE_FN * The function to call to output characters, + * or 0 to simply discard the contents of the + * queue. This will be called whenever the + * buffer becomes full. If it fails to release + * any space, the buffer will be extended. + * data void * Anonymous data to pass to write_fn(). + * Output: + * return int The number of characters successfully + * appended. This will only be < n on error. + */ +int _glq_append_chars(GlCharQueue *cq, const char *chars, int n, + GlWriteFn *write_fn, void *data) +{ + int ndone = 0; /* The number of characters appended so far */ +/* + * Check the arguments. + */ + if(!cq || !chars) { + errno = EINVAL; + return 0; + }; +/* + * The appended characters may have to be split between multiple + * buffers, so loop for each buffer. + */ + while(ndone < n) { + int ntodo; /* The number of characters remaining to be appended */ + int nleft; /* The amount of space remaining in cq->buffers.tail */ + int nnew; /* The number of characters to append to cq->buffers.tail */ +/* + * Compute the offset at which the next character should be written + * into the tail buffer segment. + */ + int boff = cq->ntotal % GL_CQ_SIZE; +/* + * Since we don't allocate a new buffer until we have at least one + * character to write into it, if boff is 0 at this point, it means + * that we hit the end of the tail buffer segment on the last append, + * so we need to allocate a new one. + * + * If allocating this new node will require a call to malloc(), as + * opposed to using a currently unused node in the freelist, first try + * flushing the current contents of the buffer to the terminal. When + * write_fn() uses blocking I/O, this stops the buffer size ever getting + * bigger than a single buffer node. When it is non-blocking, it helps + * to keep the amount of memory, but it isn't gauranteed to do so. + */ + if(boff == 0 && _idle_FreeListNodes(cq->bufmem) == 0) { + switch(_glq_flush_queue(cq, write_fn, data)) { + case GLQ_FLUSH_DONE: + break; + case GLQ_FLUSH_AGAIN: + errno = 0; /* Don't confuse the caller */ + break; + default: + return ndone; /* Error */ + }; + boff = cq->ntotal % GL_CQ_SIZE; + }; +/* + * Since we don't allocate a new buffer until we have at least one + * character to write into it, if boff is 0 at this point, it means + * that we hit the end of the tail buffer segment on the last append, + * so we need to allocate a new one. + */ + if(boff == 0) { +/* + * Allocate the new node. + */ + CqCharBuff *node = (CqCharBuff *) _new_FreeListNode(cq->bufmem); + if(!node) { + _err_record_msg(cq->err, "Insufficient memory to buffer output.", + END_ERR_MSG); + return ndone; + }; +/* + * Initialize the node. + */ + node->next = NULL; +/* + * Append the new node to the tail of the list. + */ + if(cq->buffers.tail) + cq->buffers.tail->next = node; + else + cq->buffers.head = node; + cq->buffers.tail = node; + }; +/* + * How much room is there for new characters in the current tail node? + */ + nleft = GL_CQ_SIZE - boff; +/* + * How many characters remain to be appended? + */ + ntodo = n - ndone; +/* + * How many characters should we append to the current tail node? + */ + nnew = nleft < ntodo ? nleft : ntodo; +/* + * Append the latest prefix of nnew characters. + */ + memcpy(cq->buffers.tail->bytes + boff, chars + ndone, nnew); + cq->ntotal += nnew; + ndone += nnew; + }; +/* + * Return the count of the number of characters successfully appended. + */ + return ndone; +} + +/*....................................................................... + * Discard the contents of a queue of characters. + * + * Input: + * cq GlCharQueue * The queue to clear. + */ +void _glq_empty_queue(GlCharQueue *cq) +{ + if(cq) { +/* + * Return all list nodes to their respective free-lists. + */ + _rst_FreeList(cq->bufmem); +/* + * Mark the lists as empty. + */ + cq->buffers.head = cq->buffers.tail = NULL; + cq->nflush = cq->ntotal = 0; + }; +} + +/*....................................................................... + * Return a count of the number of characters currently in the queue. + * + * Input: + * cq GlCharQueue * The queue of interest. + * Output: + * return int The number of characters in the queue. + */ +int _glq_char_count(GlCharQueue *cq) +{ + return (cq && cq->buffers.head) ? (cq->ntotal - cq->nflush) : 0; +} + +/*....................................................................... + * Write as many characters as possible from the start of a character + * queue via a given output callback function, removing those written + * from the queue. + * + * Input: + * cq GlCharQueue * The queue to write characters from. + * write_fn GL_WRITE_FN * The function to call to output characters, + * or 0 to simply discard the contents of the + * queue. + * data void * Anonymous data to pass to write_fn(). + * Output: + * return GlFlushState The status of the flush operation: + * GLQ_FLUSH_DONE - The flush operation + * completed successfully. + * GLQ_FLUSH_AGAIN - The flush operation + * couldn't be completed + * on this call. Call this + * function again when the + * output channel can accept + * further output. + * GLQ_FLUSH_ERROR Unrecoverable error. + */ +GlqFlushState _glq_flush_queue(GlCharQueue *cq, GlWriteFn *write_fn, + void *data) +{ +/* + * Check the arguments. + */ + if(!cq) { + errno = EINVAL; + return GLQ_FLUSH_ERROR; + }; +/* + * If possible keep writing until all of the chained buffers have been + * emptied and removed from the list. + */ + while(cq->buffers.head) { +/* + * Are we looking at the only node in the list? + */ + int is_tail = cq->buffers.head == cq->buffers.tail; +/* + * How many characters more than an exact multiple of the buffer-segment + * size have been added to the buffer so far? + */ + int nmodulo = cq->ntotal % GL_CQ_SIZE; +/* + * How many characters of the buffer segment at the head of the list + * have been used? Note that this includes any characters that have + * already been flushed. Also note that if nmodulo==0, this means that + * the tail buffer segment is full. The reason for this is that we + * don't allocate new tail buffer segments until there is at least one + * character to be added to them. + */ + int nhead = (!is_tail || nmodulo == 0) ? GL_CQ_SIZE : nmodulo; +/* + * How many characters remain to be flushed from the buffer + * at the head of the list? + */ + int nbuff = nhead - (cq->nflush % GL_CQ_SIZE); +/* + * Attempt to write this number. + */ + int nnew = write_fn(data, cq->buffers.head->bytes + + cq->nflush % GL_CQ_SIZE, nbuff); +/* + * Was anything written? + */ + if(nnew > 0) { +/* + * Increment the count of the number of characters that have + * been flushed from the head of the queue. + */ + cq->nflush += nnew; +/* + * If we succeded in writing all of the contents of the current + * buffer segment, remove it from the queue. + */ + if(nnew == nbuff) { +/* + * If we just emptied the last node left in the list, then the queue is + * now empty and should be reset. + */ + if(is_tail) { + _glq_empty_queue(cq); + } else { +/* + * Get the node to be removed from the head of the list. + */ + CqCharBuff *node = cq->buffers.head; +/* + * Make the node that follows it the new head of the queue. + */ + cq->buffers.head = node->next; +/* + * Return it to the freelist. + */ + node = (CqCharBuff *) _del_FreeListNode(cq->bufmem, node); + }; + }; +/* + * If the write blocked, request that this function be called again + * when space to write next becomes available. + */ + } else if(nnew==0) { + return GLQ_FLUSH_AGAIN; +/* + * I/O error. + */ + } else { + _err_record_msg(cq->err, "Error writing to terminal", END_ERR_MSG); + return GLQ_FLUSH_ERROR; + }; + }; +/* + * To get here the queue must now be empty. + */ + return GLQ_FLUSH_DONE; +} + +/*....................................................................... + * Return extra information (ie. in addition to that provided by errno) + * about the last error to occur in any of the public functions of this + * module. + * + * Input: + * cq GlCharQueue * The container of the history list. + * Output: + * return const char * A pointer to the internal buffer in which + * the error message is temporarily stored. + */ +const char *_glq_last_error(GlCharQueue *cq) +{ + return cq ? _err_get_msg(cq->err) : "NULL GlCharQueue argument"; +} diff --git a/libtecla-1.6.1/chrqueue.h b/libtecla-1.6.1/chrqueue.h new file mode 100644 index 0000000..1aca8e0 --- /dev/null +++ b/libtecla-1.6.1/chrqueue.h @@ -0,0 +1,106 @@ +#ifndef chrqueue_h +#define chrqueue_h + +/* + * Copyright (c) 2000, 2001, 2002, 2003, 2004 by Martin C. Shepherd. + * + * All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, and/or sell copies of the Software, and to permit persons + * to whom the Software is furnished to do so, provided that the above + * copyright notice(s) and this permission notice appear in all copies of + * the Software and that both the above copyright notice(s) and this + * permission notice appear in supporting documentation. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT + * OF THIRD PARTY RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR + * HOLDERS INCLUDED IN THIS NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL + * INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING + * FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, + * NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION + * WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + * Except as contained in this notice, the name of a copyright holder + * shall not be used in advertising or otherwise to promote the sale, use + * or other dealings in this Software without prior written authorization + * of the copyright holder. + */ + +/*----------------------------------------------------------------------- + * This module implements a queue of characters to be processed in some + * way. It is used by gl_get_line() to maintain a queue of characters + * to be sent to a remote terminal. Characters are recorded in a + * dynamically extensible list of fixed sized buffers. + */ + +typedef struct GlCharQueue GlCharQueue; + +/* + * Create a new character queue. + */ +GlCharQueue *_new_GlCharQueue(void); + +/* + * Delete a redundant character queue. + */ +GlCharQueue *_del_GlCharQueue(GlCharQueue *cq); + +/* + * Append an array of n characters to a character queue. + */ +int _glq_append_chars(GlCharQueue *cq, const char *chars, int n, + GlWriteFn *write_fn, void *data); + +/* + * Clear a character queue. + */ +void _glq_empty_queue(GlCharQueue *cq); + +/* + * Return a count of the number of characters in the queue. + */ +int _glq_char_count(GlCharQueue *cq); + +/* + * A structure of the following type is used by _glq_peek_chars() to + * return characters at the start of the queue. + */ +typedef struct { + const char *buff; /* A pointer to the first undeleted byte in the */ + /* first buffer of the queue. */ + int nbuff; /* The number of characters in buff[] */ +} GlCharQueueBuff; + +/* + * Enumerator values of the following type are returned by + * _glq_flush_queue() to indicate the status of the flush operation. + */ +typedef enum { + GLQ_FLUSH_DONE, /* The flush operation completed successfully */ + GLQ_FLUSH_AGAIN, /* The flush operation couldn't be completed on this */ + /* call. Call this function again when the output */ + /* channel can accept further output. */ + GLQ_FLUSH_ERROR /* Unrecoverable error. */ +} GlqFlushState; + +/* + * Transfer as much of the contents of a character queue to an output + * channel as possible, returning before the queue is empty if the + * write_fn() callback says that it can't currently write anymore. + */ +GlqFlushState _glq_flush_queue(GlCharQueue *cq, GlWriteFn *write_fn, + void *data); + +/* + * Provide information about the last error that occurred while calling + * any of the above functions. + */ +const char *_glq_last_error(GlCharQueue *cq); + +#endif diff --git a/libtecla-1.6.1/config.guess b/libtecla-1.6.1/config.guess new file mode 100644 index 0000000..500ee74 --- /dev/null +++ b/libtecla-1.6.1/config.guess @@ -0,0 +1,1410 @@ +#! /bin/sh +# Attempt to guess a canonical system name. +# Copyright (C) 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999, +# 2000, 2001, 2002, 2003 Free Software Foundation, Inc. + +timestamp='2003-10-03' + +# This file 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. +# +# As a special exception to the GNU General Public License, if you +# distribute this file as part of a program that contains a +# configuration script generated by Autoconf, you may include it under +# the same distribution terms that you use for the rest of that program. + +# Originally written by Per Bothner . +# Please send patches to . Submit a context +# diff and a properly formatted ChangeLog entry. +# +# This script attempts to guess a canonical system name similar to +# config.sub. If it succeeds, it prints the system name on stdout, and +# exits with 0. Otherwise, it exits with 1. +# +# The plan is that this can be called by configure scripts if you +# don't specify an explicit build system type. + +me=`echo "$0" | sed -e 's,.*/,,'` + +usage="\ +Usage: $0 [OPTION] + +Output the configuration name of the system \`$me' is run on. + +Operation modes: + -h, --help print this help, then exit + -t, --time-stamp print date of last modification, then exit + -v, --version print version number, then exit + +Report bugs and patches to ." + +version="\ +GNU config.guess ($timestamp) + +Originally written by Per Bothner. +Copyright (C) 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001 +Free Software Foundation, Inc. + +This is free software; see the source for copying conditions. There is NO +warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE." + +help=" +Try \`$me --help' for more information." + +# Parse command line +while test $# -gt 0 ; do + case $1 in + --time-stamp | --time* | -t ) + echo "$timestamp" ; exit 0 ;; + --version | -v ) + echo "$version" ; exit 0 ;; + --help | --h* | -h ) + echo "$usage"; exit 0 ;; + -- ) # Stop option processing + shift; break ;; + - ) # Use stdin as input. + break ;; + -* ) + echo "$me: invalid option $1$help" >&2 + exit 1 ;; + * ) + break ;; + esac +done + +if test $# != 0; then + echo "$me: too many arguments$help" >&2 + exit 1 +fi + +trap 'exit 1' 1 2 15 + +# CC_FOR_BUILD -- compiler used by this script. Note that the use of a +# compiler to aid in system detection is discouraged as it requires +# temporary files to be created and, as you can see below, it is a +# headache to deal with in a portable fashion. + +# Historically, `CC_FOR_BUILD' used to be named `HOST_CC'. We still +# use `HOST_CC' if defined, but it is deprecated. + +# Portable tmp directory creation inspired by the Autoconf team. + +set_cc_for_build=' +trap "exitcode=\$?; (rm -f \$tmpfiles 2>/dev/null; rmdir \$tmp 2>/dev/null) && exit \$exitcode" 0 ; +trap "rm -f \$tmpfiles 2>/dev/null; rmdir \$tmp 2>/dev/null; exit 1" 1 2 13 15 ; +: ${TMPDIR=/tmp} ; + { tmp=`(umask 077 && mktemp -d -q "$TMPDIR/cgXXXXXX") 2>/dev/null` && test -n "$tmp" && test -d "$tmp" ; } || + { test -n "$RANDOM" && tmp=$TMPDIR/cg$$-$RANDOM && (umask 077 && mkdir $tmp) ; } || + { tmp=$TMPDIR/cg-$$ && (umask 077 && mkdir $tmp) && echo "Warning: creating insecure temp directory" >&2 ; } || + { echo "$me: cannot create a temporary directory in $TMPDIR" >&2 ; exit 1 ; } ; +dummy=$tmp/dummy ; +tmpfiles="$dummy.c $dummy.o $dummy.rel $dummy" ; +case $CC_FOR_BUILD,$HOST_CC,$CC in + ,,) echo "int x;" > $dummy.c ; + for c in cc gcc c89 c99 ; do + if ($c -c -o $dummy.o $dummy.c) >/dev/null 2>&1 ; then + CC_FOR_BUILD="$c"; break ; + fi ; + done ; + if test x"$CC_FOR_BUILD" = x ; then + CC_FOR_BUILD=no_compiler_found ; + fi + ;; + ,,*) CC_FOR_BUILD=$CC ;; + ,*,*) CC_FOR_BUILD=$HOST_CC ;; +esac ;' + +# This is needed to find uname on a Pyramid OSx when run in the BSD universe. +# (ghazi@noc.rutgers.edu 1994-08-24) +if (test -f /.attbin/uname) >/dev/null 2>&1 ; then + PATH=$PATH:/.attbin ; export PATH +fi + +UNAME_MACHINE=`(uname -m) 2>/dev/null` || UNAME_MACHINE=unknown +UNAME_RELEASE=`(uname -r) 2>/dev/null` || UNAME_RELEASE=unknown +UNAME_SYSTEM=`(uname -s) 2>/dev/null` || UNAME_SYSTEM=unknown +UNAME_VERSION=`(uname -v) 2>/dev/null` || UNAME_VERSION=unknown + +# Note: order is significant - the case branches are not exclusive. + +case "${UNAME_MACHINE}:${UNAME_SYSTEM}:${UNAME_RELEASE}:${UNAME_VERSION}" in + *:NetBSD:*:*) + # NetBSD (nbsd) targets should (where applicable) match one or + # more of the tupples: *-*-netbsdelf*, *-*-netbsdaout*, + # *-*-netbsdecoff* and *-*-netbsd*. For targets that recently + # switched to ELF, *-*-netbsd* would select the old + # object file format. This provides both forward + # compatibility and a consistent mechanism for selecting the + # object file format. + # + # Note: NetBSD doesn't particularly care about the vendor + # portion of the name. We always set it to "unknown". + sysctl="sysctl -n hw.machine_arch" + UNAME_MACHINE_ARCH=`(/sbin/$sysctl 2>/dev/null || \ + /usr/sbin/$sysctl 2>/dev/null || echo unknown)` + case "${UNAME_MACHINE_ARCH}" in + armeb) machine=armeb-unknown ;; + arm*) machine=arm-unknown ;; + sh3el) machine=shl-unknown ;; + sh3eb) machine=sh-unknown ;; + *) machine=${UNAME_MACHINE_ARCH}-unknown ;; + esac + # The Operating System including object format, if it has switched + # to ELF recently, or will in the future. + case "${UNAME_MACHINE_ARCH}" in + arm*|i386|m68k|ns32k|sh3*|sparc|vax) + eval $set_cc_for_build + if echo __ELF__ | $CC_FOR_BUILD -E - 2>/dev/null \ + | grep __ELF__ >/dev/null + then + # Once all utilities can be ECOFF (netbsdecoff) or a.out (netbsdaout). + # Return netbsd for either. FIX? + os=netbsd + else + os=netbsdelf + fi + ;; + *) + os=netbsd + ;; + esac + # The OS release + # Debian GNU/NetBSD machines have a different userland, and + # thus, need a distinct triplet. However, they do not need + # kernel version information, so it can be replaced with a + # suitable tag, in the style of linux-gnu. + case "${UNAME_VERSION}" in + Debian*) + release='-gnu' + ;; + *) + release=`echo ${UNAME_RELEASE}|sed -e 's/[-_].*/\./'` + ;; + esac + # Since CPU_TYPE-MANUFACTURER-KERNEL-OPERATING_SYSTEM: + # contains redundant information, the shorter form: + # CPU_TYPE-MANUFACTURER-OPERATING_SYSTEM is used. + echo "${machine}-${os}${release}" + exit 0 ;; + amiga:OpenBSD:*:*) + echo m68k-unknown-openbsd${UNAME_RELEASE} + exit 0 ;; + arc:OpenBSD:*:*) + echo mipsel-unknown-openbsd${UNAME_RELEASE} + exit 0 ;; + hp300:OpenBSD:*:*) + echo m68k-unknown-openbsd${UNAME_RELEASE} + exit 0 ;; + mac68k:OpenBSD:*:*) + echo m68k-unknown-openbsd${UNAME_RELEASE} + exit 0 ;; + macppc:OpenBSD:*:*) + echo powerpc-unknown-openbsd${UNAME_RELEASE} + exit 0 ;; + mvme68k:OpenBSD:*:*) + echo m68k-unknown-openbsd${UNAME_RELEASE} + exit 0 ;; + mvme88k:OpenBSD:*:*) + echo m88k-unknown-openbsd${UNAME_RELEASE} + exit 0 ;; + mvmeppc:OpenBSD:*:*) + echo powerpc-unknown-openbsd${UNAME_RELEASE} + exit 0 ;; + pmax:OpenBSD:*:*) + echo mipsel-unknown-openbsd${UNAME_RELEASE} + exit 0 ;; + sgi:OpenBSD:*:*) + echo mipseb-unknown-openbsd${UNAME_RELEASE} + exit 0 ;; + sun3:OpenBSD:*:*) + echo m68k-unknown-openbsd${UNAME_RELEASE} + exit 0 ;; + wgrisc:OpenBSD:*:*) + echo mipsel-unknown-openbsd${UNAME_RELEASE} + exit 0 ;; + *:OpenBSD:*:*) + echo ${UNAME_MACHINE}-unknown-openbsd${UNAME_RELEASE} + exit 0 ;; + alpha:OSF1:*:*) + if test $UNAME_RELEASE = "V4.0"; then + UNAME_RELEASE=`/usr/sbin/sizer -v | awk '{print $3}'` + fi + # According to Compaq, /usr/sbin/psrinfo has been available on + # OSF/1 and Tru64 systems produced since 1995. I hope that + # covers most systems running today. This code pipes the CPU + # types through head -n 1, so we only detect the type of CPU 0. + ALPHA_CPU_TYPE=`/usr/sbin/psrinfo -v | sed -n -e 's/^ The alpha \(.*\) processor.*$/\1/p' | head -n 1` + case "$ALPHA_CPU_TYPE" in + "EV4 (21064)") + UNAME_MACHINE="alpha" ;; + "EV4.5 (21064)") + UNAME_MACHINE="alpha" ;; + "LCA4 (21066/21068)") + UNAME_MACHINE="alpha" ;; + "EV5 (21164)") + UNAME_MACHINE="alphaev5" ;; + "EV5.6 (21164A)") + UNAME_MACHINE="alphaev56" ;; + "EV5.6 (21164PC)") + UNAME_MACHINE="alphapca56" ;; + "EV5.7 (21164PC)") + UNAME_MACHINE="alphapca57" ;; + "EV6 (21264)") + UNAME_MACHINE="alphaev6" ;; + "EV6.7 (21264A)") + UNAME_MACHINE="alphaev67" ;; + "EV6.8CB (21264C)") + UNAME_MACHINE="alphaev68" ;; + "EV6.8AL (21264B)") + UNAME_MACHINE="alphaev68" ;; + "EV6.8CX (21264D)") + UNAME_MACHINE="alphaev68" ;; + "EV6.9A (21264/EV69A)") + UNAME_MACHINE="alphaev69" ;; + "EV7 (21364)") + UNAME_MACHINE="alphaev7" ;; + "EV7.9 (21364A)") + UNAME_MACHINE="alphaev79" ;; + esac + # A Vn.n version is a released version. + # A Tn.n version is a released field test version. + # A Xn.n version is an unreleased experimental baselevel. + # 1.2 uses "1.2" for uname -r. + echo ${UNAME_MACHINE}-dec-osf`echo ${UNAME_RELEASE} | sed -e 's/^[VTX]//' | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz'` + exit 0 ;; + Alpha*:OpenVMS:*:*) + echo alpha-hp-vms + exit 0 ;; + Alpha\ *:Windows_NT*:*) + # How do we know it's Interix rather than the generic POSIX subsystem? + # Should we change UNAME_MACHINE based on the output of uname instead + # of the specific Alpha model? + echo alpha-pc-interix + exit 0 ;; + 21064:Windows_NT:50:3) + echo alpha-dec-winnt3.5 + exit 0 ;; + Amiga*:UNIX_System_V:4.0:*) + echo m68k-unknown-sysv4 + exit 0;; + *:[Aa]miga[Oo][Ss]:*:*) + echo ${UNAME_MACHINE}-unknown-amigaos + exit 0 ;; + *:[Mm]orph[Oo][Ss]:*:*) + echo ${UNAME_MACHINE}-unknown-morphos + exit 0 ;; + *:OS/390:*:*) + echo i370-ibm-openedition + exit 0 ;; + arm:RISC*:1.[012]*:*|arm:riscix:1.[012]*:*) + echo arm-acorn-riscix${UNAME_RELEASE} + exit 0;; + SR2?01:HI-UX/MPP:*:* | SR8000:HI-UX/MPP:*:*) + echo hppa1.1-hitachi-hiuxmpp + exit 0;; + Pyramid*:OSx*:*:* | MIS*:OSx*:*:* | MIS*:SMP_DC-OSx*:*:*) + # akee@wpdis03.wpafb.af.mil (Earle F. Ake) contributed MIS and NILE. + if test "`(/bin/universe) 2>/dev/null`" = att ; then + echo pyramid-pyramid-sysv3 + else + echo pyramid-pyramid-bsd + fi + exit 0 ;; + NILE*:*:*:dcosx) + echo pyramid-pyramid-svr4 + exit 0 ;; + DRS?6000:unix:4.0:6*) + echo sparc-icl-nx6 + exit 0 ;; + DRS?6000:UNIX_SV:4.2*:7*) + case `/usr/bin/uname -p` in + sparc) echo sparc-icl-nx7 && exit 0 ;; + esac ;; + sun4H:SunOS:5.*:*) + echo sparc-hal-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'` + exit 0 ;; + sun4*:SunOS:5.*:* | tadpole*:SunOS:5.*:*) + echo sparc-sun-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'` + exit 0 ;; + i86pc:SunOS:5.*:*) + echo i386-pc-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'` + exit 0 ;; + sun4*:SunOS:6*:*) + # According to config.sub, this is the proper way to canonicalize + # SunOS6. Hard to guess exactly what SunOS6 will be like, but + # it's likely to be more like Solaris than SunOS4. + echo sparc-sun-solaris3`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'` + exit 0 ;; + sun4*:SunOS:*:*) + case "`/usr/bin/arch -k`" in + Series*|S4*) + UNAME_RELEASE=`uname -v` + ;; + esac + # Japanese Language versions have a version number like `4.1.3-JL'. + echo sparc-sun-sunos`echo ${UNAME_RELEASE}|sed -e 's/-/_/'` + exit 0 ;; + sun3*:SunOS:*:*) + echo m68k-sun-sunos${UNAME_RELEASE} + exit 0 ;; + sun*:*:4.2BSD:*) + UNAME_RELEASE=`(sed 1q /etc/motd | awk '{print substr($5,1,3)}') 2>/dev/null` + test "x${UNAME_RELEASE}" = "x" && UNAME_RELEASE=3 + case "`/bin/arch`" in + sun3) + echo m68k-sun-sunos${UNAME_RELEASE} + ;; + sun4) + echo sparc-sun-sunos${UNAME_RELEASE} + ;; + esac + exit 0 ;; + aushp:SunOS:*:*) + echo sparc-auspex-sunos${UNAME_RELEASE} + exit 0 ;; + # The situation for MiNT is a little confusing. The machine name + # can be virtually everything (everything which is not + # "atarist" or "atariste" at least should have a processor + # > m68000). The system name ranges from "MiNT" over "FreeMiNT" + # to the lowercase version "mint" (or "freemint"). Finally + # the system name "TOS" denotes a system which is actually not + # MiNT. But MiNT is downward compatible to TOS, so this should + # be no problem. + atarist[e]:*MiNT:*:* | atarist[e]:*mint:*:* | atarist[e]:*TOS:*:*) + echo m68k-atari-mint${UNAME_RELEASE} + exit 0 ;; + atari*:*MiNT:*:* | atari*:*mint:*:* | atarist[e]:*TOS:*:*) + echo m68k-atari-mint${UNAME_RELEASE} + exit 0 ;; + *falcon*:*MiNT:*:* | *falcon*:*mint:*:* | *falcon*:*TOS:*:*) + echo m68k-atari-mint${UNAME_RELEASE} + exit 0 ;; + milan*:*MiNT:*:* | milan*:*mint:*:* | *milan*:*TOS:*:*) + echo m68k-milan-mint${UNAME_RELEASE} + exit 0 ;; + hades*:*MiNT:*:* | hades*:*mint:*:* | *hades*:*TOS:*:*) + echo m68k-hades-mint${UNAME_RELEASE} + exit 0 ;; + *:*MiNT:*:* | *:*mint:*:* | *:*TOS:*:*) + echo m68k-unknown-mint${UNAME_RELEASE} + exit 0 ;; + powerpc:machten:*:*) + echo powerpc-apple-machten${UNAME_RELEASE} + exit 0 ;; + RISC*:Mach:*:*) + echo mips-dec-mach_bsd4.3 + exit 0 ;; + RISC*:ULTRIX:*:*) + echo mips-dec-ultrix${UNAME_RELEASE} + exit 0 ;; + VAX*:ULTRIX*:*:*) + echo vax-dec-ultrix${UNAME_RELEASE} + exit 0 ;; + 2020:CLIX:*:* | 2430:CLIX:*:*) + echo clipper-intergraph-clix${UNAME_RELEASE} + exit 0 ;; + mips:*:*:UMIPS | mips:*:*:RISCos) + eval $set_cc_for_build + sed 's/^ //' << EOF >$dummy.c +#ifdef __cplusplus +#include /* for printf() prototype */ + int main (int argc, char *argv[]) { +#else + int main (argc, argv) int argc; char *argv[]; { +#endif + #if defined (host_mips) && defined (MIPSEB) + #if defined (SYSTYPE_SYSV) + printf ("mips-mips-riscos%ssysv\n", argv[1]); exit (0); + #endif + #if defined (SYSTYPE_SVR4) + printf ("mips-mips-riscos%ssvr4\n", argv[1]); exit (0); + #endif + #if defined (SYSTYPE_BSD43) || defined(SYSTYPE_BSD) + printf ("mips-mips-riscos%sbsd\n", argv[1]); exit (0); + #endif + #endif + exit (-1); + } +EOF + $CC_FOR_BUILD -o $dummy $dummy.c \ + && $dummy `echo "${UNAME_RELEASE}" | sed -n 's/\([0-9]*\).*/\1/p'` \ + && exit 0 + echo mips-mips-riscos${UNAME_RELEASE} + exit 0 ;; + Motorola:PowerMAX_OS:*:*) + echo powerpc-motorola-powermax + exit 0 ;; + Motorola:*:4.3:PL8-*) + echo powerpc-harris-powermax + exit 0 ;; + Night_Hawk:*:*:PowerMAX_OS | Synergy:PowerMAX_OS:*:*) + echo powerpc-harris-powermax + exit 0 ;; + Night_Hawk:Power_UNIX:*:*) + echo powerpc-harris-powerunix + exit 0 ;; + m88k:CX/UX:7*:*) + echo m88k-harris-cxux7 + exit 0 ;; + m88k:*:4*:R4*) + echo m88k-motorola-sysv4 + exit 0 ;; + m88k:*:3*:R3*) + echo m88k-motorola-sysv3 + exit 0 ;; + AViiON:dgux:*:*) + # DG/UX returns AViiON for all architectures + UNAME_PROCESSOR=`/usr/bin/uname -p` + if [ $UNAME_PROCESSOR = mc88100 ] || [ $UNAME_PROCESSOR = mc88110 ] + then + if [ ${TARGET_BINARY_INTERFACE}x = m88kdguxelfx ] || \ + [ ${TARGET_BINARY_INTERFACE}x = x ] + then + echo m88k-dg-dgux${UNAME_RELEASE} + else + echo m88k-dg-dguxbcs${UNAME_RELEASE} + fi + else + echo i586-dg-dgux${UNAME_RELEASE} + fi + exit 0 ;; + M88*:DolphinOS:*:*) # DolphinOS (SVR3) + echo m88k-dolphin-sysv3 + exit 0 ;; + M88*:*:R3*:*) + # Delta 88k system running SVR3 + echo m88k-motorola-sysv3 + exit 0 ;; + XD88*:*:*:*) # Tektronix XD88 system running UTekV (SVR3) + echo m88k-tektronix-sysv3 + exit 0 ;; + Tek43[0-9][0-9]:UTek:*:*) # Tektronix 4300 system running UTek (BSD) + echo m68k-tektronix-bsd + exit 0 ;; + *:IRIX*:*:*) + echo mips-sgi-irix`echo ${UNAME_RELEASE}|sed -e 's/-/_/g'` + exit 0 ;; + ????????:AIX?:[12].1:2) # AIX 2.2.1 or AIX 2.1.1 is RT/PC AIX. + echo romp-ibm-aix # uname -m gives an 8 hex-code CPU id + exit 0 ;; # Note that: echo "'`uname -s`'" gives 'AIX ' + i*86:AIX:*:*) + echo i386-ibm-aix + exit 0 ;; + ia64:AIX:*:*) + if [ -x /usr/bin/oslevel ] ; then + IBM_REV=`/usr/bin/oslevel` + else + IBM_REV=${UNAME_VERSION}.${UNAME_RELEASE} + fi + echo ${UNAME_MACHINE}-ibm-aix${IBM_REV} + exit 0 ;; + *:AIX:2:3) + if grep bos325 /usr/include/stdio.h >/dev/null 2>&1; then + eval $set_cc_for_build + sed 's/^ //' << EOF >$dummy.c + #include + + main() + { + if (!__power_pc()) + exit(1); + puts("powerpc-ibm-aix3.2.5"); + exit(0); + } +EOF + $CC_FOR_BUILD -o $dummy $dummy.c && $dummy && exit 0 + echo rs6000-ibm-aix3.2.5 + elif grep bos324 /usr/include/stdio.h >/dev/null 2>&1; then + echo rs6000-ibm-aix3.2.4 + else + echo rs6000-ibm-aix3.2 + fi + exit 0 ;; + *:AIX:*:[45]) + IBM_CPU_ID=`/usr/sbin/lsdev -C -c processor -S available | sed 1q | awk '{ print $1 }'` + if /usr/sbin/lsattr -El ${IBM_CPU_ID} | grep ' POWER' >/dev/null 2>&1; then + IBM_ARCH=rs6000 + else + IBM_ARCH=powerpc + fi + if [ -x /usr/bin/oslevel ] ; then + IBM_REV=`/usr/bin/oslevel` + else + IBM_REV=${UNAME_VERSION}.${UNAME_RELEASE} + fi + echo ${IBM_ARCH}-ibm-aix${IBM_REV} + exit 0 ;; + *:AIX:*:*) + echo rs6000-ibm-aix + exit 0 ;; + ibmrt:4.4BSD:*|romp-ibm:BSD:*) + echo romp-ibm-bsd4.4 + exit 0 ;; + ibmrt:*BSD:*|romp-ibm:BSD:*) # covers RT/PC BSD and + echo romp-ibm-bsd${UNAME_RELEASE} # 4.3 with uname added to + exit 0 ;; # report: romp-ibm BSD 4.3 + *:BOSX:*:*) + echo rs6000-bull-bosx + exit 0 ;; + DPX/2?00:B.O.S.:*:*) + echo m68k-bull-sysv3 + exit 0 ;; + 9000/[34]??:4.3bsd:1.*:*) + echo m68k-hp-bsd + exit 0 ;; + hp300:4.4BSD:*:* | 9000/[34]??:4.3bsd:2.*:*) + echo m68k-hp-bsd4.4 + exit 0 ;; + 9000/[34678]??:HP-UX:*:*) + HPUX_REV=`echo ${UNAME_RELEASE}|sed -e 's/[^.]*.[0B]*//'` + case "${UNAME_MACHINE}" in + 9000/31? ) HP_ARCH=m68000 ;; + 9000/[34]?? ) HP_ARCH=m68k ;; + 9000/[678][0-9][0-9]) + if [ -x /usr/bin/getconf ]; then + sc_cpu_version=`/usr/bin/getconf SC_CPU_VERSION 2>/dev/null` + sc_kernel_bits=`/usr/bin/getconf SC_KERNEL_BITS 2>/dev/null` + case "${sc_cpu_version}" in + 523) HP_ARCH="hppa1.0" ;; # CPU_PA_RISC1_0 + 528) HP_ARCH="hppa1.1" ;; # CPU_PA_RISC1_1 + 532) # CPU_PA_RISC2_0 + case "${sc_kernel_bits}" in + 32) HP_ARCH="hppa2.0n" ;; + 64) HP_ARCH="hppa2.0w" ;; + '') HP_ARCH="hppa2.0" ;; # HP-UX 10.20 + esac ;; + esac + fi + if [ "${HP_ARCH}" = "" ]; then + eval $set_cc_for_build + sed 's/^ //' << EOF >$dummy.c + + #define _HPUX_SOURCE + #include + #include + + int main () + { + #if defined(_SC_KERNEL_BITS) + long bits = sysconf(_SC_KERNEL_BITS); + #endif + long cpu = sysconf (_SC_CPU_VERSION); + + switch (cpu) + { + case CPU_PA_RISC1_0: puts ("hppa1.0"); break; + case CPU_PA_RISC1_1: puts ("hppa1.1"); break; + case CPU_PA_RISC2_0: + #if defined(_SC_KERNEL_BITS) + switch (bits) + { + case 64: puts ("hppa2.0w"); break; + case 32: puts ("hppa2.0n"); break; + default: puts ("hppa2.0"); break; + } break; + #else /* !defined(_SC_KERNEL_BITS) */ + puts ("hppa2.0"); break; + #endif + default: puts ("hppa1.0"); break; + } + exit (0); + } +EOF + (CCOPTS= $CC_FOR_BUILD -o $dummy $dummy.c 2>/dev/null) && HP_ARCH=`$dummy` + test -z "$HP_ARCH" && HP_ARCH=hppa + fi ;; + esac + if [ ${HP_ARCH} = "hppa2.0w" ] + then + # avoid double evaluation of $set_cc_for_build + test -n "$CC_FOR_BUILD" || eval $set_cc_for_build + if echo __LP64__ | (CCOPTS= $CC_FOR_BUILD -E -) | grep __LP64__ >/dev/null + then + HP_ARCH="hppa2.0w" + else + HP_ARCH="hppa64" + fi + fi + echo ${HP_ARCH}-hp-hpux${HPUX_REV} + exit 0 ;; + ia64:HP-UX:*:*) + HPUX_REV=`echo ${UNAME_RELEASE}|sed -e 's/[^.]*.[0B]*//'` + echo ia64-hp-hpux${HPUX_REV} + exit 0 ;; + 3050*:HI-UX:*:*) + eval $set_cc_for_build + sed 's/^ //' << EOF >$dummy.c + #include + int + main () + { + long cpu = sysconf (_SC_CPU_VERSION); + /* The order matters, because CPU_IS_HP_MC68K erroneously returns + true for CPU_PA_RISC1_0. CPU_IS_PA_RISC returns correct + results, however. */ + if (CPU_IS_PA_RISC (cpu)) + { + switch (cpu) + { + case CPU_PA_RISC1_0: puts ("hppa1.0-hitachi-hiuxwe2"); break; + case CPU_PA_RISC1_1: puts ("hppa1.1-hitachi-hiuxwe2"); break; + case CPU_PA_RISC2_0: puts ("hppa2.0-hitachi-hiuxwe2"); break; + default: puts ("hppa-hitachi-hiuxwe2"); break; + } + } + else if (CPU_IS_HP_MC68K (cpu)) + puts ("m68k-hitachi-hiuxwe2"); + else puts ("unknown-hitachi-hiuxwe2"); + exit (0); + } +EOF + $CC_FOR_BUILD -o $dummy $dummy.c && $dummy && exit 0 + echo unknown-hitachi-hiuxwe2 + exit 0 ;; + 9000/7??:4.3bsd:*:* | 9000/8?[79]:4.3bsd:*:* ) + echo hppa1.1-hp-bsd + exit 0 ;; + 9000/8??:4.3bsd:*:*) + echo hppa1.0-hp-bsd + exit 0 ;; + *9??*:MPE/iX:*:* | *3000*:MPE/iX:*:*) + echo hppa1.0-hp-mpeix + exit 0 ;; + hp7??:OSF1:*:* | hp8?[79]:OSF1:*:* ) + echo hppa1.1-hp-osf + exit 0 ;; + hp8??:OSF1:*:*) + echo hppa1.0-hp-osf + exit 0 ;; + i*86:OSF1:*:*) + if [ -x /usr/sbin/sysversion ] ; then + echo ${UNAME_MACHINE}-unknown-osf1mk + else + echo ${UNAME_MACHINE}-unknown-osf1 + fi + exit 0 ;; + parisc*:Lites*:*:*) + echo hppa1.1-hp-lites + exit 0 ;; + C1*:ConvexOS:*:* | convex:ConvexOS:C1*:*) + echo c1-convex-bsd + exit 0 ;; + C2*:ConvexOS:*:* | convex:ConvexOS:C2*:*) + if getsysinfo -f scalar_acc + then echo c32-convex-bsd + else echo c2-convex-bsd + fi + exit 0 ;; + C34*:ConvexOS:*:* | convex:ConvexOS:C34*:*) + echo c34-convex-bsd + exit 0 ;; + C38*:ConvexOS:*:* | convex:ConvexOS:C38*:*) + echo c38-convex-bsd + exit 0 ;; + C4*:ConvexOS:*:* | convex:ConvexOS:C4*:*) + echo c4-convex-bsd + exit 0 ;; + CRAY*Y-MP:*:*:*) + echo ymp-cray-unicos${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/' + exit 0 ;; + CRAY*[A-Z]90:*:*:*) + echo ${UNAME_MACHINE}-cray-unicos${UNAME_RELEASE} \ + | sed -e 's/CRAY.*\([A-Z]90\)/\1/' \ + -e y/ABCDEFGHIJKLMNOPQRSTUVWXYZ/abcdefghijklmnopqrstuvwxyz/ \ + -e 's/\.[^.]*$/.X/' + exit 0 ;; + CRAY*TS:*:*:*) + echo t90-cray-unicos${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/' + exit 0 ;; + CRAY*T3E:*:*:*) + echo alphaev5-cray-unicosmk${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/' + exit 0 ;; + CRAY*SV1:*:*:*) + echo sv1-cray-unicos${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/' + exit 0 ;; + *:UNICOS/mp:*:*) + echo nv1-cray-unicosmp${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/' + exit 0 ;; + F30[01]:UNIX_System_V:*:* | F700:UNIX_System_V:*:*) + FUJITSU_PROC=`uname -m | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz'` + FUJITSU_SYS=`uname -p | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz' | sed -e 's/\///'` + FUJITSU_REL=`echo ${UNAME_RELEASE} | sed -e 's/ /_/'` + echo "${FUJITSU_PROC}-fujitsu-${FUJITSU_SYS}${FUJITSU_REL}" + exit 0 ;; + i*86:BSD/386:*:* | i*86:BSD/OS:*:* | *:Ascend\ Embedded/OS:*:*) + echo ${UNAME_MACHINE}-pc-bsdi${UNAME_RELEASE} + exit 0 ;; + sparc*:BSD/OS:*:*) + echo sparc-unknown-bsdi${UNAME_RELEASE} + exit 0 ;; + *:BSD/OS:*:*) + echo ${UNAME_MACHINE}-unknown-bsdi${UNAME_RELEASE} + exit 0 ;; + *:FreeBSD:*:*|*:GNU/FreeBSD:*:*) + # Determine whether the default compiler uses glibc. + eval $set_cc_for_build + sed 's/^ //' << EOF >$dummy.c + #include + #if __GLIBC__ >= 2 + LIBC=gnu + #else + LIBC= + #endif +EOF + eval `$CC_FOR_BUILD -E $dummy.c 2>/dev/null | grep ^LIBC=` + # GNU/FreeBSD systems have a "k" prefix to indicate we are using + # FreeBSD's kernel, but not the complete OS. + case ${LIBC} in gnu) kernel_only='k' ;; esac + echo ${UNAME_MACHINE}-unknown-${kernel_only}freebsd`echo ${UNAME_RELEASE}|sed -e 's/[-(].*//'`${LIBC:+-$LIBC} + exit 0 ;; + i*:CYGWIN*:*) + echo ${UNAME_MACHINE}-pc-cygwin + exit 0 ;; + i*:MINGW*:*) + echo ${UNAME_MACHINE}-pc-mingw32 + exit 0 ;; + i*:PW*:*) + echo ${UNAME_MACHINE}-pc-pw32 + exit 0 ;; + x86:Interix*:[34]*) + echo i586-pc-interix${UNAME_RELEASE}|sed -e 's/\..*//' + exit 0 ;; + [345]86:Windows_95:* | [345]86:Windows_98:* | [345]86:Windows_NT:*) + echo i${UNAME_MACHINE}-pc-mks + exit 0 ;; + i*:Windows_NT*:* | Pentium*:Windows_NT*:*) + # How do we know it's Interix rather than the generic POSIX subsystem? + # It also conflicts with pre-2.0 versions of AT&T UWIN. Should we + # UNAME_MACHINE based on the output of uname instead of i386? + echo i586-pc-interix + exit 0 ;; + i*:UWIN*:*) + echo ${UNAME_MACHINE}-pc-uwin + exit 0 ;; + p*:CYGWIN*:*) + echo powerpcle-unknown-cygwin + exit 0 ;; + prep*:SunOS:5.*:*) + echo powerpcle-unknown-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'` + exit 0 ;; + *:GNU:*:*) + echo `echo ${UNAME_MACHINE}|sed -e 's,[-/].*$,,'`-unknown-gnu`echo ${UNAME_RELEASE}|sed -e 's,/.*$,,'` + exit 0 ;; + i*86:Minix:*:*) + echo ${UNAME_MACHINE}-pc-minix + exit 0 ;; + arm*:Linux:*:*) + echo ${UNAME_MACHINE}-unknown-linux-gnu + exit 0 ;; + cris:Linux:*:*) + echo cris-axis-linux-gnu + exit 0 ;; + ia64:Linux:*:*) + echo ${UNAME_MACHINE}-unknown-linux-gnu + exit 0 ;; + m68*:Linux:*:*) + echo ${UNAME_MACHINE}-unknown-linux-gnu + exit 0 ;; + mips:Linux:*:*) + eval $set_cc_for_build + sed 's/^ //' << EOF >$dummy.c + #undef CPU + #undef mips + #undef mipsel + #if defined(__MIPSEL__) || defined(__MIPSEL) || defined(_MIPSEL) || defined(MIPSEL) + CPU=mipsel + #else + #if defined(__MIPSEB__) || defined(__MIPSEB) || defined(_MIPSEB) || defined(MIPSEB) + CPU=mips + #else + CPU= + #endif + #endif +EOF + eval `$CC_FOR_BUILD -E $dummy.c 2>/dev/null | grep ^CPU=` + test x"${CPU}" != x && echo "${CPU}-unknown-linux-gnu" && exit 0 + ;; + mips64:Linux:*:*) + eval $set_cc_for_build + sed 's/^ //' << EOF >$dummy.c + #undef CPU + #undef mips64 + #undef mips64el + #if defined(__MIPSEL__) || defined(__MIPSEL) || defined(_MIPSEL) || defined(MIPSEL) + CPU=mips64el + #else + #if defined(__MIPSEB__) || defined(__MIPSEB) || defined(_MIPSEB) || defined(MIPSEB) + CPU=mips64 + #else + CPU= + #endif + #endif +EOF + eval `$CC_FOR_BUILD -E $dummy.c 2>/dev/null | grep ^CPU=` + test x"${CPU}" != x && echo "${CPU}-unknown-linux-gnu" && exit 0 + ;; + ppc:Linux:*:*) + echo powerpc-unknown-linux-gnu + exit 0 ;; + ppc64:Linux:*:*) + echo powerpc64-unknown-linux-gnu + exit 0 ;; + alpha:Linux:*:*) + case `sed -n '/^cpu model/s/^.*: \(.*\)/\1/p' < /proc/cpuinfo` in + EV5) UNAME_MACHINE=alphaev5 ;; + EV56) UNAME_MACHINE=alphaev56 ;; + PCA56) UNAME_MACHINE=alphapca56 ;; + PCA57) UNAME_MACHINE=alphapca56 ;; + EV6) UNAME_MACHINE=alphaev6 ;; + EV67) UNAME_MACHINE=alphaev67 ;; + EV68*) UNAME_MACHINE=alphaev68 ;; + esac + objdump --private-headers /bin/sh | grep ld.so.1 >/dev/null + if test "$?" = 0 ; then LIBC="libc1" ; else LIBC="" ; fi + echo ${UNAME_MACHINE}-unknown-linux-gnu${LIBC} + exit 0 ;; + parisc:Linux:*:* | hppa:Linux:*:*) + # Look for CPU level + case `grep '^cpu[^a-z]*:' /proc/cpuinfo 2>/dev/null | cut -d' ' -f2` in + PA7*) echo hppa1.1-unknown-linux-gnu ;; + PA8*) echo hppa2.0-unknown-linux-gnu ;; + *) echo hppa-unknown-linux-gnu ;; + esac + exit 0 ;; + parisc64:Linux:*:* | hppa64:Linux:*:*) + echo hppa64-unknown-linux-gnu + exit 0 ;; + s390:Linux:*:* | s390x:Linux:*:*) + echo ${UNAME_MACHINE}-ibm-linux + exit 0 ;; + sh64*:Linux:*:*) + echo ${UNAME_MACHINE}-unknown-linux-gnu + exit 0 ;; + sh*:Linux:*:*) + echo ${UNAME_MACHINE}-unknown-linux-gnu + exit 0 ;; + sparc:Linux:*:* | sparc64:Linux:*:*) + echo ${UNAME_MACHINE}-unknown-linux-gnu + exit 0 ;; + x86_64:Linux:*:*) + echo x86_64-unknown-linux-gnu + exit 0 ;; + i*86:Linux:*:*) + # The BFD linker knows what the default object file format is, so + # first see if it will tell us. cd to the root directory to prevent + # problems with other programs or directories called `ld' in the path. + # Set LC_ALL=C to ensure ld outputs messages in English. + ld_supported_targets=`cd /; LC_ALL=C ld --help 2>&1 \ + | sed -ne '/supported targets:/!d + s/[ ][ ]*/ /g + s/.*supported targets: *// + s/ .*// + p'` + case "$ld_supported_targets" in + elf32-i386) + TENTATIVE="${UNAME_MACHINE}-pc-linux-gnu" + ;; + a.out-i386-linux) + echo "${UNAME_MACHINE}-pc-linux-gnuaout" + exit 0 ;; + coff-i386) + echo "${UNAME_MACHINE}-pc-linux-gnucoff" + exit 0 ;; + "") + # Either a pre-BFD a.out linker (linux-gnuoldld) or + # one that does not give us useful --help. + echo "${UNAME_MACHINE}-pc-linux-gnuoldld" + exit 0 ;; + esac + # Determine whether the default compiler is a.out or elf + eval $set_cc_for_build + sed 's/^ //' << EOF >$dummy.c + #include + #ifdef __ELF__ + # ifdef __GLIBC__ + # if __GLIBC__ >= 2 + LIBC=gnu + # else + LIBC=gnulibc1 + # endif + # else + LIBC=gnulibc1 + # endif + #else + #ifdef __INTEL_COMPILER + LIBC=gnu + #else + LIBC=gnuaout + #endif + #endif + #ifdef __dietlibc__ + LIBC=dietlibc + #endif +EOF + eval `$CC_FOR_BUILD -E $dummy.c 2>/dev/null | grep ^LIBC=` + test x"${LIBC}" != x && echo "${UNAME_MACHINE}-pc-linux-${LIBC}" && exit 0 + test x"${TENTATIVE}" != x && echo "${TENTATIVE}" && exit 0 + ;; + i*86:DYNIX/ptx:4*:*) + # ptx 4.0 does uname -s correctly, with DYNIX/ptx in there. + # earlier versions are messed up and put the nodename in both + # sysname and nodename. + echo i386-sequent-sysv4 + exit 0 ;; + i*86:UNIX_SV:4.2MP:2.*) + # Unixware is an offshoot of SVR4, but it has its own version + # number series starting with 2... + # I am not positive that other SVR4 systems won't match this, + # I just have to hope. -- rms. + # Use sysv4.2uw... so that sysv4* matches it. + echo ${UNAME_MACHINE}-pc-sysv4.2uw${UNAME_VERSION} + exit 0 ;; + i*86:OS/2:*:*) + # If we were able to find `uname', then EMX Unix compatibility + # is probably installed. + echo ${UNAME_MACHINE}-pc-os2-emx + exit 0 ;; + i*86:XTS-300:*:STOP) + echo ${UNAME_MACHINE}-unknown-stop + exit 0 ;; + i*86:atheos:*:*) + echo ${UNAME_MACHINE}-unknown-atheos + exit 0 ;; + i*86:LynxOS:2.*:* | i*86:LynxOS:3.[01]*:* | i*86:LynxOS:4.0*:*) + echo i386-unknown-lynxos${UNAME_RELEASE} + exit 0 ;; + i*86:*DOS:*:*) + echo ${UNAME_MACHINE}-pc-msdosdjgpp + exit 0 ;; + i*86:*:4.*:* | i*86:SYSTEM_V:4.*:*) + UNAME_REL=`echo ${UNAME_RELEASE} | sed 's/\/MP$//'` + if grep Novell /usr/include/link.h >/dev/null 2>/dev/null; then + echo ${UNAME_MACHINE}-univel-sysv${UNAME_REL} + else + echo ${UNAME_MACHINE}-pc-sysv${UNAME_REL} + fi + exit 0 ;; + i*86:*:5:[78]*) + case `/bin/uname -X | grep "^Machine"` in + *486*) UNAME_MACHINE=i486 ;; + *Pentium) UNAME_MACHINE=i586 ;; + *Pent*|*Celeron) UNAME_MACHINE=i686 ;; + esac + echo ${UNAME_MACHINE}-unknown-sysv${UNAME_RELEASE}${UNAME_SYSTEM}${UNAME_VERSION} + exit 0 ;; + i*86:*:3.2:*) + if test -f /usr/options/cb.name; then + UNAME_REL=`sed -n 's/.*Version //p' /dev/null >/dev/null ; then + UNAME_REL=`(/bin/uname -X|grep Release|sed -e 's/.*= //')` + (/bin/uname -X|grep i80486 >/dev/null) && UNAME_MACHINE=i486 + (/bin/uname -X|grep '^Machine.*Pentium' >/dev/null) \ + && UNAME_MACHINE=i586 + (/bin/uname -X|grep '^Machine.*Pent *II' >/dev/null) \ + && UNAME_MACHINE=i686 + (/bin/uname -X|grep '^Machine.*Pentium Pro' >/dev/null) \ + && UNAME_MACHINE=i686 + echo ${UNAME_MACHINE}-pc-sco$UNAME_REL + else + echo ${UNAME_MACHINE}-pc-sysv32 + fi + exit 0 ;; + pc:*:*:*) + # Left here for compatibility: + # uname -m prints for DJGPP always 'pc', but it prints nothing about + # the processor, so we play safe by assuming i386. + echo i386-pc-msdosdjgpp + exit 0 ;; + Intel:Mach:3*:*) + echo i386-pc-mach3 + exit 0 ;; + paragon:*:*:*) + echo i860-intel-osf1 + exit 0 ;; + i860:*:4.*:*) # i860-SVR4 + if grep Stardent /usr/include/sys/uadmin.h >/dev/null 2>&1 ; then + echo i860-stardent-sysv${UNAME_RELEASE} # Stardent Vistra i860-SVR4 + else # Add other i860-SVR4 vendors below as they are discovered. + echo i860-unknown-sysv${UNAME_RELEASE} # Unknown i860-SVR4 + fi + exit 0 ;; + mini*:CTIX:SYS*5:*) + # "miniframe" + echo m68010-convergent-sysv + exit 0 ;; + mc68k:UNIX:SYSTEM5:3.51m) + echo m68k-convergent-sysv + exit 0 ;; + M680?0:D-NIX:5.3:*) + echo m68k-diab-dnix + exit 0 ;; + M68*:*:R3V[567]*:*) + test -r /sysV68 && echo 'm68k-motorola-sysv' && exit 0 ;; + 3[34]??:*:4.0:3.0 | 3[34]??A:*:4.0:3.0 | 3[34]??,*:*:4.0:3.0 | 3[34]??/*:*:4.0:3.0 | 4400:*:4.0:3.0 | 4850:*:4.0:3.0 | SKA40:*:4.0:3.0 | SDS2:*:4.0:3.0 | SHG2:*:4.0:3.0) + OS_REL='' + test -r /etc/.relid \ + && OS_REL=.`sed -n 's/[^ ]* [^ ]* \([0-9][0-9]\).*/\1/p' < /etc/.relid` + /bin/uname -p 2>/dev/null | grep 86 >/dev/null \ + && echo i486-ncr-sysv4.3${OS_REL} && exit 0 + /bin/uname -p 2>/dev/null | /bin/grep entium >/dev/null \ + && echo i586-ncr-sysv4.3${OS_REL} && exit 0 ;; + 3[34]??:*:4.0:* | 3[34]??,*:*:4.0:*) + /bin/uname -p 2>/dev/null | grep 86 >/dev/null \ + && echo i486-ncr-sysv4 && exit 0 ;; + m68*:LynxOS:2.*:* | m68*:LynxOS:3.0*:*) + echo m68k-unknown-lynxos${UNAME_RELEASE} + exit 0 ;; + mc68030:UNIX_System_V:4.*:*) + echo m68k-atari-sysv4 + exit 0 ;; + TSUNAMI:LynxOS:2.*:*) + echo sparc-unknown-lynxos${UNAME_RELEASE} + exit 0 ;; + rs6000:LynxOS:2.*:*) + echo rs6000-unknown-lynxos${UNAME_RELEASE} + exit 0 ;; + PowerPC:LynxOS:2.*:* | PowerPC:LynxOS:3.[01]*:* | PowerPC:LynxOS:4.0*:*) + echo powerpc-unknown-lynxos${UNAME_RELEASE} + exit 0 ;; + SM[BE]S:UNIX_SV:*:*) + echo mips-dde-sysv${UNAME_RELEASE} + exit 0 ;; + RM*:ReliantUNIX-*:*:*) + echo mips-sni-sysv4 + exit 0 ;; + RM*:SINIX-*:*:*) + echo mips-sni-sysv4 + exit 0 ;; + *:SINIX-*:*:*) + if uname -p 2>/dev/null >/dev/null ; then + UNAME_MACHINE=`(uname -p) 2>/dev/null` + echo ${UNAME_MACHINE}-sni-sysv4 + else + echo ns32k-sni-sysv + fi + exit 0 ;; + PENTIUM:*:4.0*:*) # Unisys `ClearPath HMP IX 4000' SVR4/MP effort + # says + echo i586-unisys-sysv4 + exit 0 ;; + *:UNIX_System_V:4*:FTX*) + # From Gerald Hewes . + # How about differentiating between stratus architectures? -djm + echo hppa1.1-stratus-sysv4 + exit 0 ;; + *:*:*:FTX*) + # From seanf@swdc.stratus.com. + echo i860-stratus-sysv4 + exit 0 ;; + *:VOS:*:*) + # From Paul.Green@stratus.com. + echo hppa1.1-stratus-vos + exit 0 ;; + mc68*:A/UX:*:*) + echo m68k-apple-aux${UNAME_RELEASE} + exit 0 ;; + news*:NEWS-OS:6*:*) + echo mips-sony-newsos6 + exit 0 ;; + R[34]000:*System_V*:*:* | R4000:UNIX_SYSV:*:* | R*000:UNIX_SV:*:*) + if [ -d /usr/nec ]; then + echo mips-nec-sysv${UNAME_RELEASE} + else + echo mips-unknown-sysv${UNAME_RELEASE} + fi + exit 0 ;; + BeBox:BeOS:*:*) # BeOS running on hardware made by Be, PPC only. + echo powerpc-be-beos + exit 0 ;; + BeMac:BeOS:*:*) # BeOS running on Mac or Mac clone, PPC only. + echo powerpc-apple-beos + exit 0 ;; + BePC:BeOS:*:*) # BeOS running on Intel PC compatible. + echo i586-pc-beos + exit 0 ;; + SX-4:SUPER-UX:*:*) + echo sx4-nec-superux${UNAME_RELEASE} + exit 0 ;; + SX-5:SUPER-UX:*:*) + echo sx5-nec-superux${UNAME_RELEASE} + exit 0 ;; + SX-6:SUPER-UX:*:*) + echo sx6-nec-superux${UNAME_RELEASE} + exit 0 ;; + Power*:Rhapsody:*:*) + echo powerpc-apple-rhapsody${UNAME_RELEASE} + exit 0 ;; + *:Rhapsody:*:*) + echo ${UNAME_MACHINE}-apple-rhapsody${UNAME_RELEASE} + exit 0 ;; + *:Darwin:*:*) + case `uname -p` in + *86) UNAME_PROCESSOR=i686 ;; + powerpc) UNAME_PROCESSOR=powerpc ;; + esac + echo ${UNAME_PROCESSOR}-apple-darwin${UNAME_RELEASE} + exit 0 ;; + *:procnto*:*:* | *:QNX:[0123456789]*:*) + UNAME_PROCESSOR=`uname -p` + if test "$UNAME_PROCESSOR" = "x86"; then + UNAME_PROCESSOR=i386 + UNAME_MACHINE=pc + fi + echo ${UNAME_PROCESSOR}-${UNAME_MACHINE}-nto-qnx${UNAME_RELEASE} + exit 0 ;; + *:QNX:*:4*) + echo i386-pc-qnx + exit 0 ;; + NSR-[DGKLNPTVWY]:NONSTOP_KERNEL:*:*) + echo nsr-tandem-nsk${UNAME_RELEASE} + exit 0 ;; + *:NonStop-UX:*:*) + echo mips-compaq-nonstopux + exit 0 ;; + BS2000:POSIX*:*:*) + echo bs2000-siemens-sysv + exit 0 ;; + DS/*:UNIX_System_V:*:*) + echo ${UNAME_MACHINE}-${UNAME_SYSTEM}-${UNAME_RELEASE} + exit 0 ;; + *:Plan9:*:*) + # "uname -m" is not consistent, so use $cputype instead. 386 + # is converted to i386 for consistency with other x86 + # operating systems. + if test "$cputype" = "386"; then + UNAME_MACHINE=i386 + else + UNAME_MACHINE="$cputype" + fi + echo ${UNAME_MACHINE}-unknown-plan9 + exit 0 ;; + *:TOPS-10:*:*) + echo pdp10-unknown-tops10 + exit 0 ;; + *:TENEX:*:*) + echo pdp10-unknown-tenex + exit 0 ;; + KS10:TOPS-20:*:* | KL10:TOPS-20:*:* | TYPE4:TOPS-20:*:*) + echo pdp10-dec-tops20 + exit 0 ;; + XKL-1:TOPS-20:*:* | TYPE5:TOPS-20:*:*) + echo pdp10-xkl-tops20 + exit 0 ;; + *:TOPS-20:*:*) + echo pdp10-unknown-tops20 + exit 0 ;; + *:ITS:*:*) + echo pdp10-unknown-its + exit 0 ;; + SEI:*:*:SEIUX) + echo mips-sei-seiux${UNAME_RELEASE} + exit 0 ;; +esac + +#echo '(No uname command or uname output not recognized.)' 1>&2 +#echo "${UNAME_MACHINE}:${UNAME_SYSTEM}:${UNAME_RELEASE}:${UNAME_VERSION}" 1>&2 + +eval $set_cc_for_build +cat >$dummy.c < +# include +#endif +main () +{ +#if defined (sony) +#if defined (MIPSEB) + /* BFD wants "bsd" instead of "newsos". Perhaps BFD should be changed, + I don't know.... */ + printf ("mips-sony-bsd\n"); exit (0); +#else +#include + printf ("m68k-sony-newsos%s\n", +#ifdef NEWSOS4 + "4" +#else + "" +#endif + ); exit (0); +#endif +#endif + +#if defined (__arm) && defined (__acorn) && defined (__unix) + printf ("arm-acorn-riscix"); exit (0); +#endif + +#if defined (hp300) && !defined (hpux) + printf ("m68k-hp-bsd\n"); exit (0); +#endif + +#if defined (NeXT) +#if !defined (__ARCHITECTURE__) +#define __ARCHITECTURE__ "m68k" +#endif + int version; + version=`(hostinfo | sed -n 's/.*NeXT Mach \([0-9]*\).*/\1/p') 2>/dev/null`; + if (version < 4) + printf ("%s-next-nextstep%d\n", __ARCHITECTURE__, version); + else + printf ("%s-next-openstep%d\n", __ARCHITECTURE__, version); + exit (0); +#endif + +#if defined (MULTIMAX) || defined (n16) +#if defined (UMAXV) + printf ("ns32k-encore-sysv\n"); exit (0); +#else +#if defined (CMU) + printf ("ns32k-encore-mach\n"); exit (0); +#else + printf ("ns32k-encore-bsd\n"); exit (0); +#endif +#endif +#endif + +#if defined (__386BSD__) + printf ("i386-pc-bsd\n"); exit (0); +#endif + +#if defined (sequent) +#if defined (i386) + printf ("i386-sequent-dynix\n"); exit (0); +#endif +#if defined (ns32000) + printf ("ns32k-sequent-dynix\n"); exit (0); +#endif +#endif + +#if defined (_SEQUENT_) + struct utsname un; + + uname(&un); + + if (strncmp(un.version, "V2", 2) == 0) { + printf ("i386-sequent-ptx2\n"); exit (0); + } + if (strncmp(un.version, "V1", 2) == 0) { /* XXX is V1 correct? */ + printf ("i386-sequent-ptx1\n"); exit (0); + } + printf ("i386-sequent-ptx\n"); exit (0); + +#endif + +#if defined (vax) +# if !defined (ultrix) +# include +# if defined (BSD) +# if BSD == 43 + printf ("vax-dec-bsd4.3\n"); exit (0); +# else +# if BSD == 199006 + printf ("vax-dec-bsd4.3reno\n"); exit (0); +# else + printf ("vax-dec-bsd\n"); exit (0); +# endif +# endif +# else + printf ("vax-dec-bsd\n"); exit (0); +# endif +# else + printf ("vax-dec-ultrix\n"); exit (0); +# endif +#endif + +#if defined (alliant) && defined (i860) + printf ("i860-alliant-bsd\n"); exit (0); +#endif + + exit (1); +} +EOF + +$CC_FOR_BUILD -o $dummy $dummy.c 2>/dev/null && $dummy && exit 0 + +# Apollos put the system type in the environment. + +test -d /usr/apollo && { echo ${ISP}-apollo-${SYSTYPE}; exit 0; } + +# Convex versions that predate uname can use getsysinfo(1) + +if [ -x /usr/convex/getsysinfo ] +then + case `getsysinfo -f cpu_type` in + c1*) + echo c1-convex-bsd + exit 0 ;; + c2*) + if getsysinfo -f scalar_acc + then echo c32-convex-bsd + else echo c2-convex-bsd + fi + exit 0 ;; + c34*) + echo c34-convex-bsd + exit 0 ;; + c38*) + echo c38-convex-bsd + exit 0 ;; + c4*) + echo c4-convex-bsd + exit 0 ;; + esac +fi + +cat >&2 < in order to provide the needed +information to handle your system. + +config.guess timestamp = $timestamp + +uname -m = `(uname -m) 2>/dev/null || echo unknown` +uname -r = `(uname -r) 2>/dev/null || echo unknown` +uname -s = `(uname -s) 2>/dev/null || echo unknown` +uname -v = `(uname -v) 2>/dev/null || echo unknown` + +/usr/bin/uname -p = `(/usr/bin/uname -p) 2>/dev/null` +/bin/uname -X = `(/bin/uname -X) 2>/dev/null` + +hostinfo = `(hostinfo) 2>/dev/null` +/bin/universe = `(/bin/universe) 2>/dev/null` +/usr/bin/arch -k = `(/usr/bin/arch -k) 2>/dev/null` +/bin/arch = `(/bin/arch) 2>/dev/null` +/usr/bin/oslevel = `(/usr/bin/oslevel) 2>/dev/null` +/usr/convex/getsysinfo = `(/usr/convex/getsysinfo) 2>/dev/null` + +UNAME_MACHINE = ${UNAME_MACHINE} +UNAME_RELEASE = ${UNAME_RELEASE} +UNAME_SYSTEM = ${UNAME_SYSTEM} +UNAME_VERSION = ${UNAME_VERSION} +EOF + +exit 1 + +# Local variables: +# eval: (add-hook 'write-file-hooks 'time-stamp) +# time-stamp-start: "timestamp='" +# time-stamp-format: "%:y-%02m-%02d" +# time-stamp-end: "'" +# End: diff --git a/libtecla-1.6.1/config.sub b/libtecla-1.6.1/config.sub new file mode 100644 index 0000000..1f31816 --- /dev/null +++ b/libtecla-1.6.1/config.sub @@ -0,0 +1,1510 @@ +#! /bin/sh +# Configuration validation subroutine script. +# Copyright (C) 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999, +# 2000, 2001, 2002, 2003 Free Software Foundation, Inc. + +timestamp='2003-08-18' + +# This file is (in principle) common to ALL GNU software. +# The presence of a machine in this file suggests that SOME GNU software +# can handle that machine. It does not imply ALL GNU software can. +# +# This file 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. + +# As a special exception to the GNU General Public License, if you +# distribute this file as part of a program that contains a +# configuration script generated by Autoconf, you may include it under +# the same distribution terms that you use for the rest of that program. + +# Please send patches to . Submit a context +# diff and a properly formatted ChangeLog entry. +# +# Configuration subroutine to validate and canonicalize a configuration type. +# Supply the specified configuration type as an argument. +# If it is invalid, we print an error message on stderr and exit with code 1. +# Otherwise, we print the canonical config type on stdout and succeed. + +# This file is supposed to be the same for all GNU packages +# and recognize all the CPU types, system types and aliases +# that are meaningful with *any* GNU software. +# Each package is responsible for reporting which valid configurations +# it does not support. The user should be able to distinguish +# a failure to support a valid configuration from a meaningless +# configuration. + +# The goal of this file is to map all the various variations of a given +# machine specification into a single specification in the form: +# CPU_TYPE-MANUFACTURER-OPERATING_SYSTEM +# or in some cases, the newer four-part form: +# CPU_TYPE-MANUFACTURER-KERNEL-OPERATING_SYSTEM +# It is wrong to echo any other type of specification. + +me=`echo "$0" | sed -e 's,.*/,,'` + +usage="\ +Usage: $0 [OPTION] CPU-MFR-OPSYS + $0 [OPTION] ALIAS + +Canonicalize a configuration name. + +Operation modes: + -h, --help print this help, then exit + -t, --time-stamp print date of last modification, then exit + -v, --version print version number, then exit + +Report bugs and patches to ." + +version="\ +GNU config.sub ($timestamp) + +Copyright (C) 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001 +Free Software Foundation, Inc. + +This is free software; see the source for copying conditions. There is NO +warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE." + +help=" +Try \`$me --help' for more information." + +# Parse command line +while test $# -gt 0 ; do + case $1 in + --time-stamp | --time* | -t ) + echo "$timestamp" ; exit 0 ;; + --version | -v ) + echo "$version" ; exit 0 ;; + --help | --h* | -h ) + echo "$usage"; exit 0 ;; + -- ) # Stop option processing + shift; break ;; + - ) # Use stdin as input. + break ;; + -* ) + echo "$me: invalid option $1$help" + exit 1 ;; + + *local*) + # First pass through any local machine types. + echo $1 + exit 0;; + + * ) + break ;; + esac +done + +case $# in + 0) echo "$me: missing argument$help" >&2 + exit 1;; + 1) ;; + *) echo "$me: too many arguments$help" >&2 + exit 1;; +esac + +# Separate what the user gave into CPU-COMPANY and OS or KERNEL-OS (if any). +# Here we must recognize all the valid KERNEL-OS combinations. +maybe_os=`echo $1 | sed 's/^\(.*\)-\([^-]*-[^-]*\)$/\2/'` +case $maybe_os in + nto-qnx* | linux-gnu* | linux-dietlibc | kfreebsd*-gnu* | netbsd*-gnu* | storm-chaos* | os2-emx* | rtmk-nova*) + os=-$maybe_os + basic_machine=`echo $1 | sed 's/^\(.*\)-\([^-]*-[^-]*\)$/\1/'` + ;; + *) + basic_machine=`echo $1 | sed 's/-[^-]*$//'` + if [ $basic_machine != $1 ] + then os=`echo $1 | sed 's/.*-/-/'` + else os=; fi + ;; +esac + +### Let's recognize common machines as not being operating systems so +### that things like config.sub decstation-3100 work. We also +### recognize some manufacturers as not being operating systems, so we +### can provide default operating systems below. +case $os in + -sun*os*) + # Prevent following clause from handling this invalid input. + ;; + -dec* | -mips* | -sequent* | -encore* | -pc532* | -sgi* | -sony* | \ + -att* | -7300* | -3300* | -delta* | -motorola* | -sun[234]* | \ + -unicom* | -ibm* | -next | -hp | -isi* | -apollo | -altos* | \ + -convergent* | -ncr* | -news | -32* | -3600* | -3100* | -hitachi* |\ + -c[123]* | -convex* | -sun | -crds | -omron* | -dg | -ultra | -tti* | \ + -harris | -dolphin | -highlevel | -gould | -cbm | -ns | -masscomp | \ + -apple | -axis) + os= + basic_machine=$1 + ;; + -sim | -cisco | -oki | -wec | -winbond) + os= + basic_machine=$1 + ;; + -scout) + ;; + -wrs) + os=-vxworks + basic_machine=$1 + ;; + -chorusos*) + os=-chorusos + basic_machine=$1 + ;; + -chorusrdb) + os=-chorusrdb + basic_machine=$1 + ;; + -hiux*) + os=-hiuxwe2 + ;; + -sco5) + os=-sco3.2v5 + basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` + ;; + -sco4) + os=-sco3.2v4 + basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` + ;; + -sco3.2.[4-9]*) + os=`echo $os | sed -e 's/sco3.2./sco3.2v/'` + basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` + ;; + -sco3.2v[4-9]*) + # Don't forget version if it is 3.2v4 or newer. + basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` + ;; + -sco*) + os=-sco3.2v2 + basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` + ;; + -udk*) + basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` + ;; + -isc) + os=-isc2.2 + basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` + ;; + -clix*) + basic_machine=clipper-intergraph + ;; + -isc*) + basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` + ;; + -lynx*) + os=-lynxos + ;; + -ptx*) + basic_machine=`echo $1 | sed -e 's/86-.*/86-sequent/'` + ;; + -windowsnt*) + os=`echo $os | sed -e 's/windowsnt/winnt/'` + ;; + -psos*) + os=-psos + ;; + -mint | -mint[0-9]*) + basic_machine=m68k-atari + os=-mint + ;; +esac + +# Decode aliases for certain CPU-COMPANY combinations. +case $basic_machine in + # Recognize the basic CPU types without company name. + # Some are omitted here because they have special meanings below. + 1750a | 580 \ + | a29k \ + | alpha | alphaev[4-8] | alphaev56 | alphaev6[78] | alphapca5[67] \ + | alpha64 | alpha64ev[4-8] | alpha64ev56 | alpha64ev6[78] | alpha64pca5[67] \ + | am33_2.0 \ + | arc | arm | arm[bl]e | arme[lb] | armv[2345] | armv[345][lb] | avr \ + | c4x | clipper \ + | d10v | d30v | dlx | dsp16xx \ + | fr30 | frv \ + | h8300 | h8500 | hppa | hppa1.[01] | hppa2.0 | hppa2.0[nw] | hppa64 \ + | i370 | i860 | i960 | ia64 \ + | ip2k | iq2000 \ + | m32r | m68000 | m68k | m88k | mcore \ + | mips | mipsbe | mipseb | mipsel | mipsle \ + | mips16 \ + | mips64 | mips64el \ + | mips64vr | mips64vrel \ + | mips64orion | mips64orionel \ + | mips64vr4100 | mips64vr4100el \ + | mips64vr4300 | mips64vr4300el \ + | mips64vr5000 | mips64vr5000el \ + | mipsisa32 | mipsisa32el \ + | mipsisa32r2 | mipsisa32r2el \ + | mipsisa64 | mipsisa64el \ + | mipsisa64r2 | mipsisa64r2el \ + | mipsisa64sb1 | mipsisa64sb1el \ + | mipsisa64sr71k | mipsisa64sr71kel \ + | mipstx39 | mipstx39el \ + | mn10200 | mn10300 \ + | msp430 \ + | ns16k | ns32k \ + | openrisc | or32 \ + | pdp10 | pdp11 | pj | pjl \ + | powerpc | powerpc64 | powerpc64le | powerpcle | ppcbe \ + | pyramid \ + | sh | sh[1234] | sh[23]e | sh[34]eb | shbe | shle | sh[1234]le | sh3ele \ + | sh64 | sh64le \ + | sparc | sparc64 | sparc86x | sparclet | sparclite | sparcv9 | sparcv9b \ + | strongarm \ + | tahoe | thumb | tic4x | tic80 | tron \ + | v850 | v850e \ + | we32k \ + | x86 | xscale | xstormy16 | xtensa \ + | z8k) + basic_machine=$basic_machine-unknown + ;; + m6811 | m68hc11 | m6812 | m68hc12) + # Motorola 68HC11/12. + basic_machine=$basic_machine-unknown + os=-none + ;; + m88110 | m680[12346]0 | m683?2 | m68360 | m5200 | v70 | w65 | z8k) + ;; + + # We use `pc' rather than `unknown' + # because (1) that's what they normally are, and + # (2) the word "unknown" tends to confuse beginning users. + i*86 | x86_64) + basic_machine=$basic_machine-pc + ;; + # Object if more than one company name word. + *-*-*) + echo Invalid configuration \`$1\': machine \`$basic_machine\' not recognized 1>&2 + exit 1 + ;; + # Recognize the basic CPU types with company name. + 580-* \ + | a29k-* \ + | alpha-* | alphaev[4-8]-* | alphaev56-* | alphaev6[78]-* \ + | alpha64-* | alpha64ev[4-8]-* | alpha64ev56-* | alpha64ev6[78]-* \ + | alphapca5[67]-* | alpha64pca5[67]-* | arc-* \ + | arm-* | armbe-* | armle-* | armeb-* | armv*-* \ + | avr-* \ + | bs2000-* \ + | c[123]* | c30-* | [cjt]90-* | c4x-* | c54x-* | c55x-* | c6x-* \ + | clipper-* | cydra-* \ + | d10v-* | d30v-* | dlx-* \ + | elxsi-* \ + | f30[01]-* | f700-* | fr30-* | frv-* | fx80-* \ + | h8300-* | h8500-* \ + | hppa-* | hppa1.[01]-* | hppa2.0-* | hppa2.0[nw]-* | hppa64-* \ + | i*86-* | i860-* | i960-* | ia64-* \ + | ip2k-* | iq2000-* \ + | m32r-* \ + | m68000-* | m680[012346]0-* | m68360-* | m683?2-* | m68k-* \ + | m88110-* | m88k-* | mcore-* \ + | mips-* | mipsbe-* | mipseb-* | mipsel-* | mipsle-* \ + | mips16-* \ + | mips64-* | mips64el-* \ + | mips64vr-* | mips64vrel-* \ + | mips64orion-* | mips64orionel-* \ + | mips64vr4100-* | mips64vr4100el-* \ + | mips64vr4300-* | mips64vr4300el-* \ + | mips64vr5000-* | mips64vr5000el-* \ + | mipsisa32-* | mipsisa32el-* \ + | mipsisa32r2-* | mipsisa32r2el-* \ + | mipsisa64-* | mipsisa64el-* \ + | mipsisa64r2-* | mipsisa64r2el-* \ + | mipsisa64sb1-* | mipsisa64sb1el-* \ + | mipsisa64sr71k-* | mipsisa64sr71kel-* \ + | mipstx39-* | mipstx39el-* \ + | msp430-* \ + | none-* | np1-* | nv1-* | ns16k-* | ns32k-* \ + | orion-* \ + | pdp10-* | pdp11-* | pj-* | pjl-* | pn-* | power-* \ + | powerpc-* | powerpc64-* | powerpc64le-* | powerpcle-* | ppcbe-* \ + | pyramid-* \ + | romp-* | rs6000-* \ + | sh-* | sh[1234]-* | sh[23]e-* | sh[34]eb-* | shbe-* \ + | shle-* | sh[1234]le-* | sh3ele-* | sh64-* | sh64le-* \ + | sparc-* | sparc64-* | sparc86x-* | sparclet-* | sparclite-* \ + | sparcv9-* | sparcv9b-* | strongarm-* | sv1-* | sx?-* \ + | tahoe-* | thumb-* \ + | tic30-* | tic4x-* | tic54x-* | tic55x-* | tic6x-* | tic80-* \ + | tron-* \ + | v850-* | v850e-* | vax-* \ + | we32k-* \ + | x86-* | x86_64-* | xps100-* | xscale-* | xstormy16-* \ + | xtensa-* \ + | ymp-* \ + | z8k-*) + ;; + # Recognize the various machine names and aliases which stand + # for a CPU type and a company and sometimes even an OS. + 386bsd) + basic_machine=i386-unknown + os=-bsd + ;; + 3b1 | 7300 | 7300-att | att-7300 | pc7300 | safari | unixpc) + basic_machine=m68000-att + ;; + 3b*) + basic_machine=we32k-att + ;; + a29khif) + basic_machine=a29k-amd + os=-udi + ;; + adobe68k) + basic_machine=m68010-adobe + os=-scout + ;; + alliant | fx80) + basic_machine=fx80-alliant + ;; + altos | altos3068) + basic_machine=m68k-altos + ;; + am29k) + basic_machine=a29k-none + os=-bsd + ;; + amd64) + basic_machine=x86_64-pc + ;; + amdahl) + basic_machine=580-amdahl + os=-sysv + ;; + amiga | amiga-*) + basic_machine=m68k-unknown + ;; + amigaos | amigados) + basic_machine=m68k-unknown + os=-amigaos + ;; + amigaunix | amix) + basic_machine=m68k-unknown + os=-sysv4 + ;; + apollo68) + basic_machine=m68k-apollo + os=-sysv + ;; + apollo68bsd) + basic_machine=m68k-apollo + os=-bsd + ;; + aux) + basic_machine=m68k-apple + os=-aux + ;; + balance) + basic_machine=ns32k-sequent + os=-dynix + ;; + c90) + basic_machine=c90-cray + os=-unicos + ;; + convex-c1) + basic_machine=c1-convex + os=-bsd + ;; + convex-c2) + basic_machine=c2-convex + os=-bsd + ;; + convex-c32) + basic_machine=c32-convex + os=-bsd + ;; + convex-c34) + basic_machine=c34-convex + os=-bsd + ;; + convex-c38) + basic_machine=c38-convex + os=-bsd + ;; + cray | j90) + basic_machine=j90-cray + os=-unicos + ;; + crds | unos) + basic_machine=m68k-crds + ;; + cris | cris-* | etrax*) + basic_machine=cris-axis + ;; + da30 | da30-*) + basic_machine=m68k-da30 + ;; + decstation | decstation-3100 | pmax | pmax-* | pmin | dec3100 | decstatn) + basic_machine=mips-dec + ;; + decsystem10* | dec10*) + basic_machine=pdp10-dec + os=-tops10 + ;; + decsystem20* | dec20*) + basic_machine=pdp10-dec + os=-tops20 + ;; + delta | 3300 | motorola-3300 | motorola-delta \ + | 3300-motorola | delta-motorola) + basic_machine=m68k-motorola + ;; + delta88) + basic_machine=m88k-motorola + os=-sysv3 + ;; + dpx20 | dpx20-*) + basic_machine=rs6000-bull + os=-bosx + ;; + dpx2* | dpx2*-bull) + basic_machine=m68k-bull + os=-sysv3 + ;; + ebmon29k) + basic_machine=a29k-amd + os=-ebmon + ;; + elxsi) + basic_machine=elxsi-elxsi + os=-bsd + ;; + encore | umax | mmax) + basic_machine=ns32k-encore + ;; + es1800 | OSE68k | ose68k | ose | OSE) + basic_machine=m68k-ericsson + os=-ose + ;; + fx2800) + basic_machine=i860-alliant + ;; + genix) + basic_machine=ns32k-ns + ;; + gmicro) + basic_machine=tron-gmicro + os=-sysv + ;; + go32) + basic_machine=i386-pc + os=-go32 + ;; + h3050r* | hiux*) + basic_machine=hppa1.1-hitachi + os=-hiuxwe2 + ;; + h8300hms) + basic_machine=h8300-hitachi + os=-hms + ;; + h8300xray) + basic_machine=h8300-hitachi + os=-xray + ;; + h8500hms) + basic_machine=h8500-hitachi + os=-hms + ;; + harris) + basic_machine=m88k-harris + os=-sysv3 + ;; + hp300-*) + basic_machine=m68k-hp + ;; + hp300bsd) + basic_machine=m68k-hp + os=-bsd + ;; + hp300hpux) + basic_machine=m68k-hp + os=-hpux + ;; + hp3k9[0-9][0-9] | hp9[0-9][0-9]) + basic_machine=hppa1.0-hp + ;; + hp9k2[0-9][0-9] | hp9k31[0-9]) + basic_machine=m68000-hp + ;; + hp9k3[2-9][0-9]) + basic_machine=m68k-hp + ;; + hp9k6[0-9][0-9] | hp6[0-9][0-9]) + basic_machine=hppa1.0-hp + ;; + hp9k7[0-79][0-9] | hp7[0-79][0-9]) + basic_machine=hppa1.1-hp + ;; + hp9k78[0-9] | hp78[0-9]) + # FIXME: really hppa2.0-hp + basic_machine=hppa1.1-hp + ;; + hp9k8[67]1 | hp8[67]1 | hp9k80[24] | hp80[24] | hp9k8[78]9 | hp8[78]9 | hp9k893 | hp893) + # FIXME: really hppa2.0-hp + basic_machine=hppa1.1-hp + ;; + hp9k8[0-9][13679] | hp8[0-9][13679]) + basic_machine=hppa1.1-hp + ;; + hp9k8[0-9][0-9] | hp8[0-9][0-9]) + basic_machine=hppa1.0-hp + ;; + hppa-next) + os=-nextstep3 + ;; + hppaosf) + basic_machine=hppa1.1-hp + os=-osf + ;; + hppro) + basic_machine=hppa1.1-hp + os=-proelf + ;; + i370-ibm* | ibm*) + basic_machine=i370-ibm + ;; +# I'm not sure what "Sysv32" means. Should this be sysv3.2? + i*86v32) + basic_machine=`echo $1 | sed -e 's/86.*/86-pc/'` + os=-sysv32 + ;; + i*86v4*) + basic_machine=`echo $1 | sed -e 's/86.*/86-pc/'` + os=-sysv4 + ;; + i*86v) + basic_machine=`echo $1 | sed -e 's/86.*/86-pc/'` + os=-sysv + ;; + i*86sol2) + basic_machine=`echo $1 | sed -e 's/86.*/86-pc/'` + os=-solaris2 + ;; + i386mach) + basic_machine=i386-mach + os=-mach + ;; + i386-vsta | vsta) + basic_machine=i386-unknown + os=-vsta + ;; + iris | iris4d) + basic_machine=mips-sgi + case $os in + -irix*) + ;; + *) + os=-irix4 + ;; + esac + ;; + isi68 | isi) + basic_machine=m68k-isi + os=-sysv + ;; + m88k-omron*) + basic_machine=m88k-omron + ;; + magnum | m3230) + basic_machine=mips-mips + os=-sysv + ;; + merlin) + basic_machine=ns32k-utek + os=-sysv + ;; + mingw32) + basic_machine=i386-pc + os=-mingw32 + ;; + miniframe) + basic_machine=m68000-convergent + ;; + *mint | -mint[0-9]* | *MiNT | *MiNT[0-9]*) + basic_machine=m68k-atari + os=-mint + ;; + mips3*-*) + basic_machine=`echo $basic_machine | sed -e 's/mips3/mips64/'` + ;; + mips3*) + basic_machine=`echo $basic_machine | sed -e 's/mips3/mips64/'`-unknown + ;; + mmix*) + basic_machine=mmix-knuth + os=-mmixware + ;; + monitor) + basic_machine=m68k-rom68k + os=-coff + ;; + morphos) + basic_machine=powerpc-unknown + os=-morphos + ;; + msdos) + basic_machine=i386-pc + os=-msdos + ;; + mvs) + basic_machine=i370-ibm + os=-mvs + ;; + ncr3000) + basic_machine=i486-ncr + os=-sysv4 + ;; + netbsd386) + basic_machine=i386-unknown + os=-netbsd + ;; + netwinder) + basic_machine=armv4l-rebel + os=-linux + ;; + news | news700 | news800 | news900) + basic_machine=m68k-sony + os=-newsos + ;; + news1000) + basic_machine=m68030-sony + os=-newsos + ;; + news-3600 | risc-news) + basic_machine=mips-sony + os=-newsos + ;; + necv70) + basic_machine=v70-nec + os=-sysv + ;; + next | m*-next ) + basic_machine=m68k-next + case $os in + -nextstep* ) + ;; + -ns2*) + os=-nextstep2 + ;; + *) + os=-nextstep3 + ;; + esac + ;; + nh3000) + basic_machine=m68k-harris + os=-cxux + ;; + nh[45]000) + basic_machine=m88k-harris + os=-cxux + ;; + nindy960) + basic_machine=i960-intel + os=-nindy + ;; + mon960) + basic_machine=i960-intel + os=-mon960 + ;; + nonstopux) + basic_machine=mips-compaq + os=-nonstopux + ;; + np1) + basic_machine=np1-gould + ;; + nv1) + basic_machine=nv1-cray + os=-unicosmp + ;; + nsr-tandem) + basic_machine=nsr-tandem + ;; + op50n-* | op60c-*) + basic_machine=hppa1.1-oki + os=-proelf + ;; + or32 | or32-*) + basic_machine=or32-unknown + os=-coff + ;; + OSE68000 | ose68000) + basic_machine=m68000-ericsson + os=-ose + ;; + os68k) + basic_machine=m68k-none + os=-os68k + ;; + pa-hitachi) + basic_machine=hppa1.1-hitachi + os=-hiuxwe2 + ;; + paragon) + basic_machine=i860-intel + os=-osf + ;; + pbd) + basic_machine=sparc-tti + ;; + pbb) + basic_machine=m68k-tti + ;; + pc532 | pc532-*) + basic_machine=ns32k-pc532 + ;; + pentium | p5 | k5 | k6 | nexgen | viac3) + basic_machine=i586-pc + ;; + pentiumpro | p6 | 6x86 | athlon | athlon_*) + basic_machine=i686-pc + ;; + pentiumii | pentium2 | pentiumiii | pentium3) + basic_machine=i686-pc + ;; + pentium4) + basic_machine=i786-pc + ;; + pentium-* | p5-* | k5-* | k6-* | nexgen-* | viac3-*) + basic_machine=i586-`echo $basic_machine | sed 's/^[^-]*-//'` + ;; + pentiumpro-* | p6-* | 6x86-* | athlon-*) + basic_machine=i686-`echo $basic_machine | sed 's/^[^-]*-//'` + ;; + pentiumii-* | pentium2-* | pentiumiii-* | pentium3-*) + basic_machine=i686-`echo $basic_machine | sed 's/^[^-]*-//'` + ;; + pentium4-*) + basic_machine=i786-`echo $basic_machine | sed 's/^[^-]*-//'` + ;; + pn) + basic_machine=pn-gould + ;; + power) basic_machine=power-ibm + ;; + ppc) basic_machine=powerpc-unknown + ;; + ppc-*) basic_machine=powerpc-`echo $basic_machine | sed 's/^[^-]*-//'` + ;; + ppcle | powerpclittle | ppc-le | powerpc-little) + basic_machine=powerpcle-unknown + ;; + ppcle-* | powerpclittle-*) + basic_machine=powerpcle-`echo $basic_machine | sed 's/^[^-]*-//'` + ;; + ppc64) basic_machine=powerpc64-unknown + ;; + ppc64-*) basic_machine=powerpc64-`echo $basic_machine | sed 's/^[^-]*-//'` + ;; + ppc64le | powerpc64little | ppc64-le | powerpc64-little) + basic_machine=powerpc64le-unknown + ;; + ppc64le-* | powerpc64little-*) + basic_machine=powerpc64le-`echo $basic_machine | sed 's/^[^-]*-//'` + ;; + ps2) + basic_machine=i386-ibm + ;; + pw32) + basic_machine=i586-unknown + os=-pw32 + ;; + rom68k) + basic_machine=m68k-rom68k + os=-coff + ;; + rm[46]00) + basic_machine=mips-siemens + ;; + rtpc | rtpc-*) + basic_machine=romp-ibm + ;; + s390 | s390-*) + basic_machine=s390-ibm + ;; + s390x | s390x-*) + basic_machine=s390x-ibm + ;; + sa29200) + basic_machine=a29k-amd + os=-udi + ;; + sb1) + basic_machine=mipsisa64sb1-unknown + ;; + sb1el) + basic_machine=mipsisa64sb1el-unknown + ;; + sei) + basic_machine=mips-sei + os=-seiux + ;; + sequent) + basic_machine=i386-sequent + ;; + sh) + basic_machine=sh-hitachi + os=-hms + ;; + sh64) + basic_machine=sh64-unknown + ;; + sparclite-wrs | simso-wrs) + basic_machine=sparclite-wrs + os=-vxworks + ;; + sps7) + basic_machine=m68k-bull + os=-sysv2 + ;; + spur) + basic_machine=spur-unknown + ;; + st2000) + basic_machine=m68k-tandem + ;; + stratus) + basic_machine=i860-stratus + os=-sysv4 + ;; + sun2) + basic_machine=m68000-sun + ;; + sun2os3) + basic_machine=m68000-sun + os=-sunos3 + ;; + sun2os4) + basic_machine=m68000-sun + os=-sunos4 + ;; + sun3os3) + basic_machine=m68k-sun + os=-sunos3 + ;; + sun3os4) + basic_machine=m68k-sun + os=-sunos4 + ;; + sun4os3) + basic_machine=sparc-sun + os=-sunos3 + ;; + sun4os4) + basic_machine=sparc-sun + os=-sunos4 + ;; + sun4sol2) + basic_machine=sparc-sun + os=-solaris2 + ;; + sun3 | sun3-*) + basic_machine=m68k-sun + ;; + sun4) + basic_machine=sparc-sun + ;; + sun386 | sun386i | roadrunner) + basic_machine=i386-sun + ;; + sv1) + basic_machine=sv1-cray + os=-unicos + ;; + symmetry) + basic_machine=i386-sequent + os=-dynix + ;; + t3e) + basic_machine=alphaev5-cray + os=-unicos + ;; + t90) + basic_machine=t90-cray + os=-unicos + ;; + tic54x | c54x*) + basic_machine=tic54x-unknown + os=-coff + ;; + tic55x | c55x*) + basic_machine=tic55x-unknown + os=-coff + ;; + tic6x | c6x*) + basic_machine=tic6x-unknown + os=-coff + ;; + tx39) + basic_machine=mipstx39-unknown + ;; + tx39el) + basic_machine=mipstx39el-unknown + ;; + toad1) + basic_machine=pdp10-xkl + os=-tops20 + ;; + tower | tower-32) + basic_machine=m68k-ncr + ;; + udi29k) + basic_machine=a29k-amd + os=-udi + ;; + ultra3) + basic_machine=a29k-nyu + os=-sym1 + ;; + v810 | necv810) + basic_machine=v810-nec + os=-none + ;; + vaxv) + basic_machine=vax-dec + os=-sysv + ;; + vms) + basic_machine=vax-dec + os=-vms + ;; + vpp*|vx|vx-*) + basic_machine=f301-fujitsu + ;; + vxworks960) + basic_machine=i960-wrs + os=-vxworks + ;; + vxworks68) + basic_machine=m68k-wrs + os=-vxworks + ;; + vxworks29k) + basic_machine=a29k-wrs + os=-vxworks + ;; + w65*) + basic_machine=w65-wdc + os=-none + ;; + w89k-*) + basic_machine=hppa1.1-winbond + os=-proelf + ;; + xps | xps100) + basic_machine=xps100-honeywell + ;; + ymp) + basic_machine=ymp-cray + os=-unicos + ;; + z8k-*-coff) + basic_machine=z8k-unknown + os=-sim + ;; + none) + basic_machine=none-none + os=-none + ;; + +# Here we handle the default manufacturer of certain CPU types. It is in +# some cases the only manufacturer, in others, it is the most popular. + w89k) + basic_machine=hppa1.1-winbond + ;; + op50n) + basic_machine=hppa1.1-oki + ;; + op60c) + basic_machine=hppa1.1-oki + ;; + romp) + basic_machine=romp-ibm + ;; + rs6000) + basic_machine=rs6000-ibm + ;; + vax) + basic_machine=vax-dec + ;; + pdp10) + # there are many clones, so DEC is not a safe bet + basic_machine=pdp10-unknown + ;; + pdp11) + basic_machine=pdp11-dec + ;; + we32k) + basic_machine=we32k-att + ;; + sh3 | sh4 | sh[34]eb | sh[1234]le | sh[23]ele) + basic_machine=sh-unknown + ;; + sh64) + basic_machine=sh64-unknown + ;; + sparc | sparcv9 | sparcv9b) + basic_machine=sparc-sun + ;; + cydra) + basic_machine=cydra-cydrome + ;; + orion) + basic_machine=orion-highlevel + ;; + orion105) + basic_machine=clipper-highlevel + ;; + mac | mpw | mac-mpw) + basic_machine=m68k-apple + ;; + pmac | pmac-mpw) + basic_machine=powerpc-apple + ;; + *-unknown) + # Make sure to match an already-canonicalized machine name. + ;; + *) + echo Invalid configuration \`$1\': machine \`$basic_machine\' not recognized 1>&2 + exit 1 + ;; +esac + +# Here we canonicalize certain aliases for manufacturers. +case $basic_machine in + *-digital*) + basic_machine=`echo $basic_machine | sed 's/digital.*/dec/'` + ;; + *-commodore*) + basic_machine=`echo $basic_machine | sed 's/commodore.*/cbm/'` + ;; + *) + ;; +esac + +# Decode manufacturer-specific aliases for certain operating systems. + +if [ x"$os" != x"" ] +then +case $os in + # First match some system type aliases + # that might get confused with valid system types. + # -solaris* is a basic system type, with this one exception. + -solaris1 | -solaris1.*) + os=`echo $os | sed -e 's|solaris1|sunos4|'` + ;; + -solaris) + os=-solaris2 + ;; + -svr4*) + os=-sysv4 + ;; + -unixware*) + os=-sysv4.2uw + ;; + -gnu/linux*) + os=`echo $os | sed -e 's|gnu/linux|linux-gnu|'` + ;; + # First accept the basic system types. + # The portable systems comes first. + # Each alternative MUST END IN A *, to match a version number. + # -sysv* is not here because it comes later, after sysvr4. + -gnu* | -bsd* | -mach* | -minix* | -genix* | -ultrix* | -irix* \ + | -*vms* | -sco* | -esix* | -isc* | -aix* | -sunos | -sunos[34]*\ + | -hpux* | -unos* | -osf* | -luna* | -dgux* | -solaris* | -sym* \ + | -amigaos* | -amigados* | -msdos* | -newsos* | -unicos* | -aof* \ + | -aos* \ + | -nindy* | -vxsim* | -vxworks* | -ebmon* | -hms* | -mvs* \ + | -clix* | -riscos* | -uniplus* | -iris* | -rtu* | -xenix* \ + | -hiux* | -386bsd* | -netbsd* | -openbsd* | -kfreebsd* | -freebsd* | -riscix* \ + | -lynxos* | -bosx* | -nextstep* | -cxux* | -aout* | -elf* | -oabi* \ + | -ptx* | -coff* | -ecoff* | -winnt* | -domain* | -vsta* \ + | -udi* | -eabi* | -lites* | -ieee* | -go32* | -aux* \ + | -chorusos* | -chorusrdb* \ + | -cygwin* | -pe* | -psos* | -moss* | -proelf* | -rtems* \ + | -mingw32* | -linux-gnu* | -uxpv* | -beos* | -mpeix* | -udk* \ + | -interix* | -uwin* | -mks* | -rhapsody* | -darwin* | -opened* \ + | -openstep* | -oskit* | -conix* | -pw32* | -nonstopux* \ + | -storm-chaos* | -tops10* | -tenex* | -tops20* | -its* \ + | -os2* | -vos* | -palmos* | -uclinux* | -nucleus* \ + | -morphos* | -superux* | -rtmk* | -rtmk-nova* | -windiss* \ + | -powermax* | -dnix* | -nx6 | -nx7 | -sei*) + # Remember, each alternative MUST END IN *, to match a version number. + ;; + -qnx*) + case $basic_machine in + x86-* | i*86-*) + ;; + *) + os=-nto$os + ;; + esac + ;; + -nto-qnx*) + ;; + -nto*) + os=`echo $os | sed -e 's|nto|nto-qnx|'` + ;; + -sim | -es1800* | -hms* | -xray | -os68k* | -none* | -v88r* \ + | -windows* | -osx | -abug | -netware* | -os9* | -beos* \ + | -macos* | -mpw* | -magic* | -mmixware* | -mon960* | -lnews*) + ;; + -mac*) + os=`echo $os | sed -e 's|mac|macos|'` + ;; + -linux-dietlibc) + os=-linux-dietlibc + ;; + -linux*) + os=`echo $os | sed -e 's|linux|linux-gnu|'` + ;; + -sunos5*) + os=`echo $os | sed -e 's|sunos5|solaris2|'` + ;; + -sunos6*) + os=`echo $os | sed -e 's|sunos6|solaris3|'` + ;; + -opened*) + os=-openedition + ;; + -wince*) + os=-wince + ;; + -osfrose*) + os=-osfrose + ;; + -osf*) + os=-osf + ;; + -utek*) + os=-bsd + ;; + -dynix*) + os=-bsd + ;; + -acis*) + os=-aos + ;; + -atheos*) + os=-atheos + ;; + -386bsd) + os=-bsd + ;; + -ctix* | -uts*) + os=-sysv + ;; + -nova*) + os=-rtmk-nova + ;; + -ns2 ) + os=-nextstep2 + ;; + -nsk*) + os=-nsk + ;; + # Preserve the version number of sinix5. + -sinix5.*) + os=`echo $os | sed -e 's|sinix|sysv|'` + ;; + -sinix*) + os=-sysv4 + ;; + -triton*) + os=-sysv3 + ;; + -oss*) + os=-sysv3 + ;; + -svr4) + os=-sysv4 + ;; + -svr3) + os=-sysv3 + ;; + -sysvr4) + os=-sysv4 + ;; + # This must come after -sysvr4. + -sysv*) + ;; + -ose*) + os=-ose + ;; + -es1800*) + os=-ose + ;; + -xenix) + os=-xenix + ;; + -*mint | -mint[0-9]* | -*MiNT | -MiNT[0-9]*) + os=-mint + ;; + -aros*) + os=-aros + ;; + -kaos*) + os=-kaos + ;; + -none) + ;; + *) + # Get rid of the `-' at the beginning of $os. + os=`echo $os | sed 's/[^-]*-//'` + echo Invalid configuration \`$1\': system \`$os\' not recognized 1>&2 + exit 1 + ;; +esac +else + +# Here we handle the default operating systems that come with various machines. +# The value should be what the vendor currently ships out the door with their +# machine or put another way, the most popular os provided with the machine. + +# Note that if you're going to try to match "-MANUFACTURER" here (say, +# "-sun"), then you have to tell the case statement up towards the top +# that MANUFACTURER isn't an operating system. Otherwise, code above +# will signal an error saying that MANUFACTURER isn't an operating +# system, and we'll never get to this point. + +case $basic_machine in + *-acorn) + os=-riscix1.2 + ;; + arm*-rebel) + os=-linux + ;; + arm*-semi) + os=-aout + ;; + c4x-* | tic4x-*) + os=-coff + ;; + # This must come before the *-dec entry. + pdp10-*) + os=-tops20 + ;; + pdp11-*) + os=-none + ;; + *-dec | vax-*) + os=-ultrix4.2 + ;; + m68*-apollo) + os=-domain + ;; + i386-sun) + os=-sunos4.0.2 + ;; + m68000-sun) + os=-sunos3 + # This also exists in the configure program, but was not the + # default. + # os=-sunos4 + ;; + m68*-cisco) + os=-aout + ;; + mips*-cisco) + os=-elf + ;; + mips*-*) + os=-elf + ;; + or32-*) + os=-coff + ;; + *-tti) # must be before sparc entry or we get the wrong os. + os=-sysv3 + ;; + sparc-* | *-sun) + os=-sunos4.1.1 + ;; + *-be) + os=-beos + ;; + *-ibm) + os=-aix + ;; + *-wec) + os=-proelf + ;; + *-winbond) + os=-proelf + ;; + *-oki) + os=-proelf + ;; + *-hp) + os=-hpux + ;; + *-hitachi) + os=-hiux + ;; + i860-* | *-att | *-ncr | *-altos | *-motorola | *-convergent) + os=-sysv + ;; + *-cbm) + os=-amigaos + ;; + *-dg) + os=-dgux + ;; + *-dolphin) + os=-sysv3 + ;; + m68k-ccur) + os=-rtu + ;; + m88k-omron*) + os=-luna + ;; + *-next ) + os=-nextstep + ;; + *-sequent) + os=-ptx + ;; + *-crds) + os=-unos + ;; + *-ns) + os=-genix + ;; + i370-*) + os=-mvs + ;; + *-next) + os=-nextstep3 + ;; + *-gould) + os=-sysv + ;; + *-highlevel) + os=-bsd + ;; + *-encore) + os=-bsd + ;; + *-sgi) + os=-irix + ;; + *-siemens) + os=-sysv4 + ;; + *-masscomp) + os=-rtu + ;; + f30[01]-fujitsu | f700-fujitsu) + os=-uxpv + ;; + *-rom68k) + os=-coff + ;; + *-*bug) + os=-coff + ;; + *-apple) + os=-macos + ;; + *-atari*) + os=-mint + ;; + *) + os=-none + ;; +esac +fi + +# Here we handle the case where we know the os, and the CPU type, but not the +# manufacturer. We pick the logical manufacturer. +vendor=unknown +case $basic_machine in + *-unknown) + case $os in + -riscix*) + vendor=acorn + ;; + -sunos*) + vendor=sun + ;; + -aix*) + vendor=ibm + ;; + -beos*) + vendor=be + ;; + -hpux*) + vendor=hp + ;; + -mpeix*) + vendor=hp + ;; + -hiux*) + vendor=hitachi + ;; + -unos*) + vendor=crds + ;; + -dgux*) + vendor=dg + ;; + -luna*) + vendor=omron + ;; + -genix*) + vendor=ns + ;; + -mvs* | -opened*) + vendor=ibm + ;; + -ptx*) + vendor=sequent + ;; + -vxsim* | -vxworks* | -windiss*) + vendor=wrs + ;; + -aux*) + vendor=apple + ;; + -hms*) + vendor=hitachi + ;; + -mpw* | -macos*) + vendor=apple + ;; + -*mint | -mint[0-9]* | -*MiNT | -MiNT[0-9]*) + vendor=atari + ;; + -vos*) + vendor=stratus + ;; + esac + basic_machine=`echo $basic_machine | sed "s/unknown/$vendor/"` + ;; +esac + +echo $basic_machine$os +exit 0 + +# Local variables: +# eval: (add-hook 'write-file-hooks 'time-stamp) +# time-stamp-start: "timestamp='" +# time-stamp-format: "%:y-%02m-%02d" +# time-stamp-end: "'" +# End: diff --git a/libtecla-1.6.1/configure b/libtecla-1.6.1/configure new file mode 100755 index 0000000..62f49e0 --- /dev/null +++ b/libtecla-1.6.1/configure @@ -0,0 +1,5208 @@ +#! /bin/sh +# Guess values for system-dependent variables and create Makefiles. +# Generated by GNU Autoconf 2.57. +# +# Copyright 1992, 1993, 1994, 1995, 1996, 1998, 1999, 2000, 2001, 2002 +# Free Software Foundation, Inc. +# This configure script is free software; the Free Software Foundation +# gives unlimited permission to copy, distribute and modify it. +## --------------------- ## +## M4sh Initialization. ## +## --------------------- ## + +# Be Bourne compatible +if test -n "${ZSH_VERSION+set}" && (emulate sh) >/dev/null 2>&1; then + emulate sh + NULLCMD=: + # Zsh 3.x and 4.x performs word splitting on ${1+"$@"}, which + # is contrary to our usage. Disable this feature. + alias -g '${1+"$@"}'='"$@"' +elif test -n "${BASH_VERSION+set}" && (set -o posix) >/dev/null 2>&1; then + set -o posix +fi + +# Support unset when possible. +if (FOO=FOO; unset FOO) >/dev/null 2>&1; then + as_unset=unset +else + as_unset=false +fi + + +# Work around bugs in pre-3.0 UWIN ksh. +$as_unset ENV MAIL MAILPATH +PS1='$ ' +PS2='> ' +PS4='+ ' + +# NLS nuisances. +for as_var in \ + LANG LANGUAGE LC_ADDRESS LC_ALL LC_COLLATE LC_CTYPE LC_IDENTIFICATION \ + LC_MEASUREMENT LC_MESSAGES LC_MONETARY LC_NAME LC_NUMERIC LC_PAPER \ + LC_TELEPHONE LC_TIME +do + if (set +x; test -n "`(eval $as_var=C; export $as_var) 2>&1`"); then + eval $as_var=C; export $as_var + else + $as_unset $as_var + fi +done + +# Required to use basename. +if expr a : '\(a\)' >/dev/null 2>&1; then + as_expr=expr +else + as_expr=false +fi + +if (basename /) >/dev/null 2>&1 && test "X`basename / 2>&1`" = "X/"; then + as_basename=basename +else + as_basename=false +fi + + +# Name of the executable. +as_me=`$as_basename "$0" || +$as_expr X/"$0" : '.*/\([^/][^/]*\)/*$' \| \ + X"$0" : 'X\(//\)$' \| \ + X"$0" : 'X\(/\)$' \| \ + . : '\(.\)' 2>/dev/null || +echo X/"$0" | + sed '/^.*\/\([^/][^/]*\)\/*$/{ s//\1/; q; } + /^X\/\(\/\/\)$/{ s//\1/; q; } + /^X\/\(\/\).*/{ s//\1/; q; } + s/.*/./; q'` + + +# PATH needs CR, and LINENO needs CR and PATH. +# Avoid depending upon Character Ranges. +as_cr_letters='abcdefghijklmnopqrstuvwxyz' +as_cr_LETTERS='ABCDEFGHIJKLMNOPQRSTUVWXYZ' +as_cr_Letters=$as_cr_letters$as_cr_LETTERS +as_cr_digits='0123456789' +as_cr_alnum=$as_cr_Letters$as_cr_digits + +# The user is always right. +if test "${PATH_SEPARATOR+set}" != set; then + echo "#! /bin/sh" >conf$$.sh + echo "exit 0" >>conf$$.sh + chmod +x conf$$.sh + if (PATH="/nonexistent;."; conf$$.sh) >/dev/null 2>&1; then + PATH_SEPARATOR=';' + else + PATH_SEPARATOR=: + fi + rm -f conf$$.sh +fi + + + as_lineno_1=$LINENO + as_lineno_2=$LINENO + as_lineno_3=`(expr $as_lineno_1 + 1) 2>/dev/null` + test "x$as_lineno_1" != "x$as_lineno_2" && + test "x$as_lineno_3" = "x$as_lineno_2" || { + # Find who we are. Look in the path if we contain no path at all + # relative or not. + case $0 in + *[\\/]* ) as_myself=$0 ;; + *) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + test -r "$as_dir/$0" && as_myself=$as_dir/$0 && break +done + + ;; + esac + # We did not find ourselves, most probably we were run as `sh COMMAND' + # in which case we are not to be found in the path. + if test "x$as_myself" = x; then + as_myself=$0 + fi + if test ! -f "$as_myself"; then + { echo "$as_me: error: cannot find myself; rerun with an absolute path" >&2 + { (exit 1); exit 1; }; } + fi + case $CONFIG_SHELL in + '') + as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in /bin$PATH_SEPARATOR/usr/bin$PATH_SEPARATOR$PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for as_base in sh bash ksh sh5; do + case $as_dir in + /*) + if ("$as_dir/$as_base" -c ' + as_lineno_1=$LINENO + as_lineno_2=$LINENO + as_lineno_3=`(expr $as_lineno_1 + 1) 2>/dev/null` + test "x$as_lineno_1" != "x$as_lineno_2" && + test "x$as_lineno_3" = "x$as_lineno_2" ') 2>/dev/null; then + $as_unset BASH_ENV || test "${BASH_ENV+set}" != set || { BASH_ENV=; export BASH_ENV; } + $as_unset ENV || test "${ENV+set}" != set || { ENV=; export ENV; } + CONFIG_SHELL=$as_dir/$as_base + export CONFIG_SHELL + exec "$CONFIG_SHELL" "$0" ${1+"$@"} + fi;; + esac + done +done +;; + esac + + # Create $as_me.lineno as a copy of $as_myself, but with $LINENO + # uniformly replaced by the line number. The first 'sed' inserts a + # line-number line before each line; the second 'sed' does the real + # work. The second script uses 'N' to pair each line-number line + # with the numbered line, and appends trailing '-' during + # substitution so that $LINENO is not a special case at line end. + # (Raja R Harinath suggested sed '=', and Paul Eggert wrote the + # second 'sed' script. Blame Lee E. McMahon for sed's syntax. :-) + sed '=' <$as_myself | + sed ' + N + s,$,-, + : loop + s,^\(['$as_cr_digits']*\)\(.*\)[$]LINENO\([^'$as_cr_alnum'_]\),\1\2\1\3, + t loop + s,-$,, + s,^['$as_cr_digits']*\n,, + ' >$as_me.lineno && + chmod +x $as_me.lineno || + { echo "$as_me: error: cannot create $as_me.lineno; rerun with a POSIX shell" >&2 + { (exit 1); exit 1; }; } + + # Don't try to exec as it changes $[0], causing all sort of problems + # (the dirname of $[0] is not the place where we might find the + # original and so on. Autoconf is especially sensible to this). + . ./$as_me.lineno + # Exit status is that of the last command. + exit +} + + +case `echo "testing\c"; echo 1,2,3`,`echo -n testing; echo 1,2,3` in + *c*,-n*) ECHO_N= ECHO_C=' +' ECHO_T=' ' ;; + *c*,* ) ECHO_N=-n ECHO_C= ECHO_T= ;; + *) ECHO_N= ECHO_C='\c' ECHO_T= ;; +esac + +if expr a : '\(a\)' >/dev/null 2>&1; then + as_expr=expr +else + as_expr=false +fi + +rm -f conf$$ conf$$.exe conf$$.file +echo >conf$$.file +if ln -s conf$$.file conf$$ 2>/dev/null; then + # We could just check for DJGPP; but this test a) works b) is more generic + # and c) will remain valid once DJGPP supports symlinks (DJGPP 2.04). + if test -f conf$$.exe; then + # Don't use ln at all; we don't have any links + as_ln_s='cp -p' + else + as_ln_s='ln -s' + fi +elif ln conf$$.file conf$$ 2>/dev/null; then + as_ln_s=ln +else + as_ln_s='cp -p' +fi +rm -f conf$$ conf$$.exe conf$$.file + +if mkdir -p . 2>/dev/null; then + as_mkdir_p=: +else + as_mkdir_p=false +fi + +as_executable_p="test -f" + +# Sed expression to map a string onto a valid CPP name. +as_tr_cpp="sed y%*$as_cr_letters%P$as_cr_LETTERS%;s%[^_$as_cr_alnum]%_%g" + +# Sed expression to map a string onto a valid variable name. +as_tr_sh="sed y%*+%pp%;s%[^_$as_cr_alnum]%_%g" + + +# IFS +# We need space, tab and new line, in precisely that order. +as_nl=' +' +IFS=" $as_nl" + +# CDPATH. +$as_unset CDPATH + + +# Name of the host. +# hostname on some systems (SVR3.2, Linux) returns a bogus exit status, +# so uname gets run too. +ac_hostname=`(hostname || uname -n) 2>/dev/null | sed 1q` + +exec 6>&1 + +# +# Initializations. +# +ac_default_prefix=/usr/local +ac_config_libobj_dir=. +cross_compiling=no +subdirs= +MFLAGS= +MAKEFLAGS= +SHELL=${CONFIG_SHELL-/bin/sh} + +# Maximum number of lines to put in a shell here document. +# This variable seems obsolete. It should probably be removed, and +# only ac_max_sed_lines should be used. +: ${ac_max_here_lines=38} + +# Identity of this package. +PACKAGE_NAME= +PACKAGE_TARNAME= +PACKAGE_VERSION= +PACKAGE_STRING= +PACKAGE_BUGREPORT= + +ac_unique_file="getline.c" +# Factoring default headers for most tests. +ac_includes_default="\ +#include +#if HAVE_SYS_TYPES_H +# include +#endif +#if HAVE_SYS_STAT_H +# include +#endif +#if STDC_HEADERS +# include +# include +#else +# if HAVE_STDLIB_H +# include +# endif +#endif +#if HAVE_STRING_H +# if !STDC_HEADERS && HAVE_MEMORY_H +# include +# endif +# include +#endif +#if HAVE_STRINGS_H +# include +#endif +#if HAVE_INTTYPES_H +# include +#else +# if HAVE_STDINT_H +# include +# endif +#endif +#if HAVE_UNISTD_H +# include +#endif" + +ac_subst_vars='SHELL PATH_SEPARATOR PACKAGE_NAME PACKAGE_TARNAME PACKAGE_VERSION PACKAGE_STRING PACKAGE_BUGREPORT exec_prefix prefix program_transform_name bindir sbindir libexecdir datadir sysconfdir sharedstatedir localstatedir libdir includedir oldincludedir infodir mandir build_alias host_alias target_alias DEFS ECHO_C ECHO_N ECHO_T LIBS MAJOR_VER MINOR_VER MICRO_VER CC CFLAGS LDFLAGS CPPFLAGS ac_ct_CC EXEEXT OBJEXT SET_MAKE LN_S AWK RANLIB ac_ct_RANLIB LD ac_ct_LD build build_cpu build_vendor build_os host host_cpu host_vendor host_os target target_cpu target_vendor target_os CPP EGREP TARGETS SHARED_EXT SHARED_ALT SHARED_CFLAGS LINK_SHARED DEFS_R LIBR_MANDIR LIBR_MANEXT FUNC_MANDIR FUNC_MANEXT PROG_MANDIR PROG_MANEXT MISC_MANDIR MISC_MANEXT FILE_MANDIR FILE_MANEXT TARGET_LIBS MAKE_MAN_PAGES LIBOBJS LTLIBOBJS' +ac_subst_files='' + +# Initialize some variables set by options. +ac_init_help= +ac_init_version=false +# The variables have the same names as the options, with +# dashes changed to underlines. +cache_file=/dev/null +exec_prefix=NONE +no_create= +no_recursion= +prefix=NONE +program_prefix=NONE +program_suffix=NONE +program_transform_name=s,x,x, +silent= +site= +srcdir= +verbose= +x_includes=NONE +x_libraries=NONE + +# Installation directory options. +# These are left unexpanded so users can "make install exec_prefix=/foo" +# and all the variables that are supposed to be based on exec_prefix +# by default will actually change. +# Use braces instead of parens because sh, perl, etc. also accept them. +bindir='${exec_prefix}/bin' +sbindir='${exec_prefix}/sbin' +libexecdir='${exec_prefix}/libexec' +datadir='${prefix}/share' +sysconfdir='${prefix}/etc' +sharedstatedir='${prefix}/com' +localstatedir='${prefix}/var' +libdir='${exec_prefix}/lib' +includedir='${prefix}/include' +oldincludedir='/usr/include' +infodir='${prefix}/info' +mandir='${prefix}/man' + +ac_prev= +for ac_option +do + # If the previous option needs an argument, assign it. + if test -n "$ac_prev"; then + eval "$ac_prev=\$ac_option" + ac_prev= + continue + fi + + ac_optarg=`expr "x$ac_option" : 'x[^=]*=\(.*\)'` + + # Accept the important Cygnus configure options, so we can diagnose typos. + + case $ac_option in + + -bindir | --bindir | --bindi | --bind | --bin | --bi) + ac_prev=bindir ;; + -bindir=* | --bindir=* | --bindi=* | --bind=* | --bin=* | --bi=*) + bindir=$ac_optarg ;; + + -build | --build | --buil | --bui | --bu) + ac_prev=build_alias ;; + -build=* | --build=* | --buil=* | --bui=* | --bu=*) + build_alias=$ac_optarg ;; + + -cache-file | --cache-file | --cache-fil | --cache-fi \ + | --cache-f | --cache- | --cache | --cach | --cac | --ca | --c) + ac_prev=cache_file ;; + -cache-file=* | --cache-file=* | --cache-fil=* | --cache-fi=* \ + | --cache-f=* | --cache-=* | --cache=* | --cach=* | --cac=* | --ca=* | --c=*) + cache_file=$ac_optarg ;; + + --config-cache | -C) + cache_file=config.cache ;; + + -datadir | --datadir | --datadi | --datad | --data | --dat | --da) + ac_prev=datadir ;; + -datadir=* | --datadir=* | --datadi=* | --datad=* | --data=* | --dat=* \ + | --da=*) + datadir=$ac_optarg ;; + + -disable-* | --disable-*) + ac_feature=`expr "x$ac_option" : 'x-*disable-\(.*\)'` + # Reject names that are not valid shell variable names. + expr "x$ac_feature" : ".*[^-_$as_cr_alnum]" >/dev/null && + { echo "$as_me: error: invalid feature name: $ac_feature" >&2 + { (exit 1); exit 1; }; } + ac_feature=`echo $ac_feature | sed 's/-/_/g'` + eval "enable_$ac_feature=no" ;; + + -enable-* | --enable-*) + ac_feature=`expr "x$ac_option" : 'x-*enable-\([^=]*\)'` + # Reject names that are not valid shell variable names. + expr "x$ac_feature" : ".*[^-_$as_cr_alnum]" >/dev/null && + { echo "$as_me: error: invalid feature name: $ac_feature" >&2 + { (exit 1); exit 1; }; } + ac_feature=`echo $ac_feature | sed 's/-/_/g'` + case $ac_option in + *=*) ac_optarg=`echo "$ac_optarg" | sed "s/'/'\\\\\\\\''/g"`;; + *) ac_optarg=yes ;; + esac + eval "enable_$ac_feature='$ac_optarg'" ;; + + -exec-prefix | --exec_prefix | --exec-prefix | --exec-prefi \ + | --exec-pref | --exec-pre | --exec-pr | --exec-p | --exec- \ + | --exec | --exe | --ex) + ac_prev=exec_prefix ;; + -exec-prefix=* | --exec_prefix=* | --exec-prefix=* | --exec-prefi=* \ + | --exec-pref=* | --exec-pre=* | --exec-pr=* | --exec-p=* | --exec-=* \ + | --exec=* | --exe=* | --ex=*) + exec_prefix=$ac_optarg ;; + + -gas | --gas | --ga | --g) + # Obsolete; use --with-gas. + with_gas=yes ;; + + -help | --help | --hel | --he | -h) + ac_init_help=long ;; + -help=r* | --help=r* | --hel=r* | --he=r* | -hr*) + ac_init_help=recursive ;; + -help=s* | --help=s* | --hel=s* | --he=s* | -hs*) + ac_init_help=short ;; + + -host | --host | --hos | --ho) + ac_prev=host_alias ;; + -host=* | --host=* | --hos=* | --ho=*) + host_alias=$ac_optarg ;; + + -includedir | --includedir | --includedi | --included | --include \ + | --includ | --inclu | --incl | --inc) + ac_prev=includedir ;; + -includedir=* | --includedir=* | --includedi=* | --included=* | --include=* \ + | --includ=* | --inclu=* | --incl=* | --inc=*) + includedir=$ac_optarg ;; + + -infodir | --infodir | --infodi | --infod | --info | --inf) + ac_prev=infodir ;; + -infodir=* | --infodir=* | --infodi=* | --infod=* | --info=* | --inf=*) + infodir=$ac_optarg ;; + + -libdir | --libdir | --libdi | --libd) + ac_prev=libdir ;; + -libdir=* | --libdir=* | --libdi=* | --libd=*) + libdir=$ac_optarg ;; + + -libexecdir | --libexecdir | --libexecdi | --libexecd | --libexec \ + | --libexe | --libex | --libe) + ac_prev=libexecdir ;; + -libexecdir=* | --libexecdir=* | --libexecdi=* | --libexecd=* | --libexec=* \ + | --libexe=* | --libex=* | --libe=*) + libexecdir=$ac_optarg ;; + + -localstatedir | --localstatedir | --localstatedi | --localstated \ + | --localstate | --localstat | --localsta | --localst \ + | --locals | --local | --loca | --loc | --lo) + ac_prev=localstatedir ;; + -localstatedir=* | --localstatedir=* | --localstatedi=* | --localstated=* \ + | --localstate=* | --localstat=* | --localsta=* | --localst=* \ + | --locals=* | --local=* | --loca=* | --loc=* | --lo=*) + localstatedir=$ac_optarg ;; + + -mandir | --mandir | --mandi | --mand | --man | --ma | --m) + ac_prev=mandir ;; + -mandir=* | --mandir=* | --mandi=* | --mand=* | --man=* | --ma=* | --m=*) + mandir=$ac_optarg ;; + + -nfp | --nfp | --nf) + # Obsolete; use --without-fp. + with_fp=no ;; + + -no-create | --no-create | --no-creat | --no-crea | --no-cre \ + | --no-cr | --no-c | -n) + no_create=yes ;; + + -no-recursion | --no-recursion | --no-recursio | --no-recursi \ + | --no-recurs | --no-recur | --no-recu | --no-rec | --no-re | --no-r) + no_recursion=yes ;; + + -oldincludedir | --oldincludedir | --oldincludedi | --oldincluded \ + | --oldinclude | --oldinclud | --oldinclu | --oldincl | --oldinc \ + | --oldin | --oldi | --old | --ol | --o) + ac_prev=oldincludedir ;; + -oldincludedir=* | --oldincludedir=* | --oldincludedi=* | --oldincluded=* \ + | --oldinclude=* | --oldinclud=* | --oldinclu=* | --oldincl=* | --oldinc=* \ + | --oldin=* | --oldi=* | --old=* | --ol=* | --o=*) + oldincludedir=$ac_optarg ;; + + -prefix | --prefix | --prefi | --pref | --pre | --pr | --p) + ac_prev=prefix ;; + -prefix=* | --prefix=* | --prefi=* | --pref=* | --pre=* | --pr=* | --p=*) + prefix=$ac_optarg ;; + + -program-prefix | --program-prefix | --program-prefi | --program-pref \ + | --program-pre | --program-pr | --program-p) + ac_prev=program_prefix ;; + -program-prefix=* | --program-prefix=* | --program-prefi=* \ + | --program-pref=* | --program-pre=* | --program-pr=* | --program-p=*) + program_prefix=$ac_optarg ;; + + -program-suffix | --program-suffix | --program-suffi | --program-suff \ + | --program-suf | --program-su | --program-s) + ac_prev=program_suffix ;; + -program-suffix=* | --program-suffix=* | --program-suffi=* \ + | --program-suff=* | --program-suf=* | --program-su=* | --program-s=*) + program_suffix=$ac_optarg ;; + + -program-transform-name | --program-transform-name \ + | --program-transform-nam | --program-transform-na \ + | --program-transform-n | --program-transform- \ + | --program-transform | --program-transfor \ + | --program-transfo | --program-transf \ + | --program-trans | --program-tran \ + | --progr-tra | --program-tr | --program-t) + ac_prev=program_transform_name ;; + -program-transform-name=* | --program-transform-name=* \ + | --program-transform-nam=* | --program-transform-na=* \ + | --program-transform-n=* | --program-transform-=* \ + | --program-transform=* | --program-transfor=* \ + | --program-transfo=* | --program-transf=* \ + | --program-trans=* | --program-tran=* \ + | --progr-tra=* | --program-tr=* | --program-t=*) + program_transform_name=$ac_optarg ;; + + -q | -quiet | --quiet | --quie | --qui | --qu | --q \ + | -silent | --silent | --silen | --sile | --sil) + silent=yes ;; + + -sbindir | --sbindir | --sbindi | --sbind | --sbin | --sbi | --sb) + ac_prev=sbindir ;; + -sbindir=* | --sbindir=* | --sbindi=* | --sbind=* | --sbin=* \ + | --sbi=* | --sb=*) + sbindir=$ac_optarg ;; + + -sharedstatedir | --sharedstatedir | --sharedstatedi \ + | --sharedstated | --sharedstate | --sharedstat | --sharedsta \ + | --sharedst | --shareds | --shared | --share | --shar \ + | --sha | --sh) + ac_prev=sharedstatedir ;; + -sharedstatedir=* | --sharedstatedir=* | --sharedstatedi=* \ + | --sharedstated=* | --sharedstate=* | --sharedstat=* | --sharedsta=* \ + | --sharedst=* | --shareds=* | --shared=* | --share=* | --shar=* \ + | --sha=* | --sh=*) + sharedstatedir=$ac_optarg ;; + + -site | --site | --sit) + ac_prev=site ;; + -site=* | --site=* | --sit=*) + site=$ac_optarg ;; + + -srcdir | --srcdir | --srcdi | --srcd | --src | --sr) + ac_prev=srcdir ;; + -srcdir=* | --srcdir=* | --srcdi=* | --srcd=* | --src=* | --sr=*) + srcdir=$ac_optarg ;; + + -sysconfdir | --sysconfdir | --sysconfdi | --sysconfd | --sysconf \ + | --syscon | --sysco | --sysc | --sys | --sy) + ac_prev=sysconfdir ;; + -sysconfdir=* | --sysconfdir=* | --sysconfdi=* | --sysconfd=* | --sysconf=* \ + | --syscon=* | --sysco=* | --sysc=* | --sys=* | --sy=*) + sysconfdir=$ac_optarg ;; + + -target | --target | --targe | --targ | --tar | --ta | --t) + ac_prev=target_alias ;; + -target=* | --target=* | --targe=* | --targ=* | --tar=* | --ta=* | --t=*) + target_alias=$ac_optarg ;; + + -v | -verbose | --verbose | --verbos | --verbo | --verb) + verbose=yes ;; + + -version | --version | --versio | --versi | --vers | -V) + ac_init_version=: ;; + + -with-* | --with-*) + ac_package=`expr "x$ac_option" : 'x-*with-\([^=]*\)'` + # Reject names that are not valid shell variable names. + expr "x$ac_package" : ".*[^-_$as_cr_alnum]" >/dev/null && + { echo "$as_me: error: invalid package name: $ac_package" >&2 + { (exit 1); exit 1; }; } + ac_package=`echo $ac_package| sed 's/-/_/g'` + case $ac_option in + *=*) ac_optarg=`echo "$ac_optarg" | sed "s/'/'\\\\\\\\''/g"`;; + *) ac_optarg=yes ;; + esac + eval "with_$ac_package='$ac_optarg'" ;; + + -without-* | --without-*) + ac_package=`expr "x$ac_option" : 'x-*without-\(.*\)'` + # Reject names that are not valid shell variable names. + expr "x$ac_package" : ".*[^-_$as_cr_alnum]" >/dev/null && + { echo "$as_me: error: invalid package name: $ac_package" >&2 + { (exit 1); exit 1; }; } + ac_package=`echo $ac_package | sed 's/-/_/g'` + eval "with_$ac_package=no" ;; + + --x) + # Obsolete; use --with-x. + with_x=yes ;; + + -x-includes | --x-includes | --x-include | --x-includ | --x-inclu \ + | --x-incl | --x-inc | --x-in | --x-i) + ac_prev=x_includes ;; + -x-includes=* | --x-includes=* | --x-include=* | --x-includ=* | --x-inclu=* \ + | --x-incl=* | --x-inc=* | --x-in=* | --x-i=*) + x_includes=$ac_optarg ;; + + -x-libraries | --x-libraries | --x-librarie | --x-librari \ + | --x-librar | --x-libra | --x-libr | --x-lib | --x-li | --x-l) + ac_prev=x_libraries ;; + -x-libraries=* | --x-libraries=* | --x-librarie=* | --x-librari=* \ + | --x-librar=* | --x-libra=* | --x-libr=* | --x-lib=* | --x-li=* | --x-l=*) + x_libraries=$ac_optarg ;; + + -*) { echo "$as_me: error: unrecognized option: $ac_option +Try \`$0 --help' for more information." >&2 + { (exit 1); exit 1; }; } + ;; + + *=*) + ac_envvar=`expr "x$ac_option" : 'x\([^=]*\)='` + # Reject names that are not valid shell variable names. + expr "x$ac_envvar" : ".*[^_$as_cr_alnum]" >/dev/null && + { echo "$as_me: error: invalid variable name: $ac_envvar" >&2 + { (exit 1); exit 1; }; } + ac_optarg=`echo "$ac_optarg" | sed "s/'/'\\\\\\\\''/g"` + eval "$ac_envvar='$ac_optarg'" + export $ac_envvar ;; + + *) + # FIXME: should be removed in autoconf 3.0. + echo "$as_me: WARNING: you should use --build, --host, --target" >&2 + expr "x$ac_option" : ".*[^-._$as_cr_alnum]" >/dev/null && + echo "$as_me: WARNING: invalid host type: $ac_option" >&2 + : ${build_alias=$ac_option} ${host_alias=$ac_option} ${target_alias=$ac_option} + ;; + + esac +done + +if test -n "$ac_prev"; then + ac_option=--`echo $ac_prev | sed 's/_/-/g'` + { echo "$as_me: error: missing argument to $ac_option" >&2 + { (exit 1); exit 1; }; } +fi + +# Be sure to have absolute paths. +for ac_var in exec_prefix prefix +do + eval ac_val=$`echo $ac_var` + case $ac_val in + [\\/$]* | ?:[\\/]* | NONE | '' ) ;; + *) { echo "$as_me: error: expected an absolute directory name for --$ac_var: $ac_val" >&2 + { (exit 1); exit 1; }; };; + esac +done + +# Be sure to have absolute paths. +for ac_var in bindir sbindir libexecdir datadir sysconfdir sharedstatedir \ + localstatedir libdir includedir oldincludedir infodir mandir +do + eval ac_val=$`echo $ac_var` + case $ac_val in + [\\/$]* | ?:[\\/]* ) ;; + *) { echo "$as_me: error: expected an absolute directory name for --$ac_var: $ac_val" >&2 + { (exit 1); exit 1; }; };; + esac +done + +# There might be people who depend on the old broken behavior: `$host' +# used to hold the argument of --host etc. +# FIXME: To remove some day. +build=$build_alias +host=$host_alias +target=$target_alias + +# FIXME: To remove some day. +if test "x$host_alias" != x; then + if test "x$build_alias" = x; then + cross_compiling=maybe + echo "$as_me: WARNING: If you wanted to set the --build type, don't use --host. + If a cross compiler is detected then cross compile mode will be used." >&2 + elif test "x$build_alias" != "x$host_alias"; then + cross_compiling=yes + fi +fi + +ac_tool_prefix= +test -n "$host_alias" && ac_tool_prefix=$host_alias- + +test "$silent" = yes && exec 6>/dev/null + + +# Find the source files, if location was not specified. +if test -z "$srcdir"; then + ac_srcdir_defaulted=yes + # Try the directory containing this script, then its parent. + ac_confdir=`(dirname "$0") 2>/dev/null || +$as_expr X"$0" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ + X"$0" : 'X\(//\)[^/]' \| \ + X"$0" : 'X\(//\)$' \| \ + X"$0" : 'X\(/\)' \| \ + . : '\(.\)' 2>/dev/null || +echo X"$0" | + sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ s//\1/; q; } + /^X\(\/\/\)[^/].*/{ s//\1/; q; } + /^X\(\/\/\)$/{ s//\1/; q; } + /^X\(\/\).*/{ s//\1/; q; } + s/.*/./; q'` + srcdir=$ac_confdir + if test ! -r $srcdir/$ac_unique_file; then + srcdir=.. + fi +else + ac_srcdir_defaulted=no +fi +if test ! -r $srcdir/$ac_unique_file; then + if test "$ac_srcdir_defaulted" = yes; then + { echo "$as_me: error: cannot find sources ($ac_unique_file) in $ac_confdir or .." >&2 + { (exit 1); exit 1; }; } + else + { echo "$as_me: error: cannot find sources ($ac_unique_file) in $srcdir" >&2 + { (exit 1); exit 1; }; } + fi +fi +(cd $srcdir && test -r ./$ac_unique_file) 2>/dev/null || + { echo "$as_me: error: sources are in $srcdir, but \`cd $srcdir' does not work" >&2 + { (exit 1); exit 1; }; } +srcdir=`echo "$srcdir" | sed 's%\([^\\/]\)[\\/]*$%\1%'` +ac_env_build_alias_set=${build_alias+set} +ac_env_build_alias_value=$build_alias +ac_cv_env_build_alias_set=${build_alias+set} +ac_cv_env_build_alias_value=$build_alias +ac_env_host_alias_set=${host_alias+set} +ac_env_host_alias_value=$host_alias +ac_cv_env_host_alias_set=${host_alias+set} +ac_cv_env_host_alias_value=$host_alias +ac_env_target_alias_set=${target_alias+set} +ac_env_target_alias_value=$target_alias +ac_cv_env_target_alias_set=${target_alias+set} +ac_cv_env_target_alias_value=$target_alias +ac_env_CC_set=${CC+set} +ac_env_CC_value=$CC +ac_cv_env_CC_set=${CC+set} +ac_cv_env_CC_value=$CC +ac_env_CFLAGS_set=${CFLAGS+set} +ac_env_CFLAGS_value=$CFLAGS +ac_cv_env_CFLAGS_set=${CFLAGS+set} +ac_cv_env_CFLAGS_value=$CFLAGS +ac_env_LDFLAGS_set=${LDFLAGS+set} +ac_env_LDFLAGS_value=$LDFLAGS +ac_cv_env_LDFLAGS_set=${LDFLAGS+set} +ac_cv_env_LDFLAGS_value=$LDFLAGS +ac_env_CPPFLAGS_set=${CPPFLAGS+set} +ac_env_CPPFLAGS_value=$CPPFLAGS +ac_cv_env_CPPFLAGS_set=${CPPFLAGS+set} +ac_cv_env_CPPFLAGS_value=$CPPFLAGS +ac_env_CPP_set=${CPP+set} +ac_env_CPP_value=$CPP +ac_cv_env_CPP_set=${CPP+set} +ac_cv_env_CPP_value=$CPP + +# +# Report the --help message. +# +if test "$ac_init_help" = "long"; then + # Omit some internal or obsolete options to make the list less imposing. + # This message is too long to be a string in the A/UX 3.1 sh. + cat <<_ACEOF +\`configure' configures this package to adapt to many kinds of systems. + +Usage: $0 [OPTION]... [VAR=VALUE]... + +To assign environment variables (e.g., CC, CFLAGS...), specify them as +VAR=VALUE. See below for descriptions of some of the useful variables. + +Defaults for the options are specified in brackets. + +Configuration: + -h, --help display this help and exit + --help=short display options specific to this package + --help=recursive display the short help of all the included packages + -V, --version display version information and exit + -q, --quiet, --silent do not print \`checking...' messages + --cache-file=FILE cache test results in FILE [disabled] + -C, --config-cache alias for \`--cache-file=config.cache' + -n, --no-create do not create output files + --srcdir=DIR find the sources in DIR [configure dir or \`..'] + +_ACEOF + + cat <<_ACEOF +Installation directories: + --prefix=PREFIX install architecture-independent files in PREFIX + [$ac_default_prefix] + --exec-prefix=EPREFIX install architecture-dependent files in EPREFIX + [PREFIX] + +By default, \`make install' will install all the files in +\`$ac_default_prefix/bin', \`$ac_default_prefix/lib' etc. You can specify +an installation prefix other than \`$ac_default_prefix' using \`--prefix', +for instance \`--prefix=\$HOME'. + +For better control, use the options below. + +Fine tuning of the installation directories: + --bindir=DIR user executables [EPREFIX/bin] + --sbindir=DIR system admin executables [EPREFIX/sbin] + --libexecdir=DIR program executables [EPREFIX/libexec] + --datadir=DIR read-only architecture-independent data [PREFIX/share] + --sysconfdir=DIR read-only single-machine data [PREFIX/etc] + --sharedstatedir=DIR modifiable architecture-independent data [PREFIX/com] + --localstatedir=DIR modifiable single-machine data [PREFIX/var] + --libdir=DIR object code libraries [EPREFIX/lib] + --includedir=DIR C header files [PREFIX/include] + --oldincludedir=DIR C header files for non-gcc [/usr/include] + --infodir=DIR info documentation [PREFIX/info] + --mandir=DIR man documentation [PREFIX/man] +_ACEOF + + cat <<\_ACEOF + +System types: + --build=BUILD configure for building on BUILD [guessed] + --host=HOST cross-compile to build programs to run on HOST [BUILD] + --target=TARGET configure for building compilers for TARGET [HOST] +_ACEOF +fi + +if test -n "$ac_init_help"; then + + cat <<\_ACEOF + +Optional Packages: + --with-PACKAGE[=ARG] use PACKAGE [ARG=yes] + --without-PACKAGE do not use PACKAGE (same as --with-PACKAGE=no) + --with-file-actions Should users of gl_get_line() have access to the + filesystem (default=yes) + --with-file-system Does the target have a filesystem (default=yes) + --with-man-pages Are man pages desired (default=yes) + +Some influential environment variables: + CC C compiler command + CFLAGS C compiler flags + LDFLAGS linker flags, e.g. -L if you have libraries in a + nonstandard directory + CPPFLAGS C/C++ preprocessor flags, e.g. -I if you have + headers in a nonstandard directory + CPP C preprocessor + +Use these variables to override the choices made by `configure' or to help +it to find libraries and programs with nonstandard names/locations. + +_ACEOF +fi + +if test "$ac_init_help" = "recursive"; then + # If there are subdirs, report their specific --help. + ac_popdir=`pwd` + for ac_dir in : $ac_subdirs_all; do test "x$ac_dir" = x: && continue + test -d $ac_dir || continue + ac_builddir=. + +if test "$ac_dir" != .; then + ac_dir_suffix=/`echo "$ac_dir" | sed 's,^\.[\\/],,'` + # A "../" for each directory in $ac_dir_suffix. + ac_top_builddir=`echo "$ac_dir_suffix" | sed 's,/[^\\/]*,../,g'` +else + ac_dir_suffix= ac_top_builddir= +fi + +case $srcdir in + .) # No --srcdir option. We are building in place. + ac_srcdir=. + if test -z "$ac_top_builddir"; then + ac_top_srcdir=. + else + ac_top_srcdir=`echo $ac_top_builddir | sed 's,/$,,'` + fi ;; + [\\/]* | ?:[\\/]* ) # Absolute path. + ac_srcdir=$srcdir$ac_dir_suffix; + ac_top_srcdir=$srcdir ;; + *) # Relative path. + ac_srcdir=$ac_top_builddir$srcdir$ac_dir_suffix + ac_top_srcdir=$ac_top_builddir$srcdir ;; +esac +# Don't blindly perform a `cd "$ac_dir"/$ac_foo && pwd` since $ac_foo can be +# absolute. +ac_abs_builddir=`cd "$ac_dir" && cd $ac_builddir && pwd` +ac_abs_top_builddir=`cd "$ac_dir" && cd ${ac_top_builddir}. && pwd` +ac_abs_srcdir=`cd "$ac_dir" && cd $ac_srcdir && pwd` +ac_abs_top_srcdir=`cd "$ac_dir" && cd $ac_top_srcdir && pwd` + + cd $ac_dir + # Check for guested configure; otherwise get Cygnus style configure. + if test -f $ac_srcdir/configure.gnu; then + echo + $SHELL $ac_srcdir/configure.gnu --help=recursive + elif test -f $ac_srcdir/configure; then + echo + $SHELL $ac_srcdir/configure --help=recursive + elif test -f $ac_srcdir/configure.ac || + test -f $ac_srcdir/configure.in; then + echo + $ac_configure --help + else + echo "$as_me: WARNING: no configuration information is in $ac_dir" >&2 + fi + cd $ac_popdir + done +fi + +test -n "$ac_init_help" && exit 0 +if $ac_init_version; then + cat <<\_ACEOF + +Copyright 1992, 1993, 1994, 1995, 1996, 1998, 1999, 2000, 2001, 2002 +Free Software Foundation, Inc. +This configure script is free software; the Free Software Foundation +gives unlimited permission to copy, distribute and modify it. +_ACEOF + exit 0 +fi +exec 5>config.log +cat >&5 <<_ACEOF +This file contains any messages produced by compilers while +running configure, to aid debugging if configure makes a mistake. + +It was created by $as_me, which was +generated by GNU Autoconf 2.57. Invocation command line was + + $ $0 $@ + +_ACEOF +{ +cat <<_ASUNAME +## --------- ## +## Platform. ## +## --------- ## + +hostname = `(hostname || uname -n) 2>/dev/null | sed 1q` +uname -m = `(uname -m) 2>/dev/null || echo unknown` +uname -r = `(uname -r) 2>/dev/null || echo unknown` +uname -s = `(uname -s) 2>/dev/null || echo unknown` +uname -v = `(uname -v) 2>/dev/null || echo unknown` + +/usr/bin/uname -p = `(/usr/bin/uname -p) 2>/dev/null || echo unknown` +/bin/uname -X = `(/bin/uname -X) 2>/dev/null || echo unknown` + +/bin/arch = `(/bin/arch) 2>/dev/null || echo unknown` +/usr/bin/arch -k = `(/usr/bin/arch -k) 2>/dev/null || echo unknown` +/usr/convex/getsysinfo = `(/usr/convex/getsysinfo) 2>/dev/null || echo unknown` +hostinfo = `(hostinfo) 2>/dev/null || echo unknown` +/bin/machine = `(/bin/machine) 2>/dev/null || echo unknown` +/usr/bin/oslevel = `(/usr/bin/oslevel) 2>/dev/null || echo unknown` +/bin/universe = `(/bin/universe) 2>/dev/null || echo unknown` + +_ASUNAME + +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + echo "PATH: $as_dir" +done + +} >&5 + +cat >&5 <<_ACEOF + + +## ----------- ## +## Core tests. ## +## ----------- ## + +_ACEOF + + +# Keep a trace of the command line. +# Strip out --no-create and --no-recursion so they do not pile up. +# Strip out --silent because we don't want to record it for future runs. +# Also quote any args containing shell meta-characters. +# Make two passes to allow for proper duplicate-argument suppression. +ac_configure_args= +ac_configure_args0= +ac_configure_args1= +ac_sep= +ac_must_keep_next=false +for ac_pass in 1 2 +do + for ac_arg + do + case $ac_arg in + -no-create | --no-c* | -n | -no-recursion | --no-r*) continue ;; + -q | -quiet | --quiet | --quie | --qui | --qu | --q \ + | -silent | --silent | --silen | --sile | --sil) + continue ;; + *" "*|*" "*|*[\[\]\~\#\$\^\&\*\(\)\{\}\\\|\;\<\>\?\"\']*) + ac_arg=`echo "$ac_arg" | sed "s/'/'\\\\\\\\''/g"` ;; + esac + case $ac_pass in + 1) ac_configure_args0="$ac_configure_args0 '$ac_arg'" ;; + 2) + ac_configure_args1="$ac_configure_args1 '$ac_arg'" + if test $ac_must_keep_next = true; then + ac_must_keep_next=false # Got value, back to normal. + else + case $ac_arg in + *=* | --config-cache | -C | -disable-* | --disable-* \ + | -enable-* | --enable-* | -gas | --g* | -nfp | --nf* \ + | -q | -quiet | --q* | -silent | --sil* | -v | -verb* \ + | -with-* | --with-* | -without-* | --without-* | --x) + case "$ac_configure_args0 " in + "$ac_configure_args1"*" '$ac_arg' "* ) continue ;; + esac + ;; + -* ) ac_must_keep_next=true ;; + esac + fi + ac_configure_args="$ac_configure_args$ac_sep'$ac_arg'" + # Get rid of the leading space. + ac_sep=" " + ;; + esac + done +done +$as_unset ac_configure_args0 || test "${ac_configure_args0+set}" != set || { ac_configure_args0=; export ac_configure_args0; } +$as_unset ac_configure_args1 || test "${ac_configure_args1+set}" != set || { ac_configure_args1=; export ac_configure_args1; } + +# When interrupted or exit'd, cleanup temporary files, and complete +# config.log. We remove comments because anyway the quotes in there +# would cause problems or look ugly. +# WARNING: Be sure not to use single quotes in there, as some shells, +# such as our DU 5.0 friend, will then `close' the trap. +trap 'exit_status=$? + # Save into config.log some information that might help in debugging. + { + echo + + cat <<\_ASBOX +## ---------------- ## +## Cache variables. ## +## ---------------- ## +_ASBOX + echo + # The following way of writing the cache mishandles newlines in values, +{ + (set) 2>&1 | + case `(ac_space='"'"' '"'"'; set | grep ac_space) 2>&1` in + *ac_space=\ *) + sed -n \ + "s/'"'"'/'"'"'\\\\'"'"''"'"'/g; + s/^\\([_$as_cr_alnum]*_cv_[_$as_cr_alnum]*\\)=\\(.*\\)/\\1='"'"'\\2'"'"'/p" + ;; + *) + sed -n \ + "s/^\\([_$as_cr_alnum]*_cv_[_$as_cr_alnum]*\\)=\\(.*\\)/\\1=\\2/p" + ;; + esac; +} + echo + + cat <<\_ASBOX +## ----------------- ## +## Output variables. ## +## ----------------- ## +_ASBOX + echo + for ac_var in $ac_subst_vars + do + eval ac_val=$`echo $ac_var` + echo "$ac_var='"'"'$ac_val'"'"'" + done | sort + echo + + if test -n "$ac_subst_files"; then + cat <<\_ASBOX +## ------------- ## +## Output files. ## +## ------------- ## +_ASBOX + echo + for ac_var in $ac_subst_files + do + eval ac_val=$`echo $ac_var` + echo "$ac_var='"'"'$ac_val'"'"'" + done | sort + echo + fi + + if test -s confdefs.h; then + cat <<\_ASBOX +## ----------- ## +## confdefs.h. ## +## ----------- ## +_ASBOX + echo + sed "/^$/d" confdefs.h | sort + echo + fi + test "$ac_signal" != 0 && + echo "$as_me: caught signal $ac_signal" + echo "$as_me: exit $exit_status" + } >&5 + rm -f core core.* *.core && + rm -rf conftest* confdefs* conf$$* $ac_clean_files && + exit $exit_status + ' 0 +for ac_signal in 1 2 13 15; do + trap 'ac_signal='$ac_signal'; { (exit 1); exit 1; }' $ac_signal +done +ac_signal=0 + +# confdefs.h avoids OS command line length limits that DEFS can exceed. +rm -rf conftest* confdefs.h +# AIX cpp loses on an empty file, so make sure it contains at least a newline. +echo >confdefs.h + +# Predefined preprocessor variables. + +cat >>confdefs.h <<_ACEOF +#define PACKAGE_NAME "$PACKAGE_NAME" +_ACEOF + + +cat >>confdefs.h <<_ACEOF +#define PACKAGE_TARNAME "$PACKAGE_TARNAME" +_ACEOF + + +cat >>confdefs.h <<_ACEOF +#define PACKAGE_VERSION "$PACKAGE_VERSION" +_ACEOF + + +cat >>confdefs.h <<_ACEOF +#define PACKAGE_STRING "$PACKAGE_STRING" +_ACEOF + + +cat >>confdefs.h <<_ACEOF +#define PACKAGE_BUGREPORT "$PACKAGE_BUGREPORT" +_ACEOF + + +# Let the site file select an alternate cache file if it wants to. +# Prefer explicitly selected file to automatically selected ones. +if test -z "$CONFIG_SITE"; then + if test "x$prefix" != xNONE; then + CONFIG_SITE="$prefix/share/config.site $prefix/etc/config.site" + else + CONFIG_SITE="$ac_default_prefix/share/config.site $ac_default_prefix/etc/config.site" + fi +fi +for ac_site_file in $CONFIG_SITE; do + if test -r "$ac_site_file"; then + { echo "$as_me:$LINENO: loading site script $ac_site_file" >&5 +echo "$as_me: loading site script $ac_site_file" >&6;} + sed 's/^/| /' "$ac_site_file" >&5 + . "$ac_site_file" + fi +done + +if test -r "$cache_file"; then + # Some versions of bash will fail to source /dev/null (special + # files actually), so we avoid doing that. + if test -f "$cache_file"; then + { echo "$as_me:$LINENO: loading cache $cache_file" >&5 +echo "$as_me: loading cache $cache_file" >&6;} + case $cache_file in + [\\/]* | ?:[\\/]* ) . $cache_file;; + *) . ./$cache_file;; + esac + fi +else + { echo "$as_me:$LINENO: creating cache $cache_file" >&5 +echo "$as_me: creating cache $cache_file" >&6;} + >$cache_file +fi + +# Check that the precious variables saved in the cache have kept the same +# value. +ac_cache_corrupted=false +for ac_var in `(set) 2>&1 | + sed -n 's/^ac_env_\([a-zA-Z_0-9]*\)_set=.*/\1/p'`; do + eval ac_old_set=\$ac_cv_env_${ac_var}_set + eval ac_new_set=\$ac_env_${ac_var}_set + eval ac_old_val="\$ac_cv_env_${ac_var}_value" + eval ac_new_val="\$ac_env_${ac_var}_value" + case $ac_old_set,$ac_new_set in + set,) + { echo "$as_me:$LINENO: error: \`$ac_var' was set to \`$ac_old_val' in the previous run" >&5 +echo "$as_me: error: \`$ac_var' was set to \`$ac_old_val' in the previous run" >&2;} + ac_cache_corrupted=: ;; + ,set) + { echo "$as_me:$LINENO: error: \`$ac_var' was not set in the previous run" >&5 +echo "$as_me: error: \`$ac_var' was not set in the previous run" >&2;} + ac_cache_corrupted=: ;; + ,);; + *) + if test "x$ac_old_val" != "x$ac_new_val"; then + { echo "$as_me:$LINENO: error: \`$ac_var' has changed since the previous run:" >&5 +echo "$as_me: error: \`$ac_var' has changed since the previous run:" >&2;} + { echo "$as_me:$LINENO: former value: $ac_old_val" >&5 +echo "$as_me: former value: $ac_old_val" >&2;} + { echo "$as_me:$LINENO: current value: $ac_new_val" >&5 +echo "$as_me: current value: $ac_new_val" >&2;} + ac_cache_corrupted=: + fi;; + esac + # Pass precious variables to config.status. + if test "$ac_new_set" = set; then + case $ac_new_val in + *" "*|*" "*|*[\[\]\~\#\$\^\&\*\(\)\{\}\\\|\;\<\>\?\"\']*) + ac_arg=$ac_var=`echo "$ac_new_val" | sed "s/'/'\\\\\\\\''/g"` ;; + *) ac_arg=$ac_var=$ac_new_val ;; + esac + case " $ac_configure_args " in + *" '$ac_arg' "*) ;; # Avoid dups. Use of quotes ensures accuracy. + *) ac_configure_args="$ac_configure_args '$ac_arg'" ;; + esac + fi +done +if $ac_cache_corrupted; then + { echo "$as_me:$LINENO: error: changes in the environment can compromise the build" >&5 +echo "$as_me: error: changes in the environment can compromise the build" >&2;} + { { echo "$as_me:$LINENO: error: run \`make distclean' and/or \`rm $cache_file' and start over" >&5 +echo "$as_me: error: run \`make distclean' and/or \`rm $cache_file' and start over" >&2;} + { (exit 1); exit 1; }; } +fi + +ac_ext=c +ac_cpp='$CPP $CPPFLAGS' +ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_c_compiler_gnu + + + + + + + + + + + + + + + + + + + + + + +MAJOR_VER="1" + + + +MINOR_VER="6" + + + +MICRO_VER="1" + + +CFLAGS="$CFLAGS" +ac_ext=c +ac_cpp='$CPP $CPPFLAGS' +ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_c_compiler_gnu +if test -n "$ac_tool_prefix"; then + # Extract the first word of "${ac_tool_prefix}gcc", so it can be a program name with args. +set dummy ${ac_tool_prefix}gcc; ac_word=$2 +echo "$as_me:$LINENO: checking for $ac_word" >&5 +echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6 +if test "${ac_cv_prog_CC+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + if test -n "$CC"; then + ac_cv_prog_CC="$CC" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_CC="${ac_tool_prefix}gcc" + echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done +done + +fi +fi +CC=$ac_cv_prog_CC +if test -n "$CC"; then + echo "$as_me:$LINENO: result: $CC" >&5 +echo "${ECHO_T}$CC" >&6 +else + echo "$as_me:$LINENO: result: no" >&5 +echo "${ECHO_T}no" >&6 +fi + +fi +if test -z "$ac_cv_prog_CC"; then + ac_ct_CC=$CC + # Extract the first word of "gcc", so it can be a program name with args. +set dummy gcc; ac_word=$2 +echo "$as_me:$LINENO: checking for $ac_word" >&5 +echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6 +if test "${ac_cv_prog_ac_ct_CC+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + if test -n "$ac_ct_CC"; then + ac_cv_prog_ac_ct_CC="$ac_ct_CC" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_ac_ct_CC="gcc" + echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done +done + +fi +fi +ac_ct_CC=$ac_cv_prog_ac_ct_CC +if test -n "$ac_ct_CC"; then + echo "$as_me:$LINENO: result: $ac_ct_CC" >&5 +echo "${ECHO_T}$ac_ct_CC" >&6 +else + echo "$as_me:$LINENO: result: no" >&5 +echo "${ECHO_T}no" >&6 +fi + + CC=$ac_ct_CC +else + CC="$ac_cv_prog_CC" +fi + +if test -z "$CC"; then + if test -n "$ac_tool_prefix"; then + # Extract the first word of "${ac_tool_prefix}cc", so it can be a program name with args. +set dummy ${ac_tool_prefix}cc; ac_word=$2 +echo "$as_me:$LINENO: checking for $ac_word" >&5 +echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6 +if test "${ac_cv_prog_CC+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + if test -n "$CC"; then + ac_cv_prog_CC="$CC" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_CC="${ac_tool_prefix}cc" + echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done +done + +fi +fi +CC=$ac_cv_prog_CC +if test -n "$CC"; then + echo "$as_me:$LINENO: result: $CC" >&5 +echo "${ECHO_T}$CC" >&6 +else + echo "$as_me:$LINENO: result: no" >&5 +echo "${ECHO_T}no" >&6 +fi + +fi +if test -z "$ac_cv_prog_CC"; then + ac_ct_CC=$CC + # Extract the first word of "cc", so it can be a program name with args. +set dummy cc; ac_word=$2 +echo "$as_me:$LINENO: checking for $ac_word" >&5 +echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6 +if test "${ac_cv_prog_ac_ct_CC+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + if test -n "$ac_ct_CC"; then + ac_cv_prog_ac_ct_CC="$ac_ct_CC" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_ac_ct_CC="cc" + echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done +done + +fi +fi +ac_ct_CC=$ac_cv_prog_ac_ct_CC +if test -n "$ac_ct_CC"; then + echo "$as_me:$LINENO: result: $ac_ct_CC" >&5 +echo "${ECHO_T}$ac_ct_CC" >&6 +else + echo "$as_me:$LINENO: result: no" >&5 +echo "${ECHO_T}no" >&6 +fi + + CC=$ac_ct_CC +else + CC="$ac_cv_prog_CC" +fi + +fi +if test -z "$CC"; then + # Extract the first word of "cc", so it can be a program name with args. +set dummy cc; ac_word=$2 +echo "$as_me:$LINENO: checking for $ac_word" >&5 +echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6 +if test "${ac_cv_prog_CC+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + if test -n "$CC"; then + ac_cv_prog_CC="$CC" # Let the user override the test. +else + ac_prog_rejected=no +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + if test "$as_dir/$ac_word$ac_exec_ext" = "/usr/ucb/cc"; then + ac_prog_rejected=yes + continue + fi + ac_cv_prog_CC="cc" + echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done +done + +if test $ac_prog_rejected = yes; then + # We found a bogon in the path, so make sure we never use it. + set dummy $ac_cv_prog_CC + shift + if test $# != 0; then + # We chose a different compiler from the bogus one. + # However, it has the same basename, so the bogon will be chosen + # first if we set CC to just the basename; use the full file name. + shift + ac_cv_prog_CC="$as_dir/$ac_word${1+' '}$@" + fi +fi +fi +fi +CC=$ac_cv_prog_CC +if test -n "$CC"; then + echo "$as_me:$LINENO: result: $CC" >&5 +echo "${ECHO_T}$CC" >&6 +else + echo "$as_me:$LINENO: result: no" >&5 +echo "${ECHO_T}no" >&6 +fi + +fi +if test -z "$CC"; then + if test -n "$ac_tool_prefix"; then + for ac_prog in cl + do + # Extract the first word of "$ac_tool_prefix$ac_prog", so it can be a program name with args. +set dummy $ac_tool_prefix$ac_prog; ac_word=$2 +echo "$as_me:$LINENO: checking for $ac_word" >&5 +echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6 +if test "${ac_cv_prog_CC+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + if test -n "$CC"; then + ac_cv_prog_CC="$CC" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_CC="$ac_tool_prefix$ac_prog" + echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done +done + +fi +fi +CC=$ac_cv_prog_CC +if test -n "$CC"; then + echo "$as_me:$LINENO: result: $CC" >&5 +echo "${ECHO_T}$CC" >&6 +else + echo "$as_me:$LINENO: result: no" >&5 +echo "${ECHO_T}no" >&6 +fi + + test -n "$CC" && break + done +fi +if test -z "$CC"; then + ac_ct_CC=$CC + for ac_prog in cl +do + # Extract the first word of "$ac_prog", so it can be a program name with args. +set dummy $ac_prog; ac_word=$2 +echo "$as_me:$LINENO: checking for $ac_word" >&5 +echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6 +if test "${ac_cv_prog_ac_ct_CC+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + if test -n "$ac_ct_CC"; then + ac_cv_prog_ac_ct_CC="$ac_ct_CC" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_ac_ct_CC="$ac_prog" + echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done +done + +fi +fi +ac_ct_CC=$ac_cv_prog_ac_ct_CC +if test -n "$ac_ct_CC"; then + echo "$as_me:$LINENO: result: $ac_ct_CC" >&5 +echo "${ECHO_T}$ac_ct_CC" >&6 +else + echo "$as_me:$LINENO: result: no" >&5 +echo "${ECHO_T}no" >&6 +fi + + test -n "$ac_ct_CC" && break +done + + CC=$ac_ct_CC +fi + +fi + + +test -z "$CC" && { { echo "$as_me:$LINENO: error: no acceptable C compiler found in \$PATH +See \`config.log' for more details." >&5 +echo "$as_me: error: no acceptable C compiler found in \$PATH +See \`config.log' for more details." >&2;} + { (exit 1); exit 1; }; } + +# Provide some information about the compiler. +echo "$as_me:$LINENO:" \ + "checking for C compiler version" >&5 +ac_compiler=`set X $ac_compile; echo $2` +{ (eval echo "$as_me:$LINENO: \"$ac_compiler --version &5\"") >&5 + (eval $ac_compiler --version &5) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } +{ (eval echo "$as_me:$LINENO: \"$ac_compiler -v &5\"") >&5 + (eval $ac_compiler -v &5) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } +{ (eval echo "$as_me:$LINENO: \"$ac_compiler -V &5\"") >&5 + (eval $ac_compiler -V &5) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } + +cat >conftest.$ac_ext <<_ACEOF +#line $LINENO "configure" +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ + +int +main () +{ + + ; + return 0; +} +_ACEOF +ac_clean_files_save=$ac_clean_files +ac_clean_files="$ac_clean_files a.out a.exe b.out" +# Try to create an executable without -o first, disregard a.out. +# It will help us diagnose broken compilers, and finding out an intuition +# of exeext. +echo "$as_me:$LINENO: checking for C compiler default output" >&5 +echo $ECHO_N "checking for C compiler default output... $ECHO_C" >&6 +ac_link_default=`echo "$ac_link" | sed 's/ -o *conftest[^ ]*//'` +if { (eval echo "$as_me:$LINENO: \"$ac_link_default\"") >&5 + (eval $ac_link_default) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; then + # Find the output, starting from the most likely. This scheme is +# not robust to junk in `.', hence go to wildcards (a.*) only as a last +# resort. + +# Be careful to initialize this variable, since it used to be cached. +# Otherwise an old cache value of `no' led to `EXEEXT = no' in a Makefile. +ac_cv_exeext= +# b.out is created by i960 compilers. +for ac_file in a_out.exe a.exe conftest.exe a.out conftest a.* conftest.* b.out +do + test -f "$ac_file" || continue + case $ac_file in + *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.o | *.obj ) + ;; + conftest.$ac_ext ) + # This is the source file. + ;; + [ab].out ) + # We found the default executable, but exeext='' is most + # certainly right. + break;; + *.* ) + ac_cv_exeext=`expr "$ac_file" : '[^.]*\(\..*\)'` + # FIXME: I believe we export ac_cv_exeext for Libtool, + # but it would be cool to find out if it's true. Does anybody + # maintain Libtool? --akim. + export ac_cv_exeext + break;; + * ) + break;; + esac +done +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +{ { echo "$as_me:$LINENO: error: C compiler cannot create executables +See \`config.log' for more details." >&5 +echo "$as_me: error: C compiler cannot create executables +See \`config.log' for more details." >&2;} + { (exit 77); exit 77; }; } +fi + +ac_exeext=$ac_cv_exeext +echo "$as_me:$LINENO: result: $ac_file" >&5 +echo "${ECHO_T}$ac_file" >&6 + +# Check the compiler produces executables we can run. If not, either +# the compiler is broken, or we cross compile. +echo "$as_me:$LINENO: checking whether the C compiler works" >&5 +echo $ECHO_N "checking whether the C compiler works... $ECHO_C" >&6 +# FIXME: These cross compiler hacks should be removed for Autoconf 3.0 +# If not cross compiling, check that we can run a simple program. +if test "$cross_compiling" != yes; then + if { ac_try='./$ac_file' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + cross_compiling=no + else + if test "$cross_compiling" = maybe; then + cross_compiling=yes + else + { { echo "$as_me:$LINENO: error: cannot run C compiled programs. +If you meant to cross compile, use \`--host'. +See \`config.log' for more details." >&5 +echo "$as_me: error: cannot run C compiled programs. +If you meant to cross compile, use \`--host'. +See \`config.log' for more details." >&2;} + { (exit 1); exit 1; }; } + fi + fi +fi +echo "$as_me:$LINENO: result: yes" >&5 +echo "${ECHO_T}yes" >&6 + +rm -f a.out a.exe conftest$ac_cv_exeext b.out +ac_clean_files=$ac_clean_files_save +# Check the compiler produces executables we can run. If not, either +# the compiler is broken, or we cross compile. +echo "$as_me:$LINENO: checking whether we are cross compiling" >&5 +echo $ECHO_N "checking whether we are cross compiling... $ECHO_C" >&6 +echo "$as_me:$LINENO: result: $cross_compiling" >&5 +echo "${ECHO_T}$cross_compiling" >&6 + +echo "$as_me:$LINENO: checking for suffix of executables" >&5 +echo $ECHO_N "checking for suffix of executables... $ECHO_C" >&6 +if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 + (eval $ac_link) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; then + # If both `conftest.exe' and `conftest' are `present' (well, observable) +# catch `conftest.exe'. For instance with Cygwin, `ls conftest' will +# work properly (i.e., refer to `conftest.exe'), while it won't with +# `rm'. +for ac_file in conftest.exe conftest conftest.*; do + test -f "$ac_file" || continue + case $ac_file in + *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.o | *.obj ) ;; + *.* ) ac_cv_exeext=`expr "$ac_file" : '[^.]*\(\..*\)'` + export ac_cv_exeext + break;; + * ) break;; + esac +done +else + { { echo "$as_me:$LINENO: error: cannot compute suffix of executables: cannot compile and link +See \`config.log' for more details." >&5 +echo "$as_me: error: cannot compute suffix of executables: cannot compile and link +See \`config.log' for more details." >&2;} + { (exit 1); exit 1; }; } +fi + +rm -f conftest$ac_cv_exeext +echo "$as_me:$LINENO: result: $ac_cv_exeext" >&5 +echo "${ECHO_T}$ac_cv_exeext" >&6 + +rm -f conftest.$ac_ext +EXEEXT=$ac_cv_exeext +ac_exeext=$EXEEXT +echo "$as_me:$LINENO: checking for suffix of object files" >&5 +echo $ECHO_N "checking for suffix of object files... $ECHO_C" >&6 +if test "${ac_cv_objext+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.$ac_ext <<_ACEOF +#line $LINENO "configure" +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ + +int +main () +{ + + ; + return 0; +} +_ACEOF +rm -f conftest.o conftest.obj +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; then + for ac_file in `(ls conftest.o conftest.obj; ls conftest.*) 2>/dev/null`; do + case $ac_file in + *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg ) ;; + *) ac_cv_objext=`expr "$ac_file" : '.*\.\(.*\)'` + break;; + esac +done +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +{ { echo "$as_me:$LINENO: error: cannot compute suffix of object files: cannot compile +See \`config.log' for more details." >&5 +echo "$as_me: error: cannot compute suffix of object files: cannot compile +See \`config.log' for more details." >&2;} + { (exit 1); exit 1; }; } +fi + +rm -f conftest.$ac_cv_objext conftest.$ac_ext +fi +echo "$as_me:$LINENO: result: $ac_cv_objext" >&5 +echo "${ECHO_T}$ac_cv_objext" >&6 +OBJEXT=$ac_cv_objext +ac_objext=$OBJEXT +echo "$as_me:$LINENO: checking whether we are using the GNU C compiler" >&5 +echo $ECHO_N "checking whether we are using the GNU C compiler... $ECHO_C" >&6 +if test "${ac_cv_c_compiler_gnu+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.$ac_ext <<_ACEOF +#line $LINENO "configure" +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ + +int +main () +{ +#ifndef __GNUC__ + choke me +#endif + + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_compiler_gnu=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +ac_compiler_gnu=no +fi +rm -f conftest.$ac_objext conftest.$ac_ext +ac_cv_c_compiler_gnu=$ac_compiler_gnu + +fi +echo "$as_me:$LINENO: result: $ac_cv_c_compiler_gnu" >&5 +echo "${ECHO_T}$ac_cv_c_compiler_gnu" >&6 +GCC=`test $ac_compiler_gnu = yes && echo yes` +ac_test_CFLAGS=${CFLAGS+set} +ac_save_CFLAGS=$CFLAGS +CFLAGS="-g" +echo "$as_me:$LINENO: checking whether $CC accepts -g" >&5 +echo $ECHO_N "checking whether $CC accepts -g... $ECHO_C" >&6 +if test "${ac_cv_prog_cc_g+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.$ac_ext <<_ACEOF +#line $LINENO "configure" +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ + +int +main () +{ + + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_prog_cc_g=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +ac_cv_prog_cc_g=no +fi +rm -f conftest.$ac_objext conftest.$ac_ext +fi +echo "$as_me:$LINENO: result: $ac_cv_prog_cc_g" >&5 +echo "${ECHO_T}$ac_cv_prog_cc_g" >&6 +if test "$ac_test_CFLAGS" = set; then + CFLAGS=$ac_save_CFLAGS +elif test $ac_cv_prog_cc_g = yes; then + if test "$GCC" = yes; then + CFLAGS="-g -O2" + else + CFLAGS="-g" + fi +else + if test "$GCC" = yes; then + CFLAGS="-O2" + else + CFLAGS= + fi +fi +echo "$as_me:$LINENO: checking for $CC option to accept ANSI C" >&5 +echo $ECHO_N "checking for $CC option to accept ANSI C... $ECHO_C" >&6 +if test "${ac_cv_prog_cc_stdc+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + ac_cv_prog_cc_stdc=no +ac_save_CC=$CC +cat >conftest.$ac_ext <<_ACEOF +#line $LINENO "configure" +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include +#include +#include +#include +/* Most of the following tests are stolen from RCS 5.7's src/conf.sh. */ +struct buf { int x; }; +FILE * (*rcsopen) (struct buf *, struct stat *, int); +static char *e (p, i) + char **p; + int i; +{ + return p[i]; +} +static char *f (char * (*g) (char **, int), char **p, ...) +{ + char *s; + va_list v; + va_start (v,p); + s = g (p, va_arg (v,int)); + va_end (v); + return s; +} +int test (int i, double x); +struct s1 {int (*f) (int a);}; +struct s2 {int (*f) (double a);}; +int pairnames (int, char **, FILE *(*)(struct buf *, struct stat *, int), int, int); +int argc; +char **argv; +int +main () +{ +return f (e, argv, 0) != argv[0] || f (e, argv, 1) != argv[1]; + ; + return 0; +} +_ACEOF +# Don't try gcc -ansi; that turns off useful extensions and +# breaks some systems' header files. +# AIX -qlanglvl=ansi +# Ultrix and OSF/1 -std1 +# HP-UX 10.20 and later -Ae +# HP-UX older versions -Aa -D_HPUX_SOURCE +# SVR4 -Xc -D__EXTENSIONS__ +for ac_arg in "" -qlanglvl=ansi -std1 -Ae "-Aa -D_HPUX_SOURCE" "-Xc -D__EXTENSIONS__" +do + CC="$ac_save_CC $ac_arg" + rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_prog_cc_stdc=$ac_arg +break +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +fi +rm -f conftest.$ac_objext +done +rm -f conftest.$ac_ext conftest.$ac_objext +CC=$ac_save_CC + +fi + +case "x$ac_cv_prog_cc_stdc" in + x|xno) + echo "$as_me:$LINENO: result: none needed" >&5 +echo "${ECHO_T}none needed" >&6 ;; + *) + echo "$as_me:$LINENO: result: $ac_cv_prog_cc_stdc" >&5 +echo "${ECHO_T}$ac_cv_prog_cc_stdc" >&6 + CC="$CC $ac_cv_prog_cc_stdc" ;; +esac + +# Some people use a C++ compiler to compile C. Since we use `exit', +# in C++ we need to declare it. In case someone uses the same compiler +# for both compiling C and C++ we need to have the C++ compiler decide +# the declaration of exit, since it's the most demanding environment. +cat >conftest.$ac_ext <<_ACEOF +#ifndef __cplusplus + choke me +#endif +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + for ac_declaration in \ + ''\ + '#include ' \ + 'extern "C" void std::exit (int) throw (); using std::exit;' \ + 'extern "C" void std::exit (int); using std::exit;' \ + 'extern "C" void exit (int) throw ();' \ + 'extern "C" void exit (int);' \ + 'void exit (int);' +do + cat >conftest.$ac_ext <<_ACEOF +#line $LINENO "configure" +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include +$ac_declaration +int +main () +{ +exit (42); + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + : +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +continue +fi +rm -f conftest.$ac_objext conftest.$ac_ext + cat >conftest.$ac_ext <<_ACEOF +#line $LINENO "configure" +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +$ac_declaration +int +main () +{ +exit (42); + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + break +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +fi +rm -f conftest.$ac_objext conftest.$ac_ext +done +rm -f conftest* +if test -n "$ac_declaration"; then + echo '#ifdef __cplusplus' >>confdefs.h + echo $ac_declaration >>confdefs.h + echo '#endif' >>confdefs.h +fi + +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +fi +rm -f conftest.$ac_objext conftest.$ac_ext +ac_ext=c +ac_cpp='$CPP $CPPFLAGS' +ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_c_compiler_gnu + + + +echo "$as_me:$LINENO: checking whether ${MAKE-make} sets \$(MAKE)" >&5 +echo $ECHO_N "checking whether ${MAKE-make} sets \$(MAKE)... $ECHO_C" >&6 +set dummy ${MAKE-make}; ac_make=`echo "$2" | sed 'y,./+-,__p_,'` +if eval "test \"\${ac_cv_prog_make_${ac_make}_set+set}\" = set"; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.make <<\_ACEOF +all: + @echo 'ac_maketemp="$(MAKE)"' +_ACEOF +# GNU make sometimes prints "make[1]: Entering...", which would confuse us. +eval `${MAKE-make} -f conftest.make 2>/dev/null | grep temp=` +if test -n "$ac_maketemp"; then + eval ac_cv_prog_make_${ac_make}_set=yes +else + eval ac_cv_prog_make_${ac_make}_set=no +fi +rm -f conftest.make +fi +if eval "test \"`echo '$ac_cv_prog_make_'${ac_make}_set`\" = yes"; then + echo "$as_me:$LINENO: result: yes" >&5 +echo "${ECHO_T}yes" >&6 + SET_MAKE= +else + echo "$as_me:$LINENO: result: no" >&5 +echo "${ECHO_T}no" >&6 + SET_MAKE="MAKE=${MAKE-make}" +fi + + + +echo "$as_me:$LINENO: checking whether ln -s works" >&5 +echo $ECHO_N "checking whether ln -s works... $ECHO_C" >&6 +LN_S=$as_ln_s +if test "$LN_S" = "ln -s"; then + echo "$as_me:$LINENO: result: yes" >&5 +echo "${ECHO_T}yes" >&6 +else + echo "$as_me:$LINENO: result: no, using $LN_S" >&5 +echo "${ECHO_T}no, using $LN_S" >&6 +fi + + + +for ac_prog in gawk mawk nawk awk +do + # Extract the first word of "$ac_prog", so it can be a program name with args. +set dummy $ac_prog; ac_word=$2 +echo "$as_me:$LINENO: checking for $ac_word" >&5 +echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6 +if test "${ac_cv_prog_AWK+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + if test -n "$AWK"; then + ac_cv_prog_AWK="$AWK" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_AWK="$ac_prog" + echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done +done + +fi +fi +AWK=$ac_cv_prog_AWK +if test -n "$AWK"; then + echo "$as_me:$LINENO: result: $AWK" >&5 +echo "${ECHO_T}$AWK" >&6 +else + echo "$as_me:$LINENO: result: no" >&5 +echo "${ECHO_T}no" >&6 +fi + + test -n "$AWK" && break +done + + + +if test -n "$ac_tool_prefix"; then + # Extract the first word of "${ac_tool_prefix}ranlib", so it can be a program name with args. +set dummy ${ac_tool_prefix}ranlib; ac_word=$2 +echo "$as_me:$LINENO: checking for $ac_word" >&5 +echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6 +if test "${ac_cv_prog_RANLIB+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + if test -n "$RANLIB"; then + ac_cv_prog_RANLIB="$RANLIB" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_RANLIB="${ac_tool_prefix}ranlib" + echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done +done + +fi +fi +RANLIB=$ac_cv_prog_RANLIB +if test -n "$RANLIB"; then + echo "$as_me:$LINENO: result: $RANLIB" >&5 +echo "${ECHO_T}$RANLIB" >&6 +else + echo "$as_me:$LINENO: result: no" >&5 +echo "${ECHO_T}no" >&6 +fi + +fi +if test -z "$ac_cv_prog_RANLIB"; then + ac_ct_RANLIB=$RANLIB + # Extract the first word of "ranlib", so it can be a program name with args. +set dummy ranlib; ac_word=$2 +echo "$as_me:$LINENO: checking for $ac_word" >&5 +echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6 +if test "${ac_cv_prog_ac_ct_RANLIB+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + if test -n "$ac_ct_RANLIB"; then + ac_cv_prog_ac_ct_RANLIB="$ac_ct_RANLIB" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_ac_ct_RANLIB="ranlib" + echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done +done + +fi +fi +ac_ct_RANLIB=$ac_cv_prog_ac_ct_RANLIB +if test -n "$ac_ct_RANLIB"; then + echo "$as_me:$LINENO: result: $ac_ct_RANLIB" >&5 +echo "${ECHO_T}$ac_ct_RANLIB" >&6 +else + echo "$as_me:$LINENO: result: no" >&5 +echo "${ECHO_T}no" >&6 +fi + + RANLIB=$ac_ct_RANLIB +else + RANLIB="$ac_cv_prog_RANLIB" +fi + + + +if test -n "$ac_tool_prefix"; then + # Extract the first word of "${ac_tool_prefix}ld", so it can be a program name with args. +set dummy ${ac_tool_prefix}ld; ac_word=$2 +echo "$as_me:$LINENO: checking for $ac_word" >&5 +echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6 +if test "${ac_cv_prog_LD+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + if test -n "$LD"; then + ac_cv_prog_LD="$LD" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_LD="${ac_tool_prefix}ld" + echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done +done + +fi +fi +LD=$ac_cv_prog_LD +if test -n "$LD"; then + echo "$as_me:$LINENO: result: $LD" >&5 +echo "${ECHO_T}$LD" >&6 +else + echo "$as_me:$LINENO: result: no" >&5 +echo "${ECHO_T}no" >&6 +fi + +fi +if test -z "$ac_cv_prog_LD"; then + ac_ct_LD=$LD + # Extract the first word of "ld", so it can be a program name with args. +set dummy ld; ac_word=$2 +echo "$as_me:$LINENO: checking for $ac_word" >&5 +echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6 +if test "${ac_cv_prog_ac_ct_LD+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + if test -n "$ac_ct_LD"; then + ac_cv_prog_ac_ct_LD="$ac_ct_LD" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_ac_ct_LD="ld" + echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done +done + +fi +fi +ac_ct_LD=$ac_cv_prog_ac_ct_LD +if test -n "$ac_ct_LD"; then + echo "$as_me:$LINENO: result: $ac_ct_LD" >&5 +echo "${ECHO_T}$ac_ct_LD" >&6 +else + echo "$as_me:$LINENO: result: no" >&5 +echo "${ECHO_T}no" >&6 +fi + + LD=$ac_ct_LD +else + LD="$ac_cv_prog_LD" +fi + + + +ac_aux_dir= +for ac_dir in $srcdir $srcdir/.. $srcdir/../..; do + if test -f $ac_dir/install-sh; then + ac_aux_dir=$ac_dir + ac_install_sh="$ac_aux_dir/install-sh -c" + break + elif test -f $ac_dir/install.sh; then + ac_aux_dir=$ac_dir + ac_install_sh="$ac_aux_dir/install.sh -c" + break + elif test -f $ac_dir/shtool; then + ac_aux_dir=$ac_dir + ac_install_sh="$ac_aux_dir/shtool install -c" + break + fi +done +if test -z "$ac_aux_dir"; then + { { echo "$as_me:$LINENO: error: cannot find install-sh or install.sh in $srcdir $srcdir/.. $srcdir/../.." >&5 +echo "$as_me: error: cannot find install-sh or install.sh in $srcdir $srcdir/.. $srcdir/../.." >&2;} + { (exit 1); exit 1; }; } +fi +ac_config_guess="$SHELL $ac_aux_dir/config.guess" +ac_config_sub="$SHELL $ac_aux_dir/config.sub" +ac_configure="$SHELL $ac_aux_dir/configure" # This should be Cygnus configure. + +# Make sure we can run config.sub. +$ac_config_sub sun4 >/dev/null 2>&1 || + { { echo "$as_me:$LINENO: error: cannot run $ac_config_sub" >&5 +echo "$as_me: error: cannot run $ac_config_sub" >&2;} + { (exit 1); exit 1; }; } + +echo "$as_me:$LINENO: checking build system type" >&5 +echo $ECHO_N "checking build system type... $ECHO_C" >&6 +if test "${ac_cv_build+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + ac_cv_build_alias=$build_alias +test -z "$ac_cv_build_alias" && + ac_cv_build_alias=`$ac_config_guess` +test -z "$ac_cv_build_alias" && + { { echo "$as_me:$LINENO: error: cannot guess build type; you must specify one" >&5 +echo "$as_me: error: cannot guess build type; you must specify one" >&2;} + { (exit 1); exit 1; }; } +ac_cv_build=`$ac_config_sub $ac_cv_build_alias` || + { { echo "$as_me:$LINENO: error: $ac_config_sub $ac_cv_build_alias failed" >&5 +echo "$as_me: error: $ac_config_sub $ac_cv_build_alias failed" >&2;} + { (exit 1); exit 1; }; } + +fi +echo "$as_me:$LINENO: result: $ac_cv_build" >&5 +echo "${ECHO_T}$ac_cv_build" >&6 +build=$ac_cv_build +build_cpu=`echo $ac_cv_build | sed 's/^\([^-]*\)-\([^-]*\)-\(.*\)$/\1/'` +build_vendor=`echo $ac_cv_build | sed 's/^\([^-]*\)-\([^-]*\)-\(.*\)$/\2/'` +build_os=`echo $ac_cv_build | sed 's/^\([^-]*\)-\([^-]*\)-\(.*\)$/\3/'` + + +echo "$as_me:$LINENO: checking host system type" >&5 +echo $ECHO_N "checking host system type... $ECHO_C" >&6 +if test "${ac_cv_host+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + ac_cv_host_alias=$host_alias +test -z "$ac_cv_host_alias" && + ac_cv_host_alias=$ac_cv_build_alias +ac_cv_host=`$ac_config_sub $ac_cv_host_alias` || + { { echo "$as_me:$LINENO: error: $ac_config_sub $ac_cv_host_alias failed" >&5 +echo "$as_me: error: $ac_config_sub $ac_cv_host_alias failed" >&2;} + { (exit 1); exit 1; }; } + +fi +echo "$as_me:$LINENO: result: $ac_cv_host" >&5 +echo "${ECHO_T}$ac_cv_host" >&6 +host=$ac_cv_host +host_cpu=`echo $ac_cv_host | sed 's/^\([^-]*\)-\([^-]*\)-\(.*\)$/\1/'` +host_vendor=`echo $ac_cv_host | sed 's/^\([^-]*\)-\([^-]*\)-\(.*\)$/\2/'` +host_os=`echo $ac_cv_host | sed 's/^\([^-]*\)-\([^-]*\)-\(.*\)$/\3/'` + + +echo "$as_me:$LINENO: checking target system type" >&5 +echo $ECHO_N "checking target system type... $ECHO_C" >&6 +if test "${ac_cv_target+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + ac_cv_target_alias=$target_alias +test "x$ac_cv_target_alias" = "x" && + ac_cv_target_alias=$ac_cv_host_alias +ac_cv_target=`$ac_config_sub $ac_cv_target_alias` || + { { echo "$as_me:$LINENO: error: $ac_config_sub $ac_cv_target_alias failed" >&5 +echo "$as_me: error: $ac_config_sub $ac_cv_target_alias failed" >&2;} + { (exit 1); exit 1; }; } + +fi +echo "$as_me:$LINENO: result: $ac_cv_target" >&5 +echo "${ECHO_T}$ac_cv_target" >&6 +target=$ac_cv_target +target_cpu=`echo $ac_cv_target | sed 's/^\([^-]*\)-\([^-]*\)-\(.*\)$/\1/'` +target_vendor=`echo $ac_cv_target | sed 's/^\([^-]*\)-\([^-]*\)-\(.*\)$/\2/'` +target_os=`echo $ac_cv_target | sed 's/^\([^-]*\)-\([^-]*\)-\(.*\)$/\3/'` + + +# The aliases save the names the user supplied, while $host etc. +# will get canonicalized. +test -n "$target_alias" && + test "$program_prefix$program_suffix$program_transform_name" = \ + NONENONEs,x,x, && + program_prefix=${target_alias}- + + +case $target_os in +solaris2.[0-6]|solaris2.[0-6].*) + LIBS="$LIBS -L/usr/ccs/lib" + ;; +esac + + +if test "$GCC"_ = "yes"_; then + touch foo.c + fix=`$CC -E -Wp,-v foo.c 2>&1 | $AWK ' + /^#include <...> search starts here:/ {in_list=1;ndir=0} + / *\// && in_list {path[ndir++] = $1} + /^End of search list/ {in_list=0} + END { + if(path[0] ~ /\/usr\/local\/include/) { + for(dir=1; dir&5 +echo $ECHO_N "checking how to run the C preprocessor... $ECHO_C" >&6 +# On Suns, sometimes $CPP names a directory. +if test -n "$CPP" && test -d "$CPP"; then + CPP= +fi +if test -z "$CPP"; then + if test "${ac_cv_prog_CPP+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + # Double quotes because CPP needs to be expanded + for CPP in "$CC -E" "$CC -E -traditional-cpp" "/lib/cpp" + do + ac_preproc_ok=false +for ac_c_preproc_warn_flag in '' yes +do + # Use a header file that comes with gcc, so configuring glibc + # with a fresh cross-compiler works. + # Prefer to if __STDC__ is defined, since + # exists even on freestanding compilers. + # On the NeXT, cc -E runs the code through the compiler's parser, + # not just through cpp. "Syntax error" is here to catch this case. + cat >conftest.$ac_ext <<_ACEOF +#line $LINENO "configure" +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#ifdef __STDC__ +# include +#else +# include +#endif + Syntax error +_ACEOF +if { (eval echo "$as_me:$LINENO: \"$ac_cpp conftest.$ac_ext\"") >&5 + (eval $ac_cpp conftest.$ac_ext) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } >/dev/null; then + if test -s conftest.err; then + ac_cpp_err=$ac_c_preproc_warn_flag + else + ac_cpp_err= + fi +else + ac_cpp_err=yes +fi +if test -z "$ac_cpp_err"; then + : +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + # Broken: fails on valid input. +continue +fi +rm -f conftest.err conftest.$ac_ext + + # OK, works on sane cases. Now check whether non-existent headers + # can be detected and how. + cat >conftest.$ac_ext <<_ACEOF +#line $LINENO "configure" +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include +_ACEOF +if { (eval echo "$as_me:$LINENO: \"$ac_cpp conftest.$ac_ext\"") >&5 + (eval $ac_cpp conftest.$ac_ext) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } >/dev/null; then + if test -s conftest.err; then + ac_cpp_err=$ac_c_preproc_warn_flag + else + ac_cpp_err= + fi +else + ac_cpp_err=yes +fi +if test -z "$ac_cpp_err"; then + # Broken: success on invalid input. +continue +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + # Passes both tests. +ac_preproc_ok=: +break +fi +rm -f conftest.err conftest.$ac_ext + +done +# Because of `break', _AC_PREPROC_IFELSE's cleaning code was skipped. +rm -f conftest.err conftest.$ac_ext +if $ac_preproc_ok; then + break +fi + + done + ac_cv_prog_CPP=$CPP + +fi + CPP=$ac_cv_prog_CPP +else + ac_cv_prog_CPP=$CPP +fi +echo "$as_me:$LINENO: result: $CPP" >&5 +echo "${ECHO_T}$CPP" >&6 +ac_preproc_ok=false +for ac_c_preproc_warn_flag in '' yes +do + # Use a header file that comes with gcc, so configuring glibc + # with a fresh cross-compiler works. + # Prefer to if __STDC__ is defined, since + # exists even on freestanding compilers. + # On the NeXT, cc -E runs the code through the compiler's parser, + # not just through cpp. "Syntax error" is here to catch this case. + cat >conftest.$ac_ext <<_ACEOF +#line $LINENO "configure" +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#ifdef __STDC__ +# include +#else +# include +#endif + Syntax error +_ACEOF +if { (eval echo "$as_me:$LINENO: \"$ac_cpp conftest.$ac_ext\"") >&5 + (eval $ac_cpp conftest.$ac_ext) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } >/dev/null; then + if test -s conftest.err; then + ac_cpp_err=$ac_c_preproc_warn_flag + else + ac_cpp_err= + fi +else + ac_cpp_err=yes +fi +if test -z "$ac_cpp_err"; then + : +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + # Broken: fails on valid input. +continue +fi +rm -f conftest.err conftest.$ac_ext + + # OK, works on sane cases. Now check whether non-existent headers + # can be detected and how. + cat >conftest.$ac_ext <<_ACEOF +#line $LINENO "configure" +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include +_ACEOF +if { (eval echo "$as_me:$LINENO: \"$ac_cpp conftest.$ac_ext\"") >&5 + (eval $ac_cpp conftest.$ac_ext) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } >/dev/null; then + if test -s conftest.err; then + ac_cpp_err=$ac_c_preproc_warn_flag + else + ac_cpp_err= + fi +else + ac_cpp_err=yes +fi +if test -z "$ac_cpp_err"; then + # Broken: success on invalid input. +continue +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + # Passes both tests. +ac_preproc_ok=: +break +fi +rm -f conftest.err conftest.$ac_ext + +done +# Because of `break', _AC_PREPROC_IFELSE's cleaning code was skipped. +rm -f conftest.err conftest.$ac_ext +if $ac_preproc_ok; then + : +else + { { echo "$as_me:$LINENO: error: C preprocessor \"$CPP\" fails sanity check +See \`config.log' for more details." >&5 +echo "$as_me: error: C preprocessor \"$CPP\" fails sanity check +See \`config.log' for more details." >&2;} + { (exit 1); exit 1; }; } +fi + +ac_ext=c +ac_cpp='$CPP $CPPFLAGS' +ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_c_compiler_gnu + + +echo "$as_me:$LINENO: checking for egrep" >&5 +echo $ECHO_N "checking for egrep... $ECHO_C" >&6 +if test "${ac_cv_prog_egrep+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + if echo a | (grep -E '(a|b)') >/dev/null 2>&1 + then ac_cv_prog_egrep='grep -E' + else ac_cv_prog_egrep='egrep' + fi +fi +echo "$as_me:$LINENO: result: $ac_cv_prog_egrep" >&5 +echo "${ECHO_T}$ac_cv_prog_egrep" >&6 + EGREP=$ac_cv_prog_egrep + + +echo "$as_me:$LINENO: checking for ANSI C header files" >&5 +echo $ECHO_N "checking for ANSI C header files... $ECHO_C" >&6 +if test "${ac_cv_header_stdc+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.$ac_ext <<_ACEOF +#line $LINENO "configure" +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include +#include +#include +#include + +int +main () +{ + + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_header_stdc=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +ac_cv_header_stdc=no +fi +rm -f conftest.$ac_objext conftest.$ac_ext + +if test $ac_cv_header_stdc = yes; then + # SunOS 4.x string.h does not declare mem*, contrary to ANSI. + cat >conftest.$ac_ext <<_ACEOF +#line $LINENO "configure" +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include + +_ACEOF +if (eval "$ac_cpp conftest.$ac_ext") 2>&5 | + $EGREP "memchr" >/dev/null 2>&1; then + : +else + ac_cv_header_stdc=no +fi +rm -f conftest* + +fi + +if test $ac_cv_header_stdc = yes; then + # ISC 2.0.2 stdlib.h does not declare free, contrary to ANSI. + cat >conftest.$ac_ext <<_ACEOF +#line $LINENO "configure" +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include + +_ACEOF +if (eval "$ac_cpp conftest.$ac_ext") 2>&5 | + $EGREP "free" >/dev/null 2>&1; then + : +else + ac_cv_header_stdc=no +fi +rm -f conftest* + +fi + +if test $ac_cv_header_stdc = yes; then + # /bin/cc in Irix-4.0.5 gets non-ANSI ctype macros unless using -ansi. + if test "$cross_compiling" = yes; then + : +else + cat >conftest.$ac_ext <<_ACEOF +#line $LINENO "configure" +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include +#if ((' ' & 0x0FF) == 0x020) +# define ISLOWER(c) ('a' <= (c) && (c) <= 'z') +# define TOUPPER(c) (ISLOWER(c) ? 'A' + ((c) - 'a') : (c)) +#else +# define ISLOWER(c) \ + (('a' <= (c) && (c) <= 'i') \ + || ('j' <= (c) && (c) <= 'r') \ + || ('s' <= (c) && (c) <= 'z')) +# define TOUPPER(c) (ISLOWER(c) ? ((c) | 0x40) : (c)) +#endif + +#define XOR(e, f) (((e) && !(f)) || (!(e) && (f))) +int +main () +{ + int i; + for (i = 0; i < 256; i++) + if (XOR (islower (i), ISLOWER (i)) + || toupper (i) != TOUPPER (i)) + exit(2); + exit (0); +} +_ACEOF +rm -f conftest$ac_exeext +if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 + (eval $ac_link) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && { ac_try='./conftest$ac_exeext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + : +else + echo "$as_me: program exited with status $ac_status" >&5 +echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +( exit $ac_status ) +ac_cv_header_stdc=no +fi +rm -f core core.* *.core gmon.out bb.out conftest$ac_exeext conftest.$ac_objext conftest.$ac_ext +fi +fi +fi +echo "$as_me:$LINENO: result: $ac_cv_header_stdc" >&5 +echo "${ECHO_T}$ac_cv_header_stdc" >&6 +if test $ac_cv_header_stdc = yes; then + +cat >>confdefs.h <<\_ACEOF +#define STDC_HEADERS 1 +_ACEOF + +fi + +# On IRIX 5.3, sys/types and inttypes.h are conflicting. + + + + + + + + + +for ac_header in sys/types.h sys/stat.h stdlib.h string.h memory.h strings.h \ + inttypes.h stdint.h unistd.h +do +as_ac_Header=`echo "ac_cv_header_$ac_header" | $as_tr_sh` +echo "$as_me:$LINENO: checking for $ac_header" >&5 +echo $ECHO_N "checking for $ac_header... $ECHO_C" >&6 +if eval "test \"\${$as_ac_Header+set}\" = set"; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.$ac_ext <<_ACEOF +#line $LINENO "configure" +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +$ac_includes_default + +#include <$ac_header> +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + eval "$as_ac_Header=yes" +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +eval "$as_ac_Header=no" +fi +rm -f conftest.$ac_objext conftest.$ac_ext +fi +echo "$as_me:$LINENO: result: `eval echo '${'$as_ac_Header'}'`" >&5 +echo "${ECHO_T}`eval echo '${'$as_ac_Header'}'`" >&6 +if test `eval echo '${'$as_ac_Header'}'` = yes; then + cat >>confdefs.h <<_ACEOF +#define `echo "HAVE_$ac_header" | $as_tr_cpp` 1 +_ACEOF + +fi + +done + + +echo "$as_me:$LINENO: checking for tigetstr in -lcurses" >&5 +echo $ECHO_N "checking for tigetstr in -lcurses... $ECHO_C" >&6 +if test "${ac_cv_lib_curses_tigetstr+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + ac_check_lib_save_LIBS=$LIBS +LIBS="-lcurses $LIBS" +cat >conftest.$ac_ext <<_ACEOF +#line $LINENO "configure" +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ + +/* Override any gcc2 internal prototype to avoid an error. */ +#ifdef __cplusplus +extern "C" +#endif +/* We use char because int might match the return type of a gcc2 + builtin and then its argument prototype would still apply. */ +char tigetstr (); +int +main () +{ +tigetstr (); + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext conftest$ac_exeext +if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 + (eval $ac_link) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -s conftest$ac_exeext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_lib_curses_tigetstr=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +ac_cv_lib_curses_tigetstr=no +fi +rm -f conftest.$ac_objext conftest$ac_exeext conftest.$ac_ext +LIBS=$ac_check_lib_save_LIBS +fi +echo "$as_me:$LINENO: result: $ac_cv_lib_curses_tigetstr" >&5 +echo "${ECHO_T}$ac_cv_lib_curses_tigetstr" >&6 +if test $ac_cv_lib_curses_tigetstr = yes; then + + cat >>confdefs.h <<\_ACEOF +#define USE_TERMINFO 1 +_ACEOF + + LIBS="$LIBS -lcurses" + +else + echo "$as_me:$LINENO: checking for tigetstr in -lncurses" >&5 +echo $ECHO_N "checking for tigetstr in -lncurses... $ECHO_C" >&6 +if test "${ac_cv_lib_ncurses_tigetstr+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + ac_check_lib_save_LIBS=$LIBS +LIBS="-lncurses $LIBS" +cat >conftest.$ac_ext <<_ACEOF +#line $LINENO "configure" +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ + +/* Override any gcc2 internal prototype to avoid an error. */ +#ifdef __cplusplus +extern "C" +#endif +/* We use char because int might match the return type of a gcc2 + builtin and then its argument prototype would still apply. */ +char tigetstr (); +int +main () +{ +tigetstr (); + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext conftest$ac_exeext +if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 + (eval $ac_link) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -s conftest$ac_exeext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_lib_ncurses_tigetstr=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +ac_cv_lib_ncurses_tigetstr=no +fi +rm -f conftest.$ac_objext conftest$ac_exeext conftest.$ac_ext +LIBS=$ac_check_lib_save_LIBS +fi +echo "$as_me:$LINENO: result: $ac_cv_lib_ncurses_tigetstr" >&5 +echo "${ECHO_T}$ac_cv_lib_ncurses_tigetstr" >&6 +if test $ac_cv_lib_ncurses_tigetstr = yes; then + + cat >>confdefs.h <<\_ACEOF +#define USE_TERMINFO 1 +_ACEOF + + LIBS="$LIBS -lncurses" + +else + echo "$as_me:$LINENO: checking for tgetstr in -lcurses" >&5 +echo $ECHO_N "checking for tgetstr in -lcurses... $ECHO_C" >&6 +if test "${ac_cv_lib_curses_tgetstr+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + ac_check_lib_save_LIBS=$LIBS +LIBS="-lcurses $LIBS" +cat >conftest.$ac_ext <<_ACEOF +#line $LINENO "configure" +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ + +/* Override any gcc2 internal prototype to avoid an error. */ +#ifdef __cplusplus +extern "C" +#endif +/* We use char because int might match the return type of a gcc2 + builtin and then its argument prototype would still apply. */ +char tgetstr (); +int +main () +{ +tgetstr (); + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext conftest$ac_exeext +if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 + (eval $ac_link) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -s conftest$ac_exeext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_lib_curses_tgetstr=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +ac_cv_lib_curses_tgetstr=no +fi +rm -f conftest.$ac_objext conftest$ac_exeext conftest.$ac_ext +LIBS=$ac_check_lib_save_LIBS +fi +echo "$as_me:$LINENO: result: $ac_cv_lib_curses_tgetstr" >&5 +echo "${ECHO_T}$ac_cv_lib_curses_tgetstr" >&6 +if test $ac_cv_lib_curses_tgetstr = yes; then + + cat >>confdefs.h <<\_ACEOF +#define USE_TERMCAP 1 +_ACEOF + + LIBS="$LIBS -lcurses" + if test "${ac_cv_header_termcap_h+set}" = set; then + echo "$as_me:$LINENO: checking for termcap.h" >&5 +echo $ECHO_N "checking for termcap.h... $ECHO_C" >&6 +if test "${ac_cv_header_termcap_h+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +fi +echo "$as_me:$LINENO: result: $ac_cv_header_termcap_h" >&5 +echo "${ECHO_T}$ac_cv_header_termcap_h" >&6 +else + # Is the header compilable? +echo "$as_me:$LINENO: checking termcap.h usability" >&5 +echo $ECHO_N "checking termcap.h usability... $ECHO_C" >&6 +cat >conftest.$ac_ext <<_ACEOF +#line $LINENO "configure" +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +$ac_includes_default +#include +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_header_compiler=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +ac_header_compiler=no +fi +rm -f conftest.$ac_objext conftest.$ac_ext +echo "$as_me:$LINENO: result: $ac_header_compiler" >&5 +echo "${ECHO_T}$ac_header_compiler" >&6 + +# Is the header present? +echo "$as_me:$LINENO: checking termcap.h presence" >&5 +echo $ECHO_N "checking termcap.h presence... $ECHO_C" >&6 +cat >conftest.$ac_ext <<_ACEOF +#line $LINENO "configure" +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include +_ACEOF +if { (eval echo "$as_me:$LINENO: \"$ac_cpp conftest.$ac_ext\"") >&5 + (eval $ac_cpp conftest.$ac_ext) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } >/dev/null; then + if test -s conftest.err; then + ac_cpp_err=$ac_c_preproc_warn_flag + else + ac_cpp_err= + fi +else + ac_cpp_err=yes +fi +if test -z "$ac_cpp_err"; then + ac_header_preproc=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + ac_header_preproc=no +fi +rm -f conftest.err conftest.$ac_ext +echo "$as_me:$LINENO: result: $ac_header_preproc" >&5 +echo "${ECHO_T}$ac_header_preproc" >&6 + +# So? What about this header? +case $ac_header_compiler:$ac_header_preproc in + yes:no ) + { echo "$as_me:$LINENO: WARNING: termcap.h: accepted by the compiler, rejected by the preprocessor!" >&5 +echo "$as_me: WARNING: termcap.h: accepted by the compiler, rejected by the preprocessor!" >&2;} + { echo "$as_me:$LINENO: WARNING: termcap.h: proceeding with the preprocessor's result" >&5 +echo "$as_me: WARNING: termcap.h: proceeding with the preprocessor's result" >&2;} + ( + cat <<\_ASBOX +## ------------------------------------ ## +## Report this to bug-autoconf@gnu.org. ## +## ------------------------------------ ## +_ASBOX + ) | + sed "s/^/$as_me: WARNING: /" >&2 + ;; + no:yes ) + { echo "$as_me:$LINENO: WARNING: termcap.h: present but cannot be compiled" >&5 +echo "$as_me: WARNING: termcap.h: present but cannot be compiled" >&2;} + { echo "$as_me:$LINENO: WARNING: termcap.h: check for missing prerequisite headers?" >&5 +echo "$as_me: WARNING: termcap.h: check for missing prerequisite headers?" >&2;} + { echo "$as_me:$LINENO: WARNING: termcap.h: proceeding with the preprocessor's result" >&5 +echo "$as_me: WARNING: termcap.h: proceeding with the preprocessor's result" >&2;} + ( + cat <<\_ASBOX +## ------------------------------------ ## +## Report this to bug-autoconf@gnu.org. ## +## ------------------------------------ ## +_ASBOX + ) | + sed "s/^/$as_me: WARNING: /" >&2 + ;; +esac +echo "$as_me:$LINENO: checking for termcap.h" >&5 +echo $ECHO_N "checking for termcap.h... $ECHO_C" >&6 +if test "${ac_cv_header_termcap_h+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + ac_cv_header_termcap_h=$ac_header_preproc +fi +echo "$as_me:$LINENO: result: $ac_cv_header_termcap_h" >&5 +echo "${ECHO_T}$ac_cv_header_termcap_h" >&6 + +fi +if test $ac_cv_header_termcap_h = yes; then + cat >>confdefs.h <<\_ACEOF +#define HAVE_TERMCAP_H 1 +_ACEOF + +fi + + + +fi + +fi + +fi + + + + +for ac_header in curses.h +do +as_ac_Header=`echo "ac_cv_header_$ac_header" | $as_tr_sh` +if eval "test \"\${$as_ac_Header+set}\" = set"; then + echo "$as_me:$LINENO: checking for $ac_header" >&5 +echo $ECHO_N "checking for $ac_header... $ECHO_C" >&6 +if eval "test \"\${$as_ac_Header+set}\" = set"; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +fi +echo "$as_me:$LINENO: result: `eval echo '${'$as_ac_Header'}'`" >&5 +echo "${ECHO_T}`eval echo '${'$as_ac_Header'}'`" >&6 +else + # Is the header compilable? +echo "$as_me:$LINENO: checking $ac_header usability" >&5 +echo $ECHO_N "checking $ac_header usability... $ECHO_C" >&6 +cat >conftest.$ac_ext <<_ACEOF +#line $LINENO "configure" +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +$ac_includes_default +#include <$ac_header> +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_header_compiler=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +ac_header_compiler=no +fi +rm -f conftest.$ac_objext conftest.$ac_ext +echo "$as_me:$LINENO: result: $ac_header_compiler" >&5 +echo "${ECHO_T}$ac_header_compiler" >&6 + +# Is the header present? +echo "$as_me:$LINENO: checking $ac_header presence" >&5 +echo $ECHO_N "checking $ac_header presence... $ECHO_C" >&6 +cat >conftest.$ac_ext <<_ACEOF +#line $LINENO "configure" +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include <$ac_header> +_ACEOF +if { (eval echo "$as_me:$LINENO: \"$ac_cpp conftest.$ac_ext\"") >&5 + (eval $ac_cpp conftest.$ac_ext) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } >/dev/null; then + if test -s conftest.err; then + ac_cpp_err=$ac_c_preproc_warn_flag + else + ac_cpp_err= + fi +else + ac_cpp_err=yes +fi +if test -z "$ac_cpp_err"; then + ac_header_preproc=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + ac_header_preproc=no +fi +rm -f conftest.err conftest.$ac_ext +echo "$as_me:$LINENO: result: $ac_header_preproc" >&5 +echo "${ECHO_T}$ac_header_preproc" >&6 + +# So? What about this header? +case $ac_header_compiler:$ac_header_preproc in + yes:no ) + { echo "$as_me:$LINENO: WARNING: $ac_header: accepted by the compiler, rejected by the preprocessor!" >&5 +echo "$as_me: WARNING: $ac_header: accepted by the compiler, rejected by the preprocessor!" >&2;} + { echo "$as_me:$LINENO: WARNING: $ac_header: proceeding with the preprocessor's result" >&5 +echo "$as_me: WARNING: $ac_header: proceeding with the preprocessor's result" >&2;} + ( + cat <<\_ASBOX +## ------------------------------------ ## +## Report this to bug-autoconf@gnu.org. ## +## ------------------------------------ ## +_ASBOX + ) | + sed "s/^/$as_me: WARNING: /" >&2 + ;; + no:yes ) + { echo "$as_me:$LINENO: WARNING: $ac_header: present but cannot be compiled" >&5 +echo "$as_me: WARNING: $ac_header: present but cannot be compiled" >&2;} + { echo "$as_me:$LINENO: WARNING: $ac_header: check for missing prerequisite headers?" >&5 +echo "$as_me: WARNING: $ac_header: check for missing prerequisite headers?" >&2;} + { echo "$as_me:$LINENO: WARNING: $ac_header: proceeding with the preprocessor's result" >&5 +echo "$as_me: WARNING: $ac_header: proceeding with the preprocessor's result" >&2;} + ( + cat <<\_ASBOX +## ------------------------------------ ## +## Report this to bug-autoconf@gnu.org. ## +## ------------------------------------ ## +_ASBOX + ) | + sed "s/^/$as_me: WARNING: /" >&2 + ;; +esac +echo "$as_me:$LINENO: checking for $ac_header" >&5 +echo $ECHO_N "checking for $ac_header... $ECHO_C" >&6 +if eval "test \"\${$as_ac_Header+set}\" = set"; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + eval "$as_ac_Header=$ac_header_preproc" +fi +echo "$as_me:$LINENO: result: `eval echo '${'$as_ac_Header'}'`" >&5 +echo "${ECHO_T}`eval echo '${'$as_ac_Header'}'`" >&6 + +fi +if test `eval echo '${'$as_ac_Header'}'` = yes; then + cat >>confdefs.h <<_ACEOF +#define `echo "HAVE_$ac_header" | $as_tr_cpp` 1 +_ACEOF + +for ac_header in term.h +do +as_ac_Header=`echo "ac_cv_header_$ac_header" | $as_tr_sh` +echo "$as_me:$LINENO: checking for $ac_header" >&5 +echo $ECHO_N "checking for $ac_header... $ECHO_C" >&6 +if eval "test \"\${$as_ac_Header+set}\" = set"; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.$ac_ext <<_ACEOF +#line $LINENO "configure" +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include + +#include <$ac_header> +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + eval "$as_ac_Header=yes" +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +eval "$as_ac_Header=no" +fi +rm -f conftest.$ac_objext conftest.$ac_ext +fi +echo "$as_me:$LINENO: result: `eval echo '${'$as_ac_Header'}'`" >&5 +echo "${ECHO_T}`eval echo '${'$as_ac_Header'}'`" >&6 +if test `eval echo '${'$as_ac_Header'}'` = yes; then + cat >>confdefs.h <<_ACEOF +#define `echo "HAVE_$ac_header" | $as_tr_cpp` 1 +_ACEOF + +fi + +done + +else + +for ac_header in ncurses/curses.h +do +as_ac_Header=`echo "ac_cv_header_$ac_header" | $as_tr_sh` +if eval "test \"\${$as_ac_Header+set}\" = set"; then + echo "$as_me:$LINENO: checking for $ac_header" >&5 +echo $ECHO_N "checking for $ac_header... $ECHO_C" >&6 +if eval "test \"\${$as_ac_Header+set}\" = set"; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +fi +echo "$as_me:$LINENO: result: `eval echo '${'$as_ac_Header'}'`" >&5 +echo "${ECHO_T}`eval echo '${'$as_ac_Header'}'`" >&6 +else + # Is the header compilable? +echo "$as_me:$LINENO: checking $ac_header usability" >&5 +echo $ECHO_N "checking $ac_header usability... $ECHO_C" >&6 +cat >conftest.$ac_ext <<_ACEOF +#line $LINENO "configure" +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +$ac_includes_default +#include <$ac_header> +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_header_compiler=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +ac_header_compiler=no +fi +rm -f conftest.$ac_objext conftest.$ac_ext +echo "$as_me:$LINENO: result: $ac_header_compiler" >&5 +echo "${ECHO_T}$ac_header_compiler" >&6 + +# Is the header present? +echo "$as_me:$LINENO: checking $ac_header presence" >&5 +echo $ECHO_N "checking $ac_header presence... $ECHO_C" >&6 +cat >conftest.$ac_ext <<_ACEOF +#line $LINENO "configure" +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include <$ac_header> +_ACEOF +if { (eval echo "$as_me:$LINENO: \"$ac_cpp conftest.$ac_ext\"") >&5 + (eval $ac_cpp conftest.$ac_ext) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } >/dev/null; then + if test -s conftest.err; then + ac_cpp_err=$ac_c_preproc_warn_flag + else + ac_cpp_err= + fi +else + ac_cpp_err=yes +fi +if test -z "$ac_cpp_err"; then + ac_header_preproc=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + ac_header_preproc=no +fi +rm -f conftest.err conftest.$ac_ext +echo "$as_me:$LINENO: result: $ac_header_preproc" >&5 +echo "${ECHO_T}$ac_header_preproc" >&6 + +# So? What about this header? +case $ac_header_compiler:$ac_header_preproc in + yes:no ) + { echo "$as_me:$LINENO: WARNING: $ac_header: accepted by the compiler, rejected by the preprocessor!" >&5 +echo "$as_me: WARNING: $ac_header: accepted by the compiler, rejected by the preprocessor!" >&2;} + { echo "$as_me:$LINENO: WARNING: $ac_header: proceeding with the preprocessor's result" >&5 +echo "$as_me: WARNING: $ac_header: proceeding with the preprocessor's result" >&2;} + ( + cat <<\_ASBOX +## ------------------------------------ ## +## Report this to bug-autoconf@gnu.org. ## +## ------------------------------------ ## +_ASBOX + ) | + sed "s/^/$as_me: WARNING: /" >&2 + ;; + no:yes ) + { echo "$as_me:$LINENO: WARNING: $ac_header: present but cannot be compiled" >&5 +echo "$as_me: WARNING: $ac_header: present but cannot be compiled" >&2;} + { echo "$as_me:$LINENO: WARNING: $ac_header: check for missing prerequisite headers?" >&5 +echo "$as_me: WARNING: $ac_header: check for missing prerequisite headers?" >&2;} + { echo "$as_me:$LINENO: WARNING: $ac_header: proceeding with the preprocessor's result" >&5 +echo "$as_me: WARNING: $ac_header: proceeding with the preprocessor's result" >&2;} + ( + cat <<\_ASBOX +## ------------------------------------ ## +## Report this to bug-autoconf@gnu.org. ## +## ------------------------------------ ## +_ASBOX + ) | + sed "s/^/$as_me: WARNING: /" >&2 + ;; +esac +echo "$as_me:$LINENO: checking for $ac_header" >&5 +echo $ECHO_N "checking for $ac_header... $ECHO_C" >&6 +if eval "test \"\${$as_ac_Header+set}\" = set"; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + eval "$as_ac_Header=$ac_header_preproc" +fi +echo "$as_me:$LINENO: result: `eval echo '${'$as_ac_Header'}'`" >&5 +echo "${ECHO_T}`eval echo '${'$as_ac_Header'}'`" >&6 + +fi +if test `eval echo '${'$as_ac_Header'}'` = yes; then + cat >>confdefs.h <<_ACEOF +#define `echo "HAVE_$ac_header" | $as_tr_cpp` 1 +_ACEOF + +for ac_header in ncurses/term.h +do +as_ac_Header=`echo "ac_cv_header_$ac_header" | $as_tr_sh` +echo "$as_me:$LINENO: checking for $ac_header" >&5 +echo $ECHO_N "checking for $ac_header... $ECHO_C" >&6 +if eval "test \"\${$as_ac_Header+set}\" = set"; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.$ac_ext <<_ACEOF +#line $LINENO "configure" +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include + +#include <$ac_header> +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + eval "$as_ac_Header=yes" +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +eval "$as_ac_Header=no" +fi +rm -f conftest.$ac_objext conftest.$ac_ext +fi +echo "$as_me:$LINENO: result: `eval echo '${'$as_ac_Header'}'`" >&5 +echo "${ECHO_T}`eval echo '${'$as_ac_Header'}'`" >&6 +if test `eval echo '${'$as_ac_Header'}'` = yes; then + cat >>confdefs.h <<_ACEOF +#define `echo "HAVE_$ac_header" | $as_tr_cpp` 1 +_ACEOF + +fi + +done + +fi + +done + +fi + +done + + + + +TARGETS="normal reentrant" + + +echo "$as_me:$LINENO: checking for reentrant functions" >&5 +echo $ECHO_N "checking for reentrant functions... $ECHO_C" >&6 +if test "${tecla_cv_reentrant+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + + KEPT_CFLAGS="$CFLAGS" + CFLAGS="$CFLAGS -D_POSIX_C_SOURCE=199506L" + cat >conftest.$ac_ext <<_ACEOF +#line $LINENO "configure" +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ + +#include +#include +#include +#include +#include + +int +main () +{ + + (void) readdir_r(NULL, NULL, NULL); + (void) getpwuid_r(geteuid(), NULL, NULL, 0, NULL); + (void) getpwnam_r(NULL, NULL, NULL, 0, NULL); + + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext conftest$ac_exeext +if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 + (eval $ac_link) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -s conftest$ac_exeext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + tecla_cv_reentrant=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +tecla_cv_reentrant=no +fi +rm -f conftest.$ac_objext conftest$ac_exeext conftest.$ac_ext + CFLAGS="$KEPT_CFLAGS" + +fi +echo "$as_me:$LINENO: result: $tecla_cv_reentrant" >&5 +echo "${ECHO_T}$tecla_cv_reentrant" >&6 + + +if test $tecla_cv_reentrant = no; then + TARGETS="normal" +fi + + +if test "${ac_cv_header_sys_select_h+set}" = set; then + echo "$as_me:$LINENO: checking for sys/select.h" >&5 +echo $ECHO_N "checking for sys/select.h... $ECHO_C" >&6 +if test "${ac_cv_header_sys_select_h+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +fi +echo "$as_me:$LINENO: result: $ac_cv_header_sys_select_h" >&5 +echo "${ECHO_T}$ac_cv_header_sys_select_h" >&6 +else + # Is the header compilable? +echo "$as_me:$LINENO: checking sys/select.h usability" >&5 +echo $ECHO_N "checking sys/select.h usability... $ECHO_C" >&6 +cat >conftest.$ac_ext <<_ACEOF +#line $LINENO "configure" +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +$ac_includes_default +#include +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_header_compiler=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +ac_header_compiler=no +fi +rm -f conftest.$ac_objext conftest.$ac_ext +echo "$as_me:$LINENO: result: $ac_header_compiler" >&5 +echo "${ECHO_T}$ac_header_compiler" >&6 + +# Is the header present? +echo "$as_me:$LINENO: checking sys/select.h presence" >&5 +echo $ECHO_N "checking sys/select.h presence... $ECHO_C" >&6 +cat >conftest.$ac_ext <<_ACEOF +#line $LINENO "configure" +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include +_ACEOF +if { (eval echo "$as_me:$LINENO: \"$ac_cpp conftest.$ac_ext\"") >&5 + (eval $ac_cpp conftest.$ac_ext) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } >/dev/null; then + if test -s conftest.err; then + ac_cpp_err=$ac_c_preproc_warn_flag + else + ac_cpp_err= + fi +else + ac_cpp_err=yes +fi +if test -z "$ac_cpp_err"; then + ac_header_preproc=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + ac_header_preproc=no +fi +rm -f conftest.err conftest.$ac_ext +echo "$as_me:$LINENO: result: $ac_header_preproc" >&5 +echo "${ECHO_T}$ac_header_preproc" >&6 + +# So? What about this header? +case $ac_header_compiler:$ac_header_preproc in + yes:no ) + { echo "$as_me:$LINENO: WARNING: sys/select.h: accepted by the compiler, rejected by the preprocessor!" >&5 +echo "$as_me: WARNING: sys/select.h: accepted by the compiler, rejected by the preprocessor!" >&2;} + { echo "$as_me:$LINENO: WARNING: sys/select.h: proceeding with the preprocessor's result" >&5 +echo "$as_me: WARNING: sys/select.h: proceeding with the preprocessor's result" >&2;} + ( + cat <<\_ASBOX +## ------------------------------------ ## +## Report this to bug-autoconf@gnu.org. ## +## ------------------------------------ ## +_ASBOX + ) | + sed "s/^/$as_me: WARNING: /" >&2 + ;; + no:yes ) + { echo "$as_me:$LINENO: WARNING: sys/select.h: present but cannot be compiled" >&5 +echo "$as_me: WARNING: sys/select.h: present but cannot be compiled" >&2;} + { echo "$as_me:$LINENO: WARNING: sys/select.h: check for missing prerequisite headers?" >&5 +echo "$as_me: WARNING: sys/select.h: check for missing prerequisite headers?" >&2;} + { echo "$as_me:$LINENO: WARNING: sys/select.h: proceeding with the preprocessor's result" >&5 +echo "$as_me: WARNING: sys/select.h: proceeding with the preprocessor's result" >&2;} + ( + cat <<\_ASBOX +## ------------------------------------ ## +## Report this to bug-autoconf@gnu.org. ## +## ------------------------------------ ## +_ASBOX + ) | + sed "s/^/$as_me: WARNING: /" >&2 + ;; +esac +echo "$as_me:$LINENO: checking for sys/select.h" >&5 +echo $ECHO_N "checking for sys/select.h... $ECHO_C" >&6 +if test "${ac_cv_header_sys_select_h+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + ac_cv_header_sys_select_h=$ac_header_preproc +fi +echo "$as_me:$LINENO: result: $ac_cv_header_sys_select_h" >&5 +echo "${ECHO_T}$ac_cv_header_sys_select_h" >&6 + +fi +if test $ac_cv_header_sys_select_h = yes; then + cat >>confdefs.h <<\_ACEOF +#define HAVE_SYS_SELECT_H 1 +_ACEOF + +fi + + + + +echo "$as_me:$LINENO: checking for select system call" >&5 +echo $ECHO_N "checking for select system call... $ECHO_C" >&6 +if test "${tecla_cv_select+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + + cat >conftest.$ac_ext <<_ACEOF +#line $LINENO "configure" +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ + +#include +#include +#include +#ifdef HAVE_SYS_SELECT_H +#include +#endif + +int +main () +{ + + fd_set fds; + int nready; + FD_ZERO(&fds); + FD_SET(1, &fds); + nready = select(2, &fds, &fds, &fds, NULL); + + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext conftest$ac_exeext +if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 + (eval $ac_link) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -s conftest$ac_exeext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + tecla_cv_select=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +tecla_cv_select=no +fi +rm -f conftest.$ac_objext conftest$ac_exeext conftest.$ac_ext + +fi +echo "$as_me:$LINENO: result: $tecla_cv_select" >&5 +echo "${ECHO_T}$tecla_cv_select" >&6 + + +if test $tecla_cv_select = yes; then + cat >>confdefs.h <<\_ACEOF +#define HAVE_SELECT 1 +_ACEOF + +fi + + +echo "$as_me:$LINENO: checking for SysV pseudo-terminals" >&5 +echo $ECHO_N "checking for SysV pseudo-terminals... $ECHO_C" >&6 +if test "${tecla_cv_sysv_pty+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + + cat >conftest.$ac_ext <<_ACEOF +#line $LINENO "configure" +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ + +#include +#include +#include + +int +main () +{ + + char *name = ptsname(0); + int i1 = grantpt(0); + int i2 = unlockpt(0); + int i3 = ioctl(0, I_PUSH, "ptem"); + return 0; + + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext conftest$ac_exeext +if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 + (eval $ac_link) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -s conftest$ac_exeext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + tecla_cv_sysv_pty=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +tecla_cv_sysv_pty=no +fi +rm -f conftest.$ac_objext conftest$ac_exeext conftest.$ac_ext + +fi +echo "$as_me:$LINENO: result: $tecla_cv_sysv_pty" >&5 +echo "${ECHO_T}$tecla_cv_sysv_pty" >&6 + + +if test $tecla_cv_sysv_pty = yes; then + cat >>confdefs.h <<\_ACEOF +#define HAVE_SYSV_PTY 1 +_ACEOF + +fi + + + +SHARED_EXT="" + + + +SHARED_ALT="" + + + +SHARED_CFLAGS="" + + + +LINK_SHARED="" + + + +DEFS_R="-D_POSIX_C_SOURCE=199506L -DPREFER_REENTRANT" + + + + +LIBR_MANDIR="man3" +LIBR_MANEXT="3" + + + + +FUNC_MANDIR="man3" +FUNC_MANEXT="3" + + + + +PROG_MANDIR="man1" +PROG_MANEXT="1" + + + + +MISC_MANDIR="man7" +MISC_MANEXT="7" + + + + +FILE_MANDIR="man5" +FILE_MANEXT="5" + + + +# Check whether --with-file-actions or --without-file-actions was given. +if test "${with_file_actions+set}" = set; then + withval="$with_file_actions" + cat >>confdefs.h <<\_ACEOF +#define HIDE_FILE_SYSTEM 1 +_ACEOF + +fi; + + + +# Check whether --with-file-system or --without-file-system was given. +if test "${with_file_system+set}" = set; then + withval="$with_file_system" + cat >>confdefs.h <<\_ACEOF +#define WITHOUT_FILE_SYSTEM 1 +_ACEOF + +fi; + + +case $target in +*solaris*) + cat >>confdefs.h <<\_ACEOF +#define __EXTENSIONS__ 1 +_ACEOF + + SHARED_EXT=".so.${MAJOR_VER}" + SHARED_ALT=".so" + LINK_SHARED="$LD"' -G -M $$(srcdir)/libtecla.map -o $$@ -h $$(@F) -z defs -i $$(LIB_OBJECTS) $$(LIBS) -lc' + SHARED_CFLAGS="-Kpic" + case $CC in + */cc|cc) SHARED_CFLAGS="$SHARED_CFLAGS -xstrconst" ;; + esac + case $target_cpu in + sparc) SHARED_CFLAGS="$SHARED_CFLAGS -xregs=no%appl" + esac + case $target_os in + solaris2.[89]*|solaris2.1[0-9]*) + LIBR_MANEXT=3lib + FUNC_MANEXT=3tecla + LIBR_MANDIR=man$LIBR_MANEXT + FUNC_MANDIR=man$FUNC_MANEXT + esac + MISC_MANDIR="man5" + MISC_MANEXT="5" + FILE_MANDIR="man4" + FILE_MANEXT="4" + ;; +*linux*) + SHARED_EXT=".so.${MAJOR_VER}.${MINOR_VER}.${MICRO_VER}" + SHARED_ALT=".so .so.${MAJOR_VER}" + + + echo "$as_me:$LINENO: checking for --version-script in GNU ld" >&5 +echo $ECHO_N "checking for --version-script in GNU ld... $ECHO_C" >&6 +if test "${tecla_cv_gnu_ld_script+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + + if (echo 'void dummy(void) {return;}' > dummy.c; $CC -c -fpic dummy.c; \ + $LD -o dummy.so dummy.o -shared --version-script=$srcdir/libtecla.map) 1>&2 2>/dev/null; then + tecla_cv_gnu_ld_script=yes + else + tecla_cv_gnu_ld_script=no + fi + rm -f dummy.c dummy.o dummy.so + +fi +echo "$as_me:$LINENO: result: $tecla_cv_gnu_ld_script" >&5 +echo "${ECHO_T}$tecla_cv_gnu_ld_script" >&6 + if test $tecla_cv_gnu_ld_script = yes; then + VERSION_OPT='--version-script=$$(srcdir)/libtecla.map' + else + VERSION_OPT='' + fi + + LINK_SHARED="$LD"' -o $$@ -soname libtecla$$(SUFFIX).so.'${MAJOR_VER}' -shared '$VERSION_OPT' $$(LIB_OBJECTS) $$(LIBS) -lc' + SHARED_CFLAGS="-fpic" + + + CFLAGS="-D_SVID_SOURCE -D_BSD_SOURCE $CFLAGS" + ;; +*hpux*) + SHARED_EXT=".${MAJOR_VER}" + SHARED_ALT=".sl" + LINK_SHARED="$LD"' -b +h $$(@F) +k +vshlibunsats -o $$@ -c libtecla.map.opt $$(LIB_OBJECTS) $$(LIBS) -lc' + SHARED_CFLAGS="+z" + MISC_MANEXT=5 + FILE_MANEXT=4 + MISC_MANDIR=man$MISC_MANEXT + FILE_MANDIR=man$FILE_MANEXT + ;; +*darwin*) + SHARED_EXT=".${MAJOR_VER}.${MINOR_VER}.${MICRO_VER}.dylib" + SHARED_ALT=".dylib .${MAJOR_VER}.dylib" + LINK_SHARED='$(CC) -o $$@ -dynamiclib -flat_namespace -undefined suppress -compatibility_version '${MAJOR_VER}.${MINOR_VER}' -current_version '${MAJOR_VER}.${MINOR_VER}.${MICRO_VER}' -install_name '${libdir}'/$$@ $$(LIB_OBJECTS)' + SHARED_CFLAGS="" + ;; +*dec-osf*) + cat >>confdefs.h <<\_ACEOF +#define _OSF_SOURCE 1 +_ACEOF + + ;; +*freebsd*) + SHARED_EXT=".so.${MAJOR_VER}" + SHARED_ALT=".so" + VERSION_OPT='--version-script=$$(srcdir)/libtecla.map' + LINK_SHARED='ld -o $$@ -soname libtecla$$(SUFFIX).so.'${MAJOR_VER}' -shared '$VERSION_OPT' $$(LIB_OBJECTS) $$(LIBS) -lc' + SHARED_CFLAGS="-fpic" + ;; +mips-sgi-irix*) + DEFS_R="$DEFS_R -D_XOPEN_SOURCE=500" + if test "$RANLIB"_ = "_"; then + RANLIB=":" + fi + ;; +esac + + +if test "$GCC"_ = "yes"_ && test "$LINK_SHARED"_ != "_" ; then + SHARED_CFLAGS="-fpic" + case $target in + sparc-*-solaris*) + SHARED_CFLAGS="$SHARED_CFLAGS -mno-app-regs" + ;; + *darwin*) + SHARED_CFLAGS="" + ;; + esac + LINK_SHARED="$LINK_SHARED `gcc -print-libgcc-file-name`" +fi + + + + + +if test "$LINK_SHARED"_ != "_"; then + TARGET_LIBS="static shared" +else + TARGET_LIBS="static" + LINK_SHARED="@:" +fi + + + + +# Check whether --with-man-pages or --without-man-pages was given. +if test "${with_man_pages+set}" = set; then + withval="$with_man_pages" + MAKE_MAN_PAGES="$withval" +else + MAKE_MAN_PAGES="yes" +fi; + + +OUTPUT_FILES="Makefile" +rm -rf man/man* +if test "$MAKE_MAN_PAGES"_ = "yes"_; then + for area in libr func misc prog file; do + for page in man/$area/*.in; do + OUTPUT_FILES="$OUTPUT_FILES `echo $page | sed 's/\.in$//'`" + done + done +fi + + + ac_config_files="$ac_config_files $OUTPUT_FILES" +cat >confcache <<\_ACEOF +# This file is a shell script that caches the results of configure +# tests run on this system so they can be shared between configure +# scripts and configure runs, see configure's option --config-cache. +# It is not useful on other systems. If it contains results you don't +# want to keep, you may remove or edit it. +# +# config.status only pays attention to the cache file if you give it +# the --recheck option to rerun configure. +# +# `ac_cv_env_foo' variables (set or unset) will be overridden when +# loading this file, other *unset* `ac_cv_foo' will be assigned the +# following values. + +_ACEOF + +# The following way of writing the cache mishandles newlines in values, +# but we know of no workaround that is simple, portable, and efficient. +# So, don't put newlines in cache variables' values. +# Ultrix sh set writes to stderr and can't be redirected directly, +# and sets the high bit in the cache file unless we assign to the vars. +{ + (set) 2>&1 | + case `(ac_space=' '; set | grep ac_space) 2>&1` in + *ac_space=\ *) + # `set' does not quote correctly, so add quotes (double-quote + # substitution turns \\\\ into \\, and sed turns \\ into \). + sed -n \ + "s/'/'\\\\''/g; + s/^\\([_$as_cr_alnum]*_cv_[_$as_cr_alnum]*\\)=\\(.*\\)/\\1='\\2'/p" + ;; + *) + # `set' quotes correctly as required by POSIX, so do not add quotes. + sed -n \ + "s/^\\([_$as_cr_alnum]*_cv_[_$as_cr_alnum]*\\)=\\(.*\\)/\\1=\\2/p" + ;; + esac; +} | + sed ' + t clear + : clear + s/^\([^=]*\)=\(.*[{}].*\)$/test "${\1+set}" = set || &/ + t end + /^ac_cv_env/!s/^\([^=]*\)=\(.*\)$/\1=${\1=\2}/ + : end' >>confcache +if diff $cache_file confcache >/dev/null 2>&1; then :; else + if test -w $cache_file; then + test "x$cache_file" != "x/dev/null" && echo "updating cache $cache_file" + cat confcache >$cache_file + else + echo "not updating unwritable cache $cache_file" + fi +fi +rm -f confcache + +test "x$prefix" = xNONE && prefix=$ac_default_prefix +# Let make expand exec_prefix. +test "x$exec_prefix" = xNONE && exec_prefix='${prefix}' + +# VPATH may cause trouble with some makes, so we remove $(srcdir), +# ${srcdir} and @srcdir@ from VPATH if srcdir is ".", strip leading and +# trailing colons and then remove the whole line if VPATH becomes empty +# (actually we leave an empty line to preserve line numbers). +if test "x$srcdir" = x.; then + ac_vpsub='/^[ ]*VPATH[ ]*=/{ +s/:*\$(srcdir):*/:/; +s/:*\${srcdir}:*/:/; +s/:*@srcdir@:*/:/; +s/^\([^=]*=[ ]*\):*/\1/; +s/:*$//; +s/^[^=]*=[ ]*$//; +}' +fi + +# Transform confdefs.h into DEFS. +# Protect against shell expansion while executing Makefile rules. +# Protect against Makefile macro expansion. +# +# If the first sed substitution is executed (which looks for macros that +# take arguments), then we branch to the quote section. Otherwise, +# look for a macro that doesn't take arguments. +cat >confdef2opt.sed <<\_ACEOF +t clear +: clear +s,^[ ]*#[ ]*define[ ][ ]*\([^ (][^ (]*([^)]*)\)[ ]*\(.*\),-D\1=\2,g +t quote +s,^[ ]*#[ ]*define[ ][ ]*\([^ ][^ ]*\)[ ]*\(.*\),-D\1=\2,g +t quote +d +: quote +s,[ `~#$^&*(){}\\|;'"<>?],\\&,g +s,\[,\\&,g +s,\],\\&,g +s,\$,$$,g +p +_ACEOF +# We use echo to avoid assuming a particular line-breaking character. +# The extra dot is to prevent the shell from consuming trailing +# line-breaks from the sub-command output. A line-break within +# single-quotes doesn't work because, if this script is created in a +# platform that uses two characters for line-breaks (e.g., DOS), tr +# would break. +ac_LF_and_DOT=`echo; echo .` +DEFS=`sed -n -f confdef2opt.sed confdefs.h | tr "$ac_LF_and_DOT" ' .'` +rm -f confdef2opt.sed + + +ac_libobjs= +ac_ltlibobjs= +for ac_i in : $LIBOBJS; do test "x$ac_i" = x: && continue + # 1. Remove the extension, and $U if already installed. + ac_i=`echo "$ac_i" | + sed 's/\$U\././;s/\.o$//;s/\.obj$//'` + # 2. Add them. + ac_libobjs="$ac_libobjs $ac_i\$U.$ac_objext" + ac_ltlibobjs="$ac_ltlibobjs $ac_i"'$U.lo' +done +LIBOBJS=$ac_libobjs + +LTLIBOBJS=$ac_ltlibobjs + + + +: ${CONFIG_STATUS=./config.status} +ac_clean_files_save=$ac_clean_files +ac_clean_files="$ac_clean_files $CONFIG_STATUS" +{ echo "$as_me:$LINENO: creating $CONFIG_STATUS" >&5 +echo "$as_me: creating $CONFIG_STATUS" >&6;} +cat >$CONFIG_STATUS <<_ACEOF +#! $SHELL +# Generated by $as_me. +# Run this file to recreate the current configuration. +# Compiler output produced by configure, useful for debugging +# configure, is in config.log if it exists. + +debug=false +ac_cs_recheck=false +ac_cs_silent=false +SHELL=\${CONFIG_SHELL-$SHELL} +_ACEOF + +cat >>$CONFIG_STATUS <<\_ACEOF +## --------------------- ## +## M4sh Initialization. ## +## --------------------- ## + +# Be Bourne compatible +if test -n "${ZSH_VERSION+set}" && (emulate sh) >/dev/null 2>&1; then + emulate sh + NULLCMD=: + # Zsh 3.x and 4.x performs word splitting on ${1+"$@"}, which + # is contrary to our usage. Disable this feature. + alias -g '${1+"$@"}'='"$@"' +elif test -n "${BASH_VERSION+set}" && (set -o posix) >/dev/null 2>&1; then + set -o posix +fi + +# Support unset when possible. +if (FOO=FOO; unset FOO) >/dev/null 2>&1; then + as_unset=unset +else + as_unset=false +fi + + +# Work around bugs in pre-3.0 UWIN ksh. +$as_unset ENV MAIL MAILPATH +PS1='$ ' +PS2='> ' +PS4='+ ' + +# NLS nuisances. +for as_var in \ + LANG LANGUAGE LC_ADDRESS LC_ALL LC_COLLATE LC_CTYPE LC_IDENTIFICATION \ + LC_MEASUREMENT LC_MESSAGES LC_MONETARY LC_NAME LC_NUMERIC LC_PAPER \ + LC_TELEPHONE LC_TIME +do + if (set +x; test -n "`(eval $as_var=C; export $as_var) 2>&1`"); then + eval $as_var=C; export $as_var + else + $as_unset $as_var + fi +done + +# Required to use basename. +if expr a : '\(a\)' >/dev/null 2>&1; then + as_expr=expr +else + as_expr=false +fi + +if (basename /) >/dev/null 2>&1 && test "X`basename / 2>&1`" = "X/"; then + as_basename=basename +else + as_basename=false +fi + + +# Name of the executable. +as_me=`$as_basename "$0" || +$as_expr X/"$0" : '.*/\([^/][^/]*\)/*$' \| \ + X"$0" : 'X\(//\)$' \| \ + X"$0" : 'X\(/\)$' \| \ + . : '\(.\)' 2>/dev/null || +echo X/"$0" | + sed '/^.*\/\([^/][^/]*\)\/*$/{ s//\1/; q; } + /^X\/\(\/\/\)$/{ s//\1/; q; } + /^X\/\(\/\).*/{ s//\1/; q; } + s/.*/./; q'` + + +# PATH needs CR, and LINENO needs CR and PATH. +# Avoid depending upon Character Ranges. +as_cr_letters='abcdefghijklmnopqrstuvwxyz' +as_cr_LETTERS='ABCDEFGHIJKLMNOPQRSTUVWXYZ' +as_cr_Letters=$as_cr_letters$as_cr_LETTERS +as_cr_digits='0123456789' +as_cr_alnum=$as_cr_Letters$as_cr_digits + +# The user is always right. +if test "${PATH_SEPARATOR+set}" != set; then + echo "#! /bin/sh" >conf$$.sh + echo "exit 0" >>conf$$.sh + chmod +x conf$$.sh + if (PATH="/nonexistent;."; conf$$.sh) >/dev/null 2>&1; then + PATH_SEPARATOR=';' + else + PATH_SEPARATOR=: + fi + rm -f conf$$.sh +fi + + + as_lineno_1=$LINENO + as_lineno_2=$LINENO + as_lineno_3=`(expr $as_lineno_1 + 1) 2>/dev/null` + test "x$as_lineno_1" != "x$as_lineno_2" && + test "x$as_lineno_3" = "x$as_lineno_2" || { + # Find who we are. Look in the path if we contain no path at all + # relative or not. + case $0 in + *[\\/]* ) as_myself=$0 ;; + *) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + test -r "$as_dir/$0" && as_myself=$as_dir/$0 && break +done + + ;; + esac + # We did not find ourselves, most probably we were run as `sh COMMAND' + # in which case we are not to be found in the path. + if test "x$as_myself" = x; then + as_myself=$0 + fi + if test ! -f "$as_myself"; then + { { echo "$as_me:$LINENO: error: cannot find myself; rerun with an absolute path" >&5 +echo "$as_me: error: cannot find myself; rerun with an absolute path" >&2;} + { (exit 1); exit 1; }; } + fi + case $CONFIG_SHELL in + '') + as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in /bin$PATH_SEPARATOR/usr/bin$PATH_SEPARATOR$PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for as_base in sh bash ksh sh5; do + case $as_dir in + /*) + if ("$as_dir/$as_base" -c ' + as_lineno_1=$LINENO + as_lineno_2=$LINENO + as_lineno_3=`(expr $as_lineno_1 + 1) 2>/dev/null` + test "x$as_lineno_1" != "x$as_lineno_2" && + test "x$as_lineno_3" = "x$as_lineno_2" ') 2>/dev/null; then + $as_unset BASH_ENV || test "${BASH_ENV+set}" != set || { BASH_ENV=; export BASH_ENV; } + $as_unset ENV || test "${ENV+set}" != set || { ENV=; export ENV; } + CONFIG_SHELL=$as_dir/$as_base + export CONFIG_SHELL + exec "$CONFIG_SHELL" "$0" ${1+"$@"} + fi;; + esac + done +done +;; + esac + + # Create $as_me.lineno as a copy of $as_myself, but with $LINENO + # uniformly replaced by the line number. The first 'sed' inserts a + # line-number line before each line; the second 'sed' does the real + # work. The second script uses 'N' to pair each line-number line + # with the numbered line, and appends trailing '-' during + # substitution so that $LINENO is not a special case at line end. + # (Raja R Harinath suggested sed '=', and Paul Eggert wrote the + # second 'sed' script. Blame Lee E. McMahon for sed's syntax. :-) + sed '=' <$as_myself | + sed ' + N + s,$,-, + : loop + s,^\(['$as_cr_digits']*\)\(.*\)[$]LINENO\([^'$as_cr_alnum'_]\),\1\2\1\3, + t loop + s,-$,, + s,^['$as_cr_digits']*\n,, + ' >$as_me.lineno && + chmod +x $as_me.lineno || + { { echo "$as_me:$LINENO: error: cannot create $as_me.lineno; rerun with a POSIX shell" >&5 +echo "$as_me: error: cannot create $as_me.lineno; rerun with a POSIX shell" >&2;} + { (exit 1); exit 1; }; } + + # Don't try to exec as it changes $[0], causing all sort of problems + # (the dirname of $[0] is not the place where we might find the + # original and so on. Autoconf is especially sensible to this). + . ./$as_me.lineno + # Exit status is that of the last command. + exit +} + + +case `echo "testing\c"; echo 1,2,3`,`echo -n testing; echo 1,2,3` in + *c*,-n*) ECHO_N= ECHO_C=' +' ECHO_T=' ' ;; + *c*,* ) ECHO_N=-n ECHO_C= ECHO_T= ;; + *) ECHO_N= ECHO_C='\c' ECHO_T= ;; +esac + +if expr a : '\(a\)' >/dev/null 2>&1; then + as_expr=expr +else + as_expr=false +fi + +rm -f conf$$ conf$$.exe conf$$.file +echo >conf$$.file +if ln -s conf$$.file conf$$ 2>/dev/null; then + # We could just check for DJGPP; but this test a) works b) is more generic + # and c) will remain valid once DJGPP supports symlinks (DJGPP 2.04). + if test -f conf$$.exe; then + # Don't use ln at all; we don't have any links + as_ln_s='cp -p' + else + as_ln_s='ln -s' + fi +elif ln conf$$.file conf$$ 2>/dev/null; then + as_ln_s=ln +else + as_ln_s='cp -p' +fi +rm -f conf$$ conf$$.exe conf$$.file + +if mkdir -p . 2>/dev/null; then + as_mkdir_p=: +else + as_mkdir_p=false +fi + +as_executable_p="test -f" + +# Sed expression to map a string onto a valid CPP name. +as_tr_cpp="sed y%*$as_cr_letters%P$as_cr_LETTERS%;s%[^_$as_cr_alnum]%_%g" + +# Sed expression to map a string onto a valid variable name. +as_tr_sh="sed y%*+%pp%;s%[^_$as_cr_alnum]%_%g" + + +# IFS +# We need space, tab and new line, in precisely that order. +as_nl=' +' +IFS=" $as_nl" + +# CDPATH. +$as_unset CDPATH + +exec 6>&1 + +# Open the log real soon, to keep \$[0] and so on meaningful, and to +# report actual input values of CONFIG_FILES etc. instead of their +# values after options handling. Logging --version etc. is OK. +exec 5>>config.log +{ + echo + sed 'h;s/./-/g;s/^.../## /;s/...$/ ##/;p;x;p;x' <<_ASBOX +## Running $as_me. ## +_ASBOX +} >&5 +cat >&5 <<_CSEOF + +This file was extended by $as_me, which was +generated by GNU Autoconf 2.57. Invocation command line was + + CONFIG_FILES = $CONFIG_FILES + CONFIG_HEADERS = $CONFIG_HEADERS + CONFIG_LINKS = $CONFIG_LINKS + CONFIG_COMMANDS = $CONFIG_COMMANDS + $ $0 $@ + +_CSEOF +echo "on `(hostname || uname -n) 2>/dev/null | sed 1q`" >&5 +echo >&5 +_ACEOF + +# Files that config.status was made for. +if test -n "$ac_config_files"; then + echo "config_files=\"$ac_config_files\"" >>$CONFIG_STATUS +fi + +if test -n "$ac_config_headers"; then + echo "config_headers=\"$ac_config_headers\"" >>$CONFIG_STATUS +fi + +if test -n "$ac_config_links"; then + echo "config_links=\"$ac_config_links\"" >>$CONFIG_STATUS +fi + +if test -n "$ac_config_commands"; then + echo "config_commands=\"$ac_config_commands\"" >>$CONFIG_STATUS +fi + +cat >>$CONFIG_STATUS <<\_ACEOF + +ac_cs_usage="\ +\`$as_me' instantiates files from templates according to the +current configuration. + +Usage: $0 [OPTIONS] [FILE]... + + -h, --help print this help, then exit + -V, --version print version number, then exit + -q, --quiet do not print progress messages + -d, --debug don't remove temporary files + --recheck update $as_me by reconfiguring in the same conditions + --file=FILE[:TEMPLATE] + instantiate the configuration file FILE + +Configuration files: +$config_files + +Report bugs to ." +_ACEOF + +cat >>$CONFIG_STATUS <<_ACEOF +ac_cs_version="\\ +config.status +configured by $0, generated by GNU Autoconf 2.57, + with options \\"`echo "$ac_configure_args" | sed 's/[\\""\`\$]/\\\\&/g'`\\" + +Copyright 1992, 1993, 1994, 1995, 1996, 1998, 1999, 2000, 2001 +Free Software Foundation, Inc. +This config.status script is free software; the Free Software Foundation +gives unlimited permission to copy, distribute and modify it." +srcdir=$srcdir +_ACEOF + +cat >>$CONFIG_STATUS <<\_ACEOF +# If no file are specified by the user, then we need to provide default +# value. By we need to know if files were specified by the user. +ac_need_defaults=: +while test $# != 0 +do + case $1 in + --*=*) + ac_option=`expr "x$1" : 'x\([^=]*\)='` + ac_optarg=`expr "x$1" : 'x[^=]*=\(.*\)'` + ac_shift=: + ;; + -*) + ac_option=$1 + ac_optarg=$2 + ac_shift=shift + ;; + *) # This is not an option, so the user has probably given explicit + # arguments. + ac_option=$1 + ac_need_defaults=false;; + esac + + case $ac_option in + # Handling of the options. +_ACEOF +cat >>$CONFIG_STATUS <<\_ACEOF + -recheck | --recheck | --rechec | --reche | --rech | --rec | --re | --r) + ac_cs_recheck=: ;; + --version | --vers* | -V ) + echo "$ac_cs_version"; exit 0 ;; + --he | --h) + # Conflict between --help and --header + { { echo "$as_me:$LINENO: error: ambiguous option: $1 +Try \`$0 --help' for more information." >&5 +echo "$as_me: error: ambiguous option: $1 +Try \`$0 --help' for more information." >&2;} + { (exit 1); exit 1; }; };; + --help | --hel | -h ) + echo "$ac_cs_usage"; exit 0 ;; + --debug | --d* | -d ) + debug=: ;; + --file | --fil | --fi | --f ) + $ac_shift + CONFIG_FILES="$CONFIG_FILES $ac_optarg" + ac_need_defaults=false;; + --header | --heade | --head | --hea ) + $ac_shift + CONFIG_HEADERS="$CONFIG_HEADERS $ac_optarg" + ac_need_defaults=false;; + -q | -quiet | --quiet | --quie | --qui | --qu | --q \ + | -silent | --silent | --silen | --sile | --sil | --si | --s) + ac_cs_silent=: ;; + + # This is an error. + -*) { { echo "$as_me:$LINENO: error: unrecognized option: $1 +Try \`$0 --help' for more information." >&5 +echo "$as_me: error: unrecognized option: $1 +Try \`$0 --help' for more information." >&2;} + { (exit 1); exit 1; }; } ;; + + *) ac_config_targets="$ac_config_targets $1" ;; + + esac + shift +done + +ac_configure_extra_args= + +if $ac_cs_silent; then + exec 6>/dev/null + ac_configure_extra_args="$ac_configure_extra_args --silent" +fi + +_ACEOF +cat >>$CONFIG_STATUS <<_ACEOF +if \$ac_cs_recheck; then + echo "running $SHELL $0 " $ac_configure_args \$ac_configure_extra_args " --no-create --no-recursion" >&6 + exec $SHELL $0 $ac_configure_args \$ac_configure_extra_args --no-create --no-recursion +fi + +_ACEOF + + + + + +cat >>$CONFIG_STATUS <<\_ACEOF +for ac_config_target in $ac_config_targets +do + case "$ac_config_target" in + # Handling of arguments. + "$OUTPUT_FILES" ) CONFIG_FILES="$CONFIG_FILES $OUTPUT_FILES" ;; + *) { { echo "$as_me:$LINENO: error: invalid argument: $ac_config_target" >&5 +echo "$as_me: error: invalid argument: $ac_config_target" >&2;} + { (exit 1); exit 1; }; };; + esac +done + +# If the user did not use the arguments to specify the items to instantiate, +# then the envvar interface is used. Set only those that are not. +# We use the long form for the default assignment because of an extremely +# bizarre bug on SunOS 4.1.3. +if $ac_need_defaults; then + test "${CONFIG_FILES+set}" = set || CONFIG_FILES=$config_files +fi + +# Have a temporary directory for convenience. Make it in the build tree +# simply because there is no reason to put it here, and in addition, +# creating and moving files from /tmp can sometimes cause problems. +# Create a temporary directory, and hook for its removal unless debugging. +$debug || +{ + trap 'exit_status=$?; rm -rf $tmp && exit $exit_status' 0 + trap '{ (exit 1); exit 1; }' 1 2 13 15 +} + +# Create a (secure) tmp directory for tmp files. + +{ + tmp=`(umask 077 && mktemp -d -q "./confstatXXXXXX") 2>/dev/null` && + test -n "$tmp" && test -d "$tmp" +} || +{ + tmp=./confstat$$-$RANDOM + (umask 077 && mkdir $tmp) +} || +{ + echo "$me: cannot create a temporary directory in ." >&2 + { (exit 1); exit 1; } +} + +_ACEOF + +cat >>$CONFIG_STATUS <<_ACEOF + +# +# CONFIG_FILES section. +# + +# No need to generate the scripts if there are no CONFIG_FILES. +# This happens for instance when ./config.status config.h +if test -n "\$CONFIG_FILES"; then + # Protect against being on the right side of a sed subst in config.status. + sed 's/,@/@@/; s/@,/@@/; s/,;t t\$/@;t t/; /@;t t\$/s/[\\\\&,]/\\\\&/g; + s/@@/,@/; s/@@/@,/; s/@;t t\$/,;t t/' >\$tmp/subs.sed <<\\CEOF +s,@SHELL@,$SHELL,;t t +s,@PATH_SEPARATOR@,$PATH_SEPARATOR,;t t +s,@PACKAGE_NAME@,$PACKAGE_NAME,;t t +s,@PACKAGE_TARNAME@,$PACKAGE_TARNAME,;t t +s,@PACKAGE_VERSION@,$PACKAGE_VERSION,;t t +s,@PACKAGE_STRING@,$PACKAGE_STRING,;t t +s,@PACKAGE_BUGREPORT@,$PACKAGE_BUGREPORT,;t t +s,@exec_prefix@,$exec_prefix,;t t +s,@prefix@,$prefix,;t t +s,@program_transform_name@,$program_transform_name,;t t +s,@bindir@,$bindir,;t t +s,@sbindir@,$sbindir,;t t +s,@libexecdir@,$libexecdir,;t t +s,@datadir@,$datadir,;t t +s,@sysconfdir@,$sysconfdir,;t t +s,@sharedstatedir@,$sharedstatedir,;t t +s,@localstatedir@,$localstatedir,;t t +s,@libdir@,$libdir,;t t +s,@includedir@,$includedir,;t t +s,@oldincludedir@,$oldincludedir,;t t +s,@infodir@,$infodir,;t t +s,@mandir@,$mandir,;t t +s,@build_alias@,$build_alias,;t t +s,@host_alias@,$host_alias,;t t +s,@target_alias@,$target_alias,;t t +s,@DEFS@,$DEFS,;t t +s,@ECHO_C@,$ECHO_C,;t t +s,@ECHO_N@,$ECHO_N,;t t +s,@ECHO_T@,$ECHO_T,;t t +s,@LIBS@,$LIBS,;t t +s,@MAJOR_VER@,$MAJOR_VER,;t t +s,@MINOR_VER@,$MINOR_VER,;t t +s,@MICRO_VER@,$MICRO_VER,;t t +s,@CC@,$CC,;t t +s,@CFLAGS@,$CFLAGS,;t t +s,@LDFLAGS@,$LDFLAGS,;t t +s,@CPPFLAGS@,$CPPFLAGS,;t t +s,@ac_ct_CC@,$ac_ct_CC,;t t +s,@EXEEXT@,$EXEEXT,;t t +s,@OBJEXT@,$OBJEXT,;t t +s,@SET_MAKE@,$SET_MAKE,;t t +s,@LN_S@,$LN_S,;t t +s,@AWK@,$AWK,;t t +s,@RANLIB@,$RANLIB,;t t +s,@ac_ct_RANLIB@,$ac_ct_RANLIB,;t t +s,@LD@,$LD,;t t +s,@ac_ct_LD@,$ac_ct_LD,;t t +s,@build@,$build,;t t +s,@build_cpu@,$build_cpu,;t t +s,@build_vendor@,$build_vendor,;t t +s,@build_os@,$build_os,;t t +s,@host@,$host,;t t +s,@host_cpu@,$host_cpu,;t t +s,@host_vendor@,$host_vendor,;t t +s,@host_os@,$host_os,;t t +s,@target@,$target,;t t +s,@target_cpu@,$target_cpu,;t t +s,@target_vendor@,$target_vendor,;t t +s,@target_os@,$target_os,;t t +s,@CPP@,$CPP,;t t +s,@EGREP@,$EGREP,;t t +s,@TARGETS@,$TARGETS,;t t +s,@SHARED_EXT@,$SHARED_EXT,;t t +s,@SHARED_ALT@,$SHARED_ALT,;t t +s,@SHARED_CFLAGS@,$SHARED_CFLAGS,;t t +s,@LINK_SHARED@,$LINK_SHARED,;t t +s,@DEFS_R@,$DEFS_R,;t t +s,@LIBR_MANDIR@,$LIBR_MANDIR,;t t +s,@LIBR_MANEXT@,$LIBR_MANEXT,;t t +s,@FUNC_MANDIR@,$FUNC_MANDIR,;t t +s,@FUNC_MANEXT@,$FUNC_MANEXT,;t t +s,@PROG_MANDIR@,$PROG_MANDIR,;t t +s,@PROG_MANEXT@,$PROG_MANEXT,;t t +s,@MISC_MANDIR@,$MISC_MANDIR,;t t +s,@MISC_MANEXT@,$MISC_MANEXT,;t t +s,@FILE_MANDIR@,$FILE_MANDIR,;t t +s,@FILE_MANEXT@,$FILE_MANEXT,;t t +s,@TARGET_LIBS@,$TARGET_LIBS,;t t +s,@MAKE_MAN_PAGES@,$MAKE_MAN_PAGES,;t t +s,@LIBOBJS@,$LIBOBJS,;t t +s,@LTLIBOBJS@,$LTLIBOBJS,;t t +CEOF + +_ACEOF + + cat >>$CONFIG_STATUS <<\_ACEOF + # Split the substitutions into bite-sized pieces for seds with + # small command number limits, like on Digital OSF/1 and HP-UX. + ac_max_sed_lines=48 + ac_sed_frag=1 # Number of current file. + ac_beg=1 # First line for current file. + ac_end=$ac_max_sed_lines # Line after last line for current file. + ac_more_lines=: + ac_sed_cmds= + while $ac_more_lines; do + if test $ac_beg -gt 1; then + sed "1,${ac_beg}d; ${ac_end}q" $tmp/subs.sed >$tmp/subs.frag + else + sed "${ac_end}q" $tmp/subs.sed >$tmp/subs.frag + fi + if test ! -s $tmp/subs.frag; then + ac_more_lines=false + else + # The purpose of the label and of the branching condition is to + # speed up the sed processing (if there are no `@' at all, there + # is no need to browse any of the substitutions). + # These are the two extra sed commands mentioned above. + (echo ':t + /@[a-zA-Z_][a-zA-Z_0-9]*@/!b' && cat $tmp/subs.frag) >$tmp/subs-$ac_sed_frag.sed + if test -z "$ac_sed_cmds"; then + ac_sed_cmds="sed -f $tmp/subs-$ac_sed_frag.sed" + else + ac_sed_cmds="$ac_sed_cmds | sed -f $tmp/subs-$ac_sed_frag.sed" + fi + ac_sed_frag=`expr $ac_sed_frag + 1` + ac_beg=$ac_end + ac_end=`expr $ac_end + $ac_max_sed_lines` + fi + done + if test -z "$ac_sed_cmds"; then + ac_sed_cmds=cat + fi +fi # test -n "$CONFIG_FILES" + +_ACEOF +cat >>$CONFIG_STATUS <<\_ACEOF +for ac_file in : $CONFIG_FILES; do test "x$ac_file" = x: && continue + # Support "outfile[:infile[:infile...]]", defaulting infile="outfile.in". + case $ac_file in + - | *:- | *:-:* ) # input from stdin + cat >$tmp/stdin + ac_file_in=`echo "$ac_file" | sed 's,[^:]*:,,'` + ac_file=`echo "$ac_file" | sed 's,:.*,,'` ;; + *:* ) ac_file_in=`echo "$ac_file" | sed 's,[^:]*:,,'` + ac_file=`echo "$ac_file" | sed 's,:.*,,'` ;; + * ) ac_file_in=$ac_file.in ;; + esac + + # Compute @srcdir@, @top_srcdir@, and @INSTALL@ for subdirectories. + ac_dir=`(dirname "$ac_file") 2>/dev/null || +$as_expr X"$ac_file" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ + X"$ac_file" : 'X\(//\)[^/]' \| \ + X"$ac_file" : 'X\(//\)$' \| \ + X"$ac_file" : 'X\(/\)' \| \ + . : '\(.\)' 2>/dev/null || +echo X"$ac_file" | + sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ s//\1/; q; } + /^X\(\/\/\)[^/].*/{ s//\1/; q; } + /^X\(\/\/\)$/{ s//\1/; q; } + /^X\(\/\).*/{ s//\1/; q; } + s/.*/./; q'` + { if $as_mkdir_p; then + mkdir -p "$ac_dir" + else + as_dir="$ac_dir" + as_dirs= + while test ! -d "$as_dir"; do + as_dirs="$as_dir $as_dirs" + as_dir=`(dirname "$as_dir") 2>/dev/null || +$as_expr X"$as_dir" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ + X"$as_dir" : 'X\(//\)[^/]' \| \ + X"$as_dir" : 'X\(//\)$' \| \ + X"$as_dir" : 'X\(/\)' \| \ + . : '\(.\)' 2>/dev/null || +echo X"$as_dir" | + sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ s//\1/; q; } + /^X\(\/\/\)[^/].*/{ s//\1/; q; } + /^X\(\/\/\)$/{ s//\1/; q; } + /^X\(\/\).*/{ s//\1/; q; } + s/.*/./; q'` + done + test ! -n "$as_dirs" || mkdir $as_dirs + fi || { { echo "$as_me:$LINENO: error: cannot create directory \"$ac_dir\"" >&5 +echo "$as_me: error: cannot create directory \"$ac_dir\"" >&2;} + { (exit 1); exit 1; }; }; } + + ac_builddir=. + +if test "$ac_dir" != .; then + ac_dir_suffix=/`echo "$ac_dir" | sed 's,^\.[\\/],,'` + # A "../" for each directory in $ac_dir_suffix. + ac_top_builddir=`echo "$ac_dir_suffix" | sed 's,/[^\\/]*,../,g'` +else + ac_dir_suffix= ac_top_builddir= +fi + +case $srcdir in + .) # No --srcdir option. We are building in place. + ac_srcdir=. + if test -z "$ac_top_builddir"; then + ac_top_srcdir=. + else + ac_top_srcdir=`echo $ac_top_builddir | sed 's,/$,,'` + fi ;; + [\\/]* | ?:[\\/]* ) # Absolute path. + ac_srcdir=$srcdir$ac_dir_suffix; + ac_top_srcdir=$srcdir ;; + *) # Relative path. + ac_srcdir=$ac_top_builddir$srcdir$ac_dir_suffix + ac_top_srcdir=$ac_top_builddir$srcdir ;; +esac +# Don't blindly perform a `cd "$ac_dir"/$ac_foo && pwd` since $ac_foo can be +# absolute. +ac_abs_builddir=`cd "$ac_dir" && cd $ac_builddir && pwd` +ac_abs_top_builddir=`cd "$ac_dir" && cd ${ac_top_builddir}. && pwd` +ac_abs_srcdir=`cd "$ac_dir" && cd $ac_srcdir && pwd` +ac_abs_top_srcdir=`cd "$ac_dir" && cd $ac_top_srcdir && pwd` + + + + if test x"$ac_file" != x-; then + { echo "$as_me:$LINENO: creating $ac_file" >&5 +echo "$as_me: creating $ac_file" >&6;} + rm -f "$ac_file" + fi + # Let's still pretend it is `configure' which instantiates (i.e., don't + # use $as_me), people would be surprised to read: + # /* config.h. Generated by config.status. */ + if test x"$ac_file" = x-; then + configure_input= + else + configure_input="$ac_file. " + fi + configure_input=$configure_input"Generated from `echo $ac_file_in | + sed 's,.*/,,'` by configure." + + # First look for the input files in the build tree, otherwise in the + # src tree. + ac_file_inputs=`IFS=: + for f in $ac_file_in; do + case $f in + -) echo $tmp/stdin ;; + [\\/$]*) + # Absolute (can't be DOS-style, as IFS=:) + test -f "$f" || { { echo "$as_me:$LINENO: error: cannot find input file: $f" >&5 +echo "$as_me: error: cannot find input file: $f" >&2;} + { (exit 1); exit 1; }; } + echo $f;; + *) # Relative + if test -f "$f"; then + # Build tree + echo $f + elif test -f "$srcdir/$f"; then + # Source tree + echo $srcdir/$f + else + # /dev/null tree + { { echo "$as_me:$LINENO: error: cannot find input file: $f" >&5 +echo "$as_me: error: cannot find input file: $f" >&2;} + { (exit 1); exit 1; }; } + fi;; + esac + done` || { (exit 1); exit 1; } +_ACEOF +cat >>$CONFIG_STATUS <<_ACEOF + sed "$ac_vpsub +$extrasub +_ACEOF +cat >>$CONFIG_STATUS <<\_ACEOF +:t +/@[a-zA-Z_][a-zA-Z_0-9]*@/!b +s,@configure_input@,$configure_input,;t t +s,@srcdir@,$ac_srcdir,;t t +s,@abs_srcdir@,$ac_abs_srcdir,;t t +s,@top_srcdir@,$ac_top_srcdir,;t t +s,@abs_top_srcdir@,$ac_abs_top_srcdir,;t t +s,@builddir@,$ac_builddir,;t t +s,@abs_builddir@,$ac_abs_builddir,;t t +s,@top_builddir@,$ac_top_builddir,;t t +s,@abs_top_builddir@,$ac_abs_top_builddir,;t t +" $ac_file_inputs | (eval "$ac_sed_cmds") >$tmp/out + rm -f $tmp/stdin + if test x"$ac_file" != x-; then + mv $tmp/out $ac_file + else + cat $tmp/out + rm -f $tmp/out + fi + +done +_ACEOF + +cat >>$CONFIG_STATUS <<\_ACEOF + +{ (exit 0); exit 0; } +_ACEOF +chmod +x $CONFIG_STATUS +ac_clean_files=$ac_clean_files_save + + +# configure is writing to config.log, and then calls config.status. +# config.status does its own redirection, appending to config.log. +# Unfortunately, on DOS this fails, as config.log is still kept open +# by configure, so config.status won't be able to write to it; its +# output is simply discarded. So we exec the FD to /dev/null, +# effectively closing config.log, so it can be properly (re)opened and +# appended to by config.status. When coming back to configure, we +# need to make the FD available again. +if test "$no_create" != yes; then + ac_cs_success=: + ac_config_status_args= + test "$silent" = yes && + ac_config_status_args="$ac_config_status_args --quiet" + exec 5>/dev/null + $SHELL $CONFIG_STATUS $ac_config_status_args || ac_cs_success=false + exec 5>>config.log + # Use ||, not &&, to avoid exiting from the if with $? = 1, which + # would make configure fail if this is the last instruction. + $ac_cs_success || { (exit 1); exit 1; } +fi + diff --git a/libtecla-1.6.1/configure.in b/libtecla-1.6.1/configure.in new file mode 100644 index 0000000..78e7170 --- /dev/null +++ b/libtecla-1.6.1/configure.in @@ -0,0 +1,582 @@ +dnl This is the input file which autoconf uses to construct a +dnl "configure" script for the tecla library. It is a bourne shell +dnl script which autoconf pre-processes with the m4 preprocessor to +dnl expand autoconf-defined m4 macros such as AC_INIT(). The +dnl following line just initializes autoconf. Autoconf interprets the +dnl single argument as the name of an arbitrary file, which it uses to +dnl ensure that it is being run correctly from the directory which +dnl contains the libtecla source code. + +AC_INIT(getline.c) + +dnl Here we set the major version number of the tecla library. +dnl Incrementing this number implies that a change has been made to +dnl the library's public interface, which makes it binary incompatible +dnl with programs that were linked with previous shared versions of +dnl the tecla library. Incompatible changes of this type should be +dnl avoided at all costs, so it is hoped that the major version number +dnl won't ever have to change. The major version number must be a +dnl small integer number, preferably a single numeric digit. + +AC_SUBST(MAJOR_VER) +MAJOR_VER="1" + +dnl Set the minor version number of the tecla library. This number +dnl should be incremented by one whenever additional functionality, +dnl such as new functions or modules, are added to the library. The +dnl idea is that a program that was linked with a shared library of +dnl the same major version number, but a lower minor version number, +dnl will continue to function when the run-time loader links it +dnl against the updated version. The minor version number must be a +dnl small integer number, which should be reset to 0 whenever the +dnl major version number is incremented. + +AC_SUBST(MINOR_VER) +MINOR_VER="6" + +dnl Set the micro version number of the tecla library. This is +dnl incremented whenever modifications to the library are made which +dnl make no changes to the public interface, but which fix bugs and/or +dnl improve the behind-the-scenes implementation. The micro version +dnl number should be reset to 0 whenever the minor version number is +dnl incremented. The micro version number must be a small integer +dnl number. + +AC_SUBST(MICRO_VER) +MICRO_VER="1" + +dnl The AC_PROG_CC line looks for a C compiler, and if gcc is chosen, +dnl sets the $GCC shell variable to "yes". Make sure that CFLAGS is +dnl set to something first, to prevent AC_PROG_CC from substituting -g +dnl for the optimization level. + +CFLAGS="$CFLAGS" +AC_PROG_CC + +dnl Apparently not all implementations of the 'make' command define +dnl the MAKE variable. The following directive creates a variable +dnl called SET_MAKE which when expanded in a makefile is either empty +dnl if the local 'make' command was found to define the MAKE variable, +dnl or contains an assignment which will give the MAKE variable the +dnl value 'make'. + +AC_PROG_MAKE_SET + +dnl The following directive causes autoconf to see if symbolic links +dnl are supported on the current filesystem. If so, it sets the +dnl variable LN_S to "ln -s". Otherwise it sets LN_S to just "ln". +dnl This allows us to create symbolic links where possible, but falls +dnl back to creating hard links where symbolic links aren't available. + +AC_PROG_LN_S + +dnl The following macro searches for the best implementation of awk +dnl on the host system, and records it in the AWK shell variable. + +AC_PROG_AWK + +dnl If ranlib is needed on the target system, the RANLIB make variable +dnl is set to ranlib. Otherwise it is set to :, which is the do-nothing +dnl command of the bourne shell. +dnl Note that we do not use AC_PROG_RANLIB because (at least in +dnl autoconf 2.53) this does not check for cross-compilation. + +AC_CHECK_TOOL(RANLIB, ranlib) + +dnl Set LD as appropriate, especially when cross-compiling + +AC_CHECK_TOOL(LD, ld) + +dnl The following directive tells autoconf to figure out the target +dnl system type and assign a canonical name for this to the $target +dnl shell variable. This is used below in the target-specific case +dnl statement. + +AC_CANONICAL_SYSTEM + +dnl In early versions of Solaris, some libraries are in /usr/ccs/lib, +dnl where gcc doesn't look. The tests below for the curses library +dnl would thus fail without this directory being added to the search +dnl path. We thus add it here before the tests. Note that in the +dnl following, since [ and ] are m4 quotes, and m4 will remove the +dnl outermost quotes when it processes this file, we have to double +dnl them up here to get [0-6] to appear in the output configure +dnl script. + +case $target_os in +solaris2.[[0-6]]|solaris2.[[0-6]].*) + LIBS="$LIBS -L/usr/ccs/lib" + ;; +esac + +dnl Recent versions of gcc place /usr/local/include at the head of the +dnl system include-file search path. This causes problems when include +dnl files that have the same name as standard system include files are +dnl placed in this directory by third-party packages. To avoid this, +dnl move /usr/local/include to the end of the search path. + +if test "$GCC"_ = "yes"_; then + touch foo.c + fix=`$CC -E -Wp,-v foo.c 2>&1 | $AWK ' + /^#include <...> search starts here:/ {in_list=1;ndir=0} + / *\// && in_list {path[[ndir++]] = $1} + /^End of search list/ {in_list=0} + END { + if(path[[0]] ~ /\/usr\/local\/include/) { + for(dir=1; dir])], [AC_CHECK_HEADERS(ncurses/curses.h, [AC_CHECK_HEADERS(ncurses/term.h,[],[],[#include ])])]) + +dnl The following variable lists the targets that will be created if +dnl the user runs make without any arguments. Initially we assume +dnl that we can create both the normal and the reentrant versions +dnl of the library. + +AC_SUBST(TARGETS) +TARGETS="normal reentrant" + +dnl Check for reentrant functions by attempting to compile and link a +dnl temporary program which calls them, being sure to include the +dnl appropriate headers and define _POSIX_C_SOURCE, just in case any +dnl of the functions are defined as macros. In the following, +dnl AC_CACHE_CHECK outputs the message "checking for reentrant +dnl functions". If this check has been done before, it assigns the +dnl cached yes/no value to tecla_cv_reentrant. Otherwise it uses +dnl AC_TRY_LINK() to attempt to compile and link the specified dummy +dnl program, and sets tecla_cv_reentrant to yes or no, depending on +dnl whether this succeeds. Finally it caches the value of +dnl tecla_cv_reentrant in the file config.cache, and writes "yes" or +dnl "no" to the terminal. + +AC_CACHE_CHECK(for reentrant functions, tecla_cv_reentrant, [ + KEPT_CFLAGS="$CFLAGS" + CFLAGS="$CFLAGS -D_POSIX_C_SOURCE=199506L" + AC_TRY_LINK([ +#include +#include +#include +#include +#include + ], [ + (void) readdir_r(NULL, NULL, NULL); + (void) getpwuid_r(geteuid(), NULL, NULL, 0, NULL); + (void) getpwnam_r(NULL, NULL, NULL, 0, NULL); + ], tecla_cv_reentrant=yes, tecla_cv_reentrant=no) + CFLAGS="$KEPT_CFLAGS" +]) + +dnl If the necessary reentrant functions weren't found to be +dnl available, default to only compiling the non-reentrant version of +dnl the library. + +if test $tecla_cv_reentrant = no; then + TARGETS="normal" +fi + +dnl If sys/select.h exists, arrange for the HAVE_SYS_SELECT_H C-macro to +dnl be defined when compiling the library. + +AC_CHECK_HEADER(sys/select.h, AC_DEFINE(HAVE_SYS_SELECT_H)) + +dnl Check for the select system call with the normal arguments, +dnl by attempting to compile and link a temporary program which +dnl calls it, being sure to include the appropriate headers. +dnl In the following, AC_CACHE_CHECK outputs the message +dnl "checking for select system call". If this check has been done +dnl before, it assigns the cached yes/no value to tecla_cv_select. +dnl Otherwise it uses AC_TRY_LINK() to attempt to compile and link +dnl the specified dummy program, and sets tecla_cv_select to yes +dnl or no, depending on whether this succeeds. Finally it caches +dnl the value of tecla_cv_select in the file config.cache, and +dnl writes "yes" or "no" to the terminal. + +AC_CACHE_CHECK(for select system call, tecla_cv_select, [ + AC_TRY_LINK([ +#include +#include +#include +#ifdef HAVE_SYS_SELECT_H +#include +#endif + ], [ + fd_set fds; + int nready; + FD_ZERO(&fds); + FD_SET(1, &fds); + nready = select(2, &fds, &fds, &fds, NULL); + ], tecla_cv_select=yes, tecla_cv_select=no) +]) + +dnl If the select function was available, arrange for HAVE_SELECT to +dnl be defined by CFLAGS. + +if test $tecla_cv_select = yes; then + AC_DEFINE(HAVE_SELECT) +fi + +dnl Check if this system supports the system V pseudo terminal interface. + +AC_CACHE_CHECK(for SysV pseudo-terminals, tecla_cv_sysv_pty, [ + AC_TRY_LINK([ +#include +#include +#include + ], [ + char *name = ptsname(0); + int i1 = grantpt(0); + int i2 = unlockpt(0); + int i3 = ioctl(0, I_PUSH, "ptem"); + return 0; + ], tecla_cv_sysv_pty=yes, tecla_cv_sysv_pty=no) +]) + +dnl If the system-V pseudo-terminal interface is available, arrange +dnl for HAVE_SYSV_PTY to be defined by CFLAGS. + +if test $tecla_cv_sysv_pty = yes; then + AC_DEFINE(HAVE_SYSV_PTY) +fi + +dnl The following variable contains the extension to append to +dnl "libtecla" and "libtecla_r" when creating shared libraries on the +dnl target platform. This is system dependent and is ignored if +dnl LINK_SHARED remains an empty string. On most platforms that +dnl support shared libaries, this will be .so.$MAJOR_VER, where +dnl MAJOR_VER is the major version number described above, which on +dnl some systems, tells the run-time loader if the program being +dnl loaded is binary compatible with a given version of the library +dnl (see the discussion of MAJOR_VER near the top of this file). +dnl The following empty default can be overriden on a system by system +dnl basis later in this file. + +AC_SUBST(SHARED_EXT) +SHARED_EXT="" + +dnl When a shared library is installed with the extension $SHARED_EXT, +dnl you can optionally produce other copies of this library with +dnl different extensions. This is done using symbolic or hard links, +dnl depending on what is available on the current filesystem, and the +dnl extensions to use for these links are listed in the following +dnl variable, separated by spaces. The following empty default can be +dnl overriden on a system by system basis later in this file. + +AC_SUBST(SHARED_ALT) +SHARED_ALT="" + +dnl The following variable lists extra compilation flags needed to +dnl create object files that can be included in shared libraries. +dnl Normally one would include a flag to tell the C compiler to +dnl compile position-independent code. This option commonly includes +dnl the acronym 'pic'. + +AC_SUBST(SHARED_CFLAGS) +SHARED_CFLAGS="" + +dnl On systems that support shared libraries, the following variable +dnl provides the command needed to make a shared library. In this +dnl variable, $$@ will be replaced with the name of the shared +dnl library, $$(LIB_OBJECTS) will be replaced with a space separated +dnl list of the object files that are to be included in the library, +dnl and libtecla$$(SUFFIX) will be the name of the library being +dnl built, minus the system-specific extension (eg. libtecla or +dnl libtecla_r). If LINK_SHARED is left as an empty string, shared +dnl library creation will not attempted. If your system supports +dnl shared library creation, you should override the default value of +dnl this variable in the target-specific case statement later in this +dnl file. + +AC_SUBST(LINK_SHARED) +LINK_SHARED="" + +dnl When compiling the reentrant version of the library, the following +dnl compiler flags are presented to the compiler, in addition to those +dnl that are used when compiling the non-reentrant version of the +dnl library. The PREFER_REENTRANT macro is an internal libtecla macro +dnl whose presence reports when the reentrant version of the library +dnl is being compiled. This allows the code to determine when to +dnl disable features that can't portably be implemented reentrantly, +dnl such as username completion. The existence of the _POSIX_C_SOURCE +dnl macro can't be reliably used for this purpose, since some systems +dnl define it by default for all code. + +AC_SUBST(DEFS_R) +DEFS_R="-D_POSIX_C_SOURCE=199506L -DPREFER_REENTRANT" + +dnl For man pages relating to library features, the following two +dnl variables determine in which sub-directory of the top-level man +dnl directory the man pages should go, and what file-name extensions +dnl these files should have. On systems where the following defaults +dnl are not valid, the default values should be overriden in the +dnl target-specific case statement later in this file. + +AC_SUBST(LIBR_MANDIR) +AC_SUBST(LIBR_MANEXT) +LIBR_MANDIR="man3" +LIBR_MANEXT="3" + +dnl For man pages relating to library functions, the following two +dnl variables serve the same purpose as the previously described +dnl LIBR_MANDIR and LIBR_MANEXT variables. + +AC_SUBST(FUNC_MANDIR) +AC_SUBST(FUNC_MANEXT) +FUNC_MANDIR="man3" +FUNC_MANEXT="3" + +dnl For man pages relating to programs, the following two variables +dnl serve the same purpose as the previously described LIBR_MANDIR +dnl and LIBR_MANEXT variables. + +AC_SUBST(PROG_MANDIR) +AC_SUBST(PROG_MANEXT) +PROG_MANDIR="man1" +PROG_MANEXT="1" + +dnl For man pages on miscellaneous topics, the following two variables +dnl serve the same purpose as the previously described LIBR_MANDIR +dnl and LIBR_MANEXT variables. + +AC_SUBST(MISC_MANDIR) +AC_SUBST(MISC_MANEXT) +MISC_MANDIR="man7" +MISC_MANEXT="7" + +dnl For man pages relating to configuration files, the following two +dnl variables serve the same purpose as the previously described +dnl LIBR_MANDIR and LIBR_MANEXT variables. + +AC_SUBST(FILE_MANDIR) +AC_SUBST(FILE_MANEXT) +FILE_MANDIR="man5" +FILE_MANEXT="5" + +dnl If the application doesn't want the user to have access to the +dnl filesystem, it can remove all action functions that list, read or +dnl write files, by including the configuration argument +dnl --without-file-actions. + +AC_ARG_WITH(file-actions, AC_HELP_STRING([--with-file-actions], [Should users of gl_get_line() have access to the filesystem (default=yes)]), + AC_DEFINE(HIDE_FILE_SYSTEM), ) + +dnl If the target system either has no file-system, or file-system access +dnl isn't needed, libtecla can be made smaller by excluding all file and +dnl directory access code. This is done by adding the configuration +dnl argument --without-file-system. + +AC_ARG_WITH(file-system, AC_HELP_STRING([--with-file-system], [Does the target have a filesystem (default=yes)]), + AC_DEFINE(WITHOUT_FILE_SYSTEM), ) + +dnl The following bourne shell case statement is where system +dnl dependencies can be added. In particular, if your system supports +dnl shared library creation, the following switch is the place to +dnl configure it. To do so you will first need to find out what target +dnl type was just assigned by the AC_CANONICAL_SYSTEM macro executed +dnl previously. The target type of your current system can be +dnl determined by cd'ing to the top level directory of the tecla +dnl distribution, and typing the command "sh config.guess". This will +dnl report what autoconf thinks the system type is. Note that this +dnl will be very specific, so if you know that the configuration +dnl parameters that you are about to provide apply to different +dnl versions of the current system type, you can express this in the +dnl case statement by using a wild-card in place of the version +dnl number, or by using an | alternation to list one or more version +dnl names. Beware that autoconf uses [] as quote characters, so if you +dnl want to use a regexp character range like [a-z], you should write +dnl this as [[a-z]]. + +case $target in +*solaris*) + AC_DEFINE(__EXTENSIONS__) + SHARED_EXT=".so.${MAJOR_VER}" + SHARED_ALT=".so" + LINK_SHARED="$LD"' -G -M $$(srcdir)/libtecla.map -o $$@ -h $$(@F) -z defs -i $$(LIB_OBJECTS) $$(LIBS) -lc' + SHARED_CFLAGS="-Kpic" + case $CC in + */cc|cc) SHARED_CFLAGS="$SHARED_CFLAGS -xstrconst" ;; + esac + case $target_cpu in + sparc) SHARED_CFLAGS="$SHARED_CFLAGS -xregs=no%appl" + esac + case $target_os in + solaris2.[[89]]*|solaris2.1[[0-9]]*) + LIBR_MANEXT=3lib + FUNC_MANEXT=3tecla + LIBR_MANDIR=man$LIBR_MANEXT + FUNC_MANDIR=man$FUNC_MANEXT + esac + MISC_MANDIR="man5" + MISC_MANEXT="5" + FILE_MANDIR="man4" + FILE_MANEXT="4" + ;; +*linux*) + SHARED_EXT=".so.${MAJOR_VER}.${MINOR_VER}.${MICRO_VER}" + SHARED_ALT=".so .so.${MAJOR_VER}" + +dnl See if the installed version of Gnu ld accepts version scripts. + + AC_CACHE_CHECK([for --version-script in GNU ld], tecla_cv_gnu_ld_script, [ + if (echo 'void dummy(void) {return;}' > dummy.c; $CC -c -fpic dummy.c; \ + $LD -o dummy.so dummy.o -shared --version-script=$srcdir/libtecla.map) 1>&2 2>/dev/null; then + tecla_cv_gnu_ld_script=yes + else + tecla_cv_gnu_ld_script=no + fi + rm -f dummy.c dummy.o dummy.so + ]) + if test $tecla_cv_gnu_ld_script = yes; then + VERSION_OPT='--version-script=$$(srcdir)/libtecla.map' + else + VERSION_OPT='' + fi + + LINK_SHARED="$LD"' -o $$@ -soname libtecla$$(SUFFIX).so.'${MAJOR_VER}' -shared '$VERSION_OPT' $$(LIB_OBJECTS) $$(LIBS) -lc' + SHARED_CFLAGS="-fpic" + +dnl Reenable the inclusion of symbols which get undefined when POSIX_C_SOURCE +dnl is specified. + + CFLAGS="-D_SVID_SOURCE -D_BSD_SOURCE $CFLAGS" + ;; +*hpux*) + SHARED_EXT=".${MAJOR_VER}" + SHARED_ALT=".sl" + LINK_SHARED="$LD"' -b +h $$(@F) +k +vshlibunsats -o $$@ -c libtecla.map.opt $$(LIB_OBJECTS) $$(LIBS) -lc' + SHARED_CFLAGS="+z" + MISC_MANEXT=5 + FILE_MANEXT=4 + MISC_MANDIR=man$MISC_MANEXT + FILE_MANDIR=man$FILE_MANEXT + ;; +*darwin*) + SHARED_EXT=".${MAJOR_VER}.${MINOR_VER}.${MICRO_VER}.dylib" + SHARED_ALT=".dylib .${MAJOR_VER}.dylib" + LINK_SHARED='$(CC) -o $$@ -dynamiclib -flat_namespace -undefined suppress -compatibility_version '${MAJOR_VER}.${MINOR_VER}' -current_version '${MAJOR_VER}.${MINOR_VER}.${MICRO_VER}' -install_name '${libdir}'/$$@ $$(LIB_OBJECTS)' + SHARED_CFLAGS="" + ;; +*dec-osf*) + AC_DEFINE(_OSF_SOURCE) + ;; +*freebsd*) + SHARED_EXT=".so.${MAJOR_VER}" + SHARED_ALT=".so" + VERSION_OPT='--version-script=$$(srcdir)/libtecla.map' + LINK_SHARED='ld -o $$@ -soname libtecla$$(SUFFIX).so.'${MAJOR_VER}' -shared '$VERSION_OPT' $$(LIB_OBJECTS) $$(LIBS) -lc' + SHARED_CFLAGS="-fpic" + ;; +mips-sgi-irix*) + DEFS_R="$DEFS_R -D_XOPEN_SOURCE=500" + if test "$RANLIB"_ = "_"; then + RANLIB=":" + fi + ;; +esac + +dnl The following statement checks to see if the GNU C compiler has +dnl been chosen instead of the normal compiler of the host operating +dnl system. If it has, and shared library creation has been +dnl configured, it replaces the shared-library-specific C compilation +dnl flags with those supported by gcc. Also append the gcc run-time +dnl library to the shared library link line. + +if test "$GCC"_ = "yes"_ && test "$LINK_SHARED"_ != "_" ; then + SHARED_CFLAGS="-fpic" + case $target in + sparc-*-solaris*) + SHARED_CFLAGS="$SHARED_CFLAGS -mno-app-regs" + ;; + *darwin*) + SHARED_CFLAGS="" + ;; + esac + LINK_SHARED="$LINK_SHARED `gcc -print-libgcc-file-name`" +fi + +dnl The following variable will list which types of libraries, +dnl "static", and possibly "shared", are to be created and installed. + +AC_SUBST(TARGET_LIBS) + +dnl If shared library creation has been configured, add shared +dnl libraries to the list of libraries to be built. + +if test "$LINK_SHARED"_ != "_"; then + TARGET_LIBS="static shared" +else + TARGET_LIBS="static" + LINK_SHARED="@:" +fi + +dnl Set the shell variable and Makefile variable, MAKE_MAN_PAGES, to +dnl "yes" if man pages are desired. By default they are, but if the +dnl user specifies --with-man-pages=no or --without-man-pages, then +dnl they won't be preprocessed by the configure script or installed +dnl by the Makefile. + +AC_SUBST(MAKE_MAN_PAGES) +AC_ARG_WITH(man-pages, AC_HELP_STRING([--with-man-pages], [Are man pages desired (default=yes)]), + MAKE_MAN_PAGES="$withval", MAKE_MAN_PAGES="yes") + +dnl Create the list of files to be generated by the configure script. + +OUTPUT_FILES="Makefile" +rm -rf man/man* +if test "$MAKE_MAN_PAGES"_ = "yes"_; then + for area in libr func misc prog file; do + for page in man/$area/*.in; do + OUTPUT_FILES="$OUTPUT_FILES `echo $page | sed 's/\.in$//'`" + done + done +fi + +dnl The following directive must always be the last line of any +dnl autoconf script. It causes autoconf to create the configure +dnl script, which for each argument of AC_OUTPUT, will look for a +dnl filename formed by appending ".in" to the argument, preprocess +dnl that file, replacing @VAR@ directives with the corresponding value +dnl of the specified shell variable VAR, as set above in this file, +dnl and write the resulting output to the filename given in the +dnl argument. Note that only shell variables that were exported above +dnl with the AC_SUBST() directive will be substituted in @VAR@ +dnl directives (some macros like AC_PROG_CC also call AC_SUBST for you +dnl for the variables that they output). + +AC_OUTPUT($OUTPUT_FILES) diff --git a/libtecla-1.6.1/cplfile.c b/libtecla-1.6.1/cplfile.c new file mode 100644 index 0000000..eee3166 --- /dev/null +++ b/libtecla-1.6.1/cplfile.c @@ -0,0 +1,870 @@ +/* + * Copyright (c) 2000, 2001, 2002, 2003, 2004 by Martin C. Shepherd. + * + * All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, and/or sell copies of the Software, and to permit persons + * to whom the Software is furnished to do so, provided that the above + * copyright notice(s) and this permission notice appear in all copies of + * the Software and that both the above copyright notice(s) and this + * permission notice appear in supporting documentation. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT + * OF THIRD PARTY RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR + * HOLDERS INCLUDED IN THIS NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL + * INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING + * FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, + * NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION + * WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + * Except as contained in this notice, the name of a copyright holder + * shall not be used in advertising or otherwise to promote the sale, use + * or other dealings in this Software without prior written authorization + * of the copyright holder. + */ + +/* + * If file-system access is to be excluded, this module has no function, + * so all of its code should be excluded. + */ +#ifndef WITHOUT_FILE_SYSTEM + +/* + * Standard includes. + */ +#include +#include +#include +#include +#include +#include + +/* + * Local includes. + */ +#include "libtecla.h" +#include "direader.h" +#include "homedir.h" +#include "pathutil.h" +#include "cplfile.h" +#include "errmsg.h" + +/* + * Set the maximum length allowed for usernames. + * names. + */ +#define USR_LEN 100 + +/* + * Set the maximum length allowed for environment variable names. + */ +#define ENV_LEN 100 + +/* + * The resources needed to complete a filename are maintained in objects + * of the following type. + */ +struct CompleteFile { + ErrMsg *err; /* The error reporting buffer */ + DirReader *dr; /* A directory reader */ + HomeDir *home; /* A home directory expander */ + PathName *path; /* The buffer in which to accumulate the path */ + PathName *buff; /* A pathname work buffer */ + char usrnam[USR_LEN+1]; /* The buffer used when reading the names of */ + /* users. */ + char envnam[ENV_LEN+1]; /* The buffer used when reading the names of */ + /* environment variables. */ +}; + +static int cf_expand_home_dir(CompleteFile *cf, const char *user); +static int cf_complete_username(CompleteFile *cf, WordCompletion *cpl, + const char *prefix, const char *line, + int word_start, int word_end, int escaped); +static HOME_DIR_FN(cf_homedir_callback); +static int cf_complete_entry(CompleteFile *cf, WordCompletion *cpl, + const char *line, int word_start, int word_end, + int escaped, CplCheckFn *check_fn, + void *check_data); +static char *cf_read_name(CompleteFile *cf, const char *type, + const char *string, int slen, + char *nambuf, int nammax); +static int cf_prepare_suffix(CompleteFile *cf, const char *suffix, + int add_escapes); + +/* + * A stack based object of the following type is used to pass data to the + * cf_homedir_callback() function. + */ +typedef struct { + CompleteFile *cf; /* The file-completion resource object */ + WordCompletion *cpl; /* The string-completion rsource object */ + size_t prefix_len; /* The length of the prefix being completed */ + const char *line; /* The line from which the prefix was extracted */ + int word_start; /* The index in line[] of the start of the username */ + int word_end; /* The index in line[] following the end of the prefix */ + int escaped; /* If true, add escapes to the completion suffixes */ +} CfHomeArgs; + +/*....................................................................... + * Create a new file-completion object. + * + * Output: + * return CompleteFile * The new object, or NULL on error. + */ +CompleteFile *_new_CompleteFile(void) +{ + CompleteFile *cf; /* The object to be returned */ +/* + * Allocate the container. + */ + cf = (CompleteFile *) malloc(sizeof(CompleteFile)); + if(!cf) { + errno = ENOMEM; + return NULL; + }; +/* + * Before attempting any operation that might fail, initialize the + * container at least up to the point at which it can safely be passed + * to _del_CompleteFile(). + */ + cf->err = NULL; + cf->dr = NULL; + cf->home = NULL; + cf->path = NULL; + cf->buff = NULL; + cf->usrnam[0] = '\0'; + cf->envnam[0] = '\0'; +/* + * Allocate a place to record error messages. + */ + cf->err = _new_ErrMsg(); + if(!cf->err) + return _del_CompleteFile(cf); +/* + * Create the object that is used for reading directories. + */ + cf->dr = _new_DirReader(); + if(!cf->dr) + return _del_CompleteFile(cf); +/* + * Create the object that is used to lookup home directories. + */ + cf->home = _new_HomeDir(); + if(!cf->home) + return _del_CompleteFile(cf); +/* + * Create the buffer in which the completed pathname is accumulated. + */ + cf->path = _new_PathName(); + if(!cf->path) + return _del_CompleteFile(cf); +/* + * Create a pathname work buffer. + */ + cf->buff = _new_PathName(); + if(!cf->buff) + return _del_CompleteFile(cf); + return cf; +} + +/*....................................................................... + * Delete a file-completion object. + * + * Input: + * cf CompleteFile * The object to be deleted. + * Output: + * return CompleteFile * The deleted object (always NULL). + */ +CompleteFile *_del_CompleteFile(CompleteFile *cf) +{ + if(cf) { + cf->err = _del_ErrMsg(cf->err); + cf->dr = _del_DirReader(cf->dr); + cf->home = _del_HomeDir(cf->home); + cf->path = _del_PathName(cf->path); + cf->buff = _del_PathName(cf->buff); + free(cf); + }; + return NULL; +} + +/*....................................................................... + * Look up the possible completions of the incomplete filename that + * lies between specified indexes of a given command-line string. + * + * Input: + * cpl WordCompletion * The object in which to record the completions. + * cf CompleteFile * The filename-completion resource object. + * line const char * The string containing the incomplete filename. + * word_start int The index of the first character in line[] + * of the incomplete filename. + * word_end int The index of the character in line[] that + * follows the last character of the incomplete + * filename. + * escaped int If true, backslashes in line[] are + * interpreted as escaping the characters + * that follow them, and any spaces, tabs, + * backslashes, or wildcard characters in the + * returned suffixes will be similarly escaped. + * If false, backslashes will be interpreted as + * literal parts of the file name, and no + * backslashes will be added to the returned + * suffixes. + * check_fn CplCheckFn * If not zero, this argument specifies a + * function to call to ask whether a given + * file should be included in the list + * of completions. + * check_data void * Anonymous data to be passed to check_fn(). + * Output: + * return int 0 - OK. + * 1 - Error. A description of the error can be + * acquired by calling _cf_last_error(cf). + */ +int _cf_complete_file(WordCompletion *cpl, CompleteFile *cf, + const char *line, int word_start, int word_end, + int escaped, CplCheckFn *check_fn, void *check_data) +{ + const char *lptr; /* A pointer into line[] */ + int nleft; /* The number of characters still to be processed */ + /* in line[]. */ +/* + * Check the arguments. + */ + if(!cpl || !cf || !line || word_end < word_start) { + if(cf) { + _err_record_msg(cf->err, "_cf_complete_file: Invalid arguments", + END_ERR_MSG); + }; + return 1; + }; +/* + * Clear the buffer in which the filename will be constructed. + */ + _pn_clear_path(cf->path); +/* + * How many characters are to be processed? + */ + nleft = word_end - word_start; +/* + * Get a pointer to the start of the incomplete filename. + */ + lptr = line + word_start; +/* + * If the first character is a tilde, then perform home-directory + * interpolation. + */ + if(nleft > 0 && *lptr == '~') { + int slen; + if(!cf_read_name(cf, "User", ++lptr, --nleft, cf->usrnam, USR_LEN)) + return 1; +/* + * Advance over the username in the input line. + */ + slen = strlen(cf->usrnam); + lptr += slen; + nleft -= slen; +/* + * If we haven't hit the end of the input string then we have a complete + * username to translate to the corresponding home directory. + */ + if(nleft > 0) { + if(cf_expand_home_dir(cf, cf->usrnam)) + return 1; +/* + * ~user and ~ are usually followed by a directory separator to + * separate them from the file contained in the home directory. + * If the home directory is the root directory, then we don't want + * to follow the home directory by a directory separator, so we should + * skip over it so that it doesn't get copied into the filename. + */ + if(strcmp(cf->path->name, FS_ROOT_DIR) == 0 && + strncmp(lptr, FS_DIR_SEP, FS_DIR_SEP_LEN) == 0) { + lptr += FS_DIR_SEP_LEN; + nleft -= FS_DIR_SEP_LEN; + }; +/* + * If we have reached the end of the input string, then the username + * may be incomplete, and we should attempt to complete it. + */ + } else { +/* + * Look up the possible completions of the username. + */ + return cf_complete_username(cf, cpl, cf->usrnam, line, word_start+1, + word_end, escaped); + }; + }; +/* + * Copy the rest of the path, stopping to expand $envvar expressions + * where encountered. + */ + while(nleft > 0) { + int seglen; /* The length of the next segment to be copied */ +/* + * Find the length of the next segment to be copied, stopping if an + * unescaped '$' is seen, or the end of the path is reached. + */ + for(seglen=0; seglen < nleft; seglen++) { + int c = lptr[seglen]; + if(escaped && c == '\\') + seglen++; + else if(c == '$') + break; +/* + * We will be completing the last component of the file name, + * so whenever a directory separator is seen, assume that it + * might be the start of the last component, and mark the character + * that follows it as the start of the name that is to be completed. + */ + if(nleft >= FS_DIR_SEP_LEN && + strncmp(lptr + seglen, FS_DIR_SEP, FS_DIR_SEP_LEN)==0) { + word_start = (lptr + seglen) - line + FS_DIR_SEP_LEN; + }; + }; +/* + * We have reached either the end of the filename or the start of + * $environment_variable expression. Record the newly checked + * segment of the filename in the output filename, removing + * backslash-escapes where needed. + */ + if(_pn_append_to_path(cf->path, lptr, seglen, escaped) == NULL) { + _err_record_msg(cf->err, "Insufficient memory to complete filename", + END_ERR_MSG); + return 1; + }; + lptr += seglen; + nleft -= seglen; +/* + * If the above loop finished before we hit the end of the filename, + * then this was because an unescaped $ was seen. In this case, interpolate + * the value of the environment variable that follows it into the output + * filename. + */ + if(nleft > 0) { + char *value; /* The value of the environment variable */ + int vlen; /* The length of the value string */ + int nlen; /* The length of the environment variable name */ +/* + * Read the name of the environment variable. + */ + if(!cf_read_name(cf, "Environment", ++lptr, --nleft, cf->envnam, ENV_LEN)) + return 1; +/* + * Advance over the environment variable name in the input line. + */ + nlen = strlen(cf->envnam); + lptr += nlen; + nleft -= nlen; +/* + * Get the value of the environment variable. + */ + value = getenv(cf->envnam); + if(!value) { + _err_record_msg(cf->err, "Unknown environment variable: ", cf->envnam, + END_ERR_MSG); + return 1; + }; + vlen = strlen(value); +/* + * If we are at the start of the filename and the first character of the + * environment variable value is a '~', attempt home-directory + * interpolation. + */ + if(cf->path->name[0] == '\0' && value[0] == '~') { + if(!cf_read_name(cf, "User", value+1, vlen-1, cf->usrnam, USR_LEN) || + cf_expand_home_dir(cf, cf->usrnam)) + return 1; +/* + * If the home directory is the root directory, and the ~usrname expression + * was followed by a directory separator, prevent the directory separator + * from being appended to the root directory by skipping it in the + * input line. + */ + if(strcmp(cf->path->name, FS_ROOT_DIR) == 0 && + strncmp(lptr, FS_DIR_SEP, FS_DIR_SEP_LEN) == 0) { + lptr += FS_DIR_SEP_LEN; + nleft -= FS_DIR_SEP_LEN; + }; + } else { +/* + * Append the value of the environment variable to the output path. + */ + if(_pn_append_to_path(cf->path, value, strlen(value), escaped)==NULL) { + _err_record_msg(cf->err, "Insufficient memory to complete filename", + END_ERR_MSG); + return 1; + }; +/* + * Prevent extra directory separators from being added. + */ + if(nleft >= FS_DIR_SEP_LEN && + strcmp(cf->path->name, FS_ROOT_DIR) == 0 && + strncmp(lptr, FS_DIR_SEP, FS_DIR_SEP_LEN) == 0) { + lptr += FS_DIR_SEP_LEN; + nleft -= FS_DIR_SEP_LEN; + } else if(vlen > FS_DIR_SEP_LEN && + strcmp(value + vlen - FS_DIR_SEP_LEN, FS_DIR_SEP)==0) { + cf->path->name[vlen-FS_DIR_SEP_LEN] = '\0'; + }; + }; +/* + * If adding the environment variable didn't form a valid directory, + * we can't complete the line, since there is no way to separate append + * a partial filename to an environment variable reference without + * that appended part of the name being seen later as part of the + * environment variable name. Thus if the currently constructed path + * isn't a directory, quite now with no completions having been + * registered. + */ + if(!_pu_path_is_dir(cf->path->name)) + return 0; +/* + * For the reasons given above, if we have reached the end of the filename + * with the expansion of an environment variable, the only allowed + * completion involves the addition of a directory separator. + */ + if(nleft == 0) { + if(cpl_add_completion(cpl, line, lptr-line, word_end, FS_DIR_SEP, + "", "")) { + _err_record_msg(cf->err, cpl_last_error(cpl), END_ERR_MSG); + return 1; + }; + return 0; + }; + }; + }; +/* + * Complete the filename if possible. + */ + return cf_complete_entry(cf, cpl, line, word_start, word_end, escaped, + check_fn, check_data); +} + +/*....................................................................... + * Return a description of the last path-completion error that occurred. + * + * Input: + * cf CompleteFile * The path-completion resource object. + * Output: + * return const char * The description of the last error. + */ +const char *_cf_last_error(CompleteFile *cf) +{ + return cf ? _err_get_msg(cf->err) : "NULL CompleteFile argument"; +} + +/*....................................................................... + * Lookup the home directory of the specified user, or the current user + * if no name is specified, appending it to output pathname. + * + * Input: + * cf CompleteFile * The pathname completion resource object. + * user const char * The username to lookup, or "" to lookup the + * current user. + * Output: + * return int 0 - OK. + * 1 - Error. + */ +static int cf_expand_home_dir(CompleteFile *cf, const char *user) +{ +/* + * Attempt to lookup the home directory. + */ + const char *home_dir = _hd_lookup_home_dir(cf->home, user); +/* + * Failed? + */ + if(!home_dir) { + _err_record_msg(cf->err, _hd_last_home_dir_error(cf->home), END_ERR_MSG); + return 1; + }; +/* + * Append the home directory to the pathname string. + */ + if(_pn_append_to_path(cf->path, home_dir, -1, 0) == NULL) { + _err_record_msg(cf->err, "Insufficient memory for home directory expansion", + END_ERR_MSG); + return 1; + }; + return 0; +} + +/*....................................................................... + * Lookup and report all completions of a given username prefix. + * + * Input: + * cf CompleteFile * The filename-completion resource object. + * cpl WordCompletion * The object in which to record the completions. + * prefix const char * The prefix of the usernames to lookup. + * line const char * The command-line in which the username appears. + * word_start int The index within line[] of the start of the + * username that is being completed. + * word_end int The index within line[] of the character which + * follows the incomplete username. + * escaped int True if the completions need to have special + * characters escaped. + * Output: + * return int 0 - OK. + * 1 - Error. + */ +static int cf_complete_username(CompleteFile *cf, WordCompletion *cpl, + const char *prefix, const char *line, + int word_start, int word_end, int escaped) +{ +/* + * Set up a container of anonymous arguments to be sent to the + * username-lookup iterator. + */ + CfHomeArgs args; + args.cf = cf; + args.cpl = cpl; + args.prefix_len = strlen(prefix); + args.line = line; + args.word_start = word_start; + args.word_end = word_end; + args.escaped = escaped; +/* + * Iterate through the list of users, recording those which start + * with the specified prefix. + */ + if(_hd_scan_user_home_dirs(cf->home, prefix, &args, cf_homedir_callback)) { + _err_record_msg(cf->err, _hd_last_home_dir_error(cf->home), END_ERR_MSG); + return 1; + }; + return 0; +} + +/*....................................................................... + * The user/home-directory scanner callback function (see homedir.h) + * used by cf_complete_username(). + */ +static HOME_DIR_FN(cf_homedir_callback) +{ +/* + * Get the file-completion resources from the anonymous data argument. + */ + CfHomeArgs *args = (CfHomeArgs *) data; + WordCompletion *cpl = args->cpl; + CompleteFile *cf = args->cf; +/* + * Copy the username into the pathname work buffer, adding backslash + * escapes where needed. + */ + if(cf_prepare_suffix(cf, usrnam+args->prefix_len, args->escaped)) { + strncpy(errmsg, _err_get_msg(cf->err), maxerr); + errmsg[maxerr] = '\0'; + return 1; + }; +/* + * Report the completion suffix that was copied above. + */ + if(cpl_add_completion(cpl, args->line, args->word_start, args->word_end, + cf->buff->name, FS_DIR_SEP, FS_DIR_SEP)) { + strncpy(errmsg, cpl_last_error(cpl), maxerr); + errmsg[maxerr] = '\0'; + return 1; + }; + return 0; +} + +/*....................................................................... + * Report possible completions of the filename in cf->path->name[]. + * + * Input: + * cf CompleteFile * The file-completion resource object. + * cpl WordCompletion * The object in which to record the completions. + * line const char * The input line, as received by the callback + * function. + * word_start int The index within line[] of the start of the + * last component of the filename that is being + * completed. + * word_end int The index within line[] of the character which + * follows the incomplete filename. + * escaped int If true, escape special characters in the + * completion suffixes. + * check_fn CplCheckFn * If not zero, this argument specifies a + * function to call to ask whether a given + * file should be included in the list + * of completions. + * check_data void * Anonymous data to be passed to check_fn(). + * Output: + * return int 0 - OK. + * 1 - Error. + */ +static int cf_complete_entry(CompleteFile *cf, WordCompletion *cpl, + const char *line, int word_start, int word_end, + int escaped, CplCheckFn *check_fn, + void *check_data) +{ + const char *dirpath; /* The name of the parent directory */ + int start; /* The index of the start of the last filename */ + /* component in the transcribed filename. */ + const char *prefix; /* The filename prefix to be completed */ + int prefix_len; /* The length of the filename prefix */ + const char *file_name; /* The lastest filename being compared */ + int waserr = 0; /* True after errors */ + int terminated=0; /* True if the directory part had to be terminated */ +/* + * Get the pathname string and its current length. + */ + char *pathname = cf->path->name; + int pathlen = strlen(pathname); +/* + * Locate the start of the final component of the pathname. + */ + for(start=pathlen - 1; start >= 0 && + strncmp(pathname + start, FS_DIR_SEP, FS_DIR_SEP_LEN) != 0; start--) + ; +/* + * Is the parent directory the root directory? + */ + if(start==0 || + (start < 0 && strncmp(pathname, FS_ROOT_DIR, FS_ROOT_DIR_LEN) == 0)) { + dirpath = FS_ROOT_DIR; + start += FS_ROOT_DIR_LEN; +/* + * If we found a directory separator then the part which precedes the + * last component is the name of the directory to be opened. + */ + } else if(start > 0) { +/* + * The _dr_open_dir() function requires the directory name to be '\0' + * terminated, so temporarily do this by overwriting the first character + * of the directory separator. + */ + pathname[start] = '\0'; + dirpath = pathname; + terminated = 1; +/* + * We reached the start of the pathname before finding a directory + * separator, so arrange to open the current working directory. + */ + } else { + start = 0; + dirpath = FS_PWD; + }; +/* + * Attempt to open the directory. + */ + if(_dr_open_dir(cf->dr, dirpath, NULL)) { + _err_record_msg(cf->err, "Can't open directory: ", dirpath, END_ERR_MSG); + return 1; + }; +/* + * If removed above, restore the directory separator and skip over it + * to the start of the filename. + */ + if(terminated) { + memcpy(pathname + start, FS_DIR_SEP, FS_DIR_SEP_LEN); + start += FS_DIR_SEP_LEN; + }; +/* + * Get the filename prefix and its length. + */ + prefix = pathname + start; + prefix_len = strlen(prefix); +/* + * Traverse the directory, looking for files who's prefixes match the + * last component of the pathname. + */ + while((file_name = _dr_next_file(cf->dr)) != NULL && !waserr) { + int name_len = strlen(file_name); +/* + * Is the latest filename a possible completion of the filename prefix? + */ + if(name_len >= prefix_len && strncmp(prefix, file_name, prefix_len)==0) { +/* + * When listing all files in a directory, don't list files that start + * with '.'. This is how hidden files are denoted in UNIX. + */ + if(prefix_len > 0 || file_name[0] != '.') { +/* + * Copy the completion suffix into the work pathname cf->buff->name, + * adding backslash escapes if needed. + */ + if(cf_prepare_suffix(cf, file_name + prefix_len, escaped)) { + waserr = 1; + } else { +/* + * We want directories to be displayed with directory suffixes, + * and other fully completed filenames to be followed by spaces. + * To check the type of the file, append the current suffix + * to the path being completed, check the filetype, then restore + * the path to its original form. + */ + const char *cont_suffix = ""; /* The suffix to add if fully */ + /* completed. */ + const char *type_suffix = ""; /* The suffix to add when listing */ + if(_pn_append_to_path(cf->path, file_name + prefix_len, + -1, escaped) == NULL) { + _err_record_msg(cf->err, + "Insufficient memory to complete filename.", + END_ERR_MSG); + return 1; + }; +/* + * Specify suffixes according to the file type. + */ + if(_pu_path_is_dir(cf->path->name)) { + cont_suffix = FS_DIR_SEP; + type_suffix = FS_DIR_SEP; + } else if(!check_fn || check_fn(check_data, cf->path->name)) { + cont_suffix = " "; + } else { + cf->path->name[pathlen] = '\0'; + continue; + }; +/* + * Remove the temporarily added suffix. + */ + cf->path->name[pathlen] = '\0'; +/* + * Record the latest completion. + */ + if(cpl_add_completion(cpl, line, word_start, word_end, cf->buff->name, + type_suffix, cont_suffix)) + waserr = 1; + }; + }; + }; + }; +/* + * Close the directory. + */ + _dr_close_dir(cf->dr); + return waserr; +} + +/*....................................................................... + * Read a username or environment variable name, stopping when a directory + * separator is seen, when the end of the string is reached, or the + * output buffer overflows. + * + * Input: + * cf CompleteFile * The file-completion resource object. + * type char * The capitalized name of the type of name being read. + * string char * The string who's prefix contains the name. + * slen int The number of characters in string[]. + * nambuf char * The output name buffer. + * nammax int The longest string that will fit in nambuf[], excluding + * the '\0' terminator. + * Output: + * return char * A pointer to nambuf on success. On error NULL is + * returned and a description of the error is recorded + * in cf->err. + */ +static char *cf_read_name(CompleteFile *cf, const char *type, + const char *string, int slen, + char *nambuf, int nammax) +{ + int namlen; /* The number of characters in nambuf[] */ + const char *sptr; /* A pointer into string[] */ +/* + * Work out the max number of characters that should be copied. + */ + int nmax = nammax < slen ? nammax : slen; +/* + * Get the environment variable name that follows the dollar. + */ + for(sptr=string,namlen=0; + namlen < nmax && (slen-namlen < FS_DIR_SEP_LEN || + strncmp(sptr, FS_DIR_SEP, FS_DIR_SEP_LEN) != 0); + namlen++) { + nambuf[namlen] = *sptr++; + }; +/* + * Did the name overflow the buffer? + */ + if(namlen >= nammax) { + _err_record_msg(cf->err, type, " name too long", END_ERR_MSG); + return NULL; + }; +/* + * Terminate the string. + */ + nambuf[namlen] = '\0'; + return nambuf; +} + +/*....................................................................... + * Using the work buffer cf->buff, make a suitably escaped copy of a + * given completion suffix, ready to be passed to cpl_add_completion(). + * + * Input: + * cf CompleteFile * The file-completion resource object. + * suffix char * The suffix to be copied. + * add_escapes int If true, escape special characters. + * Output: + * return int 0 - OK. + * 1 - Error. + */ +static int cf_prepare_suffix(CompleteFile *cf, const char *suffix, + int add_escapes) +{ + const char *sptr; /* A pointer into suffix[] */ + int nbsl; /* The number of backslashes to add to the suffix */ + int i; +/* + * How long is the suffix? + */ + int suffix_len = strlen(suffix); +/* + * Clear the work buffer. + */ + _pn_clear_path(cf->buff); +/* + * Count the number of backslashes that will have to be added to + * escape spaces, tabs, backslashes and wildcard characters. + */ + nbsl = 0; + if(add_escapes) { + for(sptr = suffix; *sptr; sptr++) { + switch(*sptr) { + case ' ': case '\t': case '\\': case '*': case '?': case '[': + nbsl++; + break; + }; + }; + }; +/* + * Arrange for the output path buffer to have sufficient room for the + * both the suffix and any backslashes that have to be inserted. + */ + if(_pn_resize_path(cf->buff, suffix_len + nbsl) == NULL) { + _err_record_msg(cf->err, "Insufficient memory to complete filename", + END_ERR_MSG); + return 1; + }; +/* + * If the suffix doesn't need any escapes, copy it directly into the + * work buffer. + */ + if(nbsl==0) { + strcpy(cf->buff->name, suffix); + } else { +/* + * Make a copy with special characters escaped? + */ + if(nbsl > 0) { + const char *src = suffix; + char *dst = cf->buff->name; + for(i=0; i +#include +#include +#include + +/* + * Local includes. + */ +#include "libtecla.h" +#include "ioutil.h" +#include "stringrp.h" +#include "pathutil.h" +#include "cplfile.h" +#include "cplmatch.h" +#include "errmsg.h" + +/* + * Specify the number of strings to allocate when the string free-list + * is exhausted. This also sets the number of elements to expand the + * matches[] array by whenever it is found to be too small. + */ +#define STR_BLK_FACT 100 + +/* + * Set the default number of spaces place between columns when listing + * a set of completions. + */ +#define CPL_COL_SEP 2 + +/* + * Completion matches are recorded in containers of the following + * type. + */ +struct WordCompletion { + ErrMsg *err; /* The error reporting buffer */ + StringGroup *sg; /* Memory for a group of strings */ + int matches_dim; /* The allocated size of result.matches[] */ + CplMatches result; /* Completions to be returned to the caller */ +#ifndef WITHOUT_FILE_SYSTEM + CompleteFile *cf; /* The resources used for filename completion */ +#endif +}; + +static void cpl_sort_matches(WordCompletion *cpl); +static void cpl_zap_duplicates(WordCompletion *cpl); +static void cpl_clear_completions(WordCompletion *cpl); +static int cpl_cmp_matches(const void *v1, const void *v2); +static int cpl_cmp_suffixes(const void *v1, const void *v2); + +/* + * The new_CplFileConf() constructor sets the integer first member of + * the returned object to the following magic number. On seeing this, + * cpl_file_completions() knows when it is passed a valid CplFileConf + * object. + */ +#define CFC_ID_CODE 4568 + +#ifndef WITHOUT_FILE_SYSTEM +/* + * A pointer to a structure of the following type can be passed to + * the builtin file-completion callback function to modify its behavior. + */ +struct CplFileConf { + int id; /* new_CplFileConf() sets this to CFC_ID_CODE */ + int escaped; /* If none-zero, backslashes in the input line are */ + /* interpreted as escaping special characters and */ + /* spaces, and any special characters and spaces in */ + /* the listed completions will also be escaped with */ + /* added backslashes. This is the default behaviour. */ + /* If zero, backslashes are interpreted as being */ + /* literal parts of the filename, and none are added */ + /* to the completion suffixes. */ + int file_start; /* The index in the input line of the first character */ + /* of the filename. If you specify -1 here, */ + /* cpl_file_completions() identifies the */ + /* the start of the filename by looking backwards for */ + /* an unescaped space, or the beginning of the line. */ + CplCheckFn *chk_fn; /* If not zero, this argument specifies a */ + /* function to call to ask whether a given */ + /* file should be included in the list */ + /* of completions. */ + void *chk_data; /* Anonymous data to be passed to check_fn(). */ +}; + +static void cpl_init_FileConf(CplFileConf *cfc); + +/* + * When file-system access is being excluded, define a dummy structure + * to satisfy the typedef in libtecla.h. + */ +#else +struct CplFileConf {int dummy;}; +#endif + +/* + * Encapsulate the formatting information needed to layout a + * multi-column listing of completions. + */ +typedef struct { + int term_width; /* The width of the terminal (characters) */ + int column_width; /* The number of characters within in each column. */ + int ncol; /* The number of columns needed */ + int nline; /* The number of lines needed */ +} CplListFormat; + +/* + * Given the current terminal width, and a list of completions, determine + * how to best use the terminal width to display a multi-column listing + * of completions. + */ +static void cpl_plan_listing(CplMatches *result, int term_width, + CplListFormat *fmt); + +/* + * Display a given line of a multi-column list of completions. + */ +static int cpl_format_line(CplMatches *result, CplListFormat *fmt, int lnum, + GlWriteFn *write_fn, void *data); + +/*....................................................................... + * Create a new string-completion object. + * + * Output: + * return WordCompletion * The new object, or NULL on error. + */ +WordCompletion *new_WordCompletion(void) +{ + WordCompletion *cpl; /* The object to be returned */ +/* + * Allocate the container. + */ + cpl = (WordCompletion *) malloc(sizeof(WordCompletion)); + if(!cpl) { + errno = ENOMEM; + return NULL; + }; +/* + * Before attempting any operation that might fail, initialize the + * container at least up to the point at which it can safely be passed + * to del_WordCompletion(). + */ + cpl->err = NULL; + cpl->sg = NULL; + cpl->matches_dim = 0; + cpl->result.suffix = NULL; + cpl->result.cont_suffix = NULL; + cpl->result.matches = NULL; + cpl->result.nmatch = 0; +#ifndef WITHOUT_FILE_SYSTEM + cpl->cf = NULL; +#endif +/* + * Allocate a place to record error messages. + */ + cpl->err = _new_ErrMsg(); + if(!cpl->err) + return del_WordCompletion(cpl); +/* + * Allocate an object that allows a group of strings to be allocated + * efficiently by placing many of them in contiguous string segments. + */ +#ifdef WITHOUT_FILE_SYSTEM + cpl->sg = _new_StringGroup(MAX_PATHLEN_FALLBACK); +#else + cpl->sg = _new_StringGroup(_pu_pathname_dim()); +#endif + if(!cpl->sg) + return del_WordCompletion(cpl); +/* + * Allocate an array for matching completions. This will be extended later + * if needed. + */ + cpl->matches_dim = STR_BLK_FACT; + cpl->result.matches = (CplMatch *) malloc(sizeof(cpl->result.matches[0]) * + cpl->matches_dim); + if(!cpl->result.matches) { + errno = ENOMEM; + return del_WordCompletion(cpl); + }; +/* + * Allocate a filename-completion resource object. + */ +#ifndef WITHOUT_FILE_SYSTEM + cpl->cf = _new_CompleteFile(); + if(!cpl->cf) + return del_WordCompletion(cpl); +#endif + return cpl; +} + +/*....................................................................... + * Delete a string-completion object. + * + * Input: + * cpl WordCompletion * The object to be deleted. + * Output: + * return WordCompletion * The deleted object (always NULL). + */ +WordCompletion *del_WordCompletion(WordCompletion *cpl) +{ + if(cpl) { + cpl->err = _del_ErrMsg(cpl->err); + cpl->sg = _del_StringGroup(cpl->sg); + if(cpl->result.matches) { + free(cpl->result.matches); + cpl->result.matches = NULL; +#ifndef WITHOUT_FILE_SYSTEM + cpl->cf = _del_CompleteFile(cpl->cf); +#endif + }; + free(cpl); + }; + return NULL; +} + +/*....................................................................... + * This function is designed to be called by CplMatchFn callback + * functions. It adds one possible completion of the token that is being + * completed to an array of completions. If the completion needs any + * special quoting to be valid when displayed in the input line, this + * quoting must be included in the string. + * + * Input: + * cpl WordCompletion * The argument of the same name that was passed + * to the calling CplMatchFn callback function. + * line const char * The input line, as received by the callback + * function. + * word_start int The index within line[] of the start of the + * word that is being completed. + * word_end int The index within line[] of the character which + * follows the incomplete word, as received by the + * calling callback function. + * suffix const char * The appropriately quoted string that could + * be appended to the incomplete token to complete + * it. A copy of this string will be allocated + * internally. + * type_suffix const char * When listing multiple completions, gl_get_line() + * appends this string to the completion to indicate + * its type to the user. If not pertinent pass "". + * Otherwise pass a literal or static string. + * cont_suffix const char * If this turns out to be the only completion, + * gl_get_line() will append this string as + * a continuation. For example, the builtin + * file-completion callback registers a directory + * separator here for directory matches, and a + * space otherwise. If the match were a function + * name you might want to append an open + * parenthesis, etc.. If not relevant pass "". + * Otherwise pass a literal or static string. + * Output: + * return int 0 - OK. + * 1 - Error. + */ +int cpl_add_completion(WordCompletion *cpl, const char *line, + int word_start, int word_end, const char *suffix, + const char *type_suffix, const char *cont_suffix) +{ + CplMatch *match; /* The container of the new match */ + char *string; /* A newly allocated copy of the completion string */ +/* + * Check the arguments. + */ + if(!cpl) + return 1; + if(!suffix) + return 0; + if(!type_suffix) + type_suffix = ""; + if(!cont_suffix) + cont_suffix = ""; +/* + * Do we need to extend the array of matches[]? + */ + if(cpl->result.nmatch+1 > cpl->matches_dim) { + int needed = cpl->matches_dim + STR_BLK_FACT; + CplMatch *matches = (CplMatch *) realloc(cpl->result.matches, + sizeof(cpl->result.matches[0]) * needed); + if(!matches) { + _err_record_msg(cpl->err, + "Insufficient memory to extend array of matches.", + END_ERR_MSG); + return 1; + }; + cpl->result.matches = matches; + cpl->matches_dim = needed; + }; +/* + * Allocate memory to store the combined completion prefix and the + * new suffix. + */ + string = _sg_alloc_string(cpl->sg, word_end-word_start + strlen(suffix)); + if(!string) { + _err_record_msg(cpl->err, "Insufficient memory to extend array of matches.", + END_ERR_MSG); + return 1; + }; +/* + * Compose the string. + */ + strncpy(string, line + word_start, word_end - word_start); + strcpy(string + word_end - word_start, suffix); +/* + * Record the new match. + */ + match = cpl->result.matches + cpl->result.nmatch++; + match->completion = string; + match->suffix = string + word_end - word_start; + match->type_suffix = type_suffix; +/* + * Record the continuation suffix. + */ + cpl->result.cont_suffix = cont_suffix; + return 0; +} + +/*....................................................................... + * Sort the array of matches. + * + * Input: + * cpl WordCompletion * The completion resource object. + */ +static void cpl_sort_matches(WordCompletion *cpl) +{ + qsort(cpl->result.matches, cpl->result.nmatch, + sizeof(cpl->result.matches[0]), cpl_cmp_matches); +} + +/*....................................................................... + * This is a qsort() comparison function used to sort matches. + * + * Input: + * v1, v2 void * Pointers to the two matches to be compared. + * Output: + * return int -1 -> v1 < v2. + * 0 -> v1 == v2 + * 1 -> v1 > v2 + */ +static int cpl_cmp_matches(const void *v1, const void *v2) +{ + const CplMatch *m1 = (const CplMatch *) v1; + const CplMatch *m2 = (const CplMatch *) v2; + return strcmp(m1->completion, m2->completion); +} + +/*....................................................................... + * Sort the array of matches in order of their suffixes. + * + * Input: + * cpl WordCompletion * The completion resource object. + */ +static void cpl_sort_suffixes(WordCompletion *cpl) +{ + qsort(cpl->result.matches, cpl->result.nmatch, + sizeof(cpl->result.matches[0]), cpl_cmp_suffixes); +} + +/*....................................................................... + * This is a qsort() comparison function used to sort matches in order of + * their suffixes. + * + * Input: + * v1, v2 void * Pointers to the two matches to be compared. + * Output: + * return int -1 -> v1 < v2. + * 0 -> v1 == v2 + * 1 -> v1 > v2 + */ +static int cpl_cmp_suffixes(const void *v1, const void *v2) +{ + const CplMatch *m1 = (const CplMatch *) v1; + const CplMatch *m2 = (const CplMatch *) v2; + return strcmp(m1->suffix, m2->suffix); +} + +/*....................................................................... + * Find the common prefix of all of the matching completion matches, + * and record a pointer to it in cpl->result.suffix. Note that this has + * the side effect of sorting the matches into suffix order. + * + * Input: + * cpl WordCompletion * The completion resource object. + * Output: + * return int 0 - OK. + * 1 - Error. + */ +static int cpl_common_suffix(WordCompletion *cpl) +{ + CplMatches *result; /* The result container */ + const char *first, *last; /* The first and last matching suffixes */ + int length; /* The length of the common suffix */ +/* + * Get the container of the array of matching files. + */ + result = &cpl->result; +/* + * No matching completions? + */ + if(result->nmatch < 1) + return 0; +/* + * Sort th matches into suffix order. + */ + cpl_sort_suffixes(cpl); +/* + * Given that the array of matches is sorted, the first and last + * suffixes are those that differ most in their prefixes, so the common + * prefix of these strings is the longest common prefix of all of the + * suffixes. + */ + first = result->matches[0].suffix; + last = result->matches[result->nmatch - 1].suffix; +/* + * Find the point at which the first and last matching strings + * first difffer. + */ + while(*first && *first == *last) { + first++; + last++; + }; +/* + * How long is the common suffix? + */ + length = first - result->matches[0].suffix; +/* + * Allocate memory to record the common suffix. + */ + result->suffix = _sg_alloc_string(cpl->sg, length); + if(!result->suffix) { + _err_record_msg(cpl->err, + "Insufficient memory to record common completion suffix.", + END_ERR_MSG); + return 1; + }; +/* + * Record the common suffix. + */ + strncpy(result->suffix, result->matches[0].suffix, length); + result->suffix[length] = '\0'; + return 0; +} + +/*....................................................................... + * Discard the contents of the array of possible completion matches. + * + * Input: + * cpl WordCompletion * The word-completion resource object. + */ +static void cpl_clear_completions(WordCompletion *cpl) +{ +/* + * Discard all of the strings. + */ + _clr_StringGroup(cpl->sg); +/* + * Record the fact that the array is now empty. + */ + cpl->result.nmatch = 0; + cpl->result.suffix = NULL; + cpl->result.cont_suffix = ""; +/* + * Also clear the error message. + */ + _err_clear_msg(cpl->err); + return; +} + +/*....................................................................... + * Given an input line and the point at which it completion is to be + * attempted, return an array of possible completions. + * + * Input: + * cpl WordCompletion * The completion resource object. + * line char * The current input line. + * word_end int The index of the character in line[] which + * follows the end of the token that is being + * completed. + * data void * Anonymous 'data' to be passed to match_fn(). + * match_fn CplMatchFn * The function that will identify the prefix + * to be completed from the input line, and + * record completion matches. + * Output: + * return CplMatches * The container of the array of possible + * completions. The returned pointer refers + * to a container owned by the parent WordCompletion + * object, and its contents thus potentially + * change on every call to cpl_matches(). + * On error, NULL is returned, and a description + * of the error can be acquired by calling + * cpl_last_error(cpl). + */ +CplMatches *cpl_complete_word(WordCompletion *cpl, const char *line, + int word_end, void *data, + CplMatchFn *match_fn) +{ + int line_len; /* The total length of the input line */ +/* + * How long is the input line? + */ + line_len = strlen(line); +/* + * Check the arguments. + */ + if(!cpl || !line || !match_fn || word_end < 0 || word_end > line_len) { + if(cpl) { + _err_record_msg(cpl->err, "cpl_complete_word: Invalid arguments.", + END_ERR_MSG); + }; + return NULL; + }; +/* + * Clear the return container. + */ + cpl_clear_completions(cpl); +/* + * Have the matching function record possible completion matches in + * cpl->result.matches. + */ + if(match_fn(cpl, data, line, word_end)) { + if(_err_get_msg(cpl->err)[0] == '\0') + _err_record_msg(cpl->err, "Error completing word.", END_ERR_MSG); + return NULL; + }; +/* + * Record a copy of the common initial part of all of the prefixes + * in cpl->result.common. + */ + if(cpl_common_suffix(cpl)) + return NULL; +/* + * Sort the matches into lexicographic order. + */ + cpl_sort_matches(cpl); +/* + * Discard any duplicate matches. + */ + cpl_zap_duplicates(cpl); +/* + * If there is more than one match, discard the continuation suffix. + */ + if(cpl->result.nmatch > 1) + cpl->result.cont_suffix = ""; +/* + * Return the array of matches. + */ + return &cpl->result; +} + +/*....................................................................... + * Recall the return value of the last call to cpl_complete_word(). + * + * Input: + * cpl WordCompletion * The completion resource object. + * Output: + * return CplMatches * The container of the array of possible + * completions, as returned by the last call to + * cpl_complete_word(). The returned pointer refers + * to a container owned by the parent WordCompletion + * object, and its contents thus potentially + * change on every call to cpl_complete_word(). + * On error, either in the execution of this + * function, or in the last call to + * cpl_complete_word(), NULL is returned, and a + * description of the error can be acquired by + * calling cpl_last_error(cpl). + */ +CplMatches *cpl_recall_matches(WordCompletion *cpl) +{ + return (!cpl || *_err_get_msg(cpl->err)!='\0') ? NULL : &cpl->result; +} + +/*....................................................................... + * Print out an array of matching completions. + * + * Input: + * result CplMatches * The container of the sorted array of + * completions. + * fp FILE * The output stream to write to. + * term_width int The width of the terminal. + * Output: + * return int 0 - OK. + * 1 - Error. + */ +int cpl_list_completions(CplMatches *result, FILE *fp, int term_width) +{ + return _cpl_output_completions(result, _io_write_stdio, fp, term_width); +} + +/*....................................................................... + * Print an array of matching completions via a callback function. + * + * Input: + * result CplMatches * The container of the sorted array of + * completions. + * write_fn GlWriteFn * The function to call to write the completions, + * or 0 to discard the output. + * data void * Anonymous data to pass to write_fn(). + * term_width int The width of the terminal. + * Output: + * return int 0 - OK. + * 1 - Error. + */ +int _cpl_output_completions(CplMatches *result, GlWriteFn *write_fn, void *data, + int term_width) +{ + CplListFormat fmt; /* List formatting information */ + int lnum; /* The sequential number of the line to print next */ +/* + * Not enough space to list anything? + */ + if(term_width < 1) + return 0; +/* + * Do we have a callback to write via, and any completions to be listed? + */ + if(write_fn && result && result->nmatch>0) { +/* + * Work out how to arrange the listing into fixed sized columns. + */ + cpl_plan_listing(result, term_width, &fmt); +/* + * Print the listing via the specified callback. + */ + for(lnum=0; lnum < fmt.nline; lnum++) { + if(cpl_format_line(result, &fmt, lnum, write_fn, data)) + return 1; + }; + }; + return 0; +} + +/*....................................................................... + * Return a description of the string-completion error that occurred. + * + * Input: + * cpl WordCompletion * The string-completion resource object. + * Output: + * return const char * The description of the last error. + */ +const char *cpl_last_error(WordCompletion *cpl) +{ + return cpl ? _err_get_msg(cpl->err) : "NULL WordCompletion argument"; +} + +/*....................................................................... + * When an error occurs while performing a completion, you registerf a + * terse description of the error by calling cpl_record_error(). This + * message will then be returned on the next call to cpl_last_error(). + * + * Input: + * cpl WordCompletion * The string-completion resource object that was + * originally passed to the callback. + * errmsg const char * The description of the error. + */ +void cpl_record_error(WordCompletion *cpl, const char *errmsg) +{ + if(cpl && errmsg) + _err_record_msg(cpl->err, errmsg, END_ERR_MSG); +} + +/*....................................................................... + * This is the builtin completion callback function which performs file + * completion. + * + * Input: + * cpl WordCompletion * An opaque pointer to the object that will + * contain the matches. This should be filled + * via zero or more calls to cpl_add_completion(). + * data void * Either NULL to request the default + * file-completion behavior, or a pointer to a + * CplFileConf structure, whose members specify + * a different behavior. + * line char * The current input line. + * word_end int The index of the character in line[] which + * follows the end of the token that is being + * completed. + * Output + * return int 0 - OK. + * 1 - Error. + */ +CPL_MATCH_FN(cpl_file_completions) +{ +#ifdef WITHOUT_FILE_SYSTEM + return 0; +#else + const char *start_path; /* The pointer to the start of the pathname */ + /* in line[]. */ + CplFileConf *conf; /* The new-style configuration object. */ +/* + * The following configuration object will be used if the caller didn't + * provide one. + */ + CplFileConf default_conf; +/* + * This function can be called externally, so check its arguments. + */ + if(!cpl) + return 1; + if(!line || word_end < 0) { + _err_record_msg(cpl->err, "cpl_file_completions: Invalid arguments.", + END_ERR_MSG); + return 1; + }; +/* + * The 'data' argument is either a CplFileConf pointer, identifiable + * by having an integer id code as its first member, or the deprecated + * CplFileArgs pointer, or can be NULL to request the default + * configuration. + */ + if(data && *(int *)data == CFC_ID_CODE) { + conf = (CplFileConf *) data; + } else { +/* + * Select the defaults. + */ + conf = &default_conf; + cpl_init_FileConf(&default_conf); +/* + * If we have been passed an instance of the deprecated CplFileArgs + * structure, copy its configuration parameters over the defaults. + */ + if(data) { + CplFileArgs *args = (CplFileArgs *) data; + conf->escaped = args->escaped; + conf->file_start = args->file_start; + }; + }; +/* + * Get the start of the filename. If not specified by the caller + * identify it by searching backwards in the input line for an + * unescaped space or the start of the line. + */ + if(conf->file_start < 0) { + start_path = _pu_start_of_path(line, word_end); + if(!start_path) { + _err_record_msg(cpl->err, "Unable to find the start of the filename.", + END_ERR_MSG); + return 1; + }; + } else { + start_path = line + conf->file_start; + }; +/* + * Perform the completion. + */ + if(_cf_complete_file(cpl, cpl->cf, line, start_path - line, word_end, + conf->escaped, conf->chk_fn, conf->chk_data)) { + cpl_record_error(cpl, _cf_last_error(cpl->cf)); + return 1; + }; + return 0; +#endif +} + +/*....................................................................... + * Initialize a CplFileArgs structure with default configuration + * parameters. Note that the CplFileArgs configuration type is + * deprecated. The opaque CplFileConf object should be used in future + * applications. + * + * Input: + * cfa CplFileArgs * The configuration object of the + * cpl_file_completions() callback. + */ +void cpl_init_FileArgs(CplFileArgs *cfa) +{ + if(cfa) { + cfa->escaped = 1; + cfa->file_start = -1; + }; +} + +#ifndef WITHOUT_FILE_SYSTEM +/*....................................................................... + * Initialize a CplFileConf structure with default configuration + * parameters. + * + * Input: + * cfc CplFileConf * The configuration object of the + * cpl_file_completions() callback. + */ +static void cpl_init_FileConf(CplFileConf *cfc) +{ + if(cfc) { + cfc->id = CFC_ID_CODE; + cfc->escaped = 1; + cfc->file_start = -1; + cfc->chk_fn = 0; + cfc->chk_data = NULL; + }; +} +#endif + +/*....................................................................... + * Create a new CplFileConf object and initialize it with defaults. + * + * Output: + * return CplFileConf * The new object, or NULL on error. + */ +CplFileConf *new_CplFileConf(void) +{ +#ifdef WITHOUT_FILE_SYSTEM + errno = EINVAL; + return NULL; +#else + CplFileConf *cfc; /* The object to be returned */ +/* + * Allocate the container. + */ + cfc = (CplFileConf *)malloc(sizeof(CplFileConf)); + if(!cfc) + return NULL; +/* + * Before attempting any operation that might fail, initialize the + * container at least up to the point at which it can safely be passed + * to del_CplFileConf(). + */ + cpl_init_FileConf(cfc); + return cfc; +#endif +} + +/*....................................................................... + * Delete a CplFileConf object. + * + * Input: + * cfc CplFileConf * The object to be deleted. + * Output: + * return CplFileConf * The deleted object (always NULL). + */ +CplFileConf *del_CplFileConf(CplFileConf *cfc) +{ +#ifndef WITHOUT_FILE_SYSTEM + if(cfc) { +/* + * Delete the container. + */ + free(cfc); + }; +#endif + return NULL; +} + +/*....................................................................... + * If backslashes in the filename should be treated as literal + * characters, call the following function with literal=1. Otherwise + * the default is to treat them as escape characters, used for escaping + * spaces etc.. + * + * Input: + * cfc CplFileConf * The cpl_file_completions() configuration object + * to be configured. + * literal int Pass non-zero here to enable literal interpretation + * of backslashes. Pass 0 to turn off literal + * interpretation. + */ +void cfc_literal_escapes(CplFileConf *cfc, int literal) +{ +#ifndef WITHOUT_FILE_SYSTEM + if(cfc) + cfc->escaped = !literal; +#endif +} + +/*....................................................................... + * Call this function if you know where the index at which the + * filename prefix starts in the input line. Otherwise by default, + * or if you specify start_index to be -1, the filename is taken + * to start after the first unescaped space preceding the cursor, + * or the start of the line, which ever comes first. + * + * Input: + * cfc CplFileConf * The cpl_file_completions() configuration object + * to be configured. + * start_index int The index of the start of the filename in + * the input line, or -1 to select the default. + */ +void cfc_file_start(CplFileConf *cfc, int start_index) +{ +#ifndef WITHOUT_FILE_SYSTEM + if(cfc) + cfc->file_start = start_index; +#endif +} + +/*....................................................................... + * If you only want certain types of files to be included in the + * list of completions, you use the following function to specify a + * callback function which will be called to ask whether a given file + * should be included. + * + * Input: + * cfc CplFileConf * The cpl_file_completions() configuration object + * to be configured. + * chk_fn CplCheckFn * Zero to disable filtering, or a pointer to a + * function that returns 1 if a given file should + * be included in the list of completions. + * chk_data void * Anonymous data to be passed to chk_fn() + * every time that it is called. + */ +void cfc_set_check_fn(CplFileConf *cfc, CplCheckFn *chk_fn, void *chk_data) +{ +#ifndef WITHOUT_FILE_SYSTEM + if(cfc) { + cfc->chk_fn = chk_fn; + cfc->chk_data = chk_data; + }; +#endif +} + +/*....................................................................... + * The following CplCheckFn callback returns non-zero if the specified + * filename is that of an executable. + */ +CPL_CHECK_FN(cpl_check_exe) +{ +#ifdef WITHOUT_FILE_SYSTEM + return 0; +#else + return _pu_path_is_exe(pathname); +#endif +} + +/*....................................................................... + * Remove duplicates from a sorted array of matches. + * + * Input: + * cpl WordCompletion * The completion resource object. + */ +static void cpl_zap_duplicates(WordCompletion *cpl) +{ + CplMatch *matches; /* The array of matches */ + int nmatch; /* The number of elements in matches[] */ + const char *completion; /* The completion string of the last unique match */ + const char *type_suffix; /* The type of the last unique match */ + int src; /* The index of the match being considered */ + int dst; /* The index at which to record the next */ + /* unique match. */ +/* + * Get the array of matches and the number of matches that it + * contains. + */ + matches = cpl->result.matches; + nmatch = cpl->result.nmatch; +/* + * No matches? + */ + if(nmatch < 1) + return; +/* + * Initialize the comparison strings with the first match. + */ + completion = matches[0].completion; + type_suffix = matches[0].type_suffix; +/* + * Go through the array of matches, copying each new unrecorded + * match at the head of the array, while discarding duplicates. + */ + for(src=dst=1; srccompletion) != 0 || + strcmp(type_suffix, match->type_suffix) != 0) { + if(src != dst) + matches[dst] = *match; + dst++; + completion = match->completion; + type_suffix = match->type_suffix; + }; + }; +/* + * Record the number of unique matches that remain. + */ + cpl->result.nmatch = dst; + return; +} + +/*....................................................................... + * Work out how to arrange a given array of completions into a listing + * of one or more fixed size columns. + * + * Input: + * result CplMatches * The set of completions to be listed. + * term_width int The width of the terminal. A lower limit of + * zero is quietly enforced. + * Input/Output: + * fmt CplListFormat * The formatting information will be assigned + * to the members of *fmt. + */ +static void cpl_plan_listing(CplMatches *result, int term_width, + CplListFormat *fmt) +{ + int maxlen; /* The length of the longest matching string */ + int i; +/* + * Ensure that term_width >= 0. + */ + if(term_width < 0) + term_width = 0; +/* + * Start by assuming the worst case, that either nothing will fit + * on the screen, or that there are no matches to be listed. + */ + fmt->term_width = term_width; + fmt->column_width = 0; + fmt->nline = fmt->ncol = 0; +/* + * Work out the maximum length of the matching strings. + */ + maxlen = 0; + for(i=0; inmatch; i++) { + CplMatch *match = result->matches + i; + int len = strlen(match->completion) + strlen(match->type_suffix); + if(len > maxlen) + maxlen = len; + }; +/* + * Nothing to list? + */ + if(maxlen == 0) + return; +/* + * Split the available terminal width into columns of + * maxlen + CPL_COL_SEP characters. + */ + fmt->column_width = maxlen; + fmt->ncol = fmt->term_width / (fmt->column_width + CPL_COL_SEP); +/* + * If the column width is greater than the terminal width, zero columns + * will have been selected. Set a lower limit of one column. Leave it + * up to the caller how to deal with completions who's widths exceed + * the available terminal width. + */ + if(fmt->ncol < 1) + fmt->ncol = 1; +/* + * How many lines of output will be needed? + */ + fmt->nline = (result->nmatch + fmt->ncol - 1) / fmt->ncol; + return; +} + +/*....................................................................... + * Render one line of a multi-column listing of completions, using a + * callback function to pass the output to an arbitrary destination. + * + * Input: + * result CplMatches * The container of the sorted array of + * completions. + * fmt CplListFormat * Formatting information. + * lnum int The index of the line to print, starting + * from 0, and incrementing until the return + * value indicates that there is nothing more + * to be printed. + * write_fn GlWriteFn * The function to call to write the line, or + * 0 to discard the output. + * data void * Anonymous data to pass to write_fn(). + * Output: + * return int 0 - Line printed ok. + * 1 - Nothing to print. + */ +static int cpl_format_line(CplMatches *result, CplListFormat *fmt, int lnum, + GlWriteFn *write_fn, void *data) +{ + int col; /* The index of the list column being output */ +/* + * If the line index is out of bounds, there is nothing to be written. + */ + if(lnum < 0 || lnum >= fmt->nline) + return 1; +/* + * If no output function has been provided, return as though the + * line had been printed. + */ + if(!write_fn) + return 0; +/* + * Print the matches in 'ncol' columns, sorted in line order within each + * column. + */ + for(col=0; col < fmt->ncol; col++) { + int m = col*fmt->nline + lnum; +/* + * Is there another match to be written? Note that in general + * the last line of a listing will have fewer filled columns + * than the initial lines. + */ + if(m < result->nmatch) { + CplMatch *match = result->matches + m; +/* + * How long are the completion and type-suffix strings? + */ + int clen = strlen(match->completion); + int tlen = strlen(match->type_suffix); +/* + * Write the completion string. + */ + if(write_fn(data, match->completion, clen) != clen) + return 1; +/* + * Write the type suffix, if any. + */ + if(tlen > 0 && write_fn(data, match->type_suffix, tlen) != tlen) + return 1; +/* + * If another column follows the current one, pad to its start with spaces. + */ + if(col+1 < fmt->ncol) { +/* + * The following constant string of spaces is used to pad the output. + */ + static const char spaces[] = " "; + static const int nspace = sizeof(spaces) - 1; +/* + * Pad to the next column, using as few sub-strings of the spaces[] + * array as possible. + */ + int npad = fmt->column_width + CPL_COL_SEP - clen - tlen; + while(npad>0) { + int n = npad > nspace ? nspace : npad; + if(write_fn(data, spaces + nspace - n, n) != n) + return 1; + npad -= n; + }; + }; + }; + }; +/* + * Start a new line. + */ + { + char s[] = "\r\n"; + int n = strlen(s); + if(write_fn(data, s, n) != n) + return 1; + }; + return 0; +} diff --git a/libtecla-1.6.1/cplmatch.h b/libtecla-1.6.1/cplmatch.h new file mode 100644 index 0000000..cb50006 --- /dev/null +++ b/libtecla-1.6.1/cplmatch.h @@ -0,0 +1,47 @@ +#ifndef cplmatch_h +#define cplmatch_h + +/* + * Copyright (c) 2000, 2001, 2002, 2003, 2004 by Martin C. Shepherd. + * + * All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, and/or sell copies of the Software, and to permit persons + * to whom the Software is furnished to do so, provided that the above + * copyright notice(s) and this permission notice appear in all copies of + * the Software and that both the above copyright notice(s) and this + * permission notice appear in supporting documentation. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT + * OF THIRD PARTY RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR + * HOLDERS INCLUDED IN THIS NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL + * INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING + * FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, + * NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION + * WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + * Except as contained in this notice, the name of a copyright holder + * shall not be used in advertising or otherwise to promote the sale, use + * or other dealings in this Software without prior written authorization + * of the copyright holder. + */ + +/* + * This header is not for use by external applicatons. It contains + * internal immplementation features of the libtecla library, which + * may change incompatibly between releases. + */ + +/* + * Display a list of completions via a callback function. + */ +int _cpl_output_completions(CplMatches *result, GlWriteFn *write_fn, void *data, + int term_width); + +#endif diff --git a/libtecla-1.6.1/demo.c b/libtecla-1.6.1/demo.c new file mode 100644 index 0000000..d29b11a --- /dev/null +++ b/libtecla-1.6.1/demo.c @@ -0,0 +1,166 @@ +/* + * Copyright (c) 2000, 2001, 2002, 2003, 2004 by Martin C. Shepherd + * + * All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, and/or sell copies of the Software, and to permit persons + * to whom the Software is furnished to do so, provided that the above + * copyright notice(s) and this permission notice appear in all copies of + * the Software and that both the above copyright notice(s) and this + * permission notice appear in supporting documentation. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT + * OF THIRD PARTY RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR + * HOLDERS INCLUDED IN THIS NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL + * INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING + * FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, + * NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION + * WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + * Except as contained in this notice, the name of a copyright holder + * shall not be used in advertising or otherwise to promote the sale, use + * or other dealings in this Software without prior written authorization + * of the copyright holder. + */ + +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "libtecla.h" + +/* The function which displays the introductory text of the demo */ + +static void show_demo_introduction(GetLine *gl); + +/*....................................................................... + * This program demonstrates how to use gl_get_line() as a line editor to + * to enable users to enter input. It takes no arguments. + */ +int main(int argc, char *argv[]) +{ + char *line; /* A line of input */ + GetLine *gl; /* The line editor */ + int major,minor,micro; /* The version number of the library */ +/* + * Create the line editor, specifying a max line length of 500 bytes, + * and 10000 bytes to allocate to storage of historical input lines. + */ + gl = new_GetLine(500, 5000); + if(!gl) + return 1; +/* + * If the user has the LC_CTYPE or LC_ALL environment variables set, + * enable display of characters corresponding to the specified locale. + */ + (void) setlocale(LC_CTYPE, ""); +/* + * Lookup and display the version number of the library. + */ + libtecla_version(&major, &minor, µ); + printf("\n Welcome to the main demo program of libtecla version %d.%d.%d\n", + major, minor, micro); +/* + * Display an introductory banner. + */ + show_demo_introduction(gl); +/* + * Load history. + */ +#ifndef WITHOUT_FILE_SYSTEM + (void) gl_load_history(gl, "~/.demo_history", "#"); +#endif +/* + * Read lines of input from the user and print them to stdout. + */ + do { +/* + * Get a new line from the user. + */ + line = gl_get_line(gl, "$ ", NULL, 0); + if(!line) + break; +/* + * Display what was entered. + */ + if(printf("You entered: %s", line) < 0 || fflush(stdout)) + break; +/* + * If the user types "exit", quit the program. + */ + if(strcmp(line, "exit\n")==0) + break; + else if(strcmp(line, "history\n")==0) + gl_show_history(gl, stdout, "%N %T %H\n", 0, -1); + else if(strcmp(line, "size\n")==0) { + GlTerminalSize size = gl_terminal_size(gl, 80, 24); + printf("Terminal size = %d columns x %d lines.\n", size.ncolumn, + size.nline); + } else if(strcmp(line, "clear\n")==0) { + if(gl_erase_terminal(gl)) + return 1; + }; + } while(1); +/* + * Save historical command lines. + */ +#ifndef WITHOUT_FILE_SYSTEM + (void) gl_save_history(gl, "~/.demo_history", "#", -1); +#endif +/* + * Clean up. + */ + gl = del_GetLine(gl); + return 0; +} + +/*....................................................................... + * Display introductory text to the user, formatted according to the + * current terminal width and enclosed in a box of asterixes. + * + * Input: + * gl GetLine * The resource object of gl_get_line(). + */ +static void show_demo_introduction(GetLine *gl) +{ + int start; /* The column in which gl_display_text() left the cursor */ + int i; +/* + * Break the indtroductory text into an array of strings, so as to + * avoid overflowing any compiler string limits. + */ + const char *doc[] = { + "This program is a simple shell with which you can experiment with the ", + "line editing and tab completion facilities provided by the gl_get_line() ", + "function. The file demo.c also serves as a fully commented example ", + "of how to use gl_get_line().\n" + }; +/* + * Form the top line of the documentation box by filling the area of + * the line between a " *" prefix and a "* " suffix with asterixes. + */ + printf("\n"); + gl_display_text(gl, 0, " *", "* ", '*', 80, 0, "\n"); +/* + * Justify the documentation text within margins of asterixes. + */ + for(start=0,i=0; i= 0; i++) + start = gl_display_text(gl, 0, " * ", " * ", ' ', 80, start,doc[i]); +/* + * Draw the bottom line of the documentation box. + */ + gl_display_text(gl, 0, " *", "* ", '*', 80, 0, "\n"); + printf("\n"); +} diff --git a/libtecla-1.6.1/demo2.c b/libtecla-1.6.1/demo2.c new file mode 100644 index 0000000..63a9a0c --- /dev/null +++ b/libtecla-1.6.1/demo2.c @@ -0,0 +1,423 @@ +/* + * Copyright (c) 2000, 2001, 2002, 2003, 2004 by Martin C. Shepherd. + * + * All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, and/or sell copies of the Software, and to permit persons + * to whom the Software is furnished to do so, provided that the above + * copyright notice(s) and this permission notice appear in all copies of + * the Software and that both the above copyright notice(s) and this + * permission notice appear in supporting documentation. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT + * OF THIRD PARTY RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR + * HOLDERS INCLUDED IN THIS NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL + * INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING + * FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, + * NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION + * WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + * Except as contained in this notice, the name of a copyright holder + * shall not be used in advertising or otherwise to promote the sale, use + * or other dealings in this Software without prior written authorization + * of the copyright holder. + */ + +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "libtecla.h" + +/* + * If the library is being built with file-system access excluded, this + * demo program won't have anything to demonstrate. + */ +#ifdef WITHOUT_FILE_SYSTEM +int main(int argc, char *argv[]) +{ + fprintf(stderr, "\n" + " This program normally demonstrates tecla's path-lookup\n" + " facility. However libtecla has been installed with\n" + " file-system facilities explicitly excluded, so there is\n" + " nothing to demonstrate.\n\n"); + return 1; +} +#else +/* + * Encapsulate the resources needed by this demo. + */ +typedef struct { + GetLine *gl; /* The line editor */ + PathCache *pc; /* A cache of executables in the user's path */ + PcaPathConf *ppc; /* The configuration argument of pca_path_completions() */ +} DemoRes; + +/* + * The following functions allocate and free instances of the above + * structure. + */ +static DemoRes *new_DemoRes(void); +static DemoRes *del_DemoRes(DemoRes *res); + +/* + * Search backwards for the start of a pathname. + */ +static char *start_of_path(const char *string, int back_from); + +/* + * Find the array indexes of the first character of the first + * space-delimited word in the specified string, and of the character + * that follows it. + */ +static int get_word_limits(const char *string, int *wa, int *wb); + +/* + * This is the demonstration completion callback function (defined below). + */ +static CPL_MATCH_FN(demo_cpl_fn); + +/* The function which displays the introductory text of the demo */ + +static void show_demo_introduction(GetLine *gl); + +/*....................................................................... + * This demo takes no arguments. It reads lines of input until the + * word 'exit' is entered, or C-d is pressed. It replaces the default + * tab-completion callback with one which when invoked at the start of + * a line, looks up completions of commands in the user's execution + * path, and when invoked in other parts of the line, reverts to + * normal filename completion. Whenever a new line is entered, it + * extracts the first word on the line, looks it up in the user's + * execution path to see if it corresponds to a known executable file, + * and if so, displays the full pathname of the file, along with the + * remaining arguments. + */ +int main(int argc, char *argv[]) +{ + char *line; /* A line of input */ + DemoRes *res; /* The resources of the demo */ + int wa,wb; /* The delimiting indexes of a word in line[] */ + int major,minor,micro; /* The version number of the library */ +/* + * Allocate the resources needed by this demo. + */ + res = new_DemoRes(); + if(!res) + return 1; +/* + * If the user has the LC_CTYPE or LC_ALL environment variables set, + * enable display of characters corresponding to the specified locale. + */ + (void) setlocale(LC_CTYPE, ""); +/* + * Lookup and display the version number of the library. + */ + libtecla_version(&major, &minor, µ); + printf("\n Welcome to the path-search demo of libtecla version %d.%d.%d\n", + major, minor, micro); +/* + * Display some introductory text, left-justifying it within the current + * width of the terminal and enclosing it in a box of asterixes. + */ + show_demo_introduction(res->gl); +/* + * Read lines of input from the user and print them to stdout. + */ + do { +/* + * Get a new line from the user. + */ + line = gl_get_line(res->gl, "$ ", NULL, 0); + if(!line) + break; +/* + * Work out the extent of the first word in the input line, and + * try to identify this as a command in the path, displaying the + * full pathname of the match if found. + */ + if(get_word_limits(line, &wa, &wb) == 0) { + char *cmd = pca_lookup_file(res->pc, line + wa, wb-wa, 0); + if(cmd) { + printf("Command=%s\n", cmd); + printf("Arguments=%s", line+wb); + } else { + printf("Command not found\n"); + }; + }; +/* + * If the user types "exit", quit the program. + */ + if(strcmp(line, "exit\n")==0) + break; + } while(1); +/* + * Clean up. + */ + res = del_DemoRes(res); + return 0; +} + +/*....................................................................... + * This completion callback searches for completions of executables in + * the user's path when invoked on a word at the start of the path, and + * performs normal filename completion elsewhere. + */ +static CPL_MATCH_FN(demo_cpl_fn) +{ +/* + * Get the resource object that was passed to gl_customize_completion(). + */ + DemoRes *res = (DemoRes *) data; +/* + * Find the start of the filename prefix to be completed, searching + * backwards for the first unescaped space, or the start of the line. + */ + char *start = start_of_path(line, word_end); +/* + * Skip spaces preceding the start of the prefix. + */ + while(start > line && isspace((int)(unsigned char) start[-1])) + start--; +/* + * If the filename prefix is at the start of the line, attempt + * to complete the filename as a command in the path. Otherwise + * perform normal filename completion. + */ + return (start == line) ? + pca_path_completions(cpl, res->ppc, line, word_end) : + cpl_file_completions(cpl, NULL, line, word_end); +} + +/*....................................................................... + * Search backwards for the potential start of a filename. This + * looks backwards from the specified index in a given string, + * stopping at the first unescaped space or the start of the line. + * + * Input: + * string const char * The string to search backwards in. + * back_from int The index of the first character in string[] + * that follows the pathname. + * Output: + * return char * The pointer to the first character of + * the potential pathname, or NULL on error. + */ +static char *start_of_path(const char *string, int back_from) +{ + int i, j; +/* + * Search backwards from the specified index. + */ + for(i=back_from-1; i>=0; i--) { + int c = string[i]; +/* + * Stop on unescaped spaces. + */ + if(isspace((int)(unsigned char)c)) { +/* + * The space can't be escaped if we are at the start of the line. + */ + if(i==0) + break; +/* + * Find the extent of the escape characters which precedes the space. + */ + for(j=i-1; j>=0 && string[j]=='\\'; j--) + ; +/* + * If there isn't an odd number of escape characters before the space, + * then the space isn't escaped. + */ + if((i - 1 - j) % 2 == 0) + break; + }; + }; + return (char *)string + i + 1; +} + +/*....................................................................... + * Create a new DemoRes object containing the resources needed by the + * demo. + * + * Output: + * return DemoRes * The new object, or NULL on error. + */ +static DemoRes *new_DemoRes(void) +{ + DemoRes *res; /* The object to be returned */ +/* + * Allocate the container. + */ + res = (DemoRes *)malloc(sizeof(DemoRes)); + if(!res) { + fprintf(stderr, "new_DemoRes: Insufficient memory.\n"); + return NULL; + }; +/* + * Before attempting any operation that might fail, initialize the + * container at least up to the point at which it can safely be passed + * to del_DemoRes(). + */ + res->gl = NULL; + res->pc = NULL; + res->ppc = NULL; +/* + * Create the line editor, specifying a max line length of 500 bytes, + * and 10000 bytes to allocate to storage of historical input lines. + */ + res->gl = new_GetLine(500, 10000); + if(!res->gl) + return del_DemoRes(res); +/* + * Enable text attribute formatting directives in prompt strings. + */ + gl_prompt_style(res->gl, GL_FORMAT_PROMPT); +/* + * Allocate a cache of the executable files found in the user's path. + */ + res->pc = new_PathCache(); + if(!res->pc) + return del_DemoRes(res); +/* + * Populate the cache with the contents of the user's path. + */ + if(pca_scan_path(res->pc, getenv("PATH"))) + return del_DemoRes(res); +/* + * Arrange for susequent calls to pca_lookup_file() and pca_path_completions() + * to only report files that are executable by the user. + */ + pca_set_check_fn(res->pc, cpl_check_exe, NULL); +/* + * Allocate a configuration object for use with pca_path_completions(). + */ + res->ppc = new_PcaPathConf(res->pc); + if(!res->ppc) + return del_DemoRes(res); +/* + * Replace the builtin filename completion callback with one which + * searches for completions of executables in the user's path when + * invoked on a word at the start of the path, and completes files + * elsewhere. + */ + if(gl_customize_completion(res->gl, res, demo_cpl_fn)) + return del_DemoRes(res); + return res; +} + +/*....................................................................... + * Delete a DemoRes object. + * + * Input: + * res DemoRes * The object to be deleted. + * Output: + * return DemoRes * The deleted object (always NULL). + */ +static DemoRes *del_DemoRes(DemoRes *res) +{ + if(res) { + res->gl = del_GetLine(res->gl); + res->pc = del_PathCache(res->pc); + res->ppc = del_PcaPathConf(res->ppc); + free(res); + }; + return NULL; +} + +/*....................................................................... + * Return the limits of the word at the start of a given string, ignoring + * leading white-space, and interpretting the first unescaped space, tab or + * the end of the line, as the end of the word. + * + * Input: + * string const char * The string to tokenize. + * Input/Output: + * wa,wb int * The indexes of the first character of the word, + * and the character which follows the last + * character of the word, will be assigned to + * *wa and *wb, respectively. + * Output: + * return int 0 - A word was found. + * 1 - No word was found before the end of the + * string. + */ +static int get_word_limits(const char *string, int *wa, int *wb) +{ + int escaped = 0; /* True if the next character is escaped */ +/* + * Skip leading white-space. + */ + for(*wa=0; isspace((int)(unsigned char)string[*wa]); (*wa)++) + ; +/* + * Find the first unescaped space, stopping early if the end of the + * string is reached. + */ + for(*wb = *wa; ; (*wb)++) { + int c = string[*wb]; + if(c=='\\') + escaped = !escaped; + else if((!escaped && isspace((int)(unsigned char)c)) || c=='\0') + break; + }; + return *wa == *wb; +} + +/*....................................................................... + * Display introductory text to the user, formatted according to the + * current terminal width and enclosed in a box of asterixes. + * + * Input: + * gl GetLine * The resource object of gl_get_line(). + */ +static void show_demo_introduction(GetLine *gl) +{ + int start; /* The column in which gl_display_text() left the cursor */ + int i; +/* + * Break the indtroductory text into an array of strings, so as to + * avoid overflowing any compiler string limits. + */ + const char *doc[] = { + "This program demonstrates the use of the pca_lookup_file() function ", + "for finding executables in the UNIX PATH. It also demonstrates ", + "tab completion of the names of executables found in the path. For ", + "example, if you type:\n\n ta\n\nthen hit the tab key, you will be ", + "presented with a list of executables such as tar and tail whose names ", + "start with the string \"ta\". If you decide to add an \"r\" to select ", + "the tar command, then you type return, the full pathname of the tar ", + "program will be printed.\n\nThe file demo2.c contains the code ", + "of this program, and is fully commented to enable its use as ", + "a working example of how to use the facilities documented in the ", + "pca_lookup_file man page.\n"}; +/* + * Form the top line of the documentation box by filling the area of + * the line between a " *" prefix and a "* " suffix with asterixes. + */ + printf("\n"); + gl_display_text(gl, 0, " *", "* ", '*', 80, 0, "\n"); +/* + * Justify the documentation text within margins of asterixes. + */ + for(start=0,i=0; i= 0; i++) + start = gl_display_text(gl, 0, " * ", " * ", ' ', 80, start,doc[i]); +/* + * Draw the bottom line of the documentation box. + */ + gl_display_text(gl, 0, " *", "* ", '*', 80, 0, "\n"); + printf("\n"); +} + +#endif /* ifndef WITHOUT_FILE_SYSTEM */ diff --git a/libtecla-1.6.1/demo3.c b/libtecla-1.6.1/demo3.c new file mode 100644 index 0000000..aa204d0 --- /dev/null +++ b/libtecla-1.6.1/demo3.c @@ -0,0 +1,738 @@ +/* + * Copyright (c) 2002, 2003, 2004 by Martin C. Shepherd + * + * All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, and/or sell copies of the Software, and to permit persons + * to whom the Software is furnished to do so, provided that the above + * copyright notice(s) and this permission notice appear in all copies of + * the Software and that both the above copyright notice(s) and this + * permission notice appear in supporting documentation. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT + * OF THIRD PARTY RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR + * HOLDERS INCLUDED IN THIS NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL + * INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING + * FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, + * NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION + * WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + * Except as contained in this notice, the name of a copyright holder + * shall not be used in advertising or otherwise to promote the sale, use + * or other dealings in this Software without prior written authorization + * of the copyright holder. + */ + +#include +#include +#include +#include +#include +#include +#include + +#ifdef HAVE_SELECT +#ifdef HAVE_SYS_SELECT_H +#include +#endif +#endif + +#include +#include +#include +#include +#include + +#include "libtecla.h" + +/* + * The SignalActions object provides a way to temporarily install + * a signal handler to a given set of signals, and later restore all + * of the signal handlers that this displaced. + */ +typedef struct { + int nsignal; /* The number of signals on the host OS */ + sigset_t mask; /* The set of signals who's signal handlers */ + /* are stored in the following actions[] */ + /* array. */ + struct sigaction *actions; /* An array of nsignal actions */ +} SignalActions; + +static SignalActions *new_SignalActions(void); +static SignalActions *del_SignalActions(SignalActions *si); +static int displace_signal_handlers(SignalActions *si, sigset_t *mask, + void (*handler)(int)); +static int reinstate_signal_handlers(SignalActions *si); + +/* Return resources, restore the terminal to a usable state and exit */ + +static void cleanup_and_exit(GetLine *gl, SignalActions *si, int status); + +/* The function which displays the introductory text of the demo */ + +static void show_demo_introduction(GetLine *gl); + +/* A signal-aware version of select() */ + +static int demo_sigselect(int n, fd_set *readfds, fd_set *writefds, + fd_set *exceptfds, struct timeval *timeout, + sigset_t *mask, SignalActions *si); + +/* + * The following variables are accessed from signal handlers. Note + * that these variables don't need to be either volatile or + * sig_atomic_t because: + * + * 1. Outside of signal handlers we only access them when signal + * delivery is blocked, so we know that no signal handlers can + * be accessing them at that time. + * + * 2. When the signal handlers that set these variables are installed, + * the sa_mask member of the sigaction structure is used to ensure + * that only one instance of these signal handlers can be running + * at a time, so we also know that there can't be simultaneous + * accesses to them by multiple signal handlers. + */ +static GetLine *demo_gl; /* The line editor object */ +static sigjmp_buf demo_setjmp_buffer; /* The sigsetjmp() buffer */ +static int demo_setjmp_signo = -1; /* The signal that was caught */ + +/* Signal handlers */ + +static void demo_signal_handler(int signo); +static void demo_setjmp_handler(int signo); + +/* + * Set the amount of time that gl_get_line() should wait for I/O before + * returning to let the external event loop continue. + */ +#define DEMO_IO_TIMEOUT 100000000 /* ns => 100ms */ + +/* The timeout handler */ + +static GL_TIMEOUT_FN(demo_timeout_fn); + +/*....................................................................... + * This program demonstrates the use of gl_get_line() from an external + * event loop. It takes no arguments. + */ +int main(int argc, char *argv[]) +{ + int major,minor,micro; /* The version number of the library */ + GetLine *gl=NULL; /* The resource object of gl_get_line() */ + SignalActions *si=NULL; /* Temporary storage of displaced signal */ + /* handlers. */ + sigset_t all_signal_mask; /* The set of signals known by gl_get_line() */ +/* + * This program requires select(). + */ +#if !defined(HAVE_SELECT) + fprintf(stderr, "The select() system call isn't available - aborting.\n"); + exit(1); +#else +/* + * Create the line editor, specifying a maximum line length of 500 bytes, + * and 10000 bytes to allocate to storage of historical input lines. + */ + gl = demo_gl = new_GetLine(500, 5000); + if(!gl) + cleanup_and_exit(gl, si, 1); + +/* + * Allocate an object in which to temporarily record displaced + * signal handlers. + */ + si = new_SignalActions(); + if(!si) + cleanup_and_exit(gl, si, 1); +/* + * If the user has the LC_CTYPE or LC_ALL environment variables set, + * enable display of characters corresponding to the specified locale. + */ + (void) setlocale(LC_CTYPE, ""); +/* + * Lookup and display the version number of the library. + */ + libtecla_version(&major, &minor, µ); + printf( + "\n Welcome to the server-mode demo program of libtecla version %d.%d.%d\n", + major, minor, micro); +/* + * Display some introductory text, left-justifying it within the current + * width of the terminal and enclosing it in a box of asterixes. + */ + show_demo_introduction(gl); +/* + * Load history. + */ +#ifndef WITHOUT_FILE_SYSTEM + (void) gl_load_history(gl, "~/.demo_history", "#"); +#endif +/* + * In this demo, rather than having gl_get_line() return immediately + * when it would otherwise have to wait for I/O, we register a timeout + * callback which causes gl_get_line() to give up waiting after a short + * interval. + */ + gl_inactivity_timeout(gl, demo_timeout_fn, NULL, 0, DEMO_IO_TIMEOUT); +/* + * Install our signal handlers for process termination, suspension and + * terminal resize signals. Ignore process continuation signals. + */ + gl_tty_signals(demo_signal_handler, demo_signal_handler, SIG_DFL, + demo_signal_handler); +/* + * Get a list of all of the signals that gl_get_line() currently catches. + */ + gl_list_signals(gl, &all_signal_mask); +/* + * Switch gl_get_line() to non-blocking server mode. + */ + if(gl_io_mode(gl, GL_SERVER_MODE)) + cleanup_and_exit(gl, si, 1); +/* + * Instruct gl_get_line() to unblock any signals that it catches + * while waiting for input. Note that in non-blocking server mode, + * this is only necessary when using gl_inactivity_timeout() to make + * gl_get_line() block for a non-zero amount of time. + */ + gl_catch_blocked(gl); +/* + * Enter the event loop. + */ + while(1) { + int nready; /* The number of file-descriptors that are */ + /* ready for I/O */ + fd_set rfds; /* The set of file descriptors to watch for */ + /* readability */ + fd_set wfds; /* The set of file descriptors to watch for */ + /* writability */ +/* + * Construct the sets of file descriptors to be watched by select(), + * starting from empty sets. + */ + FD_ZERO(&rfds); + FD_ZERO(&wfds); +/* + * To ensure that no signals are received whos handlers might change + * the requirements for the contents of the above signal sets, block + * all of the signals that we are handling. + */ + sigprocmask(SIG_BLOCK, &all_signal_mask, NULL); +/* + * Depending on which direction of I/O gl_get_line()s is currently + * waiting for, add the terminal file descriptor to either the set + * of file descriptors to watch for readability, or those to watch + * for writability. Note that at the start of a new line, such as + * after an error, or the return of a completed line, we need to + * wait for writability, so that a prompt can be written. + */ + switch(gl_pending_io(gl)) { + case GLP_READ: + FD_SET(STDIN_FILENO, &rfds); + break; + default: + FD_SET(STDIN_FILENO, &wfds); + break; + }; +/* + * Wait for I/O to become possible on the selected file descriptors. + * The following is a signal-aware wrapper around the select() system + * call. This wrapper guarantees that if any of the signals marked in + * all_signal_mask arrive after the statement above where we blocked + * these signals, it will detect this and abort with nready=-1 and + * errno=EINTR. If instead, we just unblocked the above signals just + * before calling a normal call to select(), there would be a small + * window of time between those two statements in which a signal could + * arrive without aborting select(). This would be a problem, since + * the functions called by our signal handler may change the type + * of I/O that gl_get_line() wants us to wait for in select(). + */ + nready = demo_sigselect(STDIN_FILENO + 1, &rfds, &wfds, NULL, NULL, + &all_signal_mask, si); +/* + * We can now unblock our signals again. + */ + sigprocmask(SIG_UNBLOCK, &all_signal_mask, NULL); +/* + * Did an I/O error occur? + */ + if(nready < 0 && errno != EINTR) + cleanup_and_exit(gl, si, 1); +/* + * If the terminal file descriptor is now ready for I/O, call + * gl_get_line() to continue editing the current input line. + */ + if(FD_ISSET(STDIN_FILENO, &rfds) || FD_ISSET(STDIN_FILENO, &wfds)) { +/* + * Start or continue editing an input line. + */ + char *line = gl_get_line(gl, "$ ", NULL, 0); +/* + * Did the user finish entering a new line? + */ + if(line) { +/* + * Before writing messages to the terminal, start a new line and + * switch back to normal terminal I/O. + */ + gl_normal_io(gl); +/* + * Display what was entered. + */ + if(printf("You entered: %s", line) < 0 || fflush(stdout)) + break; +/* + * Implement a few simple commands. + */ + if(strcmp(line, "exit\n")==0) + cleanup_and_exit(gl, si, 0); + else if(strcmp(line, "history\n")==0) + gl_show_history(gl, stdout, "%N %T %H\n", 0, -1); + else if(strcmp(line, "size\n")==0) { + GlTerminalSize size = gl_terminal_size(gl, 80, 24); + printf("Terminal size = %d columns x %d lines.\n", size.ncolumn, + size.nline); + } else if(strcmp(line, "clear\n")==0) { + if(gl_erase_terminal(gl)) + return 1; + }; +/* + * To resume command-line editing, return the terminal to raw, + * non-blocking I/O mode. + */ + gl_raw_io(gl); +/* + * If gl_get_line() returned NULL because of an error or end-of-file, + * abort the program. + */ + } else if(gl_return_status(gl) == GLR_ERROR || + gl_return_status(gl) == GLR_EOF) { + cleanup_and_exit(gl, si, 1); + }; + }; + }; +#endif + return 0; +} + +/*....................................................................... + * This function is called to return resources to the system and restore + * the terminal to its original state before exiting the process. + * + * Input: + * gl GetLine * The line editor. + * si SignalActions * The repository for displaced signal handlers. + * status int The exit code of the process. + */ +static void cleanup_and_exit(GetLine *gl, SignalActions *si, int status) +{ +/* + * Restore the terminal to its original state before exiting the program. + */ + gl_normal_io(gl); +/* + * Save historical command lines. + */ +#ifndef WITHOUT_FILE_SYSTEM + (void) gl_save_history(gl, "~/.demo_history", "#", -1); +#endif +/* + * Clean up. + */ + gl = del_GetLine(gl); + si = del_SignalActions(si); +/* + * Exit the process. + */ + exit(status); +} + +/*....................................................................... + * This is a signal-aware wrapper around the select() system call. It + * is designed to facilitate reliable signal handling of a given set + * of signals, without the race conditions that would usually surround + * the use of select(). See the "RELIABLE SIGNAL HANDLING" section of + * the gl_get_line(3) man page for further details. + * + * Provided that the calling function has blocked the specified set of + * signals before calling this function, this function guarantees that + * select() will be aborted by any signal that arrives between the + * time that the caller blocked the specified signals and this + * function returns. On return these signals will again be blocked to + * prevent any signals that arrive after select() returns, from being + * missed by the caller. + * + * Note that this function is written not to be specific to this + * program, and is thus suitable for use in other programs, whether or + * not they use gl_get_line(). + * + * Also note that this function depends on the NSIG preprocessor + * constant being >= the maximum number of signals available on the + * host operating system. Under BSD and SysV, this macro is set + * appropriately in signal.h. On other systems, a reasonably large + * guess should be substituted. Although nothing terrible will happen + * if a value that is too small is chosen, signal numbers that exceed + * the specified value of NSIG will be ignored by this function. A + * more robust method than depending on nsig would be to use the + * POSIX sigismember() function to count valid signals, and use this + * to allocate the array of sigaction structures used to preserve + * + * + * Input: + * n int The number of file descriptors to pay + * attention to at the start of each of the + * following sets of file descriptors. + * readfds fd_set * The set of file descriptors to check for + * readability, or NULL if not pertinent. + * wwritefds fd_set * The set of file descriptors to check for + * writability, or NULL if not pertinent. + * exceptfds fd_set * The set of file descriptors to check for + * the arrival of urgent data, or NULL if + * not pertinent. + * timeout struct timeval * The maximum time that select() should + * wait, or NULL to wait forever. + * mask sigset_t * The set of signals to catch. + * si SignalHandlers * An object in which to preserve temporary + * copies signal handlers. + * Output: + * return int > 0 The number of entries in all of the + * sets of descriptors that are ready + * for I/O. + * 0 Select() timed out. + * -1 Error (see errno). + */ +static int demo_sigselect(int n, fd_set *readfds, fd_set *writefds, + fd_set *exceptfds, struct timeval *timeout, + sigset_t *mask, SignalActions *si) +{ +/* + * The reason that the the following variables are marked as volatile + * is to prevent the compiler from placing their values in registers + * that might not be saved and restored by sigsetjmp(). + */ + volatile sigset_t old_mask; /* The displaced process signal mask */ + volatile int status; /* The return value of select() */ +/* + * Make sure that all of the specified signals are blocked. This is + * redundant if the caller has already blocked signals. + */ + if(sigprocmask(SIG_BLOCK, mask, (sigset_t *) &old_mask) < 0) + return -1; +/* + * Record the fact that no signal has been caught yet. + */ + demo_setjmp_signo = -1; +/* + * Now set up the point where our temporary signal handlers will return + * control if a signal is received. + */ + if(sigsetjmp(demo_setjmp_buffer, 1) == 0) { +/* + * Now install the temporary signal handlers that cause the above + * sigsetjmp() to return non-zero when a signal is detected. + */ + if(displace_signal_handlers(si, mask, demo_setjmp_handler)) { + reinstate_signal_handlers(si); + return 1; + }; +/* + * Now that we are ready to catch the signals, unblock them. + */ + sigprocmask(SIG_UNBLOCK, mask, NULL); +/* + * At last, call select(). + */ + status = select(n, readfds, writefds, exceptfds, timeout); +/* + * Block the specified signals again. + */ + sigprocmask(SIG_BLOCK, mask, NULL); +/* + * Record the fact that no signal was caught. + */ + demo_setjmp_signo = -1; + }; +/* + * We can get to this point in one of two ways. Either no signals were + * caught, and the above block ran to completion (with demo_setjmp_signo=-1), + * or a signal was caught that caused the above block to be aborted, + * in which case demo_setjmp_signo will now equal the number of the signal that + * was caught, and sigsetjmp() will have restored the process signal + * mask to how it was before it was called (ie. all of the specified + * signals blocked). + * + * First restore the signal handlers to how they were on entry to + * this function. + */ + reinstate_signal_handlers(si); +/* + * Was a signal caught? + */ + if(demo_setjmp_signo > 0) { + sigset_t new_mask; +/* + * Send the signal again, then unblock its delivery, so that the application's + * signal handler gets invoked. + */ + raise(demo_setjmp_signo); + sigemptyset(&new_mask); + sigaddset(&new_mask, demo_setjmp_signo); + sigprocmask(SIG_UNBLOCK, &new_mask, NULL); +/* + * Set the return status to show that a signal was caught. + */ + errno = EINTR; + status = -1; + }; +/* + * Now restore the process signal mask to how it was on entry to this + * function. + */ + sigprocmask(SIG_SETMASK, (sigset_t *) &old_mask, NULL); + return status; +} + +/*....................................................................... + * This is the main signal handler of this demonstration program. If a + * SIGINT is received by the process, it arranges that the next call + * to gl_get_line() will abort entry of the current line and start + * entering a new one. Otherwise it calls the library function which + * handles terminal resize signals and process suspension and process + * termination signals. Both of the functions called by this signal + * handler are designed to be async-signal safe, provided that the + * rules laid out in the gl_io_mode(3) man page are followed. + */ +static void demo_signal_handler(int signo) +{ + if(signo==SIGINT) + gl_abandon_line(demo_gl); + else + gl_handle_signal(signo, demo_gl, 1); +} + +/*....................................................................... + * The following signal handler is installed while select() is being + * called from within a block of code protected by sigsetjmp(). It + * simply records the signal that was caught in setjmp_signo, then + * causes the sigsetjmp() to return non-zero. + */ +static void demo_setjmp_handler(int signo) +{ + demo_setjmp_signo = signo; + siglongjmp(demo_setjmp_buffer, 1); +} + +/*....................................................................... + * This optional inactivity timeout function is used in this + * demonstration to cause gl_get_line() to wait for a small amount of + * time for I/O, before returning and allowing the event loop to + * continue. This isn't needed if you want gl_get_line() to return + * immediately, rather than blocking. + */ +static GL_TIMEOUT_FN(demo_timeout_fn) +{ + return GLTO_CONTINUE; +} + +/*....................................................................... + * Display introductory text to the user, formatted according to the + * current terminal width and enclosed in a box of asterixes. + * + * Input: + * gl GetLine * The resource object of gl_get_line(). + */ +static void show_demo_introduction(GetLine *gl) +{ + int start; /* The column in which gl_display_text() left the cursor */ + int i; +/* + * Break the indtroductory text into an array of strings, so as to + * avoid overflowing any compiler string limits. + */ + const char *doc[] = { + "To the user this program appears to act identically to the main ", + "demo program. However whereas the code underlying the main demo ", + "program uses gl_get_line() in its default configuration, where each ", + "call blocks the caller until the user has entered a complete input ", + "line, demo3 uses gl_get_line() in its non-blocking server mode, ", + "where it must be called repeatedly from an external ", + "event loop to incrementally accept entry of the input ", + "line, as and when terminal I/O becomes possible. The well commented ", + "source code of demo3, which can be found in demo3.c, thus provides ", + "a working example of how to use gl_get_line() in a manner that ", + "doesn't block the caller. Documentation of this mode can be found ", + "in the gl_io_mode(3) man page.\n" + }; +/* + * Form the top line of the documentation box by filling the area of + * the line between a " *" prefix and a "* " suffix with asterixes. + */ + printf("\n"); + gl_display_text(gl, 0, " *", "* ", '*', 80, 0, "\n"); +/* + * Justify the documentation text within margins of asterixes. + */ + for(start=0,i=0; i= 0; i++) + start = gl_display_text(gl, 0, " * ", " * ", ' ', 80, start,doc[i]); +/* + * Draw the bottom line of the documentation box. + */ + gl_display_text(gl, 0, " *", "* ", '*', 80, 0, "\n"); + printf("\n"); +} + + +/*....................................................................... + * This is a constructor function for an object who's role is to allow + * a signal handler to be assigned to potentially all available signals, + * while preserving a copy of the original signal handlers, for later + * restration. + * + * Output: + * return SignalActions * The new object, or NULL on error. + */ +static SignalActions *new_SignalActions(void) +{ + SignalActions *si; /* The object to be returned */ +/* + * Allocate the container. + */ + si = malloc(sizeof(SignalActions)); + if(!si) { + fprintf(stderr, "new_SignalActions: Insufficient memory.\n"); + return NULL; + }; +/* + * Before attempting any operation that might fail, initialize the + * container at least up to the point at which it can safely be passed + * to del_SignalActions(). + */ + si->nsignal = 0; + sigemptyset(&si->mask); + si->actions = NULL; +/* + * Count the number of signals that are available of the host + * platform. Note that si->mask has no members set, and that + * sigismember() is defined to return -1 if the signal number + * isn't valid. + */ + for(si->nsignal=1; sigismember(&si->mask, si->nsignal) == 0; si->nsignal++) + ; +/* + * Allocate the array of sigaction structures to use to keep a record + * of displaced signal handlers. + */ + si->actions = (struct sigaction *) malloc(sizeof(*si->actions) * si->nsignal); + if(!si->actions) { + fprintf(stderr, "Insufficient memory for %d sigaction structures.\n", + si->nsignal); + return del_SignalActions(si); + }; + return si; +} + +/*....................................................................... + * Delete a SignalActions object. + * + * Input: + * si SignalActions * The object to be deleted. + * Output: + * return SignalActions * The deleted object (always NULL). + */ +static SignalActions *del_SignalActions(SignalActions *si) +{ + if(si) { + if(si->actions) + free(si->actions); + free(si); + }; + return NULL; +} + +/*....................................................................... + * Replace the signal handlers of all of the signals in 'mask' with + * the signal handler 'handler'. + * + * Input: + * si SignalActions * The object in which to record the displaced + * signal handlers. + * mask sigset_t * The set of signals who's signal handlers + * should be displaced. + * handler void (*handler)(int) The new signal handler to assign to each + * of the signals marked in 'mask'. + * Output: + * return int 0 - OK. + * 1 - Error. + */ +static int displace_signal_handlers(SignalActions *si, sigset_t *mask, + void (*handler)(int)) +{ + int signo; /* A signal number */ + struct sigaction action; /* The new signal handler */ +/* + * Mark the fact that so far we haven't displaced any signal handlers. + */ + sigemptyset(&si->mask); +/* + * Set up the description of the new signal handler. Note that + * we make sa_mask=mask. This ensures that only one instance of the + * signal handler will ever be running at one time. + */ + action.sa_handler = handler; + memcpy(&action.sa_mask, mask, sizeof(*mask)); + action.sa_flags = 0; +/* + * Check each of the available signals to see if it is specified in 'mask'. + * If so, install the new signal handler, record the displaced one in + * the corresponding element of si->actions[], and make a record in + * si->mask that this signal handler has been displaced. + */ + for(signo=1; signo < si->nsignal; signo++) { + if(sigismember(mask, signo)) { + if(sigaction(signo, &action, &si->actions[signo]) < 0) { + fprintf(stderr, "sigaction error (%s)\n", strerror(errno)); + return 1; + }; + sigaddset(&si->mask, signo); + }; + }; + return 0; +} + +/*....................................................................... + * Reinstate any signal handlers displaced by displace_signal_handlers(). + * + * Input: + * sig SignalActions * The object containing the displaced signal + * handlers. + * Output: + * return int 0 - OK. + * 1 - Error. + */ +static int reinstate_signal_handlers(SignalActions *si) +{ + int signo; /* A signal number */ +/* + * Check each of the available signals to see if it is specified in + * si->mask. If so, reinstate the displaced recorded in the + * corresponding element of si->actions[], and make a record in + * si->mask that this signal handler has been reinstated. + */ + for(signo=1; signo < si->nsignal; signo++) { + if(sigismember(&si->mask, signo)) { + if(sigaction(signo, &si->actions[signo], NULL) < 0) { + fprintf(stderr, "sigaction error (%s)\n", strerror(errno)); + return 1; + }; + sigdelset(&si->mask, signo); + }; + }; + return 0; +} diff --git a/libtecla-1.6.1/direader.c b/libtecla-1.6.1/direader.c new file mode 100644 index 0000000..68db93f --- /dev/null +++ b/libtecla-1.6.1/direader.c @@ -0,0 +1,309 @@ +/* + * Copyright (c) 2000, 2001, 2002, 2003, 2004 by Martin C. Shepherd. + * + * All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, and/or sell copies of the Software, and to permit persons + * to whom the Software is furnished to do so, provided that the above + * copyright notice(s) and this permission notice appear in all copies of + * the Software and that both the above copyright notice(s) and this + * permission notice appear in supporting documentation. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT + * OF THIRD PARTY RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR + * HOLDERS INCLUDED IN THIS NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL + * INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING + * FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, + * NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION + * WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + * Except as contained in this notice, the name of a copyright holder + * shall not be used in advertising or otherwise to promote the sale, use + * or other dealings in this Software without prior written authorization + * of the copyright holder. + */ + +/* + * If file-system access is to be excluded, this module has no function, + * so all of its code should be excluded. + */ +#ifndef WITHOUT_FILE_SYSTEM + +/* + * Standard includes. + */ +#include +#include +#include +#include + +/* + * Operating system includes. + */ +#include +#include +#include +#include + +#include "direader.h" +#include "errmsg.h" + +/* + * Use the reentrant POSIX threads version of readdir()? + */ +#if defined(PREFER_REENTRANT) && defined(_POSIX_C_SOURCE) && _POSIX_C_SOURCE >= 199506L +#define USE_READDIR_R 1 +#endif + +/* + * Objects of the following type are used to maintain the resources + * needed to read directories. + */ +struct DirReader { + ErrMsg *err; /* The error reporting buffer */ + DIR *dir; /* The directory stream (if open, NULL otherwise) */ + struct dirent *file; /* The latest directory entry */ +#ifdef USE_READDIR_R + struct dirent *buffer; /* A buffer used by the threaded version of */ + /* readdir() */ + int buffer_dim; /* The allocated size of buffer[] */ +#endif +}; + +static int _dr_path_is_dir(const char *pathname); + +/*....................................................................... + * Create a new DirReader object. + * + * Output: + * return DirReader * The new object, or NULL on error. + */ +DirReader *_new_DirReader(void) +{ + DirReader *dr; /* The object to be returned */ +/* + * Allocate the container. + */ + dr = (DirReader *) malloc(sizeof(DirReader)); + if(!dr) { + errno = ENOMEM; + return NULL; + }; +/* + * Before attempting any operation that might fail, initialize the + * container at least up to the point at which it can safely be passed + * to _del_DirReader(). + */ + dr->err = NULL; + dr->dir = NULL; + dr->file = NULL; +#ifdef USE_READDIR_R + dr->buffer = NULL; + dr->buffer_dim = 0; +#endif +/* + * Allocate a place to record error messages. + */ + dr->err = _new_ErrMsg(); + if(!dr->err) + return _del_DirReader(dr); + return dr; +} + +/*....................................................................... + * Delete a DirReader object. + * + * Input: + * dr DirReader * The object to be deleted. + * Output: + * return DirReader * The deleted object (always NULL). + */ +DirReader *_del_DirReader(DirReader *dr) +{ + if(dr) { + _dr_close_dir(dr); +#ifdef USE_READDIR_R + free(dr->buffer); +#endif + dr->err = _del_ErrMsg(dr->err); + free(dr); + }; + return NULL; +} + +/*....................................................................... + * Open a new directory. + * + * Input: + * dr DirReader * The directory reader resource object. + * path const char * The directory to be opened. + * Input/Output: + * errmsg char ** If an error occurs and errmsg isn't NULL, a + * pointer to an error description will be assigned + * to *errmsg. + * Output: + * return int 0 - OK. + * 1 - Error (see *errmsg for a description). + */ +int _dr_open_dir(DirReader *dr, const char *path, char **errmsg) +{ + DIR *dir = NULL; /* The directory stream */ +/* + * If a directory is already open, close it first. + */ + (void) _dr_close_dir(dr); +/* + * Is the path a directory? + */ + if(!_dr_path_is_dir(path)) { + if(errmsg) { + _err_record_msg(dr->err, "Can't open directory: ", path, END_ERR_MSG); + *errmsg = _err_get_msg(dr->err); + }; + return 1; + }; +/* + * Attempt to open the directory. + */ + dir = opendir(path); + if(!dir) { + if(errmsg) { + _err_record_msg(dr->err, "Can't open directory: ", path, END_ERR_MSG); + *errmsg = _err_get_msg(dr->err); + }; + return 1; + }; +/* + * If using POSIX threads, allocate a buffer for readdir_r(). + */ +#ifdef USE_READDIR_R + { + size_t size; + int name_max = pathconf(path, _PC_NAME_MAX); +#ifdef NAME_MAX + if(name_max < 0) + name_max = NAME_MAX; +#endif + if(name_max < 0) { + if(errmsg) { + _err_record_msg(dr->err, "Unable to deduce readdir() buffer size.", + END_ERR_MSG); + *errmsg = _err_get_msg(dr->err); + }; + closedir(dir); + return 1; + }; +/* + * How big a buffer do we need to allocate? + */ + size = sizeof(struct dirent) + name_max; +/* + * Extend the buffer? + */ + if(size > dr->buffer_dim || !dr->buffer) { + struct dirent *buffer = (struct dirent *) (dr->buffer ? + realloc(dr->buffer, size) : + malloc(size)); + if(!buffer) { + if(errmsg) { + _err_record_msg(dr->err, "Insufficient memory for readdir() buffer.", + END_ERR_MSG); + *errmsg = _err_get_msg(dr->err); + }; + closedir(dir); + errno = ENOMEM; + return 1; + }; + dr->buffer = buffer; + dr->buffer_dim = size; + }; + }; +#endif +/* + * Record the successfully opened directory. + */ + dr->dir = dir; + return 0; +} + +/*....................................................................... + * If the DirReader object is currently contains an open directory, + * close it. + * + * Input: + * dr DirReader * The directory reader resource object. + */ +void _dr_close_dir(DirReader *dr) +{ + if(dr && dr->dir) { + closedir(dr->dir); + dr->dir = NULL; + dr->file = NULL; + _err_clear_msg(dr->err); + }; +} + +/*....................................................................... + * Read the next file from the directory opened with _dr_open_dir(). + * + * Input: + * dr DirReader * The directory reader resource object. + * Output: + * return char * The name of the new file, or NULL if we reached + * the end of the directory. + */ +char *_dr_next_file(DirReader *dr) +{ +/* + * Are we currently reading a directory? + */ + if(dr->dir) { +/* + * Read the next directory entry. + */ +#ifdef USE_READDIR_R + if(readdir_r(dr->dir, dr->buffer, &dr->file) == 0 && dr->file) + return dr->file->d_name; +#else + dr->file = readdir(dr->dir); + if(dr->file) + return dr->file->d_name; +#endif + }; +/* + * When the end of a directory is reached, close it. + */ + _dr_close_dir(dr); + return NULL; +} + +/*....................................................................... + * Return 1 if the specified pathname refers to a directory. + * + * Input: + * pathname const char * The path to test. + * Output: + * return int 0 - Not a directory. + * 1 - pathname[] refers to a directory. + */ +static int _dr_path_is_dir(const char *pathname) +{ + struct stat statbuf; /* The file-statistics return buffer */ +/* + * Look up the file attributes. + */ + if(stat(pathname, &statbuf) < 0) + return 0; +/* + * Is the file a directory? + */ + return S_ISDIR(statbuf.st_mode) != 0; +} + +#endif /* ifndef WITHOUT_FILE_SYSTEM */ diff --git a/libtecla-1.6.1/direader.h b/libtecla-1.6.1/direader.h new file mode 100644 index 0000000..c529231 --- /dev/null +++ b/libtecla-1.6.1/direader.h @@ -0,0 +1,44 @@ +#ifndef dirreader_h +#define dirreader_h + +/* + * Copyright (c) 2000, 2001, 2002, 2003, 2004 by Martin C. Shepherd. + * + * All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, and/or sell copies of the Software, and to permit persons + * to whom the Software is furnished to do so, provided that the above + * copyright notice(s) and this permission notice appear in all copies of + * the Software and that both the above copyright notice(s) and this + * permission notice appear in supporting documentation. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT + * OF THIRD PARTY RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR + * HOLDERS INCLUDED IN THIS NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL + * INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING + * FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, + * NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION + * WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + * Except as contained in this notice, the name of a copyright holder + * shall not be used in advertising or otherwise to promote the sale, use + * or other dealings in this Software without prior written authorization + * of the copyright holder. + */ + +typedef struct DirReader DirReader; + +DirReader *_new_DirReader(void); +DirReader *_del_DirReader(DirReader *dr); + +int _dr_open_dir(DirReader *dr, const char *pathname, char **errmsg); +char *_dr_next_file(DirReader *dr); +void _dr_close_dir(DirReader *dr); + +#endif diff --git a/libtecla-1.6.1/enhance.c b/libtecla-1.6.1/enhance.c new file mode 100644 index 0000000..bd5af1f --- /dev/null +++ b/libtecla-1.6.1/enhance.c @@ -0,0 +1,695 @@ +#include +#include +#include +#include +#include +#include + +#include +#include + +#ifdef HAVE_SELECT +#ifdef HAVE_SYS_SELECT_H +#include +#endif +#endif + +#include +#include +#include +#include +#include + +#if HAVE_SYSV_PTY +#include /* System-V stream I/O */ +char *ptsname(int fd); +int grantpt(int fd); +int unlockpt(int fd); +#endif + +#include "libtecla.h" + +/* + * Pseudo-terminal devices are found in the following directory. + */ +#define PTY_DEV_DIR "/dev/" + +/* + * Pseudo-terminal controller device file names start with the following + * prefix. + */ +#define PTY_CNTRL "pty" + +/* + * Pseudo-terminal slave device file names start with the following + * prefix. + */ +#define PTY_SLAVE "tty" + +/* + * Specify the maximum suffix length for the control and slave device + * names. + */ +#define PTY_MAX_SUFFIX 10 + +/* + * Set the maximum length of the master and slave terminal device filenames, + * including space for a terminating '\0'. + */ +#define PTY_MAX_NAME (sizeof(PTY_DEV_DIR)-1 + \ + (sizeof(PTY_SLAVE) > sizeof(PTY_CNTRL) ? \ + sizeof(PTY_SLAVE) : sizeof(PTY_CNTRL))-1 \ + + PTY_MAX_SUFFIX + 1) +/* + * Set the maximum length of an input line. + */ +#define PTY_MAX_LINE 4096 + +/* + * Set the size of the buffer used for accumulating bytes written by the + * user's terminal to its stdout. + */ +#define PTY_MAX_READ 1000 + +/* + * Set the amount of memory used to record history. + */ +#define PTY_HIST_SIZE 10000 + +/* + * Set the timeout delay used to check for quickly arriving + * sequential output from the application. + */ +#define PTY_READ_TIMEOUT 100000 /* micro-seconds */ + +static int pty_open_master(const char *prog, int *cntrl, char *slave_name); +static int pty_open_slave(const char *prog, char *slave_name); +static int pty_child(const char *prog, int slave, char *argv[]); +static int pty_parent(const char *prog, int cntrl); +static int pty_stop_parent(int waserr, int cntrl, GetLine *gl, char *rbuff); +static GL_FD_EVENT_FN(pty_read_from_program); +static int pty_write_to_fd(int fd, const char *string, int n); +static void pty_child_exited(int sig); +static int pty_master_readable(int fd, long usec); + +/*....................................................................... + * Run a program with enhanced terminal editing facilities. + * + * Usage: + * enhance program [args...] + */ +int main(int argc, char *argv[]) +{ + int cntrl = -1; /* The fd of the pseudo-terminal controller device */ + int slave = -1; /* The fd of the pseudo-terminal slave device */ + pid_t pid; /* The return value of fork() */ + int status; /* The return statuses of the parent and child functions */ + char slave_name[PTY_MAX_NAME]; /* The filename of the slave end of the */ + /* pseudo-terminal. */ + char *prog; /* The name of the program (ie. argv[0]) */ +/* + * Check the arguments. + */ + if(argc < 2) { + fprintf(stderr, "Usage: %s [arguments...]\n", argv[0]); + return 1; + }; +/* + * Get the name of the program. + */ + prog = argv[0]; +/* + * If the user has the LC_CTYPE or LC_ALL environment variables set, + * enable display of characters corresponding to the specified locale. + */ + (void) setlocale(LC_CTYPE, ""); +/* + * If the program is taking its input from a pipe or a file, or + * sending its output to something other than a terminal, run the + * program without tecla. + */ + if(!isatty(STDIN_FILENO) || !isatty(STDOUT_FILENO)) { + if(execvp(argv[1], argv + 1) < 0) { + fprintf(stderr, "%s: Unable to execute %s (%s).\n", prog, argv[1], + strerror(errno)); + fflush(stderr); + _exit(1); + }; + }; +/* + * Open the master side of a pseudo-terminal pair, and return + * the corresponding file descriptor and the filename of the + * slave end of the pseudo-terminal. + */ + if(pty_open_master(prog, &cntrl, slave_name)) + return 1; +/* + * Set up a signal handler to watch for the child process exiting. + */ + signal(SIGCHLD, pty_child_exited); +/* + * The above signal handler sends the parent process a SIGINT signal. + * This signal is caught by gl_get_line(), which resets the terminal + * settings, and if the application signal handler for this signal + * doesn't abort the process, gl_get_line() returns NULL with errno + * set to EINTR. Arrange to ignore the signal, so that gl_get_line() + * returns and we have a chance to cleanup. + */ + signal(SIGINT, SIG_IGN); +/* + * We will read user input in one process, and run the user's program + * in a child process. + */ + pid = fork(); + if(pid < 0) { + fprintf(stderr, "%s: Unable to fork child process (%s).\n", prog, + strerror(errno)); + return 1; + }; +/* + * Are we the parent? + */ + if(pid!=0) { + status = pty_parent(prog, cntrl); + close(cntrl); + } else { + close(cntrl); /* The child doesn't use the slave device */ + signal(SIGCHLD, pty_child_exited); + if((slave = pty_open_slave(prog, slave_name)) >= 0) { + status = pty_child(prog, slave, argv + 1); + close(slave); + } else { + status = 1; + }; + }; + return status; +} + +/*....................................................................... + * Open the master side of a pseudo-terminal pair, and return + * the corresponding file descriptor and the filename of the + * slave end of the pseudo-terminal. + * + * Input/Output: + * prog const char * The name of this program. + * cntrl int * The file descriptor of the pseudo-terminal + * controller device will be assigned tp *cntrl. + * slave_name char * The file-name of the pseudo-terminal slave device + * will be recorded in slave_name[], which must have + * at least PTY_MAX_NAME elements. + * Output: + * return int 0 - OK. + * 1 - Error. + */ +static int pty_open_master(const char *prog, int *cntrl, char *slave_name) +{ + char master_name[PTY_MAX_NAME]; /* The filename of the master device */ + DIR *dir; /* The directory iterator */ + struct dirent *file; /* A file in "/dev" */ +/* + * Mark the controller device as not opened yet. + */ + *cntrl = -1; +/* + * On systems with the Sys-V pseudo-terminal interface, we don't + * have to search for a free master terminal. We just open /dev/ptmx, + * and if there is a free master terminal device, we are given a file + * descriptor connected to it. + */ +#if HAVE_SYSV_PTY + *cntrl = open("/dev/ptmx", O_RDWR); + if(*cntrl >= 0) { +/* + * Get the filename of the slave side of the pseudo-terminal. + */ + char *name = ptsname(*cntrl); + if(name) { + if(strlen(name)+1 > PTY_MAX_NAME) { + fprintf(stderr, "%s: Slave pty filename too long.\n", prog); + return 1; + }; + strcpy(slave_name, name); +/* + * If unable to get the slave name, discard the controller file descriptor, + * ready to try a search instead. + */ + } else { + close(*cntrl); + *cntrl = -1; + }; + } else { +#endif +/* + * On systems without /dev/ptmx, or if opening /dev/ptmx failed, + * we open one master terminal after another, until one that isn't + * in use by another program is found. + * + * Open the devices directory. + */ + dir = opendir(PTY_DEV_DIR); + if(!dir) { + fprintf(stderr, "%s: Couldn't open %s (%s)\n", prog, PTY_DEV_DIR, + strerror(errno)); + return 1; + }; +/* + * Look for pseudo-terminal controller device files in the devices + * directory. + */ + while(*cntrl < 0 && (file = readdir(dir))) { + if(strncmp(file->d_name, PTY_CNTRL, sizeof(PTY_CNTRL)-1) == 0) { +/* + * Get the common extension of the control and slave filenames. + */ + const char *ext = file->d_name + sizeof(PTY_CNTRL)-1; + if(strlen(ext) > PTY_MAX_SUFFIX) + continue; +/* + * Attempt to open the control file. + */ + strcpy(master_name, PTY_DEV_DIR); + strcat(master_name, PTY_CNTRL); + strcat(master_name, ext); + *cntrl = open(master_name, O_RDWR); + if(*cntrl < 0) + continue; +/* + * Attempt to open the matching slave file. + */ + strcpy(slave_name, PTY_DEV_DIR); + strcat(slave_name, PTY_SLAVE); + strcat(slave_name, ext); + }; + }; + closedir(dir); +#if HAVE_SYSV_PTY + }; +#endif +/* + * Did we fail to find a pseudo-terminal pair that we could open? + */ + if(*cntrl < 0) { + fprintf(stderr, "%s: Unable to find a free pseudo-terminal.\n", prog); + return 1; + }; +/* + * System V systems require the program that opens the master to + * grant access to the slave side of the pseudo-terminal. + */ +#ifdef HAVE_SYSV_PTY + if(grantpt(*cntrl) < 0 || + unlockpt(*cntrl) < 0) { + fprintf(stderr, "%s: Unable to unlock terminal (%s).\n", prog, + strerror(errno)); + return 1; + }; +#endif +/* + * Success. + */ + return 0; +} + +/*....................................................................... + * Open the slave end of a pseudo-terminal. + * + * Input: + * prog const char * The name of this program. + * slave_name char * The filename of the slave device. + * Output: + * return int The file descriptor of the successfully opened + * slave device, or < 0 on error. + */ +static int pty_open_slave(const char *prog, char *slave_name) +{ + int fd; /* The file descriptor of the slave device */ +/* + * Place the process in its own process group. In system-V based + * OS's, this ensures that when the pseudo-terminal is opened, it + * becomes the controlling terminal of the process. + */ + if(setsid() < 0) { + fprintf(stderr, "%s: Unable to form new process group (%s).\n", prog, + strerror(errno)); + return -1; + }; +/* + * Attempt to open the specified device. + */ + fd = open(slave_name, O_RDWR); + if(fd < 0) { + fprintf(stderr, "%s: Unable to open pseudo-terminal slave device (%s).\n", + prog, strerror(errno)); + return -1; + }; +/* + * On system-V streams based systems, we need to push the stream modules + * that implement pseudo-terminal and termio interfaces. At least on + * Solaris, which pushes these automatically when a slave is opened, + * this is redundant, so ignore errors when pushing the modules. + */ +#if HAVE_SYSV_PTY + (void) ioctl(fd, I_PUSH, "ptem"); + (void) ioctl(fd, I_PUSH, "ldterm"); +/* + * On BSD based systems other than SunOS 4.x, the following makes the + * pseudo-terminal the controlling terminal of the child process. + * According to the pseudo-terminal example code in Steven's + * Advanced programming in the unix environment, the !defined(CIBAUD) + * part of the clause prevents this from being used under SunOS. Since + * I only have his code with me, and won't have access to the book, + * I don't know why this is necessary. + */ +#elif defined(TIOCSCTTY) && !defined(CIBAUD) + if(ioctl(fd, TIOCSCTTY, (char *) 0) < 0) { + fprintf(stderr, "%s: Unable to establish controlling terminal (%s).\n", + prog, strerror(errno)); + close(fd); + return -1; + }; +#endif + return fd; +} + +/*....................................................................... + * Read input from the controlling terminal of the program, using + * gl_get_line(), and feed it to the user's program running in a child + * process, via the controller side of the pseudo-terminal. Also pass + * data received from the user's program via the conroller end of + * the pseudo-terminal, to stdout. + * + * Input: + * prog const char * The name of this program. + * cntrl int The file descriptor of the controller end of the + * pseudo-terminal. + * Output: + * return int 0 - OK. + * 1 - Error. + */ +static int pty_parent(const char *prog, int cntrl) +{ + GetLine *gl = NULL; /* The gl_get_line() resource object */ + char *line; /* An input line read from the user */ + char *rbuff=NULL; /* A buffer for reading from the pseudo terminal */ +/* + * Allocate the gl_get_line() resource object. + */ + gl = new_GetLine(PTY_MAX_LINE, PTY_HIST_SIZE); + if(!gl) + return pty_stop_parent(1, cntrl, gl, rbuff); +/* + * Allocate a buffer to use to accumulate bytes read from the + * pseudo-terminal. + */ + rbuff = (char *) malloc(PTY_MAX_READ+1); + if(!rbuff) + return pty_stop_parent(1, cntrl, gl, rbuff); + rbuff[0] = '\0'; +/* + * Register an event handler to watch for data appearing from the + * user's program on the controller end of the pseudo terminal. + */ + if(gl_watch_fd(gl, cntrl, GLFD_READ, pty_read_from_program, rbuff)) + return pty_stop_parent(1, cntrl, gl, rbuff); +/* + * Read input lines from the user and pass them on to the user's program, + * by writing to the controller end of the pseudo-terminal. + */ + while((line=gl_get_line(gl, rbuff, NULL, 0))) { + if(pty_write_to_fd(cntrl, line, strlen(line))) + return pty_stop_parent(1, cntrl, gl, rbuff); + rbuff[0] = '\0'; + }; + return pty_stop_parent(0, cntrl, gl, rbuff); +} + +/*....................................................................... + * This is a private return function of pty_parent(), used to release + * dynamically allocated resources, close the controller end of the + * pseudo-terminal, and wait for the child to exit. It returns the + * exit status of the child process, unless the caller reports an + * error itself, in which case the caller's error status is returned. + * + * Input: + * waserr int True if the caller is calling this function because + * an error occured. + * cntrl int The file descriptor of the controller end of the + * pseudo-terminal. + * gl GetLine * The resource object of gl_get_line(). + * rbuff char * The buffer used to accumulate bytes read from + * the pseudo-terminal. + * Output: + * return int The desired exit status of the program. + */ +static int pty_stop_parent(int waserr, int cntrl, GetLine *gl, char *rbuff) +{ + int status; /* The return status of the child process */ +/* + * Close the controller end of the terminal. + */ + close(cntrl); +/* + * Delete the resource object. + */ + gl = del_GetLine(gl); +/* + * Delete the read buffer. + */ + if(rbuff) + free(rbuff); +/* + * Wait for the user's program to end. + */ + (void) wait(&status); +/* + * Return either our error status, or the return status of the child + * program. + */ + return waserr ? 1 : status; +} + +/*....................................................................... + * Run the user's program, with its stdin and stdout connected to the + * slave end of the psuedo-terminal. + * + * Input: + * prog const char * The name of this program. + * slave int The file descriptor of the slave end of the + * pseudo terminal. + * argv char *[] The argument vector to pass to the user's program, + * where argv[0] is the name of the user's program, + * and the last argument is followed by a pointer + * to NULL. + * Output: + * return int If this function returns at all, an error must + * have occured when trying to overlay the process + * with the user's program. In this case 1 is + * returned. + */ +static int pty_child(const char *prog, int slave, char *argv[]) +{ + struct termios attr; /* The terminal attributes */ +/* + * We need to stop the pseudo-terminal from echoing everything that we send it. + */ + if(tcgetattr(slave, &attr)) { + fprintf(stderr, "%s: Can't get pseudo-terminal attributes (%s).\n", prog, + strerror(errno)); + return 1; + }; + attr.c_lflag &= ~(ECHO); + while(tcsetattr(slave, TCSADRAIN, &attr)) { + if(errno != EINTR) { + fprintf(stderr, "%s: tcsetattr error: %s\n", prog, strerror(errno)); + return 1; + }; + }; +/* + * Arrange for stdin, stdout and stderr to be connected to the slave device, + * ignoring errors that imply that either stdin or stdout is closed. + */ + while(dup2(slave, STDIN_FILENO) < 0 && errno==EINTR) + ; + while(dup2(slave, STDOUT_FILENO) < 0 && errno==EINTR) + ; + while(dup2(slave, STDERR_FILENO) < 0 && errno==EINTR) + ; +/* + * Run the user's program. + */ + if(execvp(argv[0], argv) < 0) { + fprintf(stderr, "%s: Unable to execute %s (%s).\n", prog, argv[0], + strerror(errno)); + fflush(stderr); + _exit(1); + }; + return 0; /* This should never be reached */ +} + +/*....................................................................... + * This is the event-handler that is called by gl_get_line() whenever + * there is tet waiting to be read from the user's program, via the + * controller end of the pseudo-terminal. See libtecla.h for details + * about its arguments. + */ +static GL_FD_EVENT_FN(pty_read_from_program) +{ + char *nlptr; /* A pointer to the last newline in the accumulated string */ + char *crptr; /* A pointer to the last '\r' in the accumulated string */ + char *nextp; /* A pointer to the next unprocessed character */ +/* + * Get the read buffer in which we are accumulating a line to be + * forwarded to stdout. + */ + char *rbuff = (char *) data; +/* + * New data may arrive while we are processing the current read, and + * it is more efficient to display this here than to keep returning to + * gl_get_line() and have it display the latest prefix as a prompt, + * followed by the current input line, so we loop, delaying a bit at + * the end of each iteration to check for more data arriving from + * the application, before finally returning to gl_get_line() when + * no more input is available. + */ + do { +/* + * Get the current length of the output string. + */ + int len = strlen(rbuff); +/* + * Read the text from the program. + */ + int nnew = read(fd, rbuff + len, PTY_MAX_READ - len); + if(nnew < 0) + return GLFD_ABORT; + len += nnew; +/* + * Nul terminate the accumulated string. + */ + rbuff[len] = '\0'; +/* + * Find the last newline and last carriage return in the buffer, if any. + */ + nlptr = strrchr(rbuff, '\n'); + crptr = strrchr(rbuff, '\r'); +/* + * We want to output up to just before the last newline or carriage + * return. If there are no newlines of carriage returns in the line, + * and the buffer is full, then we should output the whole line. In + * all cases a new output line will be started after the latest text + * has been output. The intention is to leave any incomplete line + * in the buffer, for (perhaps temporary) use as the current prompt. + */ + if(nlptr) { + nextp = crptr && crptr < nlptr ? crptr : nlptr; + } else if(crptr) { + nextp = crptr; + } else if(len >= PTY_MAX_READ) { + nextp = rbuff + len; + } else { + nextp = NULL; + }; +/* + * Do we have any text to output yet? + */ + if(nextp) { +/* + * If there was already some text in rbuff before this function + * was called, then it will have been used as a prompt. Arrange + * to rewrite this prefix, plus the new suffix, by moving back to + * the start of the line. + */ + if(len > 0) + (void) pty_write_to_fd(STDOUT_FILENO, "\r", 1); +/* + * Write everything up to the last newline to stdout. + */ + (void) pty_write_to_fd(STDOUT_FILENO, rbuff, nextp - rbuff); +/* + * Start a new line. + */ + (void) pty_write_to_fd(STDOUT_FILENO, "\r\n", 2); +/* + * Skip trailing carriage returns and newlines. + */ + while(*nextp=='\n' || *nextp=='\r') + nextp++; +/* + * Move any unwritten text following the newline, to the start of the + * buffer. + */ + memmove(rbuff, nextp, len - (nextp - rbuff) + 1); + }; + } while(pty_master_readable(fd, PTY_READ_TIMEOUT)); +/* + * Make the incomplete line in the output buffer the current prompt. + */ + gl_replace_prompt(gl, rbuff); + return GLFD_REFRESH; +} + +/*....................................................................... + * Write a given string to a specified file descriptor. + * + * Input: + * fd int The file descriptor to write to. + * string const char * The string to write (of at least 'n' characters). + * n int The number of characters to write. + * Output: + * return int 0 - OK. + * 1 - Error. + */ +static int pty_write_to_fd(int fd, const char *string, int n) +{ + int ndone = 0; /* The number of characters written so far */ +/* + * Do as many writes as are needed to write the whole string. + */ + while(ndone < n) { + int nnew = write(fd, string + ndone, n - ndone); + if(nnew > 0) + ndone += nnew; + else if(errno != EINTR) + return 1; + }; + return 0; +} + +/*....................................................................... + * This is the signal handler that is called when the child process + * that is running the user's program exits for any reason. It closes + * the slave end of the terminal, so that gl_get_line() in the parent + * process sees an end of file. + */ +static void pty_child_exited(int sig) +{ + raise(SIGINT); +} + +/*....................................................................... + * Return non-zero after a given amount of time if there is data waiting + * to be read from a given file descriptor. + * + * Input: + * fd int The descriptor to watch. + * usec long The number of micro-seconds to wait for input to + * arrive before giving up. + * Output: + * return int 0 - No data is waiting to be read (or select isn't + * available). + * 1 - Data is waiting to be read. + */ +static int pty_master_readable(int fd, long usec) +{ +#if HAVE_SELECT + fd_set rfds; /* The set of file descriptors to check */ + struct timeval timeout; /* The timeout */ + FD_ZERO(&rfds); + FD_SET(fd, &rfds); + timeout.tv_sec = 0; + timeout.tv_usec = usec; + return select(fd+1, &rfds, NULL, NULL, &timeout) == 1; +#else + return 0; +#endif +} diff --git a/libtecla-1.6.1/errmsg.c b/libtecla-1.6.1/errmsg.c new file mode 100644 index 0000000..6abce9b --- /dev/null +++ b/libtecla-1.6.1/errmsg.c @@ -0,0 +1,167 @@ +/* + * Copyright (c) 2000, 2001, 2002, 2003, 2004 by Martin C. Shepherd. + * + * All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, and/or sell copies of the Software, and to permit persons + * to whom the Software is furnished to do so, provided that the above + * copyright notice(s) and this permission notice appear in all copies of + * the Software and that both the above copyright notice(s) and this + * permission notice appear in supporting documentation. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT + * OF THIRD PARTY RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR + * HOLDERS INCLUDED IN THIS NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL + * INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING + * FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, + * NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION + * WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + * Except as contained in this notice, the name of a copyright holder + * shall not be used in advertising or otherwise to promote the sale, use + * or other dealings in this Software without prior written authorization + * of the copyright holder. + */ + +#include +#include +#include +#include + +#include "errmsg.h" + +/* + * Encapsulate the error reporting buffer in an opaque object. + */ +struct ErrMsg { + char msg[ERR_MSG_LEN+1]; /* An error message */ +}; + +/*....................................................................... + * Create a new error-message object. + * + * Output: + * return ErrMsg * The new object, or NULL on error. + */ +ErrMsg *_new_ErrMsg(void) +{ + ErrMsg *err; /* The object to be returned */ +/* + * Allocate the container. + */ + err = malloc(sizeof(ErrMsg)); + if(!err) { + errno = ENOMEM; + return NULL; + }; +/* + * Before attempting any operation that might fail, initialize the + * container at least up to the point at which it can safely be passed + * to del_ErrMsg(). + */ + err->msg[0] = '\0'; + return err; +} + +/*....................................................................... + * Delete an error-message object. + * + * Input: + * err ErrMsg * The object to be deleted. + * Output: + * return ErrMsg * The deleted object (always NULL). + */ +ErrMsg *_del_ErrMsg(ErrMsg *err) +{ + if(err) { + free(err); + }; + return NULL; +} + +/*....................................................................... + * Record the concatenation of a list of string arguments in an error + * message object. The last argument must be END_ERR_MSG to terminate + * the argument list. + * + * Input: + * err ErrMsg * The error-message container. + * ... const char * Zero or more strings to be concatenated in buff[]. + * ... const char * The last argument must always be END_ERR_MSG to + * terminate the argument list. + */ +void _err_record_msg(ErrMsg *err, ...) +{ + va_list ap; /* The variable argument list */ + const char *s; /* The string being printed */ + size_t msglen = 0; /* The total length of the message */ +/* + * Nowhere to record the result? + */ + if(!err) { + errno = EINVAL; + return; + }; +/* + * Concatenate the list of argument strings in err->msg[]. + */ + va_start(ap, err); + while((s = va_arg(ap, const char *)) != END_ERR_MSG) { +/* + * How much room is left in the output buffer (note that the output + * buffer has ERR_MSG_LEN+1 elements). + */ + int nleft = ERR_MSG_LEN - msglen; +/* + * How long is the next string to be appended? + */ + size_t slen = strlen(s); +/* + * If there is any room left, append as much of the string + * as will fit. + */ + if(nleft > 0) { + int nnew = slen < nleft ? slen : nleft; + strncpy(err->msg + msglen, s, nnew); + msglen += nnew; + }; + }; + va_end(ap); +/* + * Terminate the message. + */ + err->msg[msglen] = '\0'; + return; +} + +/*....................................................................... + * Return a pointer to the error message buffer. + * + * Input: + * err ErrMsg * The container of the error message buffer. + * Output: + * return char * The current error message, or NULL if err==NULL. + */ +char *_err_get_msg(ErrMsg *err) +{ + return err ? err->msg : NULL; +} + +/*....................................................................... + * Replace the current error message with an empty string. + * + * Input: + * err ErrMsg * The container of the error message buffer. + */ +void _err_clear_msg(ErrMsg *err) +{ + if(err) + err->msg[0] = '\0'; +} + diff --git a/libtecla-1.6.1/errmsg.h b/libtecla-1.6.1/errmsg.h new file mode 100644 index 0000000..565d7cd --- /dev/null +++ b/libtecla-1.6.1/errmsg.h @@ -0,0 +1,85 @@ +#ifndef errmsg_h +#define errmsg_h + +/* + * Copyright (c) 2000, 2001, 2002, 2003, 2004 by Martin C. Shepherd. + * + * All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, and/or sell copies of the Software, and to permit persons + * to whom the Software is furnished to do so, provided that the above + * copyright notice(s) and this permission notice appear in all copies of + * the Software and that both the above copyright notice(s) and this + * permission notice appear in supporting documentation. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT + * OF THIRD PARTY RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR + * HOLDERS INCLUDED IN THIS NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL + * INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING + * FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, + * NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION + * WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + * Except as contained in this notice, the name of a copyright holder + * shall not be used in advertising or otherwise to promote the sale, use + * or other dealings in this Software without prior written authorization + * of the copyright holder. + */ + +/* + * Set the longest expected length of an error message (excluding its + * '\0' terminator. Since any message over a nominal terminal width of + * 80 characters is going to look a mess, it makes no sense to support + * huge lengths. Note that many uses of strings declared with this + * macro assume that it will be at least 81, so don't reduce it below + * this limit. + */ +#define ERR_MSG_LEN 128 + +/* + * Provide an opaque typedef to the error-message object. + */ +typedef struct ErrMsg ErrMsg; + +/* + * The following token is used to terminate the argument lists of calls + * to _err_record_msg(). + */ +#define END_ERR_MSG ((const char *)0) + +/* + * Allocate a new error-message buffer. + */ +ErrMsg *_new_ErrMsg(void); + +/* + * Delete an error message buffer. + */ +ErrMsg *_del_ErrMsg(ErrMsg *err); + +/* + * Concatenate a list of string arguments into the specified buffer, buff[], + * which has an allocated size of buffdim characters. + * The last argument must be END_ERR_MSG to terminate the argument list. + */ +void _err_record_msg(ErrMsg *err, ...); + +/* + * Replace the current error message with an empty string. + */ +void _err_clear_msg(ErrMsg *err); + +/* + * Return a pointer to the error message buffer. This is + * a '\0' terminated character array containing ERR_MSG_LEN+1 + * elements. + */ +char *_err_get_msg(ErrMsg *err); + +#endif diff --git a/libtecla-1.6.1/expand.c b/libtecla-1.6.1/expand.c new file mode 100644 index 0000000..b3d80e2 --- /dev/null +++ b/libtecla-1.6.1/expand.c @@ -0,0 +1,1448 @@ +/* + * Copyright (c) 2000, 2001, 2002, 2003, 2004 by Martin C. Shepherd. + * + * All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, and/or sell copies of the Software, and to permit persons + * to whom the Software is furnished to do so, provided that the above + * copyright notice(s) and this permission notice appear in all copies of + * the Software and that both the above copyright notice(s) and this + * permission notice appear in supporting documentation. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT + * OF THIRD PARTY RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR + * HOLDERS INCLUDED IN THIS NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL + * INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING + * FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, + * NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION + * WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + * Except as contained in this notice, the name of a copyright holder + * shall not be used in advertising or otherwise to promote the sale, use + * or other dealings in this Software without prior written authorization + * of the copyright holder. + */ + +/* + * If file-system access is to be excluded, this module has no function, + * so all of its code should be excluded. + */ +#ifndef WITHOUT_FILE_SYSTEM + +#include +#include +#include +#include + +#include "freelist.h" +#include "direader.h" +#include "pathutil.h" +#include "homedir.h" +#include "stringrp.h" +#include "libtecla.h" +#include "ioutil.h" +#include "expand.h" +#include "errmsg.h" + +/* + * Specify the number of elements to extend the files[] array by + * when it proves to be too small. This also sets the initial size + * of the array. + */ +#define MATCH_BLK_FACT 256 + +/* + * A list of directory iterators is maintained using nodes of the + * following form. + */ +typedef struct DirNode DirNode; +struct DirNode { + DirNode *next; /* The next directory in the list */ + DirNode *prev; /* The node that precedes this node in the list */ + DirReader *dr; /* The directory reader object */ +}; + +typedef struct { + FreeList *mem; /* Memory for DirNode list nodes */ + DirNode *head; /* The head of the list of used and unused cache nodes */ + DirNode *next; /* The next unused node between head and tail */ + DirNode *tail; /* The tail of the list of unused cache nodes */ +} DirCache; + +/* + * Specify how many directory cache nodes to allocate at a time. + */ +#define DIR_CACHE_BLK 20 + +/* + * Set the maximum length allowed for usernames. + */ +#define USR_LEN 100 + +/* + * Set the maximum length allowed for environment variable names. + */ +#define ENV_LEN 100 + +/* + * Set the default number of spaces place between columns when listing + * a set of expansions. + */ +#define EF_COL_SEP 2 + +struct ExpandFile { + ErrMsg *err; /* The error reporting buffer */ + StringGroup *sg; /* A list of string segments in which */ + /* matching filenames are stored. */ + DirCache cache; /* The cache of directory reader objects */ + PathName *path; /* The pathname being matched */ + HomeDir *home; /* Home-directory lookup object */ + int files_dim; /* The allocated dimension of result.files[] */ + char usrnam[USR_LEN+1]; /* A user name */ + char envnam[ENV_LEN+1]; /* An environment variable name */ + FileExpansion result; /* The container used to return the results of */ + /* expanding a path. */ +}; + +static int ef_record_pathname(ExpandFile *ef, const char *pathname, + int remove_escapes); +static char *ef_cache_pathname(ExpandFile *ef, const char *pathname, + int remove_escapes); +static void ef_clear_files(ExpandFile *ef); + +static DirNode *ef_open_dir(ExpandFile *ef, const char *pathname); +static DirNode *ef_close_dir(ExpandFile *ef, DirNode *node); +static char *ef_expand_special(ExpandFile *ef, const char *path, int pathlen); +static int ef_match_relative_pathname(ExpandFile *ef, DirReader *dr, + const char *pattern, int separate); +static int ef_matches_range(int c, const char *pattern, const char **endp); +static int ef_string_matches_pattern(const char *file, const char *pattern, + int xplicit, const char *nextp); +static int ef_cmp_strings(const void *v1, const void *v2); + +/* + * Encapsulate the formatting information needed to layout a + * multi-column listing of expansions. + */ +typedef struct { + int term_width; /* The width of the terminal (characters) */ + int column_width; /* The number of characters within in each column. */ + int ncol; /* The number of columns needed */ + int nline; /* The number of lines needed */ +} EfListFormat; + +/* + * Given the current terminal width, and a list of file expansions, + * determine how to best use the terminal width to display a multi-column + * listing of expansions. + */ +static void ef_plan_listing(FileExpansion *result, int term_width, + EfListFormat *fmt); + +/* + * Display a given line of a multi-column list of file-expansions. + */ +static int ef_format_line(FileExpansion *result, EfListFormat *fmt, int lnum, + GlWriteFn *write_fn, void *data); + +/*....................................................................... + * Create the resources needed to expand filenames. + * + * Output: + * return ExpandFile * The new object, or NULL on error. + */ +ExpandFile *new_ExpandFile(void) +{ + ExpandFile *ef; /* The object to be returned */ +/* + * Allocate the container. + */ + ef = (ExpandFile *) malloc(sizeof(ExpandFile)); + if(!ef) { + errno = ENOMEM; + return NULL; + }; +/* + * Before attempting any operation that might fail, initialize the + * container at least up to the point at which it can safely be passed + * to del_ExpandFile(). + */ + ef->err = NULL; + ef->sg = NULL; + ef->cache.mem = NULL; + ef->cache.head = NULL; + ef->cache.next = NULL; + ef->cache.tail = NULL; + ef->path = NULL; + ef->home = NULL; + ef->result.files = NULL; + ef->result.nfile = 0; + ef->usrnam[0] = '\0'; + ef->envnam[0] = '\0'; +/* + * Allocate a place to record error messages. + */ + ef->err = _new_ErrMsg(); + if(!ef->err) + return del_ExpandFile(ef); +/* + * Allocate a list of string segments for storing filenames. + */ + ef->sg = _new_StringGroup(_pu_pathname_dim()); + if(!ef->sg) + return del_ExpandFile(ef); +/* + * Allocate a freelist for allocating directory cache nodes. + */ + ef->cache.mem = _new_FreeList(sizeof(DirNode), DIR_CACHE_BLK); + if(!ef->cache.mem) + return del_ExpandFile(ef); +/* + * Allocate a pathname buffer. + */ + ef->path = _new_PathName(); + if(!ef->path) + return del_ExpandFile(ef); +/* + * Allocate an object for looking up home-directories. + */ + ef->home = _new_HomeDir(); + if(!ef->home) + return del_ExpandFile(ef); +/* + * Allocate an array for files. This will be extended later if needed. + */ + ef->files_dim = MATCH_BLK_FACT; + ef->result.files = (char **) malloc(sizeof(ef->result.files[0]) * + ef->files_dim); + if(!ef->result.files) { + errno = ENOMEM; + return del_ExpandFile(ef); + }; + return ef; +} + +/*....................................................................... + * Delete a ExpandFile object. + * + * Input: + * ef ExpandFile * The object to be deleted. + * Output: + * return ExpandFile * The deleted object (always NULL). + */ +ExpandFile *del_ExpandFile(ExpandFile *ef) +{ + if(ef) { + DirNode *dnode; +/* + * Delete the string segments. + */ + ef->sg = _del_StringGroup(ef->sg); +/* + * Delete the cached directory readers. + */ + for(dnode=ef->cache.head; dnode; dnode=dnode->next) + dnode->dr = _del_DirReader(dnode->dr); +/* + * Delete the memory from which the DirNode list was allocated, thus + * deleting the list at the same time. + */ + ef->cache.mem = _del_FreeList(ef->cache.mem, 1); + ef->cache.head = ef->cache.tail = ef->cache.next = NULL; +/* + * Delete the pathname buffer. + */ + ef->path = _del_PathName(ef->path); +/* + * Delete the home-directory lookup object. + */ + ef->home = _del_HomeDir(ef->home); +/* + * Delete the array of pointers to files. + */ + if(ef->result.files) { + free(ef->result.files); + ef->result.files = NULL; + }; +/* + * Delete the error report buffer. + */ + ef->err = _del_ErrMsg(ef->err); +/* + * Delete the container. + */ + free(ef); + }; + return NULL; +} + +/*....................................................................... + * Expand a pathname, converting ~user/ and ~/ patterns at the start + * of the pathname to the corresponding home directories, replacing + * $envvar with the value of the corresponding environment variable, + * and then, if there are any wildcards, matching these against existing + * filenames. + * + * If no errors occur, a container is returned containing the array of + * files that resulted from the expansion. If there were no wildcards + * in the input pathname, this will contain just the original pathname + * after expansion of ~ and $ expressions. If there were any wildcards, + * then the array will contain the files that matched them. Note that + * if there were any wildcards but no existing files match them, this + * is counted as an error and NULL is returned. + * + * The supported wildcards and their meanings are: + * * - Match any sequence of zero or more characters. + * ? - Match any single character. + * [chars] - Match any single character that appears in 'chars'. + * If 'chars' contains an expression of the form a-b, + * then any character between a and b, including a and b, + * matches. The '-' character looses its special meaning + * as a range specifier when it appears at the start + * of the sequence of characters. + * [^chars] - The same as [chars] except that it matches any single + * character that doesn't appear in 'chars'. + * + * Wildcard expressions are applied to individual filename components. + * They don't match across directory separators. A '.' character at + * the beginning of a filename component must also be matched + * explicitly by a '.' character in the input pathname, since these + * are UNIX's hidden files. + * + * Input: + * ef ExpandFile * The pathname expansion resource object. + * path char * The path name to be expanded. + * pathlen int The length of the suffix of path[] that + * constitutes the filename to be expanded, + * or -1 to specify that the whole of the + * path string should be used. Note that + * regardless of the value of this argument, + * path[] must contain a '\0' terminated + * string, since this function checks that + * pathlen isn't mistakenly too long. + * Output: + * return FileExpansion * A pointer to a container within the given + * ExpandFile object. This contains an array + * of the pathnames that resulted from expanding + * ~ and $ expressions and from matching any + * wildcards, sorted into lexical order. + * This container and its contents will be + * recycled on subsequent calls, so if you need + * to keep the results of two successive runs, + * you will either have to allocate a private + * copy of the array, or use two ExpandFile + * objects. + * + * On error NULL is returned. A description + * of the error can be acquired by calling the + * ef_last_error() function. + */ +FileExpansion *ef_expand_file(ExpandFile *ef, const char *path, int pathlen) +{ + DirNode *dnode; /* A directory-reader cache node */ + const char *dirname; /* The name of the top level directory of the search */ + const char *pptr; /* A pointer into path[] */ + int wild; /* True if the path contains any wildcards */ +/* + * Check the arguments. + */ + if(!ef || !path) { + if(ef) { + _err_record_msg(ef->err, "ef_expand_file: NULL path argument", + END_ERR_MSG); + }; + errno = EINVAL; + return NULL; + }; +/* + * If the caller specified that the whole of path[] be matched, + * work out the corresponding length. + */ + if(pathlen < 0 || pathlen > strlen(path)) + pathlen = strlen(path); +/* + * Discard previous expansion results. + */ + ef_clear_files(ef); +/* + * Preprocess the path, expanding ~/, ~user/ and $envvar references, + * using ef->path as a work directory and returning a pointer to + * a copy of the resulting pattern in the cache. + */ + path = ef_expand_special(ef, path, pathlen); + if(!path) + return NULL; +/* + * Clear the pathname buffer. + */ + _pn_clear_path(ef->path); +/* + * Does the pathname contain any wildcards? + */ + for(wild=0,pptr=path; !wild && *pptr; pptr++) { + switch(*pptr) { + case '\\': /* Skip escaped characters */ + if(pptr[1]) + pptr++; + break; + case '*': case '?': case '[': /* A wildcard character? */ + wild = 1; + break; + }; + }; +/* + * If there are no wildcards to match, copy the current expanded + * path into the output array, removing backslash escapes while doing so. + */ + if(!wild) { + if(ef_record_pathname(ef, path, 1)) + return NULL; +/* + * Does the filename exist? + */ + ef->result.exists = _pu_file_exists(ef->result.files[0]); +/* + * Match wildcards against existing files. + */ + } else { +/* + * Only existing files that match the pattern will be returned in the + * cache. + */ + ef->result.exists = 1; +/* + * Treat matching of the root-directory as a special case since it + * isn't contained in a directory. + */ + if(strcmp(path, FS_ROOT_DIR) == 0) { + if(ef_record_pathname(ef, FS_ROOT_DIR, 0)) + return NULL; + } else { +/* + * What should the top level directory of the search be? + */ + if(strncmp(path, FS_ROOT_DIR, FS_ROOT_DIR_LEN) == 0) { + dirname = FS_ROOT_DIR; + if(!_pn_append_to_path(ef->path, FS_ROOT_DIR, -1, 0)) { + _err_record_msg(ef->err, "Insufficient memory to record path", + END_ERR_MSG); + return NULL; + }; + path += FS_ROOT_DIR_LEN; + } else { + dirname = FS_PWD; + }; +/* + * Open the top-level directory of the search. + */ + dnode = ef_open_dir(ef, dirname); + if(!dnode) + return NULL; +/* + * Recursively match successive directory components of the path. + */ + if(ef_match_relative_pathname(ef, dnode->dr, path, 0)) { + dnode = ef_close_dir(ef, dnode); + return NULL; + }; +/* + * Cleanup. + */ + dnode = ef_close_dir(ef, dnode); + }; +/* + * No files matched? + */ + if(ef->result.nfile < 1) { + _err_record_msg(ef->err, "No files match", END_ERR_MSG); + return NULL; + }; +/* + * Sort the pathnames that matched. + */ + qsort(ef->result.files, ef->result.nfile, sizeof(ef->result.files[0]), + ef_cmp_strings); + }; +/* + * Return the result container. + */ + return &ef->result; +} + +/*....................................................................... + * Attempt to recursively match the given pattern with the contents of + * the current directory, descending sub-directories as needed. + * + * Input: + * ef ExpandFile * The pathname expansion resource object. + * dr DirReader * The directory reader object of the directory + * to be searched. + * pattern const char * The pattern to match with files in the current + * directory. + * separate int When appending a filename from the specified + * directory to ef->pathname, insert a directory + * separator between the existing pathname and + * the filename, unless separate is zero. + * Output: + * return int 0 - OK. + * 1 - Error. + */ +static int ef_match_relative_pathname(ExpandFile *ef, DirReader *dr, + const char *pattern, int separate) +{ + const char *nextp; /* The pointer to the character that follows the part */ + /* of the pattern that is to be matched with files */ + /* in the current directory. */ + char *file; /* The name of the file being matched */ + int pathlen; /* The length of ef->pathname[] on entry to this */ + /* function */ +/* + * Record the current length of the pathname string recorded in + * ef->pathname[]. + */ + pathlen = strlen(ef->path->name); +/* + * Get a pointer to the character that follows the end of the part of + * the pattern that should be matched to files within the current directory. + * This will either point to a directory separator, or to the '\0' terminator + * of the pattern string. + */ + for(nextp=pattern; *nextp && strncmp(nextp, FS_DIR_SEP, FS_DIR_SEP_LEN) != 0; + nextp++) + ; +/* + * Read each file from the directory, attempting to match it to the + * current pattern. + */ + while((file=_dr_next_file(dr)) != NULL) { +/* + * Does the latest file match the pattern up to nextp? + */ + if(ef_string_matches_pattern(file, pattern, file[0]=='.', nextp)) { +/* + * Append the new directory entry to the current matching pathname. + */ + if((separate && _pn_append_to_path(ef->path, FS_DIR_SEP, -1, 0)==NULL) || + _pn_append_to_path(ef->path, file, -1, 0)==NULL) { + _err_record_msg(ef->err, "Insufficient memory to record path", + END_ERR_MSG); + return 1; + }; +/* + * If we have reached the end of the pattern, record the accumulated + * pathname in the list of matching files. + */ + if(*nextp == '\0') { + if(ef_record_pathname(ef, ef->path->name, 0)) + return 1; +/* + * If the matching directory entry is a subdirectory, and the + * next character of the pattern is a directory separator, + * recursively call the current function to scan the sub-directory + * for matches. + */ + } else if(_pu_path_is_dir(ef->path->name) && + strncmp(nextp, FS_DIR_SEP, FS_DIR_SEP_LEN) == 0) { +/* + * If the pattern finishes with the directory separator, then + * record the pathame as matching. + */ + if(nextp[FS_DIR_SEP_LEN] == '\0') { + if(ef_record_pathname(ef, ef->path->name, 0)) + return 1; +/* + * Match files within the directory. + */ + } else { + DirNode *subdnode = ef_open_dir(ef, ef->path->name); + if(subdnode) { + if(ef_match_relative_pathname(ef, subdnode->dr, + nextp+FS_DIR_SEP_LEN, 1)) { + subdnode = ef_close_dir(ef, subdnode); + return 1; + }; + subdnode = ef_close_dir(ef, subdnode); + }; + }; + }; +/* + * Remove the latest filename from the pathname string, so that + * another matching file can be appended. + */ + ef->path->name[pathlen] = '\0'; + }; + }; + return 0; +} + +/*....................................................................... + * Record a new matching filename. + * + * Input: + * ef ExpandFile * The filename-match resource object. + * pathname const char * The pathname to record. + * remove_escapes int If true, remove backslash escapes in the + * recorded copy of the pathname. + * Output: + * return int 0 - OK. + * 1 - Error (ef->err will contain a + * description of the error). + */ +static int ef_record_pathname(ExpandFile *ef, const char *pathname, + int remove_escapes) +{ + char *copy; /* The recorded copy of pathname[] */ +/* + * Attempt to make a copy of the pathname in the cache. + */ + copy = ef_cache_pathname(ef, pathname, remove_escapes); + if(!copy) + return 1; +/* + * If there isn't room to record a pointer to the recorded pathname in the + * array of files, attempt to extend the array. + */ + if(ef->result.nfile + 1 > ef->files_dim) { + int files_dim = ef->files_dim + MATCH_BLK_FACT; + char **files = (char **) realloc(ef->result.files, + files_dim * sizeof(files[0])); + if(!files) { + _err_record_msg(ef->err, + "Insufficient memory to record all of the matching filenames", + END_ERR_MSG); + errno = ENOMEM; + return 1; + }; + ef->result.files = files; + ef->files_dim = files_dim; + }; +/* + * Record a pointer to the new match. + */ + ef->result.files[ef->result.nfile++] = copy; + return 0; +} + +/*....................................................................... + * Record a pathname in the cache. + * + * Input: + * ef ExpandFile * The filename-match resource object. + * pathname char * The pathname to record. + * remove_escapes int If true, remove backslash escapes in the + * copy of the pathname. + * Output: + * return char * The pointer to the copy of the pathname. + * On error NULL is returned and a description + * of the error is left in ef->err. + */ +static char *ef_cache_pathname(ExpandFile *ef, const char *pathname, + int remove_escapes) +{ + char *copy = _sg_store_string(ef->sg, pathname, remove_escapes); + if(!copy) + _err_record_msg(ef->err, "Insufficient memory to store pathname", + END_ERR_MSG); + return copy; +} + +/*....................................................................... + * Clear the results of the previous expansion operation, ready for the + * next. + * + * Input: + * ef ExpandFile * The pathname expansion resource object. + */ +static void ef_clear_files(ExpandFile *ef) +{ + _clr_StringGroup(ef->sg); + _pn_clear_path(ef->path); + ef->result.exists = 0; + ef->result.nfile = 0; + _err_clear_msg(ef->err); + return; +} + +/*....................................................................... + * Get a new directory reader object from the cache. + * + * Input: + * ef ExpandFile * The pathname expansion resource object. + * pathname const char * The pathname of the directory. + * Output: + * return DirNode * The cache entry of the new directory reader, + * or NULL on error. On error, ef->err will + * contain a description of the error. + */ +static DirNode *ef_open_dir(ExpandFile *ef, const char *pathname) +{ + char *errmsg = NULL; /* An error message from a called function */ + DirNode *node; /* The cache node used */ +/* + * Get the directory reader cache. + */ + DirCache *cache = &ef->cache; +/* + * Extend the cache if there are no free cache nodes. + */ + if(!cache->next) { + node = (DirNode *) _new_FreeListNode(cache->mem); + if(!node) { + _err_record_msg(ef->err, "Insufficient memory to open a new directory", + END_ERR_MSG); + return NULL; + }; +/* + * Initialize the cache node. + */ + node->next = NULL; + node->prev = NULL; + node->dr = NULL; +/* + * Allocate a directory reader object. + */ + node->dr = _new_DirReader(); + if(!node->dr) { + _err_record_msg(ef->err, "Insufficient memory to open a new directory", + END_ERR_MSG); + node = (DirNode *) _del_FreeListNode(cache->mem, node); + return NULL; + }; +/* + * Append the node to the cache list. + */ + node->prev = cache->tail; + if(cache->tail) + cache->tail->next = node; + else + cache->head = node; + cache->next = cache->tail = node; + }; +/* + * Get the first unused node, but don't remove it from the list yet. + */ + node = cache->next; +/* + * Attempt to open the specified directory. + */ + if(_dr_open_dir(node->dr, pathname, &errmsg)) { + _err_record_msg(ef->err, errmsg, END_ERR_MSG); + return NULL; + }; +/* + * Now that we have successfully opened the specified directory, + * remove the cache node from the list, and relink the list around it. + */ + cache->next = node->next; + if(node->prev) + node->prev->next = node->next; + else + cache->head = node->next; + if(node->next) + node->next->prev = node->prev; + else + cache->tail = node->prev; + node->next = node->prev = NULL; +/* + * Return the successfully initialized cache node to the caller. + */ + return node; +} + +/*....................................................................... + * Return a directory reader object to the cache, after first closing + * the directory that it was managing. + * + * Input: + * ef ExpandFile * The pathname expansion resource object. + * node DirNode * The cache entry of the directory reader, as returned + * by ef_open_dir(). + * Output: + * return DirNode * The deleted DirNode (ie. allways NULL). + */ +static DirNode *ef_close_dir(ExpandFile *ef, DirNode *node) +{ +/* + * Get the directory reader cache. + */ + DirCache *cache = &ef->cache; +/* + * Close the directory. + */ + _dr_close_dir(node->dr); +/* + * Return the node to the tail of the cache list. + */ + node->next = NULL; + node->prev = cache->tail; + if(cache->tail) + cache->tail->next = node; + else + cache->head = cache->tail = node; + if(!cache->next) + cache->next = node; + return NULL; +} + +/*....................................................................... + * Return non-zero if the specified file name matches a given glob + * pattern. + * + * Input: + * file const char * The file-name component to be matched to the pattern. + * pattern const char * The start of the pattern to match against file[]. + * xplicit int If non-zero, the first character must be matched + * explicitly (ie. not with a wildcard). + * nextp const char * The pointer to the the character following the + * end of the pattern in pattern[]. + * Output: + * return int 0 - Doesn't match. + * 1 - The file-name string matches the pattern. + */ +static int ef_string_matches_pattern(const char *file, const char *pattern, + int xplicit, const char *nextp) +{ + const char *pptr = pattern; /* The pointer used to scan the pattern */ + const char *fptr = file; /* The pointer used to scan the filename string */ +/* + * Match each character of the pattern in turn. + */ + while(pptr < nextp) { +/* + * Handle the next character of the pattern. + */ + switch(*pptr) { +/* + * A match zero-or-more characters wildcard operator. + */ + case '*': +/* + * Skip the '*' character in the pattern. + */ + pptr++; +/* + * If wildcards aren't allowed, the pattern doesn't match. + */ + if(xplicit) + return 0; +/* + * If the pattern ends with a the '*' wildcard, then the + * rest of the filename matches this. + */ + if(pptr >= nextp) + return 1; +/* + * Using the wildcard to match successively longer sections of + * the remaining characters of the filename, attempt to match + * the tail of the filename against the tail of the pattern. + */ + for( ; *fptr; fptr++) { + if(ef_string_matches_pattern(fptr, pptr, 0, nextp)) + return 1; + }; + return 0; /* The pattern following the '*' didn't match */ + break; +/* + * A match-one-character wildcard operator. + */ + case '?': +/* + * If there is a character to be matched, skip it and advance the + * pattern pointer. + */ + if(!xplicit && *fptr) { + fptr++; + pptr++; +/* + * If we hit the end of the filename string, there is no character + * matching the operator, so the string doesn't match. + */ + } else { + return 0; + }; + break; +/* + * A character range operator, with the character ranges enclosed + * in matching square brackets. + */ + case '[': + if(xplicit || !ef_matches_range(*fptr++, ++pptr, &pptr)) + return 0; + break; +/* + * A backslash in the pattern prevents the following character as + * being seen as a special character. + */ + case '\\': + pptr++; + /* Note fallthrough to default */ +/* + * A normal character to be matched explicitly. + */ + default: + if(*fptr == *pptr) { + fptr++; + pptr++; + } else { + return 0; + }; + break; + }; +/* + * After passing the first character, turn off the explicit match + * requirement. + */ + xplicit = 0; + }; +/* + * To get here the pattern must have been exhausted. If the filename + * string matched, then the filename string must also have been + * exhausted. + */ + return *fptr == '\0'; +} + +/*....................................................................... + * Match a character range expression terminated by an unescaped close + * square bracket. + * + * Input: + * c int The character to be matched with the range + * pattern. + * pattern const char * The range pattern to be matched (ie. after the + * initiating '[' character). + * endp const char ** On output a pointer to the character following the + * range expression will be assigned to *endp. + * Output: + * return int 0 - Doesn't match. + * 1 - The character matched. + */ +static int ef_matches_range(int c, const char *pattern, const char **endp) +{ + const char *pptr = pattern; /* The pointer used to scan the pattern */ + int invert = 0; /* True to invert the sense of the match */ + int matched = 0; /* True if the character matched the pattern */ +/* + * If the first character is a caret, the sense of the match is + * inverted and only if the character isn't one of those in the + * range, do we say that it matches. + */ + if(*pptr == '^') { + pptr++; + invert = 1; + }; +/* + * The hyphen is only a special character when it follows the first + * character of the range (not including the caret). + */ + if(*pptr == '-') { + pptr++; + if(c == '-') { + *endp = pptr; + matched = 1; + }; +/* + * Skip other leading '-' characters since they make no sense. + */ + while(*pptr == '-') + pptr++; + }; +/* + * The hyphen is only a special character when it follows the first + * character of the range (not including the caret or a hyphen). + */ + if(*pptr == ']') { + pptr++; + if(c == ']') { + *endp = pptr; + matched = 1; + }; + }; +/* + * Having dealt with the characters that have special meanings at + * the beginning of a character range expression, see if the + * character matches any of the remaining characters of the range, + * up until a terminating ']' character is seen. + */ + while(!matched && *pptr && *pptr != ']') { +/* + * Is this a range of characters signaled by the two end characters + * separated by a hyphen? + */ + if(*pptr == '-') { + if(pptr[1] != ']') { + if(c >= pptr[-1] && c <= pptr[1]) + matched = 1; + pptr += 2; + }; +/* + * A normal character to be compared directly. + */ + } else if(*pptr++ == c) { + matched = 1; + }; + }; +/* + * Find the terminating ']'. + */ + while(*pptr && *pptr != ']') + pptr++; +/* + * Did we find a terminating ']'? + */ + if(*pptr == ']') { + *endp = pptr + 1; + return matched ? !invert : invert; + }; +/* + * If the pattern didn't end with a ']' then it doesn't match, regardless + * of the value of the required sense of the match. + */ + *endp = pptr; + return 0; +} + +/*....................................................................... + * This is a qsort() comparison function used to sort strings. + * + * Input: + * v1, v2 void * Pointers to the two strings to be compared. + * Output: + * return int -1 -> v1 < v2. + * 0 -> v1 == v2 + * 1 -> v1 > v2 + */ +static int ef_cmp_strings(const void *v1, const void *v2) +{ + char * const *s1 = (char * const *) v1; + char * const *s2 = (char * const *) v2; + return strcmp(*s1, *s2); +} + +/*....................................................................... + * Preprocess a path, expanding ~/, ~user/ and $envvar references, using + * ef->path as a work buffer, then copy the result into a cache entry, + * and return a pointer to this copy. + * + * Input: + * ef ExpandFile * The resource object of the file matcher. + * pathlen int The length of the prefix of path[] to be expanded. + * Output: + * return char * A pointer to a copy of the output path in the + * cache. On error NULL is returned, and a description + * of the error is left in ef->err. + */ +static char *ef_expand_special(ExpandFile *ef, const char *path, int pathlen) +{ + int spos; /* The index of the start of the path segment that needs */ + /* to be copied from path[] to the output pathname. */ + int ppos; /* The index of a character in path[] */ + char *pptr; /* A pointer into the output path */ + int escaped; /* True if the previous character was a '\' */ + int i; +/* + * Clear the pathname buffer. + */ + _pn_clear_path(ef->path); +/* + * We need to perform two passes, one to expand environment variables + * and a second to do tilde expansion. This caters for the case + * where an initial dollar expansion yields a tilde expression. + */ + escaped = 0; + for(spos=ppos=0; ppos < pathlen; ppos++) { + int c = path[ppos]; + if(escaped) { + escaped = 0; + } else if(c == '\\') { + escaped = 1; + } else if(c == '$') { + int envlen; /* The length of the environment variable */ + char *value; /* The value of the environment variable */ +/* + * Record the preceding unrecorded part of the pathname. + */ + if(spos < ppos && _pn_append_to_path(ef->path, path + spos, ppos-spos, 0) + == NULL) { + _err_record_msg(ef->err, "Insufficient memory to expand path", + END_ERR_MSG); + return NULL; + }; +/* + * Skip the dollar. + */ + ppos++; +/* + * Copy the environment variable name that follows the dollar into + * ef->envnam[], stopping if a directory separator or end of string + * is seen. + */ + for(envlen=0; envlenenvnam[envlen] = path[ppos++]; +/* + * If the username overflowed the buffer, treat it as invalid (note that + * on most unix systems only 8 characters are allowed in a username, + * whereas our ENV_LEN is much bigger than that. + */ + if(envlen >= ENV_LEN) { + _err_record_msg(ef->err, "Environment variable name too long", + END_ERR_MSG); + return NULL; + }; +/* + * Terminate the environment variable name. + */ + ef->envnam[envlen] = '\0'; +/* + * Lookup the value of the environment variable. + */ + value = getenv(ef->envnam); + if(!value) { + _err_record_msg(ef->err, "No expansion found for: $", ef->envnam, + END_ERR_MSG); + return NULL; + }; +/* + * Copy the value of the environment variable into the output pathname. + */ + if(_pn_append_to_path(ef->path, value, -1, 0) == NULL) { + _err_record_msg(ef->err, "Insufficient memory to expand path", + END_ERR_MSG); + return NULL; + }; +/* + * Record the start of the uncopied tail of the input pathname. + */ + spos = ppos; + }; + }; +/* + * Record the uncopied tail of the pathname. + */ + if(spos < ppos && _pn_append_to_path(ef->path, path + spos, ppos-spos, 0) + == NULL) { + _err_record_msg(ef->err, "Insufficient memory to expand path", END_ERR_MSG); + return NULL; + }; +/* + * If the first character of the resulting pathname is a tilde, + * then attempt to substitute the home directory of the specified user. + */ + pptr = ef->path->name; + if(*pptr == '~' && path[0] != '\\') { + int usrlen; /* The length of the username following the tilde */ + const char *homedir; /* The home directory of the user */ + int homelen; /* The length of the home directory string */ + int plen; /* The current length of the path */ + int skip=0; /* The number of characters to skip after the ~user */ +/* + * Get the current length of the output path. + */ + plen = strlen(ef->path->name); +/* + * Skip the tilde. + */ + pptr++; +/* + * Copy the optional username that follows the tilde into ef->usrnam[]. + */ + for(usrlen=0; usrlenusrnam[usrlen] = *pptr++; +/* + * If the username overflowed the buffer, treat it as invalid (note that + * on most unix systems only 8 characters are allowed in a username, + * whereas our USR_LEN is much bigger than that. + */ + if(usrlen >= USR_LEN) { + _err_record_msg(ef->err, "Username too long", END_ERR_MSG); + return NULL; + }; +/* + * Terminate the username string. + */ + ef->usrnam[usrlen] = '\0'; +/* + * Lookup the home directory of the user. + */ + homedir = _hd_lookup_home_dir(ef->home, ef->usrnam); + if(!homedir) { + _err_record_msg(ef->err, _hd_last_home_dir_error(ef->home), END_ERR_MSG); + return NULL; + }; + homelen = strlen(homedir); +/* + * ~user and ~ are usually followed by a directory separator to + * separate them from the file contained in the home directory. + * If the home directory is the root directory, then we don't want + * to follow the home directory by a directory separator, so we must + * erase it. + */ + if(strcmp(homedir, FS_ROOT_DIR) == 0 && + strncmp(pptr, FS_DIR_SEP, FS_DIR_SEP_LEN) == 0) { + skip = FS_DIR_SEP_LEN; + }; +/* + * If needed, increase the size of the pathname buffer to allow it + * to accomodate the home directory instead of the tilde expression. + * Note that pptr may not be valid after this call. + */ + if(_pn_resize_path(ef->path, plen - usrlen - 1 - skip + homelen)==NULL) { + _err_record_msg(ef->err, "Insufficient memory to expand filename", + END_ERR_MSG); + return NULL; + }; +/* + * Move the part of the pathname that follows the tilde expression to + * the end of where the home directory will need to be inserted. + */ + memmove(ef->path->name + homelen, + ef->path->name + 1 + usrlen + skip, plen - usrlen - 1 - skip+1); +/* + * Write the home directory at the beginning of the string. + */ + for(i=0; ipath->name[i] = homedir[i]; + }; +/* + * Copy the result into the cache, and return a pointer to the copy. + */ + return ef_cache_pathname(ef, ef->path->name, 0); +} + +/*....................................................................... + * Return a description of the last path-expansion error that occurred. + * + * Input: + * ef ExpandFile * The path-expansion resource object. + * Output: + * return char * The description of the last error. + */ +const char *ef_last_error(ExpandFile *ef) +{ + return ef ? _err_get_msg(ef->err) : "NULL ExpandFile argument"; +} + +/*....................................................................... + * Print out an array of matching files. + * + * Input: + * result FileExpansion * The container of the sorted array of + * expansions. + * fp FILE * The output stream to write to. + * term_width int The width of the terminal. + * Output: + * return int 0 - OK. + * 1 - Error. + */ +int ef_list_expansions(FileExpansion *result, FILE *fp, int term_width) +{ + return _ef_output_expansions(result, _io_write_stdio, fp, term_width); +} + +/*....................................................................... + * Print out an array of matching files via a callback. + * + * Input: + * result FileExpansion * The container of the sorted array of + * expansions. + * write_fn GlWriteFn * The function to call to write the + * expansions or 0 to discard the output. + * data void * Anonymous data to pass to write_fn(). + * term_width int The width of the terminal. + * Output: + * return int 0 - OK. + * 1 - Error. + */ +int _ef_output_expansions(FileExpansion *result, GlWriteFn *write_fn, + void *data, int term_width) +{ + EfListFormat fmt; /* List formatting information */ + int lnum; /* The sequential number of the line to print next */ +/* + * Not enough space to list anything? + */ + if(term_width < 1) + return 0; +/* + * Do we have a callback to write via, and any expansions to be listed? + */ + if(write_fn && result && result->nfile>0) { +/* + * Work out how to arrange the listing into fixed sized columns. + */ + ef_plan_listing(result, term_width, &fmt); +/* + * Print the listing to the specified stream. + */ + for(lnum=0; lnum < fmt.nline; lnum++) { + if(ef_format_line(result, &fmt, lnum, write_fn, data)) + return 1; + }; + }; + return 0; +} + +/*....................................................................... + * Work out how to arrange a given array of completions into a listing + * of one or more fixed size columns. + * + * Input: + * result FileExpansion * The set of completions to be listed. + * term_width int The width of the terminal. A lower limit of + * zero is quietly enforced. + * Input/Output: + * fmt EfListFormat * The formatting information will be assigned + * to the members of *fmt. + */ +static void ef_plan_listing(FileExpansion *result, int term_width, + EfListFormat *fmt) +{ + int maxlen; /* The length of the longest matching string */ + int i; +/* + * Ensure that term_width >= 0. + */ + if(term_width < 0) + term_width = 0; +/* + * Start by assuming the worst case, that either nothing will fit + * on the screen, or that there are no matches to be listed. + */ + fmt->term_width = term_width; + fmt->column_width = 0; + fmt->nline = fmt->ncol = 0; +/* + * Work out the maximum length of the matching strings. + */ + maxlen = 0; + for(i=0; infile; i++) { + int len = strlen(result->files[i]); + if(len > maxlen) + maxlen = len; + }; +/* + * Nothing to list? + */ + if(maxlen == 0) + return; +/* + * Split the available terminal width into columns of + * maxlen + EF_COL_SEP characters. + */ + fmt->column_width = maxlen; + fmt->ncol = fmt->term_width / (fmt->column_width + EF_COL_SEP); +/* + * If the column width is greater than the terminal width, zero columns + * will have been selected. Set a lower limit of one column. Leave it + * up to the caller how to deal with completions who's widths exceed + * the available terminal width. + */ + if(fmt->ncol < 1) + fmt->ncol = 1; +/* + * How many lines of output will be needed? + */ + fmt->nline = (result->nfile + fmt->ncol - 1) / fmt->ncol; + return; +} + +/*....................................................................... + * Render one line of a multi-column listing of completions, using a + * callback function to pass the output to an arbitrary destination. + * + * Input: + * result FileExpansion * The container of the sorted array of + * completions. + * fmt EfListFormat * Formatting information. + * lnum int The index of the line to print, starting + * from 0, and incrementing until the return + * value indicates that there is nothing more + * to be printed. + * write_fn GlWriteFn * The function to call to write the line, or + * 0 to discard the output. + * data void * Anonymous data to pass to write_fn(). + * Output: + * return int 0 - Line printed ok. + * 1 - Nothing to print. + */ +static int ef_format_line(FileExpansion *result, EfListFormat *fmt, int lnum, + GlWriteFn *write_fn, void *data) +{ + int col; /* The index of the list column being output */ +/* + * If the line index is out of bounds, there is nothing to be written. + */ + if(lnum < 0 || lnum >= fmt->nline) + return 1; +/* + * If no output function has been provided, return as though the line + * had been printed. + */ + if(!write_fn) + return 0; +/* + * Print the matches in 'ncol' columns, sorted in line order within each + * column. + */ + for(col=0; col < fmt->ncol; col++) { + int m = col*fmt->nline + lnum; +/* + * Is there another match to be written? Note that in general + * the last line of a listing will have fewer filled columns + * than the initial lines. + */ + if(m < result->nfile) { + char *file = result->files[m]; +/* + * How long are the completion and type-suffix strings? + */ + int flen = strlen(file); +/* + * Write the completion string. + */ + if(write_fn(data, file, flen) != flen) + return 1; +/* + * If another column follows the current one, pad to its start with spaces. + */ + if(col+1 < fmt->ncol) { +/* + * The following constant string of spaces is used to pad the output. + */ + static const char spaces[] = " "; + static const int nspace = sizeof(spaces) - 1; +/* + * Pad to the next column, using as few sub-strings of the spaces[] + * array as possible. + */ + int npad = fmt->column_width + EF_COL_SEP - flen; + while(npad>0) { + int n = npad > nspace ? nspace : npad; + if(write_fn(data, spaces + nspace - n, n) != n) + return 1; + npad -= n; + }; + }; + }; + }; +/* + * Start a new line. + */ + { + char s[] = "\r\n"; + int n = strlen(s); + if(write_fn(data, s, n) != n) + return 1; + }; + return 0; +} + +#endif /* ifndef WITHOUT_FILE_SYSTEM */ diff --git a/libtecla-1.6.1/expand.h b/libtecla-1.6.1/expand.h new file mode 100644 index 0000000..8c0bad8 --- /dev/null +++ b/libtecla-1.6.1/expand.h @@ -0,0 +1,48 @@ +#ifndef expand_h +#define expand_h + +/* + * Copyright (c) 2000, 2001, 2002, 2003, 2004 by Martin C. Shepherd. + * + * All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, and/or sell copies of the Software, and to permit persons + * to whom the Software is furnished to do so, provided that the above + * copyright notice(s) and this permission notice appear in all copies of + * the Software and that both the above copyright notice(s) and this + * permission notice appear in supporting documentation. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT + * OF THIRD PARTY RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR + * HOLDERS INCLUDED IN THIS NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL + * INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING + * FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, + * NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION + * WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + * Except as contained in this notice, the name of a copyright holder + * shall not be used in advertising or otherwise to promote the sale, use + * or other dealings in this Software without prior written authorization + * of the copyright holder. + */ + +/* + * This header is not for use by external applicatons. It contains + * internal immplementation features of the libtecla library, which + * may change incompatibly between releases. + */ + +/* + * Print a list of expansions via a callback function. + */ +int _ef_output_expansions(FileExpansion *result, GlWriteFn *write_fn, + void *data, int term_width); + + +#endif diff --git a/libtecla-1.6.1/freelist.c b/libtecla-1.6.1/freelist.c new file mode 100644 index 0000000..2a85ba1 --- /dev/null +++ b/libtecla-1.6.1/freelist.c @@ -0,0 +1,400 @@ +/* + * Copyright (c) 2000, 2001, 2002, 2003, 2004 by Martin C. Shepherd. + * + * All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, and/or sell copies of the Software, and to permit persons + * to whom the Software is furnished to do so, provided that the above + * copyright notice(s) and this permission notice appear in all copies of + * the Software and that both the above copyright notice(s) and this + * permission notice appear in supporting documentation. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT + * OF THIRD PARTY RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR + * HOLDERS INCLUDED IN THIS NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL + * INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING + * FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, + * NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION + * WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + * Except as contained in this notice, the name of a copyright holder + * shall not be used in advertising or otherwise to promote the sale, use + * or other dealings in this Software without prior written authorization + * of the copyright holder. + */ + +#include +#include +#include + +#include "freelist.h" + +typedef struct FreeListBlock FreeListBlock; +struct FreeListBlock { + FreeListBlock *next; /* The next block in the list */ + char *nodes; /* The array of free-list nodes */ +}; + +struct FreeList { + size_t node_size; /* The size of a free-list node */ + unsigned blocking_factor; /* The number of nodes per block */ + long nbusy; /* The number of nodes that are in use */ + long ntotal; /* The total number of nodes in the free list */ + FreeListBlock *block; /* The head of the list of free-list blocks */ + void *free_list; /* The free-list of nodes */ +}; + +static FreeListBlock *_new_FreeListBlock(FreeList *fl); +static FreeListBlock *_del_FreeListBlock(FreeListBlock *fl); +static void _thread_FreeListBlock(FreeList *fl, FreeListBlock *block); + +/*....................................................................... + * Allocate a new free-list from blocks of 'blocking_factor' objects of size + * node_size. + * + * Input: + * node_size size_t The size of the free-list nodes to be returned + * by _new_FreeListNode(). Use sizeof() to + * determine this. + * blocking_factor unsigned The number of objects of size 'object_size' + * to allocate per block. + * Output: + * return FreeList * The new freelist, or NULL on error. + */ +FreeList *_new_FreeList(size_t node_size, unsigned blocking_factor) +{ + FreeList *fl; /* The new free-list container */ +/* + * When a free-list node is on the free-list, it is used as a (void *) + * link field. Roundup node_size to a mulitple of the size of a void + * pointer. This, plus the fact that the array of nodes is obtained via + * malloc, which returns memory suitably aligned for any object, will + * ensure that the first sizeof(void *) bytes of each node will be + * suitably aligned to use as a (void *) link pointer. + */ + node_size = sizeof(void *) * + ((node_size + sizeof(void *) - 1) / sizeof(void *)); +/* + * Enfore a minimum block size. + */ + if(blocking_factor < 1) + blocking_factor = 1; +/* + * Allocate the container of the free list. + */ + fl = (FreeList *) malloc(sizeof(FreeList)); + if(!fl) { + errno = ENOMEM; + return NULL; + }; +/* + * Before attempting any operation that might fail, initialize the + * container at least up to the point at which it can safely be passed + * to _del_FreeList(). + */ + fl->node_size = node_size; + fl->blocking_factor = blocking_factor; + fl->nbusy = 0; + fl->ntotal = 0; + fl->block = NULL; + fl->free_list = NULL; +/* + * Allocate the first block of memory. + */ + fl->block = _new_FreeListBlock(fl); + if(!fl->block) { + errno = ENOMEM; + return _del_FreeList(fl, 1); + }; +/* + * Add the new list of nodes to the free-list. + */ + fl->free_list = fl->block->nodes; +/* + * Return the free-list for use. + */ + return fl; +} + +/*....................................................................... + * Re-thread a freelist to reclaim all allocated nodes. + * This function should not be called unless if it is known that none + * of the currently allocated nodes are still being used. + * + * Input: + * fl FreeList * The free-list to be reset, or NULL. + */ +void _rst_FreeList(FreeList *fl) +{ + if(fl) { + FreeListBlock *block; +/* + * Re-thread the nodes of each block into individual free-lists. + */ + for(block=fl->block; block; block=block->next) + _thread_FreeListBlock(fl, block); +/* + * Link all of the block freelists into one large freelist. + */ + fl->free_list = NULL; + for(block=fl->block; block; block=block->next) { +/* + * Locate the last node of the current block. + */ + char *last_node = block->nodes + fl->node_size * + (fl->blocking_factor - 1); +/* + * Make the link-field of the last node point to the first + * node of the current freelist, then make the first node of the + * new block the start of the freelist. + */ + *(void **)last_node = fl->free_list; + fl->free_list = block->nodes; + }; +/* + * All allocated nodes have now been returned to the freelist. + */ + fl->nbusy = 0; + }; +} + +/*....................................................................... + * Delete a free-list. + * + * Input: + * fl FreeList * The free-list to be deleted, or NULL. + * force int If force==0 then _del_FreeList() will complain + * and refuse to delete the free-list if any + * of nodes have not been returned to the free-list. + * If force!=0 then _del_FreeList() will not check + * whether any nodes are still in use and will + * always delete the list. + * Output: + * return FreeList * Always NULL (even if the list couldn't be + * deleted). + */ +FreeList *_del_FreeList(FreeList *fl, int force) +{ + if(fl) { +/* + * Check whether any nodes are in use. + */ + if(!force && _busy_FreeListNodes(fl) != 0) { + errno = EBUSY; + return NULL; + }; +/* + * Delete the list blocks. + */ + { + FreeListBlock *next = fl->block; + while(next) { + FreeListBlock *block = next; + next = block->next; + block = _del_FreeListBlock(block); + }; + }; + fl->block = NULL; + fl->free_list = NULL; +/* + * Discard the container. + */ + free(fl); + }; + return NULL; +} + +/*....................................................................... + * Allocate a new object from a free-list. + * + * Input: + * fl FreeList * The free-list to return an object from. + * Output: + * return void * A new object of the size that was specified via + * the node_size argument of _new_FreeList() when + * the free-list was created, or NULL if there + * is insufficient memory, or 'fl' is NULL. + */ +void *_new_FreeListNode(FreeList *fl) +{ + void *node; /* The node to be returned */ +/* + * Check arguments. + */ + if(!fl) + return NULL; +/* + * If the free-list has been exhausted extend it by allocating + * another block of nodes. + */ + if(!fl->free_list) { + FreeListBlock *block = _new_FreeListBlock(fl); + if(!block) + return NULL; +/* + * Prepend the new block to the list of free-list blocks. + */ + block->next = fl->block; + fl->block = block; +/* + * Add the new list of nodes to the free-list. + */ + fl->free_list = fl->block->nodes; + }; +/* + * Remove and return a node from the front of the free list. + */ + node = fl->free_list; + fl->free_list = *(void **)node; +/* + * Record the loss of a node from the free-list. + */ + fl->nbusy++; +/* + * Return the node. + */ + return node; +} + +/*....................................................................... + * Return an object to the free-list that it was allocated from. + * + * Input: + * fl FreeList * The free-list from which the object was taken. + * object void * The node to be returned. + * Output: + * return void * Always NULL. + */ +void *_del_FreeListNode(FreeList *fl, void *object) +{ +/* + * Check arguments. + */ + if(!fl) + return NULL; +/* + * Return the node to the head of the free list. + */ + if(object) { + *(void **)object = fl->free_list; + fl->free_list = object; +/* + * Record the return of the node to the free-list. + */ + fl->nbusy--; + }; + return NULL; +} + +/*....................................................................... + * Return a count of the number of nodes that are currently allocated. + * + * Input: + * fl FreeList * The list to count wrt, or NULL. + * Output: + * return long The number of nodes (or 0 if fl==NULL). + */ +long _busy_FreeListNodes(FreeList *fl) +{ + return fl ? fl->nbusy : 0; +} + +/*....................................................................... + * Query the number of allocated nodes in the freelist which are + * currently unused. + * + * Input: + * fl FreeList * The list to count wrt, or NULL. + * Output: + * return long The number of unused nodes (or 0 if fl==NULL). + */ +long _idle_FreeListNodes(FreeList *fl) +{ + return fl ? (fl->ntotal - fl->nbusy) : 0; +} + +/*....................................................................... + * Allocate a new list of free-list nodes. On return the nodes will + * be linked together as a list starting with the node at the lowest + * address and ending with a NULL next pointer. + * + * Input: + * fl FreeList * The free-list to allocate the list for. + * Output: + * return FreeListBlock * The new linked block of free-list nodes, + * or NULL on error. + */ +static FreeListBlock *_new_FreeListBlock(FreeList *fl) +{ + FreeListBlock *block; /* The new block to be returned */ +/* + * Allocate the container. + */ + block = (FreeListBlock *) malloc(sizeof(FreeListBlock)); + if(!block) + return NULL; +/* + * Before attempting any operation that might fail, initialize the + * container at least up to the point at which it can safely be passed + * to _del_FreeListBlock(). + */ + block->next = NULL; + block->nodes = NULL; +/* + * Allocate the block of nodes. + */ + block->nodes = (char *) malloc(fl->node_size * fl->blocking_factor); + if(!block->nodes) + return _del_FreeListBlock(block); +/* + * Initialize the block as a linked list of FreeListNode's. + */ + _thread_FreeListBlock(fl, block); +/* + * Update the record of the number of nodes in the freelist. + */ + fl->ntotal += fl->blocking_factor; + return block; +} + +/*....................................................................... + * Link each node of a freelist block to the node that follows it. + * + * Input: + * fl FreeList * The freelist that contains the block. + * block FreeListBlock * The block to be threaded. + */ +static void _thread_FreeListBlock(FreeList *fl, FreeListBlock *block) +{ + char *mem = block->nodes; + int i; + for(i=0; iblocking_factor - 1; i++, mem += fl->node_size) + *(void **)mem = mem + fl->node_size; /* Link to the next node */ + *(void **)mem = NULL; /* Terminate the list */ +} + +/*....................................................................... + * Delete a free-list block. + * + * Input: + * fl FreeListBlock * The block to be deleted, or NULL. + * Output: + * return FreeListBlock * Always NULL. + */ +static FreeListBlock *_del_FreeListBlock(FreeListBlock *fl) +{ + if(fl) { + fl->next = NULL; + if(fl->nodes) + free(fl->nodes); + fl->nodes = NULL; + free(fl); + }; + return NULL; +} diff --git a/libtecla-1.6.1/freelist.h b/libtecla-1.6.1/freelist.h new file mode 100644 index 0000000..09d966f --- /dev/null +++ b/libtecla-1.6.1/freelist.h @@ -0,0 +1,87 @@ +#ifndef freelist_h +#define freelist_h + +/* + * Copyright (c) 2000, 2001, 2002, 2003, 2004 by Martin C. Shepherd. + * + * All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, and/or sell copies of the Software, and to permit persons + * to whom the Software is furnished to do so, provided that the above + * copyright notice(s) and this permission notice appear in all copies of + * the Software and that both the above copyright notice(s) and this + * permission notice appear in supporting documentation. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT + * OF THIRD PARTY RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR + * HOLDERS INCLUDED IN THIS NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL + * INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING + * FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, + * NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION + * WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + * Except as contained in this notice, the name of a copyright holder + * shall not be used in advertising or otherwise to promote the sale, use + * or other dealings in this Software without prior written authorization + * of the copyright holder. + */ + +/* + * This module provides a memory allocation scheme that helps to + * prevent memory fragmentation by allocating large blocks of + * fixed sized objects and forming them into a free-list for + * subsequent allocations. The free-list is expanded as needed. + */ +typedef struct FreeList FreeList; + +/* + * Allocate a new free-list from blocks of 'blocking_factor' objects of size + * node_size. The node_size argument should be determined by applying + * the sizeof() operator to the object type that you intend to allocate from + * the freelist. + */ +FreeList *_new_FreeList(size_t node_size, unsigned blocking_factor); + +/* + * If it is known that none of the nodes currently allocated from + * a freelist are still in use, the following function can be called + * to return all nodes to the freelist without the overhead of + * having to call del_FreeListNode() for every allocated node. The + * nodes of the freelist can then be reused by future callers to + * new_FreeListNode(). + */ +void _rst_FreeList(FreeList *fl); + +/* + * Delete a free-list. + */ +FreeList *_del_FreeList(FreeList *fl, int force); + +/* + * Determine the number of nodes that are currently in use. + */ +long _busy_FreeListNodes(FreeList *fl); + +/* + * Query the number of allocated nodes in the freelist which are + * currently unused. + */ +long _idle_FreeListNodes(FreeList *fl); + +/* + * Allocate a new object from a free-list. + */ +void *_new_FreeListNode(FreeList *fl); + +/* + * Return an object to the free-list that it was allocated from. + */ +void *_del_FreeListNode(FreeList *fl, void *object); + +#endif diff --git a/libtecla-1.6.1/getline.c b/libtecla-1.6.1/getline.c new file mode 100644 index 0000000..324aadb --- /dev/null +++ b/libtecla-1.6.1/getline.c @@ -0,0 +1,12844 @@ +/* + * Copyright (c) 2000, 2001, 2002, 2003, 2004 by Martin C. Shepherd. + * + * All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, and/or sell copies of the Software, and to permit persons + * to whom the Software is furnished to do so, provided that the above + * copyright notice(s) and this permission notice appear in all copies of + * the Software and that both the above copyright notice(s) and this + * permission notice appear in supporting documentation. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT + * OF THIRD PARTY RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR + * HOLDERS INCLUDED IN THIS NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL + * INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING + * FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, + * NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION + * WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + * Except as contained in this notice, the name of a copyright holder + * shall not be used in advertising or otherwise to promote the sale, use + * or other dealings in this Software without prior written authorization + * of the copyright holder. + */ + +/* + * Standard headers. + */ +#include +#include +#include +#include +#include +#include +#include +#include + +/* + * UNIX headers. + */ +#include +#ifdef HAVE_SELECT +#ifdef HAVE_SYS_SELECT_H +#include +#endif +#include +#include +#endif + +/* + * Handle the different sources of terminal control string and size + * information. Note that if no terminal information database is available, + * ANSI VT100 control sequences are used. + */ +#if defined(USE_TERMINFO) || defined(USE_TERMCAP) +/* + * Include curses.h or ncurses/curses.h depending on which is available. + */ +#ifdef HAVE_CURSES_H +#include +#elif defined(HAVE_NCURSES_CURSES_H) +#include +#endif +/* + * Include term.h where available. + */ +#if defined(HAVE_TERM_H) +#include +#elif defined(HAVE_NCURSES_TERM_H) +#include +#endif +/* + * When using termcap, include termcap.h on systems that have it. + * Otherwise assume that all prototypes are provided by curses.h. + */ +#if defined(USE_TERMCAP) && defined(HAVE_TERMCAP_H) +#include +#endif + +/* + * Under Solaris default Curses the output function that tputs takes is + * declared to have a char argument. On all other systems and on Solaris + * X/Open Curses (Issue 4, Version 2) it expects an int argument (using + * c89 or options -I /usr/xpg4/include -L /usr/xpg4/lib -R /usr/xpg4/lib + * selects XPG4v2 Curses on Solaris 2.6 and later). + * + * Similarly, under Mac OS X, the return value of the tputs output + * function is declared as void, whereas it is declared as int on + * other systems. + */ +#if defined __sun && defined __SVR4 && !defined _XOPEN_CURSES +typedef int TputsRetType; +typedef char TputsArgType; /* int tputs(char c, FILE *fp) */ +#define TPUTS_RETURNS_VALUE 1 +#elif defined(__APPLE__) && defined(__MACH__) +typedef void TputsRetType; +typedef int TputsArgType; /* void tputs(int c, FILE *fp) */ +#define TPUTS_RETURNS_VALUE 0 +#else +typedef int TputsRetType; +typedef int TputsArgType; /* int tputs(int c, FILE *fp) */ +#define TPUTS_RETURNS_VALUE 1 +#endif + +/* + * Use the above specifications to prototype our tputs callback function. + */ +static TputsRetType gl_tputs_putchar(TputsArgType c); + +#endif /* defined(USE_TERMINFO) || defined(USE_TERMCAP) */ + +/* + * If the library is being compiled without filesystem access facilities, + * ensure that none of the action functions that normally do access the + * filesystem are bound by default, and that it they do get bound, that + * they don't do anything. + */ +#if WITHOUT_FILE_SYSTEM +#define HIDE_FILE_SYSTEM +#endif + +/* + * POSIX headers. + */ +#include +#include +#include + +/* + * Provide typedefs for standard POSIX structures. + */ +typedef struct sigaction SigAction; +typedef struct termios Termios; + +/* + * Which flag is used to select non-blocking I/O with fcntl()? + */ +#undef NON_BLOCKING_FLAG +#if defined(O_NONBLOCK) +#define NON_BLOCKING_FLAG (O_NONBLOCK) +#elif defined(O_NDELAY) +#define NON_BLOCKING_FLAG (O_NDELAY) +#endif + +/* + * What value should we give errno if I/O blocks when it shouldn't. + */ +#undef BLOCKED_ERRNO +#if defined(EAGAIN) +#define BLOCKED_ERRNO (EAGAIN) +#elif defined(EWOULDBLOCK) +#define BLOCKED_ERRNO (EWOULDBLOCK) +#elif defined(EIO) +#define BLOCKED_ERRNO (EIO) +#else +#define BLOCKED_ERRNO 0 +#endif + +/* + * Local headers. + */ +#ifndef WITHOUT_FILE_SYSTEM +#include "pathutil.h" +#endif +#include "libtecla.h" +#include "keytab.h" +#include "getline.h" +#include "ioutil.h" +#include "history.h" +#include "freelist.h" +#include "stringrp.h" +#include "chrqueue.h" +#include "cplmatch.h" +#ifndef WITHOUT_FILE_SYSTEM +#include "expand.h" +#endif +#include "errmsg.h" + +/* + * Enumerate the available editing styles. + */ +typedef enum { + GL_EMACS_MODE, /* Emacs style editing */ + GL_VI_MODE, /* Vi style editing */ + GL_NO_EDITOR /* Fall back to the basic OS-provided editing */ +} GlEditor; + +/* + * Set the largest key-sequence that can be handled. + */ +#define GL_KEY_MAX 64 + +/* + * In vi mode, the following datatype is used to implement the + * undo command. It records a copy of the input line from before + * the command-mode action which edited the input line. + */ +typedef struct { + char *line; /* A historical copy of the input line */ + int buff_curpos; /* The historical location of the cursor in */ + /* line[] when the line was modified. */ + int ntotal; /* The number of characters in line[] */ + int saved; /* True once a line has been saved after the */ + /* last call to gl_interpret_char(). */ +} ViUndo; + +/* + * In vi mode, the following datatype is used to record information + * needed by the vi-repeat-change command. + */ +typedef struct { + KtAction action; /* The last action function that made a */ + /* change to the line. */ + int count; /* The repeat count that was passed to the */ + /* above command. */ + int input_curpos; /* Whenever vi command mode is entered, the */ + /* the position at which it was first left */ + /* is recorded here. */ + int command_curpos; /* Whenever vi command mode is entered, the */ + /* the location of the cursor is recorded */ + /* here. */ + char input_char; /* Commands that call gl_read_terminal() */ + /* record the character here, so that it can */ + /* used on repeating the function. */ + int saved; /* True if a function has been saved since the */ + /* last call to gl_interpret_char(). */ + int active; /* True while a function is being repeated. */ +} ViRepeat; + +/* + * The following datatype is used to encapsulate information specific + * to vi mode. + */ +typedef struct { + ViUndo undo; /* Information needed to implement the vi */ + /* undo command. */ + ViRepeat repeat; /* Information needed to implement the vi */ + /* repeat command. */ + int command; /* True in vi command-mode */ + int find_forward; /* True if the last character search was in the */ + /* forward direction. */ + int find_onto; /* True if the last character search left the */ + /* on top of the located character, as opposed */ + /* to just before or after it. */ + char find_char; /* The last character sought, or '\0' if no */ + /* searches have been performed yet. */ +} ViMode; + +#ifdef HAVE_SELECT +/* + * Define a type for recording a file-descriptor callback and its associated + * data. + */ +typedef struct { + GlFdEventFn *fn; /* The callback function */ + void *data; /* Anonymous data to pass to the callback function */ +} GlFdHandler; + +/* + * A list of nodes of the following type is used to record file-activity + * event handlers, but only on systems that have the select() system call. + */ +typedef struct GlFdNode GlFdNode; +struct GlFdNode { + GlFdNode *next; /* The next in the list of nodes */ + int fd; /* The file descriptor being watched */ + GlFdHandler rd; /* The callback to call when fd is readable */ + GlFdHandler wr; /* The callback to call when fd is writable */ + GlFdHandler ur; /* The callback to call when fd has urgent data */ +}; + +/* + * Set the number of the above structures to allocate every time that + * the freelist of GlFdNode's becomes exhausted. + */ +#define GLFD_FREELIST_BLOCKING 10 + + +static int gl_call_fd_handler(GetLine *gl, GlFdHandler *gfh, int fd, + GlFdEvent event); + +static int gl_call_timeout_handler(GetLine *gl); + +#endif + +/* + * Each signal that gl_get_line() traps is described by a list node + * of the following type. + */ +typedef struct GlSignalNode GlSignalNode; +struct GlSignalNode { + GlSignalNode *next; /* The next signal in the list */ + int signo; /* The number of the signal */ + sigset_t proc_mask; /* A process mask which only includes signo */ + SigAction original; /* The signal disposition of the calling program */ + /* for this signal. */ + unsigned flags; /* A bitwise union of GlSignalFlags enumerators */ + GlAfterSignal after; /* What to do after the signal has been handled */ + int errno_value; /* What to set errno to */ +}; + +/* + * Set the number of the above structures to allocate every time that + * the freelist of GlSignalNode's becomes exhausted. + */ +#define GLS_FREELIST_BLOCKING 30 + +/* + * Completion handlers and their callback data are recorded in + * nodes of the following type. + */ +typedef struct GlCplCallback GlCplCallback; +struct GlCplCallback { + CplMatchFn *fn; /* The completion callback function */ + void *data; /* Arbitrary callback data */ +}; + +/* + * The following function is used as the default completion handler when + * the filesystem is to be hidden. It simply reports no completions. + */ +#ifdef HIDE_FILE_SYSTEM +static CPL_MATCH_FN(gl_no_completions); +#endif + +/* + * Specify how many GlCplCallback nodes are added to the GlCplCallback freelist + * whenever it becomes exhausted. + */ +#define GL_CPL_FREELIST_BLOCKING 10 + +/* + * External action functions and their callback data are recorded in + * nodes of the following type. + */ +typedef struct GlExternalAction GlExternalAction; +struct GlExternalAction { + GlActionFn *fn; /* The function which implements the action */ + void *data; /* Arbitrary callback data */ +}; + +/* + * Specify how many GlExternalAction nodes are added to the + * GlExternalAction freelist whenever it becomes exhausted. + */ +#define GL_EXT_ACT_FREELIST_BLOCKING 10 + +/* + * Define the contents of the GetLine object. + * Note that the typedef for this object can be found in libtecla.h. + */ +struct GetLine { + ErrMsg *err; /* The error-reporting buffer */ + GlHistory *glh; /* The line-history buffer */ + WordCompletion *cpl; /* String completion resource object */ + GlCplCallback cplfn; /* The completion callback */ +#ifndef WITHOUT_FILE_SYSTEM + ExpandFile *ef; /* ~user/, $envvar and wildcard expansion */ + /* resource object. */ +#endif + StringGroup *capmem; /* Memory for recording terminal capability */ + /* strings. */ + GlCharQueue *cq; /* The terminal output character queue */ + int input_fd; /* The file descriptor to read on */ + int output_fd; /* The file descriptor to write to */ + FILE *input_fp; /* A stream wrapper around input_fd */ + FILE *output_fp; /* A stream wrapper around output_fd */ + FILE *file_fp; /* When input is being temporarily taken from */ + /* a file, this is its file-pointer. Otherwise */ + /* it is NULL. */ + char *term; /* The terminal type specified on the last call */ + /* to gl_change_terminal(). */ + int is_term; /* True if stdin is a terminal */ + GlWriteFn *flush_fn; /* The function to call to write to the terminal */ + GlIOMode io_mode; /* The I/O mode established by gl_io_mode() */ + int raw_mode; /* True while the terminal is in raw mode */ + GlPendingIO pending_io; /* The type of I/O that is currently pending */ + GlReturnStatus rtn_status; /* The reason why gl_get_line() returned */ + int rtn_errno; /* THe value of errno associated with rtn_status */ + size_t linelen; /* The max number of characters per line */ + char *line; /* A line-input buffer of allocated size */ + /* linelen+2. The extra 2 characters are */ + /* reserved for "\n\0". */ + char *cutbuf; /* A cut-buffer of the same size as line[] */ + char *prompt; /* The current prompt string */ + int prompt_len; /* The length of the prompt string */ + int prompt_changed; /* True after a callback changes the prompt */ + int prompt_style; /* How the prompt string is displayed */ + FreeList *cpl_mem; /* Memory for GlCplCallback objects */ + FreeList *ext_act_mem; /* Memory for GlExternalAction objects */ + FreeList *sig_mem; /* Memory for nodes of the signal list */ + GlSignalNode *sigs; /* The head of the list of signals */ + int signals_masked; /* True between calls to gl_mask_signals() and */ + /* gl_unmask_signals() */ + int signals_overriden; /* True between calls to gl_override_signals() */ + /* and gl_restore_signals() */ + sigset_t all_signal_set; /* The set of all signals that we are trapping */ + sigset_t old_signal_set; /* The set of blocked signals on entry to */ + /* gl_get_line(). */ + sigset_t use_signal_set; /* The subset of all_signal_set to unblock */ + /* while waiting for key-strokes */ + Termios oldattr; /* Saved terminal attributes. */ + KeyTab *bindings; /* A table of key-bindings */ + int ntotal; /* The number of characters in gl->line[] */ + int buff_curpos; /* The cursor position within gl->line[] */ + int term_curpos; /* The cursor position on the terminal */ + int term_len; /* The number of terminal characters used to */ + /* display the current input line. */ + int buff_mark; /* A marker location in the buffer */ + int insert_curpos; /* The cursor position at start of insert */ + int insert; /* True in insert mode */ + int number; /* If >= 0, a numeric argument is being read */ + int endline; /* True to tell gl_get_input_line() to return */ + /* the current contents of gl->line[] */ + int displayed; /* True if an input line is currently displayed */ + int redisplay; /* If true, the input line will be redrawn */ + /* either after the current action function */ + /* returns, or when gl_get_input_line() */ + /* is next called. */ + int postpone; /* _gl_normal_io() sets this flag, to */ + /* postpone any redisplays until */ + /* is next called, to resume line editing. */ + char keybuf[GL_KEY_MAX+1]; /* A buffer of currently unprocessed key presses */ + int nbuf; /* The number of characters in keybuf[] */ + int nread; /* The number of characters read from keybuf[] */ + KtAction current_action; /* The action function that is being invoked */ + int current_count; /* The repeat count passed to */ + /* current_acction.fn() */ + GlhLineID preload_id; /* When not zero, this should be the ID of a */ + /* line in the history buffer for potential */ + /* recall. */ + int preload_history; /* If true, preload the above history line when */ + /* gl_get_input_line() is next called. */ + long keyseq_count; /* The number of key sequences entered by the */ + /* the user since new_GetLine() was called. */ + long last_search; /* The value of keyseq_count during the last */ + /* history search operation. */ + GlEditor editor; /* The style of editing, (eg. vi or emacs) */ + int silence_bell; /* True if gl_ring_bell() should do nothing. */ + int automatic_history; /* True to automatically archive entered lines */ + /* in the history list. */ + ViMode vi; /* Parameters used when editing in vi mode */ + const char *left; /* The string that moves the cursor 1 character */ + /* left. */ + const char *right; /* The string that moves the cursor 1 character */ + /* right. */ + const char *up; /* The string that moves the cursor 1 character */ + /* up. */ + const char *down; /* The string that moves the cursor 1 character */ + /* down. */ + const char *home; /* The string that moves the cursor home */ + const char *bol; /* Move cursor to beginning of line */ + const char *clear_eol; /* The string that clears from the cursor to */ + /* the end of the line. */ + const char *clear_eod; /* The string that clears from the cursor to */ + /* the end of the display. */ + const char *u_arrow; /* The string returned by the up-arrow key */ + const char *d_arrow; /* The string returned by the down-arrow key */ + const char *l_arrow; /* The string returned by the left-arrow key */ + const char *r_arrow; /* The string returned by the right-arrow key */ + const char *sound_bell; /* The string needed to ring the terminal bell */ + const char *bold; /* Switch to the bold font */ + const char *underline; /* Underline subsequent characters */ + const char *standout; /* Turn on standout mode */ + const char *dim; /* Switch to a dim font */ + const char *reverse; /* Turn on reverse video */ + const char *blink; /* Switch to a blinking font */ + const char *text_attr_off; /* Turn off all text attributes */ + int nline; /* The height of the terminal in lines */ + int ncolumn; /* The width of the terminal in columns */ +#ifdef USE_TERMCAP + char *tgetent_buf; /* The buffer that is used by tgetent() to */ + /* store a terminal description. */ + char *tgetstr_buf; /* The buffer that is used by tgetstr() to */ + /* store terminal capabilities. */ +#endif +#ifdef USE_TERMINFO + const char *left_n; /* The parameter string that moves the cursor */ + /* n characters left. */ + const char *right_n; /* The parameter string that moves the cursor */ + /* n characters right. */ +#endif + char *app_file; /* The pathname of the application-specific */ + /* .teclarc configuration file, or NULL. */ + char *user_file; /* The pathname of the user-specific */ + /* .teclarc configuration file, or NULL. */ + int configured; /* True as soon as any teclarc configuration */ + /* file has been read. */ + int echo; /* True to display the line as it is being */ + /* entered. If 0, only the prompt will be */ + /* displayed, and the line will not be */ + /* archived in the history list. */ + int last_signal; /* The last signal that was caught by */ + /* the last call to gl_get_line(), or -1 */ + /* if no signal has been caught yet. */ +#ifdef HAVE_SELECT + FreeList *fd_node_mem; /* A freelist of GlFdNode structures */ + GlFdNode *fd_nodes; /* The list of fd event descriptions */ + fd_set rfds; /* The set of fds to watch for readability */ + fd_set wfds; /* The set of fds to watch for writability */ + fd_set ufds; /* The set of fds to watch for urgent data */ + int max_fd; /* The maximum file-descriptor being watched */ + struct { /* Inactivity timeout related data */ + struct timeval dt; /* The inactivity timeout when timer.fn() */ + /* isn't 0 */ + GlTimeoutFn *fn; /* The application callback to call when */ + /* the inactivity timer expires, or 0 if */ + /* timeouts are not required. */ + void *data; /* Application provided data to be passed to */ + /* timer.fn(). */ + } timer; +#endif +}; + +/* + * Define the max amount of space needed to store a termcap terminal + * description. Unfortunately this has to be done by guesswork, so + * there is the potential for buffer overflows if we guess too small. + * Fortunately termcap has been replaced by terminfo on most + * platforms, and with terminfo this isn't an issue. The value that I + * am using here is the conventional value, as recommended by certain + * web references. + */ +#ifdef USE_TERMCAP +#define TERMCAP_BUF_SIZE 2048 +#endif + +/* + * Set the size of the string segments used to store terminal capability + * strings. + */ +#define CAPMEM_SEGMENT_SIZE 512 + +/* + * If no terminal size information is available, substitute the + * following vt100 default sizes. + */ +#define GL_DEF_NLINE 24 +#define GL_DEF_NCOLUMN 80 + +/* + * Enumerate the attributes needed to classify different types of + * signals. These attributes reflect the standard default + * characteristics of these signals (according to Richard Steven's + * Advanced Programming in the UNIX Environment). Note that these values + * are all powers of 2, so that they can be combined in a bitwise union. + */ +typedef enum { + GLSA_TERM=1, /* A signal that terminates processes */ + GLSA_SUSP=2, /* A signal that suspends processes */ + GLSA_CONT=4, /* A signal that is sent when suspended processes resume */ + GLSA_IGN=8, /* A signal that is ignored */ + GLSA_CORE=16, /* A signal that generates a core dump */ + GLSA_HARD=32, /* A signal generated by a hardware exception */ + GLSA_SIZE=64 /* A signal indicating terminal size changes */ +} GlSigAttr; + +/* + * List the signals that we need to catch. In general these are + * those that by default terminate or suspend the process, since + * in such cases we need to restore terminal settings. + */ +static const struct GlDefSignal { + int signo; /* The number of the signal */ + unsigned flags; /* A bitwise union of GlSignalFlags enumerators */ + GlAfterSignal after; /* What to do after the signal has been delivered */ + int attr; /* The default attributes of this signal, expressed */ + /* as a bitwise union of GlSigAttr enumerators */ + int errno_value; /* What to set errno to */ +} gl_signal_list[] = { + {SIGABRT, GLS_SUSPEND_INPUT, GLS_ABORT, GLSA_TERM|GLSA_CORE, EINTR}, + {SIGALRM, GLS_RESTORE_ENV, GLS_CONTINUE, GLSA_TERM, 0}, + {SIGCONT, GLS_RESTORE_ENV, GLS_CONTINUE, GLSA_CONT|GLSA_IGN, 0}, +#if defined(SIGHUP) +#ifdef ENOTTY + {SIGHUP, GLS_SUSPEND_INPUT, GLS_ABORT, GLSA_TERM, ENOTTY}, +#else + {SIGHUP, GLS_SUSPEND_INPUT, GLS_ABORT, GLSA_TERM, EINTR}, +#endif +#endif + {SIGINT, GLS_SUSPEND_INPUT, GLS_ABORT, GLSA_TERM, EINTR}, +#if defined(SIGPIPE) +#ifdef EPIPE + {SIGPIPE, GLS_SUSPEND_INPUT, GLS_ABORT, GLSA_TERM, EPIPE}, +#else + {SIGPIPE, GLS_SUSPEND_INPUT, GLS_ABORT, GLSA_TERM, EINTR}, +#endif +#endif +#ifdef SIGPOLL + {SIGPOLL, GLS_SUSPEND_INPUT, GLS_ABORT, GLSA_TERM, EINTR}, +#endif +#ifdef SIGPWR + {SIGPWR, GLS_RESTORE_ENV, GLS_CONTINUE, GLSA_IGN, 0}, +#endif +#ifdef SIGQUIT + {SIGQUIT, GLS_SUSPEND_INPUT, GLS_ABORT, GLSA_TERM|GLSA_CORE, EINTR}, +#endif + {SIGTERM, GLS_SUSPEND_INPUT, GLS_ABORT, GLSA_TERM, EINTR}, +#ifdef SIGTSTP + {SIGTSTP, GLS_SUSPEND_INPUT, GLS_CONTINUE, GLSA_SUSP, 0}, +#endif +#ifdef SIGTTIN + {SIGTTIN, GLS_SUSPEND_INPUT, GLS_CONTINUE, GLSA_SUSP, 0}, +#endif +#ifdef SIGTTOU + {SIGTTOU, GLS_SUSPEND_INPUT, GLS_CONTINUE, GLSA_SUSP, 0}, +#endif +#ifdef SIGUSR1 + {SIGUSR1, GLS_RESTORE_ENV, GLS_CONTINUE, GLSA_TERM, 0}, +#endif +#ifdef SIGUSR2 + {SIGUSR2, GLS_RESTORE_ENV, GLS_CONTINUE, GLSA_TERM, 0}, +#endif +#ifdef SIGVTALRM + {SIGVTALRM, GLS_RESTORE_ENV, GLS_CONTINUE, GLSA_TERM, 0}, +#endif +#ifdef SIGWINCH + {SIGWINCH, GLS_RESTORE_ENV, GLS_CONTINUE, GLSA_SIZE|GLSA_IGN, 0}, +#endif +#ifdef SIGXCPU + {SIGXCPU, GLS_RESTORE_ENV, GLS_CONTINUE, GLSA_TERM|GLSA_CORE, 0}, +#endif +#ifdef SIGXFSZ + {SIGXFSZ, GLS_RESTORE_ENV, GLS_CONTINUE, GLSA_TERM|GLSA_CORE, 0}, +#endif +}; + +/* + * Define file-scope variables for use in signal handlers. + */ +static volatile sig_atomic_t gl_pending_signal = -1; +static sigjmp_buf gl_setjmp_buffer; + +static void gl_signal_handler(int signo); + +static int gl_check_caught_signal(GetLine *gl); + +/* + * Respond to an externally caught process suspension or + * termination signal. + */ +static void gl_suspend_process(int signo, GetLine *gl, int ngl); + +/* Return the default attributes of a given signal */ + +static int gl_classify_signal(int signo); + +/* + * Unfortunately both terminfo and termcap require one to use the tputs() + * function to output terminal control characters, and this function + * doesn't allow one to specify a file stream. As a result, the following + * file-scope variable is used to pass the current output file stream. + * This is bad, but there doesn't seem to be any alternative. + */ +static GetLine *tputs_gl = NULL; + +/* + * Define a tab to be a string of 8 spaces. + */ +#define TAB_WIDTH 8 + +/* + * Lookup the current size of the terminal. + */ +static void gl_query_size(GetLine *gl, int *ncolumn, int *nline); + +/* + * Getline calls this to temporarily override certain signal handlers + * of the calling program. + */ +static int gl_override_signal_handlers(GetLine *gl); + +/* + * Getline calls this to restore the signal handlers of the calling + * program. + */ +static int gl_restore_signal_handlers(GetLine *gl); + +/* + * Temporarily block the delivery of all signals that gl_get_line() + * is currently configured to trap. + */ +static int gl_mask_signals(GetLine *gl, sigset_t *oldset); + +/* + * Restore the process signal mask that was overriden by a previous + * call to gl_mask_signals(). + */ +static int gl_unmask_signals(GetLine *gl, sigset_t *oldset); + +/* + * Unblock the signals that gl_get_line() has been configured to catch. + */ +static int gl_catch_signals(GetLine *gl); + +/* + * Return the set of all trappable signals. + */ +static void gl_list_trappable_signals(sigset_t *signals); + +/* + * Put the terminal into raw input mode, after saving the original + * terminal attributes in gl->oldattr. + */ +static int gl_raw_terminal_mode(GetLine *gl); + +/* + * Restore the terminal attributes from gl->oldattr. + */ +static int gl_restore_terminal_attributes(GetLine *gl); + +/* + * Switch to non-blocking I/O if possible. + */ +static int gl_nonblocking_io(GetLine *gl, int fd); + +/* + * Switch to blocking I/O if possible. + */ +static int gl_blocking_io(GetLine *gl, int fd); + +/* + * Read a line from the user in raw mode. + */ +static int gl_get_input_line(GetLine *gl, const char *prompt, + const char *start_line, int start_pos); + +/* + * Query the user for a single character. + */ +static int gl_get_query_char(GetLine *gl, const char *prompt, int defchar); + +/* + * Read input from a non-interactive input stream. + */ +static int gl_read_stream_line(GetLine *gl); + +/* + * Read a single character from a non-interactive input stream. + */ +static int gl_read_stream_char(GetLine *gl); + +/* + * Prepare to edit a new line. + */ +static int gl_present_line(GetLine *gl, const char *prompt, + const char *start_line, int start_pos); + +/* + * Reset all line-editing parameters for a new input line. + */ +static void gl_reset_editor(GetLine *gl); + +/* + * Handle the receipt of the potential start of a new key-sequence from + * the user. + */ +static int gl_interpret_char(GetLine *gl, char c); + +/* + * Bind a single control or meta character to an action. + */ +static int gl_bind_control_char(GetLine *gl, KtBinder binder, + char c, const char *action); + +/* + * Set up terminal-specific key bindings. + */ +static int gl_bind_terminal_keys(GetLine *gl); + +/* + * Lookup terminal control string and size information. + */ +static int gl_control_strings(GetLine *gl, const char *term); + +/* + * Wrappers around the terminfo and termcap functions that lookup + * strings in the terminal information databases. + */ +#ifdef USE_TERMINFO +static const char *gl_tigetstr(GetLine *gl, const char *name); +#elif defined(USE_TERMCAP) +static const char *gl_tgetstr(GetLine *gl, const char *name, char **bufptr); +#endif + +/* + * Output a binary string directly to the terminal. + */ +static int gl_print_raw_string(GetLine *gl, int buffered, + const char *string, int n); + +/* + * Print an informational message, starting and finishing on new lines. + * After the list of strings to be printed, the last argument MUST be + * GL_END_INFO. + */ +static int gl_print_info(GetLine *gl, ...); +#define GL_END_INFO ((const char *)0) + +/* + * Start a newline and place the cursor at its start. + */ +static int gl_start_newline(GetLine *gl, int buffered); + +/* + * Output a terminal control sequence. + */ +static int gl_print_control_sequence(GetLine *gl, int nline, + const char *string); + +/* + * Output a character or string to the terminal after converting tabs + * to spaces and control characters to a caret followed by the modified + * character. + */ +static int gl_print_char(GetLine *gl, char c, char pad); +static int gl_print_string(GetLine *gl, const char *string, char pad); + +/* + * Delete nc characters starting from the one under the cursor. + * Optionally copy the deleted characters to the cut buffer. + */ +static int gl_delete_chars(GetLine *gl, int nc, int cut); + +/* + * Add a character to the line buffer at the current cursor position, + * inserting or overwriting according the current mode. + */ +static int gl_add_char_to_line(GetLine *gl, char c); + +/* + * Insert/append a string to the line buffer and terminal at the current + * cursor position. + */ +static int gl_add_string_to_line(GetLine *gl, const char *s); + +/* + * Record a new character in the input-line buffer. + */ +static int gl_buffer_char(GetLine *gl, char c, int bufpos); + +/* + * Record a string in the input-line buffer. + */ +static int gl_buffer_string(GetLine *gl, const char *s, int n, int bufpos); + +/* + * Make way to insert a string in the input-line buffer. + */ +static int gl_make_gap_in_buffer(GetLine *gl, int start, int n); + +/* + * Remove characters from the input-line buffer, and move any characters + * that followed them to the start of the vacated space. + */ +static void gl_remove_from_buffer(GetLine *gl, int start, int n); + +/* + * Terminate the input-line buffer after a specified number of characters. + */ +static int gl_truncate_buffer(GetLine *gl, int n); + +/* + * Delete the displayed part of the input line that follows the current + * terminal cursor position. + */ +static int gl_truncate_display(GetLine *gl); + +/* + * Accomodate changes to the contents of the input line buffer + * that weren't made by the above gl_*buffer functions. + */ +static void gl_update_buffer(GetLine *gl); + +/* + * Read a single character from the terminal. + */ +static int gl_read_terminal(GetLine *gl, int keep, char *c); + +/* + * Discard processed characters from the key-press lookahead buffer. + */ +static void gl_discard_chars(GetLine *gl, int nused); + +/* + * Move the terminal cursor n positions to the left or right. + */ +static int gl_terminal_move_cursor(GetLine *gl, int n); + +/* + * Move the terminal cursor to a given position. + */ +static int gl_set_term_curpos(GetLine *gl, int term_curpos); + +/* + * Set the position of the cursor both in the line input buffer and on the + * terminal. + */ +static int gl_place_cursor(GetLine *gl, int buff_curpos); + +/* + * How many characters are needed to write a number as an octal string? + */ +static int gl_octal_width(unsigned num); + +/* + * Return the number of spaces needed to display a tab character at + * a given location of the terminal. + */ +static int gl_displayed_tab_width(GetLine *gl, int term_curpos); + +/* + * Return the number of terminal characters needed to display a + * given raw character. + */ +static int gl_displayed_char_width(GetLine *gl, char c, int term_curpos); + +/* + * Return the number of terminal characters needed to display a + * given substring. + */ +static int gl_displayed_string_width(GetLine *gl, const char *string, int nc, + int term_curpos); + +/* + * Return non-zero if 'c' is to be considered part of a word. + */ +static int gl_is_word_char(int c); + +/* + * Read a tecla configuration file. + */ +static int _gl_read_config_file(GetLine *gl, const char *filename, KtBinder who); + +/* + * Read a tecla configuration string. + */ +static int _gl_read_config_string(GetLine *gl, const char *buffer, KtBinder who); + +/* + * Define the callback function used by _gl_parse_config_line() to + * read the next character of a configuration stream. + */ +#define GLC_GETC_FN(fn) int (fn)(void *stream) +typedef GLC_GETC_FN(GlcGetcFn); + +static GLC_GETC_FN(glc_file_getc); /* Read from a file */ +static GLC_GETC_FN(glc_buff_getc); /* Read from a string */ + +/* + * Parse a single configuration command line. + */ +static int _gl_parse_config_line(GetLine *gl, void *stream, GlcGetcFn *getc_fn, + const char *origin, KtBinder who, int *lineno); +static int gl_report_config_error(GetLine *gl, const char *origin, int lineno, + const char *errmsg); + +/* + * Bind the actual arrow key bindings to match those of the symbolic + * arrow-key bindings. + */ +static int _gl_bind_arrow_keys(GetLine *gl); + +/* + * Copy the binding of the specified symbolic arrow-key binding to + * the terminal specific, and default arrow-key key-sequences. + */ +static int _gl_rebind_arrow_key(GetLine *gl, const char *name, + const char *term_seq, + const char *def_seq1, + const char *def_seq2); + +/* + * After the gl_read_from_file() action has been used to tell gl_get_line() + * to temporarily read input from a file, gl_revert_input() arranges + * for input to be reverted to the input stream last registered with + * gl_change_terminal(). + */ +static void gl_revert_input(GetLine *gl); + +/* + * Flush unwritten characters to the terminal. + */ +static int gl_flush_output(GetLine *gl); + +/* + * The callback through which all terminal output is routed. + * This simply appends characters to a queue buffer, which is + * subsequently flushed to the output channel by gl_flush_output(). + */ +static GL_WRITE_FN(gl_write_fn); + +/* + * The callback function which the output character queue object + * calls to transfer characters to the output channel. + */ +static GL_WRITE_FN(gl_flush_terminal); + +/* + * Enumerate the possible return statuses of gl_read_input(). + */ +typedef enum { + GL_READ_OK, /* A character was read successfully */ + GL_READ_ERROR, /* A read-error occurred */ + GL_READ_BLOCKED, /* The read would have blocked the caller */ + GL_READ_EOF /* The end of the current input file was reached */ +} GlReadStatus; + +static GlReadStatus gl_read_input(GetLine *gl, char *c); +/* + * Private functions of gl_read_input(). + */ +static int gl_event_handler(GetLine *gl, int fd); +static GlReadStatus gl_read_unmasked(GetLine *gl, int fd, char *c); + + +/* + * A private function of gl_tty_signals(). + */ +static int gl_set_tty_signal(int signo, void (*handler)(int)); + +/* + * Change the editor style being emulated. + */ +static int gl_change_editor(GetLine *gl, GlEditor editor); + +/* + * Searching in a given direction, return the index of a given (or + * read) character in the input line, or the character that precedes + * it in the specified search direction. Return -1 if not found. + */ +static int gl_find_char(GetLine *gl, int count, int forward, int onto, char c); + +/* + * Return the buffer index of the nth word ending after the cursor. + */ +static int gl_nth_word_end_forward(GetLine *gl, int n); + +/* + * Return the buffer index of the nth word start after the cursor. + */ +static int gl_nth_word_start_forward(GetLine *gl, int n); + +/* + * Return the buffer index of the nth word start before the cursor. + */ +static int gl_nth_word_start_backward(GetLine *gl, int n); + +/* + * When called when vi command mode is enabled, this function saves the + * current line and cursor position for potential restoration later + * by the vi undo command. + */ +static void gl_save_for_undo(GetLine *gl); + +/* + * If in vi mode, switch to vi command mode. + */ +static void gl_vi_command_mode(GetLine *gl); + +/* + * In vi mode this is used to delete up to or onto a given or read + * character in the input line. Also switch to insert mode if requested + * after the deletion. + */ +static int gl_delete_find(GetLine *gl, int count, char c, int forward, + int onto, int change); + +/* + * Copy the characters between the cursor and the count'th instance of + * a specified (or read) character in the input line, into the cut buffer. + */ +static int gl_copy_find(GetLine *gl, int count, char c, int forward, int onto); + +/* + * Return the line index of the parenthesis that either matches the one under + * the cursor, or not over a parenthesis character, the index of the next + * close parenthesis. Return -1 if not found. + */ +static int gl_index_of_matching_paren(GetLine *gl); + +/* + * Replace a malloc'd string (or NULL), with another malloc'd copy of + * a string (or NULL). + */ +static int gl_record_string(char **sptr, const char *string); + +/* + * Enumerate text display attributes as powers of two, suitable for + * use in a bit-mask. + */ +typedef enum { + GL_TXT_STANDOUT=1, /* Display text highlighted */ + GL_TXT_UNDERLINE=2, /* Display text underlined */ + GL_TXT_REVERSE=4, /* Display text with reverse video */ + GL_TXT_BLINK=8, /* Display blinking text */ + GL_TXT_DIM=16, /* Display text in a dim font */ + GL_TXT_BOLD=32 /* Display text using a bold font */ +} GlTextAttr; + +/* + * Display the prompt regardless of the current visibility mode. + */ +static int gl_display_prompt(GetLine *gl); + +/* + * Return the number of characters used by the prompt on the terminal. + */ +static int gl_displayed_prompt_width(GetLine *gl); + +/* + * Prepare to return the current input line to the caller of gl_get_line(). + */ +static int gl_line_ended(GetLine *gl, int newline_char); + +/* + * Arrange for the input line to be redisplayed when the current contents + * of the output queue have been flushed. + */ +static void gl_queue_redisplay(GetLine *gl); + +/* + * Erase the displayed representation of the input line, without + * touching the buffered copy. + */ +static int gl_erase_line(GetLine *gl); + +/* + * This function is called whenever the input line has been erased. + */ +static void gl_line_erased(GetLine *gl); + +/* + * Arrange for the current input line to be discarded. + */ +void _gl_abandon_line(GetLine *gl); + +/* + * The following are private internally callable versions of pertinent + * public functions. Unlike their public wrapper functions, they don't + * block signals while running, and assume that their arguments are valid. + * They are designed to be called from places where signals are already + * blocked, and where simple sanity checks have already been applied to + * their arguments. + */ +static char *_gl_get_line(GetLine *gl, const char *prompt, + const char *start_line, int start_pos); +static int _gl_query_char(GetLine *gl, const char *prompt, char defchar); +static int _gl_read_char(GetLine *gl); +static int _gl_update_size(GetLine *gl); +/* + * Redraw the current input line to account for a change in the terminal + * size. Also install the new size in gl. + */ +static int gl_handle_tty_resize(GetLine *gl, int ncolumn, int nline); + +static int _gl_change_terminal(GetLine *gl, FILE *input_fp, FILE *output_fp, + const char *term); +static int _gl_configure_getline(GetLine *gl, const char *app_string, + const char *app_file, const char *user_file); +static int _gl_save_history(GetLine *gl, const char *filename, + const char *comment, int max_lines); +static int _gl_load_history(GetLine *gl, const char *filename, + const char *comment); +static int _gl_watch_fd(GetLine *gl, int fd, GlFdEvent event, + GlFdEventFn *callback, void *data); +static void _gl_terminal_size(GetLine *gl, int def_ncolumn, int def_nline, + GlTerminalSize *size); +static void _gl_replace_prompt(GetLine *gl, const char *prompt); +static int _gl_trap_signal(GetLine *gl, int signo, unsigned flags, + GlAfterSignal after, int errno_value); +static int _gl_raw_io(GetLine *gl, int redisplay); +static int _gl_normal_io(GetLine *gl); +static int _gl_completion_action(GetLine *gl, void *data, CplMatchFn *match_fn, + int list_only, const char *name, + const char *keyseq); +static int _gl_register_action(GetLine *gl, void *data, GlActionFn *fn, + const char *name, const char *keyseq); +static int _gl_io_mode(GetLine *gl, GlIOMode mode); +static int _gl_set_term_size(GetLine *gl, int ncolumn, int nline); +static int _gl_append_history(GetLine *gl, const char *line); + +/* + * Reset the completion status and associated errno value in + * gl->rtn_status and gl->rtn_errno. + */ +static void gl_clear_status(GetLine *gl); + +/* + * Record a completion status, unless a previous abnormal completion + * status has already been recorded for the current call. + */ +static void gl_record_status(GetLine *gl, GlReturnStatus rtn_status, + int rtn_errno); + +/* + * Set the maximum length of a line in a user's tecla configuration + * file (not counting comments). + */ +#define GL_CONF_BUFLEN 100 + +/* + * Set the maximum number of arguments supported by individual commands + * in tecla configuration files. + */ +#define GL_CONF_MAXARG 10 + +/* + * Prototype the available action functions. + */ +static KT_KEY_FN(gl_user_interrupt); +static KT_KEY_FN(gl_abort); +static KT_KEY_FN(gl_suspend); +static KT_KEY_FN(gl_stop_output); +static KT_KEY_FN(gl_start_output); +static KT_KEY_FN(gl_literal_next); +static KT_KEY_FN(gl_cursor_left); +static KT_KEY_FN(gl_cursor_right); +static KT_KEY_FN(gl_insert_mode); +static KT_KEY_FN(gl_beginning_of_line); +static KT_KEY_FN(gl_end_of_line); +static KT_KEY_FN(gl_delete_line); +static KT_KEY_FN(gl_kill_line); +static KT_KEY_FN(gl_forward_word); +static KT_KEY_FN(gl_backward_word); +static KT_KEY_FN(gl_forward_delete_char); +static KT_KEY_FN(gl_backward_delete_char); +static KT_KEY_FN(gl_forward_delete_word); +static KT_KEY_FN(gl_backward_delete_word); +static KT_KEY_FN(gl_delete_refind); +static KT_KEY_FN(gl_delete_invert_refind); +static KT_KEY_FN(gl_delete_to_column); +static KT_KEY_FN(gl_delete_to_parenthesis); +static KT_KEY_FN(gl_forward_delete_find); +static KT_KEY_FN(gl_backward_delete_find); +static KT_KEY_FN(gl_forward_delete_to); +static KT_KEY_FN(gl_backward_delete_to); +static KT_KEY_FN(gl_upcase_word); +static KT_KEY_FN(gl_downcase_word); +static KT_KEY_FN(gl_capitalize_word); +static KT_KEY_FN(gl_redisplay); +static KT_KEY_FN(gl_clear_screen); +static KT_KEY_FN(gl_transpose_chars); +static KT_KEY_FN(gl_set_mark); +static KT_KEY_FN(gl_exchange_point_and_mark); +static KT_KEY_FN(gl_kill_region); +static KT_KEY_FN(gl_copy_region_as_kill); +static KT_KEY_FN(gl_yank); +static KT_KEY_FN(gl_up_history); +static KT_KEY_FN(gl_down_history); +static KT_KEY_FN(gl_history_search_backward); +static KT_KEY_FN(gl_history_re_search_backward); +static KT_KEY_FN(gl_history_search_forward); +static KT_KEY_FN(gl_history_re_search_forward); +static KT_KEY_FN(gl_complete_word); +#ifndef HIDE_FILE_SYSTEM +static KT_KEY_FN(gl_expand_filename); +static KT_KEY_FN(gl_read_from_file); +static KT_KEY_FN(gl_read_init_files); +static KT_KEY_FN(gl_list_glob); +#endif +static KT_KEY_FN(gl_del_char_or_list_or_eof); +static KT_KEY_FN(gl_list_or_eof); +static KT_KEY_FN(gl_beginning_of_history); +static KT_KEY_FN(gl_end_of_history); +static KT_KEY_FN(gl_digit_argument); +static KT_KEY_FN(gl_newline); +static KT_KEY_FN(gl_repeat_history); +static KT_KEY_FN(gl_vi_insert); +static KT_KEY_FN(gl_vi_overwrite); +static KT_KEY_FN(gl_change_case); +static KT_KEY_FN(gl_vi_insert_at_bol); +static KT_KEY_FN(gl_vi_append_at_eol); +static KT_KEY_FN(gl_vi_append); +static KT_KEY_FN(gl_backward_kill_line); +static KT_KEY_FN(gl_goto_column); +static KT_KEY_FN(gl_forward_to_word); +static KT_KEY_FN(gl_vi_replace_char); +static KT_KEY_FN(gl_vi_change_rest_of_line); +static KT_KEY_FN(gl_vi_change_line); +static KT_KEY_FN(gl_vi_change_to_bol); +static KT_KEY_FN(gl_vi_change_refind); +static KT_KEY_FN(gl_vi_change_invert_refind); +static KT_KEY_FN(gl_vi_change_to_column); +static KT_KEY_FN(gl_vi_change_to_parenthesis); +static KT_KEY_FN(gl_vi_forward_change_word); +static KT_KEY_FN(gl_vi_backward_change_word); +static KT_KEY_FN(gl_vi_forward_change_find); +static KT_KEY_FN(gl_vi_backward_change_find); +static KT_KEY_FN(gl_vi_forward_change_to); +static KT_KEY_FN(gl_vi_backward_change_to); +static KT_KEY_FN(gl_vi_forward_change_char); +static KT_KEY_FN(gl_vi_backward_change_char); +static KT_KEY_FN(gl_forward_copy_char); +static KT_KEY_FN(gl_backward_copy_char); +static KT_KEY_FN(gl_forward_find_char); +static KT_KEY_FN(gl_backward_find_char); +static KT_KEY_FN(gl_forward_to_char); +static KT_KEY_FN(gl_backward_to_char); +static KT_KEY_FN(gl_repeat_find_char); +static KT_KEY_FN(gl_invert_refind_char); +static KT_KEY_FN(gl_append_yank); +static KT_KEY_FN(gl_backward_copy_word); +static KT_KEY_FN(gl_forward_copy_word); +static KT_KEY_FN(gl_copy_to_bol); +static KT_KEY_FN(gl_copy_refind); +static KT_KEY_FN(gl_copy_invert_refind); +static KT_KEY_FN(gl_copy_to_column); +static KT_KEY_FN(gl_copy_to_parenthesis); +static KT_KEY_FN(gl_copy_rest_of_line); +static KT_KEY_FN(gl_copy_line); +static KT_KEY_FN(gl_backward_copy_find); +static KT_KEY_FN(gl_forward_copy_find); +static KT_KEY_FN(gl_backward_copy_to); +static KT_KEY_FN(gl_forward_copy_to); +static KT_KEY_FN(gl_vi_undo); +static KT_KEY_FN(gl_emacs_editing_mode); +static KT_KEY_FN(gl_vi_editing_mode); +static KT_KEY_FN(gl_ring_bell); +static KT_KEY_FN(gl_vi_repeat_change); +static KT_KEY_FN(gl_find_parenthesis); +static KT_KEY_FN(gl_list_history); +static KT_KEY_FN(gl_list_completions); +static KT_KEY_FN(gl_run_external_action); + +/* + * Name the available action functions. + */ +static const struct {const char *name; KT_KEY_FN(*fn);} gl_actions[] = { + {"user-interrupt", gl_user_interrupt}, + {"abort", gl_abort}, + {"suspend", gl_suspend}, + {"stop-output", gl_stop_output}, + {"start-output", gl_start_output}, + {"literal-next", gl_literal_next}, + {"cursor-right", gl_cursor_right}, + {"cursor-left", gl_cursor_left}, + {"insert-mode", gl_insert_mode}, + {"beginning-of-line", gl_beginning_of_line}, + {"end-of-line", gl_end_of_line}, + {"delete-line", gl_delete_line}, + {"kill-line", gl_kill_line}, + {"forward-word", gl_forward_word}, + {"backward-word", gl_backward_word}, + {"forward-delete-char", gl_forward_delete_char}, + {"backward-delete-char", gl_backward_delete_char}, + {"forward-delete-word", gl_forward_delete_word}, + {"backward-delete-word", gl_backward_delete_word}, + {"delete-refind", gl_delete_refind}, + {"delete-invert-refind", gl_delete_invert_refind}, + {"delete-to-column", gl_delete_to_column}, + {"delete-to-parenthesis", gl_delete_to_parenthesis}, + {"forward-delete-find", gl_forward_delete_find}, + {"backward-delete-find", gl_backward_delete_find}, + {"forward-delete-to", gl_forward_delete_to}, + {"backward-delete-to", gl_backward_delete_to}, + {"upcase-word", gl_upcase_word}, + {"downcase-word", gl_downcase_word}, + {"capitalize-word", gl_capitalize_word}, + {"redisplay", gl_redisplay}, + {"clear-screen", gl_clear_screen}, + {"transpose-chars", gl_transpose_chars}, + {"set-mark", gl_set_mark}, + {"exchange-point-and-mark", gl_exchange_point_and_mark}, + {"kill-region", gl_kill_region}, + {"copy-region-as-kill", gl_copy_region_as_kill}, + {"yank", gl_yank}, + {"up-history", gl_up_history}, + {"down-history", gl_down_history}, + {"history-search-backward", gl_history_search_backward}, + {"history-re-search-backward", gl_history_re_search_backward}, + {"history-search-forward", gl_history_search_forward}, + {"history-re-search-forward", gl_history_re_search_forward}, + {"complete-word", gl_complete_word}, +#ifndef HIDE_FILE_SYSTEM + {"expand-filename", gl_expand_filename}, + {"read-from-file", gl_read_from_file}, + {"read-init-files", gl_read_init_files}, + {"list-glob", gl_list_glob}, +#endif + {"del-char-or-list-or-eof", gl_del_char_or_list_or_eof}, + {"beginning-of-history", gl_beginning_of_history}, + {"end-of-history", gl_end_of_history}, + {"digit-argument", gl_digit_argument}, + {"newline", gl_newline}, + {"repeat-history", gl_repeat_history}, + {"vi-insert", gl_vi_insert}, + {"vi-overwrite", gl_vi_overwrite}, + {"vi-insert-at-bol", gl_vi_insert_at_bol}, + {"vi-append-at-eol", gl_vi_append_at_eol}, + {"vi-append", gl_vi_append}, + {"change-case", gl_change_case}, + {"backward-kill-line", gl_backward_kill_line}, + {"goto-column", gl_goto_column}, + {"forward-to-word", gl_forward_to_word}, + {"vi-replace-char", gl_vi_replace_char}, + {"vi-change-rest-of-line", gl_vi_change_rest_of_line}, + {"vi-change-line", gl_vi_change_line}, + {"vi-change-to-bol", gl_vi_change_to_bol}, + {"vi-change-refind", gl_vi_change_refind}, + {"vi-change-invert-refind", gl_vi_change_invert_refind}, + {"vi-change-to-column", gl_vi_change_to_column}, + {"vi-change-to-parenthesis", gl_vi_change_to_parenthesis}, + {"forward-copy-char", gl_forward_copy_char}, + {"backward-copy-char", gl_backward_copy_char}, + {"forward-find-char", gl_forward_find_char}, + {"backward-find-char", gl_backward_find_char}, + {"forward-to-char", gl_forward_to_char}, + {"backward-to-char", gl_backward_to_char}, + {"repeat-find-char", gl_repeat_find_char}, + {"invert-refind-char", gl_invert_refind_char}, + {"append-yank", gl_append_yank}, + {"backward-copy-word", gl_backward_copy_word}, + {"forward-copy-word", gl_forward_copy_word}, + {"copy-to-bol", gl_copy_to_bol}, + {"copy-refind", gl_copy_refind}, + {"copy-invert-refind", gl_copy_invert_refind}, + {"copy-to-column", gl_copy_to_column}, + {"copy-to-parenthesis", gl_copy_to_parenthesis}, + {"copy-rest-of-line", gl_copy_rest_of_line}, + {"copy-line", gl_copy_line}, + {"backward-copy-find", gl_backward_copy_find}, + {"forward-copy-find", gl_forward_copy_find}, + {"backward-copy-to", gl_backward_copy_to}, + {"forward-copy-to", gl_forward_copy_to}, + {"list-or-eof", gl_list_or_eof}, + {"vi-undo", gl_vi_undo}, + {"vi-backward-change-word", gl_vi_backward_change_word}, + {"vi-forward-change-word", gl_vi_forward_change_word}, + {"vi-backward-change-find", gl_vi_backward_change_find}, + {"vi-forward-change-find", gl_vi_forward_change_find}, + {"vi-backward-change-to", gl_vi_backward_change_to}, + {"vi-forward-change-to", gl_vi_forward_change_to}, + {"vi-backward-change-char", gl_vi_backward_change_char}, + {"vi-forward-change-char", gl_vi_forward_change_char}, + {"emacs-mode", gl_emacs_editing_mode}, + {"vi-mode", gl_vi_editing_mode}, + {"ring-bell", gl_ring_bell}, + {"vi-repeat-change", gl_vi_repeat_change}, + {"find-parenthesis", gl_find_parenthesis}, + {"list-history", gl_list_history}, +}; + +/* + * Define the default key-bindings in emacs mode. + */ +static const KtKeyBinding gl_emacs_bindings[] = { + {"right", "cursor-right"}, + {"^F", "cursor-right"}, + {"left", "cursor-left"}, + {"^B", "cursor-left"}, + {"M-i", "insert-mode"}, + {"M-I", "insert-mode"}, + {"^A", "beginning-of-line"}, + {"^E", "end-of-line"}, + {"^U", "delete-line"}, + {"^K", "kill-line"}, + {"M-f", "forward-word"}, + {"M-F", "forward-word"}, + {"M-b", "backward-word"}, + {"M-B", "backward-word"}, + {"^D", "del-char-or-list-or-eof"}, + {"^H", "backward-delete-char"}, + {"^?", "backward-delete-char"}, + {"M-d", "forward-delete-word"}, + {"M-D", "forward-delete-word"}, + {"M-^H", "backward-delete-word"}, + {"M-^?", "backward-delete-word"}, + {"M-u", "upcase-word"}, + {"M-U", "upcase-word"}, + {"M-l", "downcase-word"}, + {"M-L", "downcase-word"}, + {"M-c", "capitalize-word"}, + {"M-C", "capitalize-word"}, + {"^R", "redisplay"}, + {"^L", "clear-screen"}, + {"^T", "transpose-chars"}, + {"^@", "set-mark"}, + {"^X^X", "exchange-point-and-mark"}, + {"^W", "kill-region"}, + {"M-w", "copy-region-as-kill"}, + {"M-W", "copy-region-as-kill"}, + {"^Y", "yank"}, + {"^P", "up-history"}, + {"up", "up-history"}, + {"^N", "down-history"}, + {"down", "down-history"}, + {"M-p", "history-search-backward"}, + {"M-P", "history-search-backward"}, + {"M-n", "history-search-forward"}, + {"M-N", "history-search-forward"}, + {"\t", "complete-word"}, +#ifndef HIDE_FILE_SYSTEM + {"^X*", "expand-filename"}, + {"^X^F", "read-from-file"}, + {"^X^R", "read-init-files"}, + {"^Xg", "list-glob"}, + {"^XG", "list-glob"}, +#endif + {"^Xh", "list-history"}, + {"^XH", "list-history"}, + {"M-<", "beginning-of-history"}, + {"M->", "end-of-history"}, + {"M-0", "digit-argument"}, + {"M-1", "digit-argument"}, + {"M-2", "digit-argument"}, + {"M-3", "digit-argument"}, + {"M-4", "digit-argument"}, + {"M-5", "digit-argument"}, + {"M-6", "digit-argument"}, + {"M-7", "digit-argument"}, + {"M-8", "digit-argument"}, + {"M-9", "digit-argument"}, + {"\r", "newline"}, + {"\n", "newline"}, + {"M-o", "repeat-history"}, + {"M-C-v", "vi-mode"}, +}; + +/* + * Define the default key-bindings in vi mode. Note that in vi-mode + * meta-key bindings are command-mode bindings. For example M-i first + * switches to command mode if not already in that mode, then moves + * the cursor one position right, as in vi. + */ +static const KtKeyBinding gl_vi_bindings[] = { + {"^D", "list-or-eof"}, +#ifndef HIDE_FILE_SYSTEM + {"^G", "list-glob"}, +#endif + {"^H", "backward-delete-char"}, + {"\t", "complete-word"}, + {"\r", "newline"}, + {"\n", "newline"}, + {"^L", "clear-screen"}, + {"^N", "down-history"}, + {"^P", "up-history"}, + {"^R", "redisplay"}, + {"^U", "backward-kill-line"}, + {"^W", "backward-delete-word"}, +#ifndef HIDE_FILE_SYSTEM + {"^X^F", "read-from-file"}, + {"^X^R", "read-init-files"}, + {"^X*", "expand-filename"}, +#endif + {"^?", "backward-delete-char"}, + {"M- ", "cursor-right"}, + {"M-$", "end-of-line"}, +#ifndef HIDE_FILE_SYSTEM + {"M-*", "expand-filename"}, +#endif + {"M-+", "down-history"}, + {"M--", "up-history"}, + {"M-<", "beginning-of-history"}, + {"M->", "end-of-history"}, + {"M-^", "beginning-of-line"}, + {"M-;", "repeat-find-char"}, + {"M-,", "invert-refind-char"}, + {"M-|", "goto-column"}, + {"M-~", "change-case"}, + {"M-.", "vi-repeat-change"}, + {"M-%", "find-parenthesis"}, + {"M-0", "digit-argument"}, + {"M-1", "digit-argument"}, + {"M-2", "digit-argument"}, + {"M-3", "digit-argument"}, + {"M-4", "digit-argument"}, + {"M-5", "digit-argument"}, + {"M-6", "digit-argument"}, + {"M-7", "digit-argument"}, + {"M-8", "digit-argument"}, + {"M-9", "digit-argument"}, + {"M-a", "vi-append"}, + {"M-A", "vi-append-at-eol"}, + {"M-b", "backward-word"}, + {"M-B", "backward-word"}, + {"M-C", "vi-change-rest-of-line"}, + {"M-cb", "vi-backward-change-word"}, + {"M-cB", "vi-backward-change-word"}, + {"M-cc", "vi-change-line"}, + {"M-ce", "vi-forward-change-word"}, + {"M-cE", "vi-forward-change-word"}, + {"M-cw", "vi-forward-change-word"}, + {"M-cW", "vi-forward-change-word"}, + {"M-cF", "vi-backward-change-find"}, + {"M-cf", "vi-forward-change-find"}, + {"M-cT", "vi-backward-change-to"}, + {"M-ct", "vi-forward-change-to"}, + {"M-c;", "vi-change-refind"}, + {"M-c,", "vi-change-invert-refind"}, + {"M-ch", "vi-backward-change-char"}, + {"M-c^H", "vi-backward-change-char"}, + {"M-c^?", "vi-backward-change-char"}, + {"M-cl", "vi-forward-change-char"}, + {"M-c ", "vi-forward-change-char"}, + {"M-c^", "vi-change-to-bol"}, + {"M-c0", "vi-change-to-bol"}, + {"M-c$", "vi-change-rest-of-line"}, + {"M-c|", "vi-change-to-column"}, + {"M-c%", "vi-change-to-parenthesis"}, + {"M-dh", "backward-delete-char"}, + {"M-d^H", "backward-delete-char"}, + {"M-d^?", "backward-delete-char"}, + {"M-dl", "forward-delete-char"}, + {"M-d ", "forward-delete-char"}, + {"M-dd", "delete-line"}, + {"M-db", "backward-delete-word"}, + {"M-dB", "backward-delete-word"}, + {"M-de", "forward-delete-word"}, + {"M-dE", "forward-delete-word"}, + {"M-dw", "forward-delete-word"}, + {"M-dW", "forward-delete-word"}, + {"M-dF", "backward-delete-find"}, + {"M-df", "forward-delete-find"}, + {"M-dT", "backward-delete-to"}, + {"M-dt", "forward-delete-to"}, + {"M-d;", "delete-refind"}, + {"M-d,", "delete-invert-refind"}, + {"M-d^", "backward-kill-line"}, + {"M-d0", "backward-kill-line"}, + {"M-d$", "kill-line"}, + {"M-D", "kill-line"}, + {"M-d|", "delete-to-column"}, + {"M-d%", "delete-to-parenthesis"}, + {"M-e", "forward-word"}, + {"M-E", "forward-word"}, + {"M-f", "forward-find-char"}, + {"M-F", "backward-find-char"}, + {"M--", "up-history"}, + {"M-h", "cursor-left"}, + {"M-H", "beginning-of-history"}, + {"M-i", "vi-insert"}, + {"M-I", "vi-insert-at-bol"}, + {"M-j", "down-history"}, + {"M-J", "history-search-forward"}, + {"M-k", "up-history"}, + {"M-K", "history-search-backward"}, + {"M-l", "cursor-right"}, + {"M-L", "end-of-history"}, + {"M-n", "history-re-search-forward"}, + {"M-N", "history-re-search-backward"}, + {"M-p", "append-yank"}, + {"M-P", "yank"}, + {"M-r", "vi-replace-char"}, + {"M-R", "vi-overwrite"}, + {"M-s", "vi-forward-change-char"}, + {"M-S", "vi-change-line"}, + {"M-t", "forward-to-char"}, + {"M-T", "backward-to-char"}, + {"M-u", "vi-undo"}, + {"M-w", "forward-to-word"}, + {"M-W", "forward-to-word"}, + {"M-x", "forward-delete-char"}, + {"M-X", "backward-delete-char"}, + {"M-yh", "backward-copy-char"}, + {"M-y^H", "backward-copy-char"}, + {"M-y^?", "backward-copy-char"}, + {"M-yl", "forward-copy-char"}, + {"M-y ", "forward-copy-char"}, + {"M-ye", "forward-copy-word"}, + {"M-yE", "forward-copy-word"}, + {"M-yw", "forward-copy-word"}, + {"M-yW", "forward-copy-word"}, + {"M-yb", "backward-copy-word"}, + {"M-yB", "backward-copy-word"}, + {"M-yf", "forward-copy-find"}, + {"M-yF", "backward-copy-find"}, + {"M-yt", "forward-copy-to"}, + {"M-yT", "backward-copy-to"}, + {"M-y;", "copy-refind"}, + {"M-y,", "copy-invert-refind"}, + {"M-y^", "copy-to-bol"}, + {"M-y0", "copy-to-bol"}, + {"M-y$", "copy-rest-of-line"}, + {"M-yy", "copy-line"}, + {"M-Y", "copy-line"}, + {"M-y|", "copy-to-column"}, + {"M-y%", "copy-to-parenthesis"}, + {"M-^E", "emacs-mode"}, + {"M-^H", "cursor-left"}, + {"M-^?", "cursor-left"}, + {"M-^L", "clear-screen"}, + {"M-^N", "down-history"}, + {"M-^P", "up-history"}, + {"M-^R", "redisplay"}, + {"M-^D", "list-or-eof"}, + {"M-\r", "newline"}, + {"M-\t", "complete-word"}, + {"M-\n", "newline"}, +#ifndef HIDE_FILE_SYSTEM + {"M-^X^R", "read-init-files"}, +#endif + {"M-^Xh", "list-history"}, + {"M-^XH", "list-history"}, + {"down", "down-history"}, + {"up", "up-history"}, + {"left", "cursor-left"}, + {"right", "cursor-right"}, +}; + +/*....................................................................... + * Create a new GetLine object. + * + * Input: + * linelen size_t The maximum line length to allow for. + * histlen size_t The number of bytes to allocate for recording + * a circular buffer of history lines. + * Output: + * return GetLine * The new object, or NULL on error. + */ +GetLine *new_GetLine(size_t linelen, size_t histlen) +{ + GetLine *gl; /* The object to be returned */ + int i; +/* + * Check the arguments. + */ + if(linelen < 10) { + errno = ENOMEM; + return NULL; + }; +/* + * Allocate the container. + */ + gl = (GetLine *) malloc(sizeof(GetLine)); + if(!gl) { + errno = ENOMEM; + return NULL; + }; +/* + * Before attempting any operation that might fail, initialize the + * container at least up to the point at which it can safely be passed + * to del_GetLine(). + */ + gl->err = NULL; + gl->glh = NULL; + gl->cpl = NULL; +#ifndef HIDE_FILE_SYSTEM + gl->cplfn.fn = cpl_file_completions; +#else + gl->cplfn.fn = gl_no_completions; +#endif + gl->cplfn.data = NULL; +#ifndef WITHOUT_FILE_SYSTEM + gl->ef = NULL; +#endif + gl->capmem = NULL; + gl->cq = NULL; + gl->input_fd = -1; + gl->output_fd = -1; + gl->input_fp = NULL; + gl->output_fp = NULL; + gl->file_fp = NULL; + gl->term = NULL; + gl->is_term = 0; + gl->flush_fn = gl_flush_terminal; + gl->io_mode = GL_NORMAL_MODE; + gl->raw_mode = 0; + gl->pending_io = GLP_WRITE; /* We will start by writing the prompt */ + gl_clear_status(gl); + gl->linelen = linelen; + gl->line = NULL; + gl->cutbuf = NULL; + gl->prompt = NULL; + gl->prompt_len = 0; + gl->prompt_changed = 0; + gl->prompt_style = GL_LITERAL_PROMPT; + gl->cpl_mem = NULL; + gl->ext_act_mem = NULL; + gl->sig_mem = NULL; + gl->sigs = NULL; + gl->signals_masked = 0; + gl->signals_overriden = 0; + sigemptyset(&gl->all_signal_set); + sigemptyset(&gl->old_signal_set); + sigemptyset(&gl->use_signal_set); + gl->bindings = NULL; + gl->ntotal = 0; + gl->buff_curpos = 0; + gl->term_curpos = 0; + gl->term_len = 0; + gl->buff_mark = 0; + gl->insert_curpos = 0; + gl->insert = 1; + gl->number = -1; + gl->endline = 1; + gl->displayed = 0; + gl->redisplay = 0; + gl->postpone = 0; + gl->keybuf[0]='\0'; + gl->nbuf = 0; + gl->nread = 0; + gl->current_action.fn = 0; + gl->current_action.data = NULL; + gl->current_count = 0; + gl->preload_id = 0; + gl->preload_history = 0; + gl->keyseq_count = 0; + gl->last_search = -1; + gl->editor = GL_EMACS_MODE; + gl->silence_bell = 0; + gl->automatic_history = 1; + gl->vi.undo.line = NULL; + gl->vi.undo.buff_curpos = 0; + gl->vi.undo.ntotal = 0; + gl->vi.undo.saved = 0; + gl->vi.repeat.action.fn = 0; + gl->vi.repeat.action.data = 0; + gl->vi.repeat.count = 0; + gl->vi.repeat.input_curpos = 0; + gl->vi.repeat.command_curpos = 0; + gl->vi.repeat.input_char = '\0'; + gl->vi.repeat.saved = 0; + gl->vi.repeat.active = 0; + gl->vi.command = 0; + gl->vi.find_forward = 0; + gl->vi.find_onto = 0; + gl->vi.find_char = '\0'; + gl->left = NULL; + gl->right = NULL; + gl->up = NULL; + gl->down = NULL; + gl->home = NULL; + gl->bol = 0; + gl->clear_eol = NULL; + gl->clear_eod = NULL; + gl->u_arrow = NULL; + gl->d_arrow = NULL; + gl->l_arrow = NULL; + gl->r_arrow = NULL; + gl->sound_bell = NULL; + gl->bold = NULL; + gl->underline = NULL; + gl->standout = NULL; + gl->dim = NULL; + gl->reverse = NULL; + gl->blink = NULL; + gl->text_attr_off = NULL; + gl->nline = 0; + gl->ncolumn = 0; +#ifdef USE_TERMINFO + gl->left_n = NULL; + gl->right_n = NULL; +#elif defined(USE_TERMCAP) + gl->tgetent_buf = NULL; + gl->tgetstr_buf = NULL; +#endif + gl->app_file = NULL; + gl->user_file = NULL; + gl->configured = 0; + gl->echo = 1; + gl->last_signal = -1; +#ifdef HAVE_SELECT + gl->fd_node_mem = NULL; + gl->fd_nodes = NULL; + FD_ZERO(&gl->rfds); + FD_ZERO(&gl->wfds); + FD_ZERO(&gl->ufds); + gl->max_fd = 0; + gl->timer.dt.tv_sec = 0; + gl->timer.dt.tv_usec = 0; + gl->timer.fn = 0; + gl->timer.data = NULL; +#endif +/* + * Allocate an error reporting buffer. + */ + gl->err = _new_ErrMsg(); + if(!gl->err) + return del_GetLine(gl); +/* + * Allocate the history buffer. + */ + gl->glh = _new_GlHistory(histlen); + if(!gl->glh) + return del_GetLine(gl); +/* + * Allocate the resource object for file-completion. + */ + gl->cpl = new_WordCompletion(); + if(!gl->cpl) + return del_GetLine(gl); +/* + * Allocate the resource object for file-completion. + */ +#ifndef WITHOUT_FILE_SYSTEM + gl->ef = new_ExpandFile(); + if(!gl->ef) + return del_GetLine(gl); +#endif +/* + * Allocate a string-segment memory allocator for use in storing terminal + * capablity strings. + */ + gl->capmem = _new_StringGroup(CAPMEM_SEGMENT_SIZE); + if(!gl->capmem) + return del_GetLine(gl); +/* + * Allocate the character queue that is used to buffer terminal output. + */ + gl->cq = _new_GlCharQueue(); + if(!gl->cq) + return del_GetLine(gl); +/* + * Allocate a line buffer, leaving 2 extra characters for the terminating + * '\n' and '\0' characters + */ + gl->line = (char *) malloc(linelen + 2); + if(!gl->line) { + errno = ENOMEM; + return del_GetLine(gl); + }; +/* + * Start with an empty input line. + */ + gl_truncate_buffer(gl, 0); +/* + * Allocate a cut buffer. + */ + gl->cutbuf = (char *) malloc(linelen + 2); + if(!gl->cutbuf) { + errno = ENOMEM; + return del_GetLine(gl); + }; + gl->cutbuf[0] = '\0'; +/* + * Allocate an initial empty prompt. + */ + _gl_replace_prompt(gl, NULL); + if(!gl->prompt) { + errno = ENOMEM; + return del_GetLine(gl); + }; +/* + * Allocate a vi undo buffer. + */ + gl->vi.undo.line = (char *) malloc(linelen + 2); + if(!gl->vi.undo.line) { + errno = ENOMEM; + return del_GetLine(gl); + }; + gl->vi.undo.line[0] = '\0'; +/* + * Allocate a freelist from which to allocate nodes for the list + * of completion functions. + */ + gl->cpl_mem = _new_FreeList(sizeof(GlCplCallback), GL_CPL_FREELIST_BLOCKING); + if(!gl->cpl_mem) + return del_GetLine(gl); +/* + * Allocate a freelist from which to allocate nodes for the list + * of external action functions. + */ + gl->ext_act_mem = _new_FreeList(sizeof(GlExternalAction), + GL_EXT_ACT_FREELIST_BLOCKING); + if(!gl->ext_act_mem) + return del_GetLine(gl); +/* + * Allocate a freelist from which to allocate nodes for the list + * of signals. + */ + gl->sig_mem = _new_FreeList(sizeof(GlSignalNode), GLS_FREELIST_BLOCKING); + if(!gl->sig_mem) + return del_GetLine(gl); +/* + * Install initial dispositions for the default list of signals that + * gl_get_line() traps. + */ + for(i=0; isigno, sig->flags, sig->after, + sig->errno_value)) + return del_GetLine(gl); + }; +/* + * Allocate an empty table of key bindings. + */ + gl->bindings = _new_KeyTab(); + if(!gl->bindings) + return del_GetLine(gl); +/* + * Define the available actions that can be bound to key sequences. + */ + for(i=0; ibindings, gl_actions[i].name, gl_actions[i].fn, NULL)) + return del_GetLine(gl); + }; +/* + * Set up the default bindings. + */ + if(gl_change_editor(gl, gl->editor)) + return del_GetLine(gl); +/* + * Allocate termcap buffers. + */ +#ifdef USE_TERMCAP + gl->tgetent_buf = (char *) malloc(TERMCAP_BUF_SIZE); + gl->tgetstr_buf = (char *) malloc(TERMCAP_BUF_SIZE); + if(!gl->tgetent_buf || !gl->tgetstr_buf) { + errno = ENOMEM; + return del_GetLine(gl); + }; +#endif +/* + * Set up for I/O assuming stdin and stdout. + */ + if(_gl_change_terminal(gl, stdin, stdout, getenv("TERM"))) + return del_GetLine(gl); +/* + * Create a freelist for use in allocating GlFdNode list nodes. + */ +#ifdef HAVE_SELECT + gl->fd_node_mem = _new_FreeList(sizeof(GlFdNode), GLFD_FREELIST_BLOCKING); + if(!gl->fd_node_mem) + return del_GetLine(gl); +#endif +/* + * We are done for now. + */ + return gl; +} + +/*....................................................................... + * Delete a GetLine object. + * + * Input: + * gl GetLine * The object to be deleted. + * Output: + * return GetLine * The deleted object (always NULL). + */ +GetLine *del_GetLine(GetLine *gl) +{ + if(gl) { +/* + * If the terminal is in raw server mode, reset it. + */ + _gl_normal_io(gl); +/* + * Deallocate all objects contained by gl. + */ + gl->err = _del_ErrMsg(gl->err); + gl->glh = _del_GlHistory(gl->glh); + gl->cpl = del_WordCompletion(gl->cpl); +#ifndef WITHOUT_FILE_SYSTEM + gl->ef = del_ExpandFile(gl->ef); +#endif + gl->capmem = _del_StringGroup(gl->capmem); + gl->cq = _del_GlCharQueue(gl->cq); + if(gl->file_fp) + fclose(gl->file_fp); + if(gl->term) + free(gl->term); + if(gl->line) + free(gl->line); + if(gl->cutbuf) + free(gl->cutbuf); + if(gl->prompt) + free(gl->prompt); + gl->cpl_mem = _del_FreeList(gl->cpl_mem, 1); + gl->ext_act_mem = _del_FreeList(gl->ext_act_mem, 1); + gl->sig_mem = _del_FreeList(gl->sig_mem, 1); + gl->sigs = NULL; /* Already freed by freeing sig_mem */ + gl->bindings = _del_KeyTab(gl->bindings); + if(gl->vi.undo.line) + free(gl->vi.undo.line); +#ifdef USE_TERMCAP + if(gl->tgetent_buf) + free(gl->tgetent_buf); + if(gl->tgetstr_buf) + free(gl->tgetstr_buf); +#endif + if(gl->app_file) + free(gl->app_file); + if(gl->user_file) + free(gl->user_file); +#ifdef HAVE_SELECT + gl->fd_node_mem = _del_FreeList(gl->fd_node_mem, 1); + gl->fd_nodes = NULL; /* Already freed by freeing gl->fd_node_mem */ +#endif +/* + * Delete the now empty container. + */ + free(gl); + }; + return NULL; +} + +/*....................................................................... + * Bind a control or meta character to an action. + * + * Input: + * gl GetLine * The resource object of this program. + * binder KtBinder The source of the binding. + * c char The control or meta character. + * If this is '\0', the call is ignored. + * action const char * The action name to bind the key to. + * Output: + * return int 0 - OK. + * 1 - Error. + */ +static int gl_bind_control_char(GetLine *gl, KtBinder binder, char c, + const char *action) +{ + char keyseq[2]; +/* + * Quietly reject binding to the NUL control character, since this + * is an ambiguous prefix of all bindings. + */ + if(c == '\0') + return 0; +/* + * Making sure not to bind characters which aren't either control or + * meta characters. + */ + if(IS_CTRL_CHAR(c) || IS_META_CHAR(c)) { + keyseq[0] = c; + keyseq[1] = '\0'; + } else { + return 0; + }; +/* + * Install the binding. + */ + if(_kt_set_keybinding(gl->bindings, binder, keyseq, action)) { + _err_record_msg(gl->err, _kt_last_error(gl->bindings), END_ERR_MSG); + return 1; + }; + return 0; +} + +/*....................................................................... + * Read a line from the user. + * + * Input: + * gl GetLine * A resource object returned by new_GetLine(). + * prompt char * The prompt to prefix the line with. + * start_line char * The initial contents of the input line, or NULL + * if it should start out empty. + * start_pos int If start_line isn't NULL, this specifies the + * index of the character over which the cursor + * should initially be positioned within the line. + * If you just want it to follow the last character + * of the line, send -1. + * Output: + * return char * An internal buffer containing the input line, or + * NULL at the end of input. If the line fitted in + * the buffer there will be a '\n' newline character + * before the terminating '\0'. If it was truncated + * there will be no newline character, and the remains + * of the line should be retrieved via further calls + * to this function. + */ +char *gl_get_line(GetLine *gl, const char *prompt, + const char *start_line, int start_pos) +{ + char *retval; /* The return value of _gl_get_line() */ +/* + * Check the arguments. + */ + if(!gl) { + errno = EINVAL; + return NULL; + }; +/* + * Temporarily block all of the signals that we have been asked to trap. + */ + if(gl_mask_signals(gl, &gl->old_signal_set)) + return NULL; +/* + * Perform the command-line editing task. + */ + retval = _gl_get_line(gl, prompt, start_line, start_pos); +/* + * Restore the process signal mask to how it was when this function was + * first called. + */ + gl_unmask_signals(gl, &gl->old_signal_set); + return retval; +} + + +/*....................................................................... + * This is the main body of the public function gl_get_line(). + */ +static char *_gl_get_line(GetLine *gl, const char *prompt, + const char *start_line, int start_pos) +{ + int waserr = 0; /* True if an error occurs */ +/* + * Assume that this call will successfully complete the input + * line until proven otherwise. + */ + gl_clear_status(gl); +/* + * If this is the first call to this function since new_GetLine(), + * complete any postponed configuration. + */ + if(!gl->configured) { + (void) _gl_configure_getline(gl, NULL, NULL, TECLA_CONFIG_FILE); + gl->configured = 1; + }; +/* + * Before installing our signal handler functions, record the fact + * that there are no pending signals. + */ + gl_pending_signal = -1; +/* + * Temporarily override the signal handlers of the calling program, + * so that we can intercept signals that would leave the terminal + * in a bad state. + */ + waserr = gl_override_signal_handlers(gl); +/* + * After recording the current terminal settings, switch the terminal + * into raw input mode. + */ + waserr = waserr || _gl_raw_io(gl, 1); +/* + * Attempt to read the line. This will require more than one attempt if + * either a current temporary input file is opened by gl_get_input_line() + * or the end of a temporary input file is reached by gl_read_stream_line(). + */ + while(!waserr) { +/* + * Read a line from a non-interactive stream? + */ + if(gl->file_fp || !gl->is_term) { + if(gl_read_stream_line(gl)==0) { + break; + } else if(gl->file_fp) { + gl_revert_input(gl); + gl_record_status(gl, GLR_NEWLINE, 0); + } else { + waserr = 1; + break; + }; + }; +/* + * Read from the terminal? Note that the above if() block may have + * changed gl->file_fp, so it is necessary to retest it here, rather + * than using an else statement. + */ + if(!gl->file_fp && gl->is_term) { + if(gl_get_input_line(gl, prompt, start_line, start_pos)) + waserr = 1; + else + break; + }; + }; +/* + * If an error occurred, but gl->rtn_status is still set to + * GLR_NEWLINE, change the status to GLR_ERROR. Otherwise + * leave it at whatever specific value was assigned by the function + * that aborted input. This means that only functions that trap + * non-generic errors have to remember to update gl->rtn_status + * themselves. + */ + if(waserr && gl->rtn_status == GLR_NEWLINE) + gl_record_status(gl, GLR_ERROR, errno); +/* + * Restore terminal settings. + */ + if(gl->io_mode != GL_SERVER_MODE) + _gl_normal_io(gl); +/* + * Restore the signal handlers. + */ + gl_restore_signal_handlers(gl); +/* + * If gl_get_line() gets aborted early, the errno value associated + * with the event that caused this to happen is recorded in + * gl->rtn_errno. Since errno may have been overwritten by cleanup + * functions after this, restore its value to the value that it had + * when the error condition occured, so that the caller can examine it + * to find out what happened. + */ + errno = gl->rtn_errno; +/* + * Check the completion status to see how to return. + */ + switch(gl->rtn_status) { + case GLR_NEWLINE: /* Success */ + return gl->line; + case GLR_BLOCKED: /* These events abort the current input line, */ + case GLR_SIGNAL: /* when in normal blocking I/O mode, but only */ + case GLR_TIMEOUT: /* temporarily pause line editing when in */ + case GLR_FDABORT: /* non-blocking server I/O mode. */ + if(gl->io_mode != GL_SERVER_MODE) + _gl_abandon_line(gl); + return NULL; + case GLR_ERROR: /* Unrecoverable errors abort the input line, */ + case GLR_EOF: /* regardless of the I/O mode. */ + default: + _gl_abandon_line(gl); + return NULL; + }; +} + +/*....................................................................... + * Read a single character from the user. + * + * Input: + * gl GetLine * A resource object returned by new_GetLine(). + * prompt char * The prompt to prefix the line with, or NULL if + * no prompt is required. + * defchar char The character to substitute if the + * user simply hits return, or '\n' if you don't + * need to substitute anything. + * Output: + * return int The character that was read, or EOF if the read + * had to be aborted (in which case you can call + * gl_return_status() to find out why). + */ +int gl_query_char(GetLine *gl, const char *prompt, char defchar) +{ + int retval; /* The return value of _gl_query_char() */ +/* + * Check the arguments. + */ + if(!gl) { + errno = EINVAL; + return EOF; + }; +/* + * Temporarily block all of the signals that we have been asked to trap. + */ + if(gl_mask_signals(gl, &gl->old_signal_set)) + return EOF; +/* + * Perform the character reading task. + */ + retval = _gl_query_char(gl, prompt, defchar); +/* + * Restore the process signal mask to how it was when this function was + * first called. + */ + gl_unmask_signals(gl, &gl->old_signal_set); + return retval; +} + +/*....................................................................... + * This is the main body of the public function gl_query_char(). + */ +static int _gl_query_char(GetLine *gl, const char *prompt, char defchar) +{ + int c = EOF; /* The character to be returned */ + int waserr = 0; /* True if an error occurs */ +/* + * Assume that this call will successfully complete the input operation + * until proven otherwise. + */ + gl_clear_status(gl); +/* + * If this is the first call to this function or gl_get_line(), + * since new_GetLine(), complete any postponed configuration. + */ + if(!gl->configured) { + (void) _gl_configure_getline(gl, NULL, NULL, TECLA_CONFIG_FILE); + gl->configured = 1; + }; +/* + * Before installing our signal handler functions, record the fact + * that there are no pending signals. + */ + gl_pending_signal = -1; +/* + * Temporarily override the signal handlers of the calling program, + * so that we can intercept signals that would leave the terminal + * in a bad state. + */ + waserr = gl_override_signal_handlers(gl); +/* + * After recording the current terminal settings, switch the terminal + * into raw input mode without redisplaying any partially entered + * input line. + */ + waserr = waserr || _gl_raw_io(gl, 0); +/* + * Attempt to read the line. This will require more than one attempt if + * either a current temporary input file is opened by gl_get_input_line() + * or the end of a temporary input file is reached by gl_read_stream_line(). + */ + while(!waserr) { +/* + * Read a line from a non-interactive stream? + */ + if(gl->file_fp || !gl->is_term) { + c = gl_read_stream_char(gl); + if(c != EOF) { /* Success? */ + if(c=='\n') c = defchar; + break; + } else if(gl->file_fp) { /* End of temporary input file? */ + gl_revert_input(gl); + gl_record_status(gl, GLR_NEWLINE, 0); + } else { /* An error? */ + waserr = 1; + break; + }; + }; +/* + * Read from the terminal? Note that the above if() block may have + * changed gl->file_fp, so it is necessary to retest it here, rather + * than using an else statement. + */ + if(!gl->file_fp && gl->is_term) { + c = gl_get_query_char(gl, prompt, defchar); + if(c==EOF) + waserr = 1; + else + break; + }; + }; +/* + * If an error occurred, but gl->rtn_status is still set to + * GLR_NEWLINE, change the status to GLR_ERROR. Otherwise + * leave it at whatever specific value was assigned by the function + * that aborted input. This means that only functions that trap + * non-generic errors have to remember to update gl->rtn_status + * themselves. + */ + if(waserr && gl->rtn_status == GLR_NEWLINE) + gl_record_status(gl, GLR_ERROR, errno); +/* + * Restore terminal settings. + */ + if(gl->io_mode != GL_SERVER_MODE) + _gl_normal_io(gl); +/* + * Restore the signal handlers. + */ + gl_restore_signal_handlers(gl); +/* + * If this function gets aborted early, the errno value associated + * with the event that caused this to happen is recorded in + * gl->rtn_errno. Since errno may have been overwritten by cleanup + * functions after this, restore its value to the value that it had + * when the error condition occured, so that the caller can examine it + * to find out what happened. + */ + errno = gl->rtn_errno; +/* + * Error conditions are signalled to the caller, by setting the returned + * character to EOF. + */ + if(gl->rtn_status != GLR_NEWLINE) + c = EOF; +/* + * In this mode, every character that is read is a completed + * transaction, just like reading a completed input line, so prepare + * for the next input line or character. + */ + _gl_abandon_line(gl); +/* + * Return the acquired character. + */ + return c; +} + +/*....................................................................... + * Record of the signal handlers of the calling program, so that they + * can be restored later. + * + * Input: + * gl GetLine * The resource object of this library. + * Output: + * return int 0 - OK. + * 1 - Error. + */ +static int gl_override_signal_handlers(GetLine *gl) +{ + GlSignalNode *sig; /* A node in the list of signals to be caught */ +/* + * Set up our signal handler. + */ + SigAction act; + act.sa_handler = gl_signal_handler; + memcpy(&act.sa_mask, &gl->all_signal_set, sizeof(sigset_t)); + act.sa_flags = 0; +/* + * Get the subset of the signals that we are supposed to trap that + * should actually be trapped. + */ + sigemptyset(&gl->use_signal_set); + for(sig=gl->sigs; sig; sig=sig->next) { +/* + * Trap this signal? If it is blocked by the calling program and we + * haven't been told to unblock it, don't arrange to trap this signal. + */ + if(sig->flags & GLS_UNBLOCK_SIG || + !sigismember(&gl->old_signal_set, sig->signo)) { + if(sigaddset(&gl->use_signal_set, sig->signo) == -1) { + _err_record_msg(gl->err, "sigaddset error", END_ERR_MSG); + return 1; + }; + }; + }; +/* + * Override the actions of the signals that we are trapping. + */ + for(sig=gl->sigs; sig; sig=sig->next) { + if(sigismember(&gl->use_signal_set, sig->signo)) { + sigdelset(&act.sa_mask, sig->signo); + if(sigaction(sig->signo, &act, &sig->original)) { + _err_record_msg(gl->err, "sigaction error", END_ERR_MSG); + return 1; + }; + sigaddset(&act.sa_mask, sig->signo); + }; + }; +/* + * Record the fact that the application's signal handlers have now + * been overriden. + */ + gl->signals_overriden = 1; +/* + * Just in case a SIGWINCH signal was sent to the process while our + * SIGWINCH signal handler wasn't in place, check to see if the terminal + * size needs updating. + */ + if(_gl_update_size(gl)) + return 1; + return 0; +} + +/*....................................................................... + * Restore the signal handlers of the calling program. + * + * Input: + * gl GetLine * The resource object of this library. + * Output: + * return int 0 - OK. + * 1 - Error. + */ +static int gl_restore_signal_handlers(GetLine *gl) +{ + GlSignalNode *sig; /* A node in the list of signals to be caught */ +/* + * Restore application signal handlers that were overriden + * by gl_override_signal_handlers(). + */ + for(sig=gl->sigs; sig; sig=sig->next) { + if(sigismember(&gl->use_signal_set, sig->signo) && + sigaction(sig->signo, &sig->original, NULL)) { + _err_record_msg(gl->err, "sigaction error", END_ERR_MSG); + return 1; + }; + }; +/* + * Record the fact that the application's signal handlers have now + * been restored. + */ + gl->signals_overriden = 0; + return 0; +} + +/*....................................................................... + * This signal handler simply records the fact that a given signal was + * caught in the file-scope gl_pending_signal variable. + */ +static void gl_signal_handler(int signo) +{ + gl_pending_signal = signo; + siglongjmp(gl_setjmp_buffer, 1); +} + +/*....................................................................... + * Switch the terminal into raw mode after storing the previous terminal + * settings in gl->attributes. + * + * Input: + * gl GetLine * The resource object of this program. + * Output: + * return int 0 - OK. + * 1 - Error. + */ +static int gl_raw_terminal_mode(GetLine *gl) +{ + Termios newattr; /* The new terminal attributes */ +/* + * If the terminal is already in raw mode, do nothing. + */ + if(gl->raw_mode) + return 0; +/* + * Record the current terminal attributes. + */ + if(tcgetattr(gl->input_fd, &gl->oldattr)) { + _err_record_msg(gl->err, "tcgetattr error", END_ERR_MSG); + return 1; + }; +/* + * This function shouldn't do anything but record the current terminal + * attritubes if editing has been disabled. + */ + if(gl->editor == GL_NO_EDITOR) + return 0; +/* + * Modify the existing attributes. + */ + newattr = gl->oldattr; +/* + * Turn off local echo, canonical input mode and extended input processing. + */ + newattr.c_lflag &= ~(ECHO | ICANON | IEXTEN); +/* + * Don't translate carriage return to newline, turn off input parity + * checking, don't strip off 8th bit, turn off output flow control. + */ + newattr.c_iflag &= ~(ICRNL | INPCK | ISTRIP); +/* + * Clear size bits, turn off parity checking, and allow 8-bit characters. + */ + newattr.c_cflag &= ~(CSIZE | PARENB); + newattr.c_cflag |= CS8; +/* + * Turn off output processing. + */ + newattr.c_oflag &= ~(OPOST); +/* + * Request one byte at a time, without waiting. + */ + newattr.c_cc[VMIN] = gl->io_mode==GL_SERVER_MODE ? 0:1; + newattr.c_cc[VTIME] = 0; +/* + * Install the new terminal modes. + */ + while(tcsetattr(gl->input_fd, TCSADRAIN, &newattr)) { + if(errno != EINTR) { + _err_record_msg(gl->err, "tcsetattr error", END_ERR_MSG); + return 1; + }; + }; +/* + * Record the new terminal mode. + */ + gl->raw_mode = 1; + return 0; +} + +/*....................................................................... + * Restore the terminal attributes recorded in gl->oldattr. + * + * Input: + * gl GetLine * The resource object of this library. + * Output: + * return int 0 - OK. + * 1 - Error. + */ +static int gl_restore_terminal_attributes(GetLine *gl) +{ + int waserr = 0; +/* + * If not in raw mode, do nothing. + */ + if(!gl->raw_mode) + return 0; +/* + * Before changing the terminal attributes, make sure that all output + * has been passed to the terminal. + */ + if(gl_flush_output(gl)) + waserr = 1; +/* + * Reset the terminal attributes to the values that they had on + * entry to gl_get_line(). + */ + while(tcsetattr(gl->input_fd, TCSADRAIN, &gl->oldattr)) { + if(errno != EINTR) { + _err_record_msg(gl->err, "tcsetattr error", END_ERR_MSG); + waserr = 1; + break; + }; + }; +/* + * Record the new terminal mode. + */ + gl->raw_mode = 0; + return waserr; +} + +/*....................................................................... + * Switch the terminal file descriptor to use non-blocking I/O. + * + * Input: + * gl GetLine * The resource object of gl_get_line(). + * fd int The file descriptor to make non-blocking. + */ +static int gl_nonblocking_io(GetLine *gl, int fd) +{ + int fcntl_flags; /* The new file-descriptor control flags */ +/* + * Is non-blocking I/O supported on this system? Note that even + * without non-blocking I/O, the terminal will probably still act as + * though it was non-blocking, because we also set the terminal + * attributes to return immediately if no input is available and we + * use select() to wait to be able to write. If select() also isn't + * available, then input will probably remain fine, but output could + * block, depending on the behaviour of the terminal driver. + */ +#if defined(NON_BLOCKING_FLAG) +/* + * Query the current file-control flags, and add the + * non-blocking I/O flag. + */ + fcntl_flags = fcntl(fd, F_GETFL) | NON_BLOCKING_FLAG; +/* + * Install the new control flags. + */ + if(fcntl(fd, F_SETFL, fcntl_flags) == -1) { + _err_record_msg(gl->err, "fcntl error", END_ERR_MSG); + return 1; + }; +#endif + return 0; +} + +/*....................................................................... + * Switch to blocking terminal I/O. + * + * Input: + * gl GetLine * The resource object of gl_get_line(). + * fd int The file descriptor to make blocking. + */ +static int gl_blocking_io(GetLine *gl, int fd) +{ + int fcntl_flags; /* The new file-descriptor control flags */ +/* + * Is non-blocking I/O implemented on this system? + */ +#if defined(NON_BLOCKING_FLAG) +/* + * Query the current file control flags and remove the non-blocking + * I/O flag. + */ + fcntl_flags = fcntl(fd, F_GETFL) & ~NON_BLOCKING_FLAG; +/* + * Install the modified control flags. + */ + if(fcntl(fd, F_SETFL, fcntl_flags) == -1) { + _err_record_msg(gl->err, "fcntl error", END_ERR_MSG); + return 1; + }; +#endif + return 0; +} + +/*....................................................................... + * Read a new input line from the user. + * + * Input: + * gl GetLine * The resource object of this library. + * prompt char * The prompt to prefix the line with, or NULL to + * use the same prompt that was used by the previous + * line. + * start_line char * The initial contents of the input line, or NULL + * if it should start out empty. + * start_pos int If start_line isn't NULL, this specifies the + * index of the character over which the cursor + * should initially be positioned within the line. + * If you just want it to follow the last character + * of the line, send -1. + * Output: + * return int 0 - OK. + * 1 - Error. + */ +static int gl_get_input_line(GetLine *gl, const char *prompt, + const char *start_line, int start_pos) +{ + char c; /* The character being read */ +/* + * Flush any pending output to the terminal. + */ + if(_glq_char_count(gl->cq) > 0 && gl_flush_output(gl)) + return 1; +/* + * Are we starting a new line? + */ + if(gl->endline) { +/* + * Delete any incompletely enterred line. + */ + if(gl_erase_line(gl)) + return 1; +/* + * Display the new line to be edited. + */ + if(gl_present_line(gl, prompt, start_line, start_pos)) + return 1; + }; +/* + * Read one character at a time. + */ + while(gl_read_terminal(gl, 1, &c) == 0) { +/* + * Increment the count of the number of key sequences entered. + */ + gl->keyseq_count++; +/* + * Interpret the character either as the start of a new key-sequence, + * as a continuation of a repeat count, or as a printable character + * to be added to the line. + */ + if(gl_interpret_char(gl, c)) + break; +/* + * If we just ran an action function which temporarily asked for + * input to be taken from a file, abort this call. + */ + if(gl->file_fp) + return 0; +/* + * Has the line been completed? + */ + if(gl->endline) + return gl_line_ended(gl, c); + }; +/* + * To get here, gl_read_terminal() must have returned non-zero. See + * whether a signal was caught that requested that the current line + * be returned. + */ + if(gl->endline) + return gl_line_ended(gl, '\n'); +/* + * If I/O blocked while attempting to get the latest character + * of the key sequence, rewind the key buffer to allow interpretation of + * the current key sequence to be restarted on the next call to this + * function. + */ + if(gl->rtn_status == GLR_BLOCKED && gl->pending_io == GLP_READ) + gl->nread = 0; + return 1; +} + +/*....................................................................... + * This is the private function of gl_query_char() that handles + * prompting the user, reading a character from the terminal, and + * displaying what the user entered. + * + * Input: + * gl GetLine * The resource object of this library. + * prompt char * The prompt to prefix the line with. + * defchar char The character to substitute if the + * user simply hits return, or '\n' if you don't + * need to substitute anything. + * Output: + * return int The character that was read, or EOF if something + * prevented a character from being read. + */ +static int gl_get_query_char(GetLine *gl, const char *prompt, int defchar) +{ + char c; /* The character being read */ + int retval; /* The return value of this function */ +/* + * Flush any pending output to the terminal. + */ + if(_glq_char_count(gl->cq) > 0 && gl_flush_output(gl)) + return EOF; +/* + * Delete any incompletely entered line. + */ + if(gl_erase_line(gl)) + return EOF; +/* + * Reset the line input parameters and display the prompt, if any. + */ + if(gl_present_line(gl, prompt, NULL, 0)) + return EOF; +/* + * Read one character. + */ + if(gl_read_terminal(gl, 1, &c) == 0) { +/* + * In this mode, count each character as being a new key-sequence. + */ + gl->keyseq_count++; +/* + * Delete the character that was read, from the key-press buffer. + */ + gl_discard_chars(gl, gl->nread); +/* + * Convert carriage returns to newlines. + */ + if(c == '\r') + c = '\n'; +/* + * If the user just hit return, subsitute the default character. + */ + if(c == '\n') + c = defchar; +/* + * Display the entered character to the right of the prompt. + */ + if(c!='\n') { + if(gl_end_of_line(gl, 1, NULL)==0) + gl_print_char(gl, c, ' '); + }; +/* + * Record the return character, and mark the call as successful. + */ + retval = c; + gl_record_status(gl, GLR_NEWLINE, 0); +/* + * Was a signal caught whose disposition is to cause the current input + * line to be returned? If so return a newline character. + */ + } else if(gl->endline) { + retval = '\n'; + gl_record_status(gl, GLR_NEWLINE, 0); + } else { + retval = EOF; + }; +/* + * Start a new line. + */ + if(gl_start_newline(gl, 1)) + return EOF; +/* + * Attempt to flush any pending output. + */ + (void) gl_flush_output(gl); +/* + * Return either the character that was read, or EOF if an error occurred. + */ + return retval; +} + +/*....................................................................... + * Add a character to the line buffer at the current cursor position, + * inserting or overwriting according the current mode. + * + * Input: + * gl GetLine * The resource object of this library. + * c char The character to be added. + * Output: + * return int 0 - OK. + * 1 - Insufficient room. + */ +static int gl_add_char_to_line(GetLine *gl, char c) +{ +/* + * Keep a record of the current cursor position. + */ + int buff_curpos = gl->buff_curpos; + int term_curpos = gl->term_curpos; +/* + * Work out the displayed width of the new character. + */ + int width = gl_displayed_char_width(gl, c, term_curpos); +/* + * If we are in insert mode, or at the end of the line, + * check that we can accomodate a new character in the buffer. + * If not, simply return, leaving it up to the calling program + * to check for the absence of a newline character. + */ + if((gl->insert || buff_curpos >= gl->ntotal) && gl->ntotal >= gl->linelen) + return 0; +/* + * Are we adding characters to the line (ie. inserting or appending)? + */ + if(gl->insert || buff_curpos >= gl->ntotal) { +/* + * If inserting, make room for the new character. + */ + if(buff_curpos < gl->ntotal) + gl_make_gap_in_buffer(gl, buff_curpos, 1); +/* + * Copy the character into the buffer. + */ + gl_buffer_char(gl, c, buff_curpos); + gl->buff_curpos++; +/* + * Redraw the line from the cursor position to the end of the line, + * and move the cursor to just after the added character. + */ + if(gl_print_string(gl, gl->line + buff_curpos, '\0') || + gl_set_term_curpos(gl, term_curpos + width)) + return 1; +/* + * Are we overwriting an existing character? + */ + } else { +/* + * Get the width of the character being overwritten. + */ + int old_width = gl_displayed_char_width(gl, gl->line[buff_curpos], + term_curpos); +/* + * Overwrite the character in the buffer. + */ + gl_buffer_char(gl, c, buff_curpos); +/* + * If we are replacing with a narrower character, we need to + * redraw the terminal string to the end of the line, then + * overwrite the trailing old_width - width characters + * with spaces. + */ + if(old_width > width) { + if(gl_print_string(gl, gl->line + buff_curpos, '\0')) + return 1; +/* + * Clear to the end of the terminal. + */ + if(gl_truncate_display(gl)) + return 1; +/* + * Move the cursor to the end of the new character. + */ + if(gl_set_term_curpos(gl, term_curpos + width)) + return 1; + gl->buff_curpos++; +/* + * If we are replacing with a wider character, then we will be + * inserting new characters, and thus extending the line. + */ + } else if(width > old_width) { +/* + * Redraw the line from the cursor position to the end of the line, + * and move the cursor to just after the added character. + */ + if(gl_print_string(gl, gl->line + buff_curpos, '\0') || + gl_set_term_curpos(gl, term_curpos + width)) + return 1; + gl->buff_curpos++; +/* + * The original and replacement characters have the same width, + * so simply overwrite. + */ + } else { +/* + * Copy the character into the buffer. + */ + gl_buffer_char(gl, c, buff_curpos); + gl->buff_curpos++; +/* + * Overwrite the original character. + */ + if(gl_print_char(gl, c, gl->line[gl->buff_curpos])) + return 1; + }; + }; + return 0; +} + +/*....................................................................... + * Insert/append a string to the line buffer and terminal at the current + * cursor position. + * + * Input: + * gl GetLine * The resource object of this library. + * s char * The string to be added. + * Output: + * return int 0 - OK. + * 1 - Insufficient room. + */ +static int gl_add_string_to_line(GetLine *gl, const char *s) +{ + int buff_slen; /* The length of the string being added to line[] */ + int term_slen; /* The length of the string being written to the terminal */ + int buff_curpos; /* The original value of gl->buff_curpos */ + int term_curpos; /* The original value of gl->term_curpos */ +/* + * Keep a record of the current cursor position. + */ + buff_curpos = gl->buff_curpos; + term_curpos = gl->term_curpos; +/* + * How long is the string to be added? + */ + buff_slen = strlen(s); + term_slen = gl_displayed_string_width(gl, s, buff_slen, term_curpos); +/* + * Check that we can accomodate the string in the buffer. + * If not, simply return, leaving it up to the calling program + * to check for the absence of a newline character. + */ + if(gl->ntotal + buff_slen > gl->linelen) + return 0; +/* + * Move the characters that follow the cursor in the buffer by + * buff_slen characters to the right. + */ + if(gl->ntotal > gl->buff_curpos) + gl_make_gap_in_buffer(gl, gl->buff_curpos, buff_slen); +/* + * Copy the string into the buffer. + */ + gl_buffer_string(gl, s, buff_slen, gl->buff_curpos); + gl->buff_curpos += buff_slen; +/* + * Write the modified part of the line to the terminal, then move + * the terminal cursor to the end of the displayed input string. + */ + if(gl_print_string(gl, gl->line + buff_curpos, '\0') || + gl_set_term_curpos(gl, term_curpos + term_slen)) + return 1; + return 0; +} + +/*....................................................................... + * Read a single character from the terminal. + * + * Input: + * gl GetLine * The resource object of this library. + * keep int If true, the returned character will be kept in + * the input buffer, for potential replays. It should + * subsequently be removed from the buffer when the + * key sequence that it belongs to has been fully + * processed, by calling gl_discard_chars(). + * Input/Output: + * c char * The character that is read, is assigned to *c. + * Output: + * return int 0 - OK. + * 1 - Either an I/O error occurred, or a signal was + * caught who's disposition is to abort gl_get_line() + * or to have gl_get_line() return the current line + * as though the user had pressed return. In the + * latter case gl->endline will be non-zero. + */ +static int gl_read_terminal(GetLine *gl, int keep, char *c) +{ +/* + * Before waiting for a new character to be input, flush unwritten + * characters to the terminal. + */ + if(gl_flush_output(gl)) + return 1; +/* + * Record the fact that we are about to read from the terminal. + */ + gl->pending_io = GLP_READ; +/* + * If there is already an unread character in the buffer, + * return it. + */ + if(gl->nread < gl->nbuf) { + *c = gl->keybuf[gl->nread]; +/* + * Retain the character in the key buffer, but mark it as having been read? + */ + if(keep) { + gl->nread++; +/* + * Completely remove the character from the key buffer? + */ + } else { + memmove(gl->keybuf + gl->nread, gl->keybuf + gl->nread + 1, + gl->nbuf - gl->nread - 1); + }; + return 0; + }; +/* + * Make sure that there is space in the key buffer for one more character. + * This should always be true if gl_interpret_char() is called for each + * new character added, since it will clear the buffer once it has recognized + * or rejected a key sequence. + */ + if(gl->nbuf + 1 > GL_KEY_MAX) { + gl_print_info(gl, "gl_read_terminal: Buffer overflow avoided.", + GL_END_INFO); + errno = EIO; + return 1; + }; +/* + * Read one character from the terminal. + */ + switch(gl_read_input(gl, c)) { + case GL_READ_OK: + break; + case GL_READ_BLOCKED: + gl_record_status(gl, GLR_BLOCKED, BLOCKED_ERRNO); + return 1; + break; + default: + return 1; + break; + }; +/* + * Append the character to the key buffer? + */ + if(keep) { + gl->keybuf[gl->nbuf] = *c; + gl->nread = ++gl->nbuf; + }; + return 0; +} + +/*....................................................................... + * Read one or more keypresses from the terminal of an input stream. + * + * Input: + * gl GetLine * The resource object of this module. + * c char * The character that was read is assigned to *c. + * Output: + * return GlReadStatus The completion status of the read operation. + */ +static GlReadStatus gl_read_input(GetLine *gl, char *c) +{ +/* + * We may have to repeat the read if window change signals are received. + */ + for(;;) { +/* + * Which file descriptor should we read from? Mark this volatile, so + * that siglongjmp() can't clobber it. + */ + volatile int fd = gl->file_fp ? fileno(gl->file_fp) : gl->input_fd; +/* + * If the endline flag becomes set, don't wait for another character. + */ + if(gl->endline) + return GL_READ_ERROR; +/* + * Since the code in this function can block, trap signals. + */ + if(sigsetjmp(gl_setjmp_buffer, 1)==0) { +/* + * Handle the different I/O modes. + */ + switch(gl->io_mode) { +/* + * In normal I/O mode, we call the event handler before attempting + * to read, since read() blocks. + */ + case GL_NORMAL_MODE: + if(gl_event_handler(gl, fd)) + return GL_READ_ERROR; + return gl_read_unmasked(gl, fd, c); /* Read one character */ + break; +/* + * In non-blocking server I/O mode, we attempt to read a character, + * and only if this fails, call the event handler to wait for a any + * user-configured timeout and any other user-configured events. In + * addition, we turn off the fcntl() non-blocking flag when reading + * from the terminal, to work around a bug in Solaris. We can do this + * without causing the read() to block, because when in non-blocking + * server-I/O mode, gl_raw_io() sets the VMIN terminal attribute to 0, + * which tells the terminal driver to return immediately if no + * characters are available to be read. + */ + case GL_SERVER_MODE: + { + GlReadStatus status; /* The return status */ + if(isatty(fd)) /* If we reading from a terminal, */ + gl_blocking_io(gl, fd); /* switch to blocking I/O */ + status = gl_read_unmasked(gl, fd, c); /* Try reading */ + if(status == GL_READ_BLOCKED) { /* Nothing readable yet */ + if(gl_event_handler(gl, fd)) /* Wait for input */ + status = GL_READ_ERROR; + else + status = gl_read_unmasked(gl, fd, c); /* Try reading again */ + }; + gl_nonblocking_io(gl, fd); /* Restore non-blocking I/O */ + return status; + }; + break; + }; + }; +/* + * To get here, one of the signals that we are trapping must have + * been received. Note that by using sigsetjmp() instead of setjmp() + * the signal mask that was blocking these signals will have been + * reinstated, so we can be sure that no more of these signals will + * be received until we explicitly unblock them again. + * + * First, if non-blocking I/O was temporarily disabled, reinstate it. + */ + if(gl->io_mode == GL_SERVER_MODE) + gl_nonblocking_io(gl, fd); +/* + * Now respond to the signal that was caught. + */ + if(gl_check_caught_signal(gl)) + return GL_READ_ERROR; + }; +} + +/*....................................................................... + * This is a private function of gl_read_input(), which unblocks signals + * temporarily while it reads a single character from the specified file + * descriptor. + * + * Input: + * gl GetLine * The resource object of this module. + * fd int The file descriptor to read from. + * c char * The character that was read is assigned to *c. + * Output: + * return GlReadStatus The completion status of the read. + */ +static GlReadStatus gl_read_unmasked(GetLine *gl, int fd, char *c) +{ + int nread; /* The return value of read() */ +/* + * Unblock the signals that we are trapping, while waiting for I/O. + */ + gl_catch_signals(gl); +/* + * Attempt to read one character from the terminal, restarting the read + * if any signals that we aren't trapping, are received. + */ + do { + errno = 0; + nread = read(fd, c, 1); + } while(nread < 0 && errno==EINTR); +/* + * Block all of the signals that we are trapping. + */ + gl_mask_signals(gl, NULL); +/* + * Check the completion status of the read. + */ + switch(nread) { + case 1: + return GL_READ_OK; + case 0: + return (isatty(fd) || errno != 0) ? GL_READ_BLOCKED : GL_READ_EOF; + default: + return GL_READ_ERROR; + }; +} + +/*....................................................................... + * Remove a specified number of characters from the start of the + * key-press lookahead buffer, gl->keybuf[], and arrange for the next + * read to start from the character at the start of the shifted buffer. + * + * Input: + * gl GetLine * The resource object of this module. + * nused int The number of characters to discard from the start + * of the buffer. + */ +static void gl_discard_chars(GetLine *gl, int nused) +{ + int nkeep = gl->nbuf - nused; + if(nkeep > 0) { + memmove(gl->keybuf, gl->keybuf + nused, nkeep); + gl->nbuf = nkeep; + gl->nread = 0; + } else { + gl->nbuf = gl->nread = 0; + }; +} + +/*....................................................................... + * This function is called to handle signals caught between calls to + * sigsetjmp() and siglongjmp(). + * + * Input: + * gl GetLine * The resource object of this library. + * Output: + * return int 0 - Signal handled internally. + * 1 - Signal requires gl_get_line() to abort. + */ +static int gl_check_caught_signal(GetLine *gl) +{ + GlSignalNode *sig; /* The signal disposition */ + SigAction keep_action; /* The signal disposition of tecla signal handlers */ + unsigned flags; /* The signal processing flags to use */ + int signo; /* The signal to be handled */ +/* + * Was no signal caught? + */ + if(gl_pending_signal == -1) + return 0; +/* + * Get the signal to be handled. + */ + signo = gl_pending_signal; +/* + * Mark the signal as handled. Note that at this point, all of + * the signals that we are trapping are blocked from delivery. + */ + gl_pending_signal = -1; +/* + * Record the signal that was caught, so that the user can query it later. + */ + gl->last_signal = signo; +/* + * In non-blocking server mode, the application is responsible for + * responding to terminal signals, and we don't want gl_get_line()s + * normal signal handling to clash with this, so whenever a signal + * is caught, we arrange for gl_get_line() to abort and requeue the + * signal while signals are still blocked. If the application + * had the signal unblocked when gl_get_line() was called, the signal + * will be delivered again as soon as gl_get_line() restores the + * process signal mask, just before returning to the application. + * Note that the caller of this function should set gl->pending_io + * to the appropriate choice of GLP_READ and GLP_WRITE, before returning. + */ + if(gl->io_mode==GL_SERVER_MODE) { + gl_record_status(gl, GLR_SIGNAL, EINTR); + raise(signo); + return 1; + }; +/* + * Lookup the requested disposition of this signal. + */ + for(sig=gl->sigs; sig && sig->signo != signo; sig=sig->next) + ; + if(!sig) + return 0; +/* + * Get the signal response flags for this signal. + */ + flags = sig->flags; +/* + * Only perform terminal-specific actions if the session is interactive. + */ + if(gl->is_term) { +/* + * Did we receive a terminal size signal? + */ +#ifdef SIGWINCH + if(signo == SIGWINCH && _gl_update_size(gl)) + return 1; +#endif +/* + * Start a fresh line? + */ + if(flags & GLS_RESTORE_LINE) { + if(gl_start_newline(gl, 0)) + return 1; + }; +/* + * Restore terminal settings to how they were before gl_get_line() was + * called? + */ + if(flags & GLS_RESTORE_TTY) + gl_restore_terminal_attributes(gl); + }; +/* + * Restore signal handlers to how they were before gl_get_line() was + * called? If this hasn't been requested, only reinstate the signal + * handler of the signal that we are handling. + */ + if(flags & GLS_RESTORE_SIG) { + gl_restore_signal_handlers(gl); + gl_unmask_signals(gl, &gl->old_signal_set); + } else { + (void) sigaction(sig->signo, &sig->original, &keep_action); + (void) sigprocmask(SIG_UNBLOCK, &sig->proc_mask, NULL); + }; +/* + * Forward the signal to the application's signal handler. + */ + if(!(flags & GLS_DONT_FORWARD)) + raise(signo); +/* + * Reinstate our signal handlers. + */ + if(flags & GLS_RESTORE_SIG) { + gl_mask_signals(gl, NULL); + gl_override_signal_handlers(gl); + } else { + (void) sigaction(sig->signo, &keep_action, NULL); + (void) sigprocmask(SIG_BLOCK, &sig->proc_mask, NULL); + }; +/* + * Prepare the terminal for continued editing, if this is an interactive + * session. + */ + if(gl->is_term) { +/* + * Do we need to reinstate our terminal settings? + */ + if(flags & GLS_RESTORE_TTY) + gl_raw_terminal_mode(gl); +/* + * Redraw the line? + */ + if(flags & GLS_REDRAW_LINE) + gl_queue_redisplay(gl); + }; +/* + * What next? + */ + switch(sig->after) { + case GLS_RETURN: + gl_newline(gl, 1, NULL); + return gl->is_term && gl_flush_output(gl); + break; + case GLS_ABORT: + gl_record_status(gl, GLR_SIGNAL, sig->errno_value); + return 1; + break; + case GLS_CONTINUE: + return gl->is_term && gl_flush_output(gl); + break; + }; + return 0; +} + +/*....................................................................... + * Get pertinent terminal control strings and the initial terminal size. + * + * Input: + * gl GetLine * The resource object of this library. + * term char * The type of the terminal. + * Output: + * return int 0 - OK. + * 1 - Error. + */ +static int gl_control_strings(GetLine *gl, const char *term) +{ + int bad_term = 0; /* True if term is unusable */ +/* + * Discard any existing control strings from a previous terminal. + */ + gl->left = NULL; + gl->right = NULL; + gl->up = NULL; + gl->down = NULL; + gl->home = NULL; + gl->bol = 0; + gl->clear_eol = NULL; + gl->clear_eod = NULL; + gl->u_arrow = NULL; + gl->d_arrow = NULL; + gl->l_arrow = NULL; + gl->r_arrow = NULL; + gl->sound_bell = NULL; + gl->bold = NULL; + gl->underline = NULL; + gl->standout = NULL; + gl->dim = NULL; + gl->reverse = NULL; + gl->blink = NULL; + gl->text_attr_off = NULL; + gl->nline = 0; + gl->ncolumn = 0; +#ifdef USE_TERMINFO + gl->left_n = NULL; + gl->right_n = NULL; +#endif +/* + * If possible lookup the information in a terminal information + * database. + */ +#ifdef USE_TERMINFO + { + int errret; + if(!term || setupterm((char *)term, gl->input_fd, &errret) == ERR) { + bad_term = 1; + } else { + _clr_StringGroup(gl->capmem); + gl->left = gl_tigetstr(gl, "cub1"); + gl->right = gl_tigetstr(gl, "cuf1"); + gl->up = gl_tigetstr(gl, "cuu1"); + gl->down = gl_tigetstr(gl, "cud1"); + gl->home = gl_tigetstr(gl, "home"); + gl->clear_eol = gl_tigetstr(gl, "el"); + gl->clear_eod = gl_tigetstr(gl, "ed"); + gl->u_arrow = gl_tigetstr(gl, "kcuu1"); + gl->d_arrow = gl_tigetstr(gl, "kcud1"); + gl->l_arrow = gl_tigetstr(gl, "kcub1"); + gl->r_arrow = gl_tigetstr(gl, "kcuf1"); + gl->left_n = gl_tigetstr(gl, "cub"); + gl->right_n = gl_tigetstr(gl, "cuf"); + gl->sound_bell = gl_tigetstr(gl, "bel"); + gl->bold = gl_tigetstr(gl, "bold"); + gl->underline = gl_tigetstr(gl, "smul"); + gl->standout = gl_tigetstr(gl, "smso"); + gl->dim = gl_tigetstr(gl, "dim"); + gl->reverse = gl_tigetstr(gl, "rev"); + gl->blink = gl_tigetstr(gl, "blink"); + gl->text_attr_off = gl_tigetstr(gl, "sgr0"); + }; + }; +#elif defined(USE_TERMCAP) + if(!term || tgetent(gl->tgetent_buf, (char *)term) < 0) { + bad_term = 1; + } else { + char *tgetstr_buf_ptr = gl->tgetstr_buf; + _clr_StringGroup(gl->capmem); + gl->left = gl_tgetstr(gl, "le", &tgetstr_buf_ptr); + gl->right = gl_tgetstr(gl, "nd", &tgetstr_buf_ptr); + gl->up = gl_tgetstr(gl, "up", &tgetstr_buf_ptr); + gl->down = gl_tgetstr(gl, "do", &tgetstr_buf_ptr); + gl->home = gl_tgetstr(gl, "ho", &tgetstr_buf_ptr); + gl->clear_eol = gl_tgetstr(gl, "ce", &tgetstr_buf_ptr); + gl->clear_eod = gl_tgetstr(gl, "cd", &tgetstr_buf_ptr); + gl->u_arrow = gl_tgetstr(gl, "ku", &tgetstr_buf_ptr); + gl->d_arrow = gl_tgetstr(gl, "kd", &tgetstr_buf_ptr); + gl->l_arrow = gl_tgetstr(gl, "kl", &tgetstr_buf_ptr); + gl->r_arrow = gl_tgetstr(gl, "kr", &tgetstr_buf_ptr); + gl->sound_bell = gl_tgetstr(gl, "bl", &tgetstr_buf_ptr); + gl->bold = gl_tgetstr(gl, "md", &tgetstr_buf_ptr); + gl->underline = gl_tgetstr(gl, "us", &tgetstr_buf_ptr); + gl->standout = gl_tgetstr(gl, "so", &tgetstr_buf_ptr); + gl->dim = gl_tgetstr(gl, "mh", &tgetstr_buf_ptr); + gl->reverse = gl_tgetstr(gl, "mr", &tgetstr_buf_ptr); + gl->blink = gl_tgetstr(gl, "mb", &tgetstr_buf_ptr); + gl->text_attr_off = gl_tgetstr(gl, "me", &tgetstr_buf_ptr); + }; +#endif +/* + * Report term being unusable. + */ + if(bad_term) { + gl_print_info(gl, "Bad terminal type: \"", term ? term : "(null)", + "\". Will assume vt100.", GL_END_INFO); + }; +/* + * Fill in missing information with ANSI VT100 strings. + */ + if(!gl->left) + gl->left = "\b"; /* ^H */ + if(!gl->right) + gl->right = GL_ESC_STR "[C"; + if(!gl->up) + gl->up = GL_ESC_STR "[A"; + if(!gl->down) + gl->down = "\n"; + if(!gl->home) + gl->home = GL_ESC_STR "[H"; + if(!gl->bol) + gl->bol = "\r"; + if(!gl->clear_eol) + gl->clear_eol = GL_ESC_STR "[K"; + if(!gl->clear_eod) + gl->clear_eod = GL_ESC_STR "[J"; + if(!gl->u_arrow) + gl->u_arrow = GL_ESC_STR "[A"; + if(!gl->d_arrow) + gl->d_arrow = GL_ESC_STR "[B"; + if(!gl->l_arrow) + gl->l_arrow = GL_ESC_STR "[D"; + if(!gl->r_arrow) + gl->r_arrow = GL_ESC_STR "[C"; + if(!gl->sound_bell) + gl->sound_bell = "\a"; + if(!gl->bold) + gl->bold = GL_ESC_STR "[1m"; + if(!gl->underline) + gl->underline = GL_ESC_STR "[4m"; + if(!gl->standout) + gl->standout = GL_ESC_STR "[1;7m"; + if(!gl->dim) + gl->dim = ""; /* Not available */ + if(!gl->reverse) + gl->reverse = GL_ESC_STR "[7m"; + if(!gl->blink) + gl->blink = GL_ESC_STR "[5m"; + if(!gl->text_attr_off) + gl->text_attr_off = GL_ESC_STR "[m"; +/* + * Find out the current terminal size. + */ + (void) _gl_terminal_size(gl, GL_DEF_NCOLUMN, GL_DEF_NLINE, NULL); + return 0; +} + +#ifdef USE_TERMINFO +/*....................................................................... + * This is a private function of gl_control_strings() used to look up + * a termninal capability string from the terminfo database and make + * a private copy of it. + * + * Input: + * gl GetLine * The resource object of gl_get_line(). + * name const char * The name of the terminfo string to look up. + * Output: + * return const char * The local copy of the capability, or NULL + * if not available. + */ +static const char *gl_tigetstr(GetLine *gl, const char *name) +{ + const char *value = tigetstr((char *)name); + if(!value || value == (char *) -1) + return NULL; + return _sg_store_string(gl->capmem, value, 0); +} +#elif defined(USE_TERMCAP) +/*....................................................................... + * This is a private function of gl_control_strings() used to look up + * a termninal capability string from the termcap database and make + * a private copy of it. Note that some emulations of tgetstr(), such + * as that used by Solaris, ignores the buffer pointer that is past to + * it, so we can't assume that a private copy has been made that won't + * be trashed by another call to gl_control_strings() by another + * GetLine object. So we make what may be a redundant private copy + * of the string in gl->capmem. + * + * Input: + * gl GetLine * The resource object of gl_get_line(). + * name const char * The name of the terminfo string to look up. + * Input/Output: + * bufptr char ** On input *bufptr points to the location in + * gl->tgetstr_buf at which to record the + * capability string. On output *bufptr is + * incremented over the stored string. + * Output: + * return const char * The local copy of the capability, or NULL + * on error. + */ +static const char *gl_tgetstr(GetLine *gl, const char *name, char **bufptr) +{ + const char *value = tgetstr((char *)name, bufptr); + if(!value || value == (char *) -1) + return NULL; + return _sg_store_string(gl->capmem, value, 0); +} +#endif + +/*....................................................................... + * This is an action function that implements a user interrupt (eg. ^C). + */ +static KT_KEY_FN(gl_user_interrupt) +{ + raise(SIGINT); + return 1; +} + +/*....................................................................... + * This is an action function that implements the abort signal. + */ +static KT_KEY_FN(gl_abort) +{ + raise(SIGABRT); + return 1; +} + +/*....................................................................... + * This is an action function that sends a suspend signal (eg. ^Z) to the + * the parent process. + */ +static KT_KEY_FN(gl_suspend) +{ + raise(SIGTSTP); + return 0; +} + +/*....................................................................... + * This is an action function that halts output to the terminal. + */ +static KT_KEY_FN(gl_stop_output) +{ + tcflow(gl->output_fd, TCOOFF); + return 0; +} + +/*....................................................................... + * This is an action function that resumes halted terminal output. + */ +static KT_KEY_FN(gl_start_output) +{ + tcflow(gl->output_fd, TCOON); + return 0; +} + +/*....................................................................... + * This is an action function that allows the next character to be accepted + * without any interpretation as a special character. + */ +static KT_KEY_FN(gl_literal_next) +{ + char c; /* The character to be added to the line */ + int i; +/* + * Get the character to be inserted literally. + */ + if(gl_read_terminal(gl, 1, &c)) + return 1; +/* + * Add the character to the line 'count' times. + */ + for(i=0; incolumn) % TAB_WIDTH); +} + +/*....................................................................... + * Return the number of characters needed to display a given character + * on the screen. Tab characters require eight spaces, and control + * characters are represented by a caret followed by the modified + * character. + * + * Input: + * gl GetLine * The resource object of this library. + * c char The character to be displayed. + * term_curpos int The destination terminal location of the character. + * This is needed because the width of tab characters + * depends on where they are, relative to the + * preceding tab stops. + * Output: + * return int The number of terminal charaters needed. + */ +static int gl_displayed_char_width(GetLine *gl, char c, int term_curpos) +{ + if(c=='\t') + return gl_displayed_tab_width(gl, term_curpos); + if(IS_CTRL_CHAR(c)) + return 2; + if(!isprint((int)(unsigned char) c)) + return gl_octal_width((int)(unsigned char)c) + 1; + return 1; +} + + +/*....................................................................... + * Work out the length of given string of characters on the terminal. + * + * Input: + * gl GetLine * The resource object of this library. + * string char * The string to be measured. + * nc int The number of characters to be measured, or -1 + * to measure the whole string. + * term_curpos int The destination terminal location of the character. + * This is needed because the width of tab characters + * depends on where they are, relative to the + * preceding tab stops. + * Output: + * return int The number of displayed characters. + */ +static int gl_displayed_string_width(GetLine *gl, const char *string, int nc, + int term_curpos) +{ + int slen = 0; /* The displayed number of characters */ + int i; +/* + * How many characters are to be measured? + */ + if(nc < 0) + nc = strlen(string); +/* + * Add up the length of the displayed string. + */ + for(i=0; iflush_fn; +/* + * Only display output when echoing is turned on. + */ + if(gl->echo) { + int ndone = 0; /* The number of characters written so far */ +/* + * When using un-buffered I/O, flush pending output first. + */ + if(!buffered) { + if(gl_flush_output(gl)) + return 1; + }; +/* + * If no length has been provided, measure the length of the string. + */ + if(n < 0) + n = strlen(string); +/* + * Write the string. + */ + if(write_fn(gl, string + ndone, n-ndone) != n) + return 1; + }; + return 0; +} + +/*....................................................................... + * Output a terminal control sequence. When using terminfo, + * this must be a sequence returned by tgetstr() or tigetstr() + * respectively. + * + * Input: + * gl GetLine * The resource object of this library. + * nline int The number of lines affected by the operation, + * or 1 if not relevant. + * string char * The control sequence to be sent. + * Output: + * return int 0 - OK. + * 1 - Error. + */ +static int gl_print_control_sequence(GetLine *gl, int nline, const char *string) +{ + int waserr = 0; /* True if an error occurs */ +/* + * Only write characters to the terminal when echoing is enabled. + */ + if(gl->echo) { +#if defined(USE_TERMINFO) || defined(USE_TERMCAP) + tputs_gl = gl; + errno = 0; + tputs((char *)string, nline, gl_tputs_putchar); + waserr = errno != 0; +#else + waserr = gl_print_raw_string(gl, 1, string, -1); +#endif + }; + return waserr; +} + +#if defined(USE_TERMINFO) || defined(USE_TERMCAP) +/*....................................................................... + * The following callback function is called by tputs() to output a raw + * control character to the terminal. + */ +static TputsRetType gl_tputs_putchar(TputsArgType c) +{ + char ch = c; +#if TPUTS_RETURNS_VALUE + return gl_print_raw_string(tputs_gl, 1, &ch, 1); +#else + (void) gl_print_raw_string(tputs_gl, 1, &ch, 1); +#endif +} +#endif + +/*....................................................................... + * Move the terminal cursor n characters to the left or right. + * + * Input: + * gl GetLine * The resource object of this program. + * n int number of positions to the right (> 0) or left (< 0). + * Output: + * return int 0 - OK. + * 1 - Error. + */ +static int gl_terminal_move_cursor(GetLine *gl, int n) +{ + int cur_row, cur_col; /* The current terminal row and column index of */ + /* the cursor wrt the start of the input line. */ + int new_row, new_col; /* The target terminal row and column index of */ + /* the cursor wrt the start of the input line. */ +/* + * Do nothing if the input line isn't currently displayed. In this + * case, the cursor will be moved to the right place when the line + * is next redisplayed. + */ + if(!gl->displayed) + return 0; +/* + * How far can we move left? + */ + if(gl->term_curpos + n < 0) + n = gl->term_curpos; +/* + * Break down the current and target cursor locations into rows and columns. + */ + cur_row = gl->term_curpos / gl->ncolumn; + cur_col = gl->term_curpos % gl->ncolumn; + new_row = (gl->term_curpos + n) / gl->ncolumn; + new_col = (gl->term_curpos + n) % gl->ncolumn; +/* + * Move down to the next line. + */ + for(; cur_row < new_row; cur_row++) { + if(gl_print_control_sequence(gl, 1, gl->down)) + return 1; + }; +/* + * Move up to the previous line. + */ + for(; cur_row > new_row; cur_row--) { + if(gl_print_control_sequence(gl, 1, gl->up)) + return 1; + }; +/* + * Move to the right within the target line? + */ + if(cur_col < new_col) { +#ifdef USE_TERMINFO +/* + * Use a parameterized control sequence if it generates less control + * characters (guess based on ANSI terminal termcap entry). + */ + if(gl->right_n != NULL && new_col - cur_col > 1) { + if(gl_print_control_sequence(gl, 1, tparm((char *)gl->right_n, + (long)(new_col - cur_col), 0l, 0l, 0l, 0l, 0l, 0l, 0l, 0l))) + return 1; + } else +#endif + { + for(; cur_col < new_col; cur_col++) { + if(gl_print_control_sequence(gl, 1, gl->right)) + return 1; + }; + }; +/* + * Move to the left within the target line? + */ + } else if(cur_col > new_col) { +#ifdef USE_TERMINFO +/* + * Use a parameterized control sequence if it generates less control + * characters (guess based on ANSI terminal termcap entry). + */ + if(gl->left_n != NULL && cur_col - new_col > 3) { + if(gl_print_control_sequence(gl, 1, tparm((char *)gl->left_n, + (long)(cur_col - new_col), 0l, 0l, 0l, 0l, 0l, 0l, 0l, 0l))) + return 1; + } else +#endif + { + for(; cur_col > new_col; cur_col--) { + if(gl_print_control_sequence(gl, 1, gl->left)) + return 1; + }; + }; + } +/* + * Update the recorded position of the terminal cursor. + */ + gl->term_curpos += n; + return 0; +} + +/*....................................................................... + * Write a character to the terminal after expanding tabs and control + * characters to their multi-character representations. + * + * Input: + * gl GetLine * The resource object of this program. + * c char The character to be output. + * pad char Many terminals have the irritating feature that + * when one writes a character in the last column of + * of the terminal, the cursor isn't wrapped to the + * start of the next line until one more character + * is written. Some terminals don't do this, so + * after such a write, we don't know where the + * terminal is unless we output an extra character. + * This argument specifies the character to write. + * If at the end of the input line send '\0' or a + * space, and a space will be written. Otherwise, + * pass the next character in the input line + * following the one being written. + * Output: + * return int 0 - OK. + */ +static int gl_print_char(GetLine *gl, char c, char pad) +{ + char string[TAB_WIDTH + 4]; /* A work area for composing compound strings */ + int nchar; /* The number of terminal characters */ + int i; +/* + * Check for special characters. + */ + if(c == '\t') { +/* + * How many spaces do we need to represent a tab at the current terminal + * column? + */ + nchar = gl_displayed_tab_width(gl, gl->term_curpos); +/* + * Compose the tab string. + */ + for(i=0; iterm_curpos += nchar; +/* + * Keep a record of the number of characters in the terminal version + * of the input line. + */ + if(gl->term_curpos > gl->term_len) + gl->term_len = gl->term_curpos; +/* + * If the new character ended exactly at the end of a line, + * most terminals won't move the cursor onto the next line until we + * have written a character on the next line, so append an extra + * space then move the cursor back. + */ + if(gl->term_curpos % gl->ncolumn == 0) { + int term_curpos = gl->term_curpos; + if(gl_print_char(gl, pad ? pad : ' ', ' ') || + gl_set_term_curpos(gl, term_curpos)) + return 1; + }; + return 0; +} + +/*....................................................................... + * Write a string to the terminal after expanding tabs and control + * characters to their multi-character representations. + * + * Input: + * gl GetLine * The resource object of this program. + * string char * The string to be output. + * pad char Many terminals have the irritating feature that + * when one writes a character in the last column of + * of the terminal, the cursor isn't wrapped to the + * start of the next line until one more character + * is written. Some terminals don't do this, so + * after such a write, we don't know where the + * terminal is unless we output an extra character. + * This argument specifies the character to write. + * If at the end of the input line send '\0' or a + * space, and a space will be written. Otherwise, + * pass the next character in the input line + * following the one being written. + * Output: + * return int 0 - OK. + */ +static int gl_print_string(GetLine *gl, const char *string, char pad) +{ + const char *cptr; /* A pointer into string[] */ + for(cptr=string; *cptr; cptr++) { + char nextc = cptr[1]; + if(gl_print_char(gl, *cptr, nextc ? nextc : pad)) + return 1; + }; + return 0; +} + +/*....................................................................... + * Move the terminal cursor position. + * + * Input: + * gl GetLine * The resource object of this library. + * term_curpos int The destination terminal cursor position. + * Output: + * return int 0 - OK. + * 1 - Error. + */ +static int gl_set_term_curpos(GetLine *gl, int term_curpos) +{ + return gl_terminal_move_cursor(gl, term_curpos - gl->term_curpos); +} + +/*....................................................................... + * This is an action function that moves the buffer cursor one character + * left, and updates the terminal cursor to match. + */ +static KT_KEY_FN(gl_cursor_left) +{ + return gl_place_cursor(gl, gl->buff_curpos - count); +} + +/*....................................................................... + * This is an action function that moves the buffer cursor one character + * right, and updates the terminal cursor to match. + */ +static KT_KEY_FN(gl_cursor_right) +{ + return gl_place_cursor(gl, gl->buff_curpos + count); +} + +/*....................................................................... + * This is an action function that toggles between overwrite and insert + * mode. + */ +static KT_KEY_FN(gl_insert_mode) +{ + gl->insert = !gl->insert; + return 0; +} + +/*....................................................................... + * This is an action function which moves the cursor to the beginning of + * the line. + */ +static KT_KEY_FN(gl_beginning_of_line) +{ + return gl_place_cursor(gl, 0); +} + +/*....................................................................... + * This is an action function which moves the cursor to the end of + * the line. + */ +static KT_KEY_FN(gl_end_of_line) +{ + return gl_place_cursor(gl, gl->ntotal); +} + +/*....................................................................... + * This is an action function which deletes the entire contents of the + * current line. + */ +static KT_KEY_FN(gl_delete_line) +{ +/* + * If in vi command mode, preserve the current line for potential + * use by vi-undo. + */ + gl_save_for_undo(gl); +/* + * Copy the contents of the line to the cut buffer. + */ + strcpy(gl->cutbuf, gl->line); +/* + * Clear the buffer. + */ + gl_truncate_buffer(gl, 0); +/* + * Move the terminal cursor to just after the prompt. + */ + if(gl_place_cursor(gl, 0)) + return 1; +/* + * Clear from the end of the prompt to the end of the terminal. + */ + if(gl_truncate_display(gl)) + return 1; + return 0; +} + +/*....................................................................... + * This is an action function which deletes all characters between the + * current cursor position and the end of the line. + */ +static KT_KEY_FN(gl_kill_line) +{ +/* + * If in vi command mode, preserve the current line for potential + * use by vi-undo. + */ + gl_save_for_undo(gl); +/* + * Copy the part of the line that is about to be deleted to the cut buffer. + */ + strcpy(gl->cutbuf, gl->line + gl->buff_curpos); +/* + * Terminate the buffered line at the current cursor position. + */ + gl_truncate_buffer(gl, gl->buff_curpos); +/* + * Clear the part of the line that follows the cursor. + */ + if(gl_truncate_display(gl)) + return 1; +/* + * Explicitly reset the cursor position to allow vi command mode + * constraints on its position to be set. + */ + return gl_place_cursor(gl, gl->buff_curpos); +} + +/*....................................................................... + * This is an action function which deletes all characters between the + * start of the line and the current cursor position. + */ +static KT_KEY_FN(gl_backward_kill_line) +{ +/* + * How many characters are to be deleted from before the cursor? + */ + int nc = gl->buff_curpos - gl->insert_curpos; + if (!nc) + return 0; +/* + * Move the cursor to the start of the line, or in vi input mode, + * the start of the sub-line at which insertion started, and delete + * up to the old cursor position. + */ + return gl_place_cursor(gl, gl->insert_curpos) || + gl_delete_chars(gl, nc, gl->editor == GL_EMACS_MODE || gl->vi.command); +} + +/*....................................................................... + * This is an action function which moves the cursor forward by a word. + */ +static KT_KEY_FN(gl_forward_word) +{ + return gl_place_cursor(gl, gl_nth_word_end_forward(gl, count) + + (gl->editor==GL_EMACS_MODE)); +} + +/*....................................................................... + * This is an action function which moves the cursor forward to the start + * of the next word. + */ +static KT_KEY_FN(gl_forward_to_word) +{ + return gl_place_cursor(gl, gl_nth_word_start_forward(gl, count)); +} + +/*....................................................................... + * This is an action function which moves the cursor backward by a word. + */ +static KT_KEY_FN(gl_backward_word) +{ + return gl_place_cursor(gl, gl_nth_word_start_backward(gl, count)); +} + +/*....................................................................... + * Delete one or more characters, starting with the one under the cursor. + * + * Input: + * gl GetLine * The resource object of this library. + * nc int The number of characters to delete. + * cut int If true, copy the characters to the cut buffer. + * Output: + * return int 0 - OK. + * 1 - Error. + */ +static int gl_delete_chars(GetLine *gl, int nc, int cut) +{ +/* + * If in vi command mode, preserve the current line for potential + * use by vi-undo. + */ + gl_save_for_undo(gl); +/* + * If there are fewer than nc characters following the cursor, limit + * nc to the number available. + */ + if(gl->buff_curpos + nc > gl->ntotal) + nc = gl->ntotal - gl->buff_curpos; +/* + * Copy the about to be deleted region to the cut buffer. + */ + if(cut) { + memcpy(gl->cutbuf, gl->line + gl->buff_curpos, nc); + gl->cutbuf[nc] = '\0'; + } +/* + * Nothing to delete? + */ + if(nc <= 0) + return 0; +/* + * In vi overwrite mode, restore any previously overwritten characters + * from the undo buffer. + */ + if(gl->editor == GL_VI_MODE && !gl->vi.command && !gl->insert) { +/* + * How many of the characters being deleted can be restored from the + * undo buffer? + */ + int nrestore = gl->buff_curpos + nc <= gl->vi.undo.ntotal ? + nc : gl->vi.undo.ntotal - gl->buff_curpos; +/* + * Restore any available characters. + */ + if(nrestore > 0) { + gl_buffer_string(gl, gl->vi.undo.line + gl->buff_curpos, nrestore, + gl->buff_curpos); + }; +/* + * If their were insufficient characters in the undo buffer, then this + * implies that we are deleting from the end of the line, so we need + * to terminate the line either where the undo buffer ran out, or if + * we are deleting from beyond the end of the undo buffer, at the current + * cursor position. + */ + if(nc != nrestore) { + gl_truncate_buffer(gl, (gl->vi.undo.ntotal > gl->buff_curpos) ? + gl->vi.undo.ntotal : gl->buff_curpos); + }; + } else { +/* + * Copy the remaining part of the line back over the deleted characters. + */ + gl_remove_from_buffer(gl, gl->buff_curpos, nc); + }; +/* + * Redraw the remaining characters following the cursor. + */ + if(gl_print_string(gl, gl->line + gl->buff_curpos, '\0')) + return 1; +/* + * Clear to the end of the terminal. + */ + if(gl_truncate_display(gl)) + return 1; +/* + * Place the cursor at the start of where the deletion was performed. + */ + return gl_place_cursor(gl, gl->buff_curpos); +} + +/*....................................................................... + * This is an action function which deletes character(s) under the + * cursor without moving the cursor. + */ +static KT_KEY_FN(gl_forward_delete_char) +{ +/* + * Delete 'count' characters. + */ + return gl_delete_chars(gl, count, gl->vi.command); +} + +/*....................................................................... + * This is an action function which deletes character(s) under the + * cursor and moves the cursor back one character. + */ +static KT_KEY_FN(gl_backward_delete_char) +{ +/* + * Restrict the deletion count to the number of characters that + * precede the insertion point. + */ + if(count > gl->buff_curpos - gl->insert_curpos) + count = gl->buff_curpos - gl->insert_curpos; +/* + * If in vi command mode, preserve the current line for potential + * use by vi-undo. + */ + gl_save_for_undo(gl); + return gl_cursor_left(gl, count, NULL) || + gl_delete_chars(gl, count, gl->vi.command); +} + +/*....................................................................... + * Starting from the cursor position delete to the specified column. + */ +static KT_KEY_FN(gl_delete_to_column) +{ + if (--count >= gl->buff_curpos) + return gl_forward_delete_char(gl, count - gl->buff_curpos, NULL); + else + return gl_backward_delete_char(gl, gl->buff_curpos - count, NULL); +} + +/*....................................................................... + * Starting from the cursor position delete characters to a matching + * parenthesis. + */ +static KT_KEY_FN(gl_delete_to_parenthesis) +{ + int curpos = gl_index_of_matching_paren(gl); + if(curpos >= 0) { + gl_save_for_undo(gl); + if(curpos >= gl->buff_curpos) + return gl_forward_delete_char(gl, curpos - gl->buff_curpos + 1, NULL); + else + return gl_backward_delete_char(gl, ++gl->buff_curpos - curpos + 1, NULL); + }; + return 0; +} + +/*....................................................................... + * This is an action function which deletes from the cursor to the end + * of the word that the cursor is either in or precedes. + */ +static KT_KEY_FN(gl_forward_delete_word) +{ +/* + * If in vi command mode, preserve the current line for potential + * use by vi-undo. + */ + gl_save_for_undo(gl); +/* + * In emacs mode delete to the end of the word. In vi mode delete to the + * start of the net word. + */ + if(gl->editor == GL_EMACS_MODE) { + return gl_delete_chars(gl, + gl_nth_word_end_forward(gl,count) - gl->buff_curpos + 1, 1); + } else { + return gl_delete_chars(gl, + gl_nth_word_start_forward(gl,count) - gl->buff_curpos, + gl->vi.command); + }; +} + +/*....................................................................... + * This is an action function which deletes the word that precedes the + * cursor. + */ +static KT_KEY_FN(gl_backward_delete_word) +{ +/* + * Keep a record of the current cursor position. + */ + int buff_curpos = gl->buff_curpos; +/* + * If in vi command mode, preserve the current line for potential + * use by vi-undo. + */ + gl_save_for_undo(gl); +/* + * Move back 'count' words. + */ + if(gl_backward_word(gl, count, NULL)) + return 1; +/* + * Delete from the new cursor position to the original one. + */ + return gl_delete_chars(gl, buff_curpos - gl->buff_curpos, + gl->editor == GL_EMACS_MODE || gl->vi.command); +} + +/*....................................................................... + * Searching in a given direction, delete to the count'th + * instance of a specified or queried character, in the input line. + * + * Input: + * gl GetLine * The getline resource object. + * count int The number of times to search. + * c char The character to be searched for, or '\0' if + * the character should be read from the user. + * forward int True if searching forward. + * onto int True if the search should end on top of the + * character, false if the search should stop + * one character before the character in the + * specified search direction. + * change int If true, this function is being called upon + * to do a vi change command, in which case the + * user will be left in insert mode after the + * deletion. + * Output: + * return int 0 - OK. + * 1 - Error. + */ +static int gl_delete_find(GetLine *gl, int count, char c, int forward, + int onto, int change) +{ +/* + * Search for the character, and abort the deletion if not found. + */ + int pos = gl_find_char(gl, count, forward, onto, c); + if(pos < 0) + return 0; +/* + * If in vi command mode, preserve the current line for potential + * use by vi-undo. + */ + gl_save_for_undo(gl); +/* + * Allow the cursor to be at the end of the line if this is a change + * command. + */ + if(change) + gl->vi.command = 0; +/* + * Delete the appropriate span of characters. + */ + if(forward) { + if(gl_delete_chars(gl, pos - gl->buff_curpos + 1, 1)) + return 1; + } else { + int buff_curpos = gl->buff_curpos; + if(gl_place_cursor(gl, pos) || + gl_delete_chars(gl, buff_curpos - gl->buff_curpos, 1)) + return 1; + }; +/* + * If this is a change operation, switch the insert mode. + */ + if(change && gl_vi_insert(gl, 0, NULL)) + return 1; + return 0; +} + +/*....................................................................... + * This is an action function which deletes forward from the cursor up to and + * including a specified character. + */ +static KT_KEY_FN(gl_forward_delete_find) +{ + return gl_delete_find(gl, count, '\0', 1, 1, 0); +} + +/*....................................................................... + * This is an action function which deletes backward from the cursor back to + * and including a specified character. + */ +static KT_KEY_FN(gl_backward_delete_find) +{ + return gl_delete_find(gl, count, '\0', 0, 1, 0); +} + +/*....................................................................... + * This is an action function which deletes forward from the cursor up to but + * not including a specified character. + */ +static KT_KEY_FN(gl_forward_delete_to) +{ + return gl_delete_find(gl, count, '\0', 1, 0, 0); +} + +/*....................................................................... + * This is an action function which deletes backward from the cursor back to + * but not including a specified character. + */ +static KT_KEY_FN(gl_backward_delete_to) +{ + return gl_delete_find(gl, count, '\0', 0, 0, 0); +} + +/*....................................................................... + * This is an action function which deletes to a character specified by a + * previous search. + */ +static KT_KEY_FN(gl_delete_refind) +{ + return gl_delete_find(gl, count, gl->vi.find_char, gl->vi.find_forward, + gl->vi.find_onto, 0); +} + +/*....................................................................... + * This is an action function which deletes to a character specified by a + * previous search, but in the opposite direction. + */ +static KT_KEY_FN(gl_delete_invert_refind) +{ + return gl_delete_find(gl, count, gl->vi.find_char, + !gl->vi.find_forward, gl->vi.find_onto, 0); +} + +/*....................................................................... + * This is an action function which converts the characters in the word + * following the cursor to upper case. + */ +static KT_KEY_FN(gl_upcase_word) +{ +/* + * Locate the count'th word ending after the cursor. + */ + int last = gl_nth_word_end_forward(gl, count); +/* + * If in vi command mode, preserve the current line for potential + * use by vi-undo. + */ + gl_save_for_undo(gl); +/* + * Upcase characters from the current cursor position to 'last'. + */ + while(gl->buff_curpos <= last) { + char *cptr = gl->line + gl->buff_curpos; +/* + * Convert the character to upper case? + */ + if(islower((int)(unsigned char) *cptr)) + gl_buffer_char(gl, toupper((int) *cptr), gl->buff_curpos); + gl->buff_curpos++; +/* + * Write the possibly modified character back. Note that for non-modified + * characters we want to do this as well, so as to advance the cursor. + */ + if(gl_print_char(gl, *cptr, cptr[1])) + return 1; + }; + return gl_place_cursor(gl, gl->buff_curpos); /* bounds check */ +} + +/*....................................................................... + * This is an action function which converts the characters in the word + * following the cursor to lower case. + */ +static KT_KEY_FN(gl_downcase_word) +{ +/* + * Locate the count'th word ending after the cursor. + */ + int last = gl_nth_word_end_forward(gl, count); +/* + * If in vi command mode, preserve the current line for potential + * use by vi-undo. + */ + gl_save_for_undo(gl); +/* + * Upcase characters from the current cursor position to 'last'. + */ + while(gl->buff_curpos <= last) { + char *cptr = gl->line + gl->buff_curpos; +/* + * Convert the character to upper case? + */ + if(isupper((int)(unsigned char) *cptr)) + gl_buffer_char(gl, tolower((int) *cptr), gl->buff_curpos); + gl->buff_curpos++; +/* + * Write the possibly modified character back. Note that for non-modified + * characters we want to do this as well, so as to advance the cursor. + */ + if(gl_print_char(gl, *cptr, cptr[1])) + return 1; + }; + return gl_place_cursor(gl, gl->buff_curpos); /* bounds check */ +} + +/*....................................................................... + * This is an action function which converts the first character of the + * following word to upper case, in order to capitalize the word, and + * leaves the cursor at the end of the word. + */ +static KT_KEY_FN(gl_capitalize_word) +{ + char *cptr; /* &gl->line[gl->buff_curpos] */ + int first; /* True for the first letter of the word */ + int i; +/* + * Keep a record of the current insert mode and the cursor position. + */ + int insert = gl->insert; +/* + * If in vi command mode, preserve the current line for potential + * use by vi-undo. + */ + gl_save_for_undo(gl); +/* + * We want to overwrite the modified word. + */ + gl->insert = 0; +/* + * Capitalize 'count' words. + */ + for(i=0; ibuff_curpos < gl->ntotal; i++) { + int pos = gl->buff_curpos; +/* + * If we are not already within a word, skip to the start of the word. + */ + for(cptr = gl->line + pos ; posntotal && !gl_is_word_char((int) *cptr); + pos++, cptr++) + ; +/* + * Move the cursor to the new position. + */ + if(gl_place_cursor(gl, pos)) + return 1; +/* + * While searching for the end of the word, change lower case letters + * to upper case. + */ + for(first=1; gl->buff_curposntotal && gl_is_word_char((int) *cptr); + gl->buff_curpos++, cptr++) { +/* + * Convert the character to upper case? + */ + if(first) { + if(islower((int)(unsigned char) *cptr)) + gl_buffer_char(gl, toupper((int) *cptr), cptr - gl->line); + } else { + if(isupper((int)(unsigned char) *cptr)) + gl_buffer_char(gl, tolower((int) *cptr), cptr - gl->line); + }; + first = 0; +/* + * Write the possibly modified character back. Note that for non-modified + * characters we want to do this as well, so as to advance the cursor. + */ + if(gl_print_char(gl, *cptr, cptr[1])) + return 1; + }; + }; +/* + * Restore the insertion mode. + */ + gl->insert = insert; + return gl_place_cursor(gl, gl->buff_curpos); /* bounds check */ +} + +/*....................................................................... + * This is an action function which redraws the current line. + */ +static KT_KEY_FN(gl_redisplay) +{ +/* + * Keep a record of the current cursor position. + */ + int buff_curpos = gl->buff_curpos; +/* + * Do nothing if there is no line to be redisplayed. + */ + if(gl->endline) + return 0; +/* + * Erase the current input line. + */ + if(gl_erase_line(gl)) + return 1; +/* + * Display the current prompt. + */ + if(gl_display_prompt(gl)) + return 1; +/* + * Render the part of the line that the user has typed in so far. + */ + if(gl_print_string(gl, gl->line, '\0')) + return 1; +/* + * Restore the cursor position. + */ + if(gl_place_cursor(gl, buff_curpos)) + return 1; +/* + * Mark the redisplay operation as having been completed. + */ + gl->redisplay = 0; +/* + * Flush the redisplayed line to the terminal. + */ + return gl_flush_output(gl); +} + +/*....................................................................... + * This is an action function which clears the display and redraws the + * input line from the home position. + */ +static KT_KEY_FN(gl_clear_screen) +{ +/* + * Home the cursor and clear from there to the end of the display. + */ + if(gl_print_control_sequence(gl, gl->nline, gl->home) || + gl_print_control_sequence(gl, gl->nline, gl->clear_eod)) + return 1; +/* + * The input line is no longer displayed. + */ + gl_line_erased(gl); +/* + * Arrange for the input line to be redisplayed. + */ + gl_queue_redisplay(gl); + return 0; +} + +/*....................................................................... + * This is an action function which swaps the character under the cursor + * with the character to the left of the cursor. + */ +static KT_KEY_FN(gl_transpose_chars) +{ + char from[3]; /* The original string of 2 characters */ + char swap[3]; /* The swapped string of two characters */ +/* + * If we are at the beginning or end of the line, there aren't two + * characters to swap. + */ + if(gl->buff_curpos < 1 || gl->buff_curpos >= gl->ntotal) + return 0; +/* + * If in vi command mode, preserve the current line for potential + * use by vi-undo. + */ + gl_save_for_undo(gl); +/* + * Get the original and swapped strings of the two characters. + */ + from[0] = gl->line[gl->buff_curpos - 1]; + from[1] = gl->line[gl->buff_curpos]; + from[2] = '\0'; + swap[0] = gl->line[gl->buff_curpos]; + swap[1] = gl->line[gl->buff_curpos - 1]; + swap[2] = '\0'; +/* + * Move the cursor to the start of the two characters. + */ + if(gl_place_cursor(gl, gl->buff_curpos-1)) + return 1; +/* + * Swap the two characters in the buffer. + */ + gl_buffer_char(gl, swap[0], gl->buff_curpos); + gl_buffer_char(gl, swap[1], gl->buff_curpos+1); +/* + * If the sum of the displayed width of the two characters + * in their current and final positions is the same, swapping can + * be done by just overwriting with the two swapped characters. + */ + if(gl_displayed_string_width(gl, from, -1, gl->term_curpos) == + gl_displayed_string_width(gl, swap, -1, gl->term_curpos)) { + int insert = gl->insert; + gl->insert = 0; + if(gl_print_char(gl, swap[0], swap[1]) || + gl_print_char(gl, swap[1], gl->line[gl->buff_curpos+2])) + return 1; + gl->insert = insert; +/* + * If the swapped substring has a different displayed size, we need to + * redraw everything after the first of the characters. + */ + } else { + if(gl_print_string(gl, gl->line + gl->buff_curpos, '\0') || + gl_truncate_display(gl)) + return 1; + }; +/* + * Advance the cursor to the character after the swapped pair. + */ + return gl_place_cursor(gl, gl->buff_curpos + 2); +} + +/*....................................................................... + * This is an action function which sets a mark at the current cursor + * location. + */ +static KT_KEY_FN(gl_set_mark) +{ + gl->buff_mark = gl->buff_curpos; + return 0; +} + +/*....................................................................... + * This is an action function which swaps the mark location for the + * cursor location. + */ +static KT_KEY_FN(gl_exchange_point_and_mark) +{ +/* + * Get the old mark position, and limit to the extent of the input + * line. + */ + int old_mark = gl->buff_mark <= gl->ntotal ? gl->buff_mark : gl->ntotal; +/* + * Make the current cursor position the new mark. + */ + gl->buff_mark = gl->buff_curpos; +/* + * Move the cursor to the old mark position. + */ + return gl_place_cursor(gl, old_mark); +} + +/*....................................................................... + * This is an action function which deletes the characters between the + * mark and the cursor, recording them in gl->cutbuf for later pasting. + */ +static KT_KEY_FN(gl_kill_region) +{ +/* + * If in vi command mode, preserve the current line for potential + * use by vi-undo. + */ + gl_save_for_undo(gl); +/* + * Limit the mark to be within the line. + */ + if(gl->buff_mark > gl->ntotal) + gl->buff_mark = gl->ntotal; +/* + * If there are no characters between the cursor and the mark, simply clear + * the cut buffer. + */ + if(gl->buff_mark == gl->buff_curpos) { + gl->cutbuf[0] = '\0'; + return 0; + }; +/* + * If the mark is before the cursor, swap the cursor and the mark. + */ + if(gl->buff_mark < gl->buff_curpos && gl_exchange_point_and_mark(gl,1,NULL)) + return 1; +/* + * Delete the characters. + */ + if(gl_delete_chars(gl, gl->buff_mark - gl->buff_curpos, 1)) + return 1; +/* + * Make the mark the same as the cursor position. + */ + gl->buff_mark = gl->buff_curpos; + return 0; +} + +/*....................................................................... + * This is an action function which records the characters between the + * mark and the cursor, in gl->cutbuf for later pasting. + */ +static KT_KEY_FN(gl_copy_region_as_kill) +{ + int ca, cb; /* The indexes of the first and last characters in the region */ + int mark; /* The position of the mark */ +/* + * Get the position of the mark, limiting it to lie within the line. + */ + mark = gl->buff_mark > gl->ntotal ? gl->ntotal : gl->buff_mark; +/* + * If there are no characters between the cursor and the mark, clear + * the cut buffer. + */ + if(mark == gl->buff_curpos) { + gl->cutbuf[0] = '\0'; + return 0; + }; +/* + * Get the line indexes of the first and last characters in the region. + */ + if(mark < gl->buff_curpos) { + ca = mark; + cb = gl->buff_curpos - 1; + } else { + ca = gl->buff_curpos; + cb = mark - 1; + }; +/* + * Copy the region to the cut buffer. + */ + memcpy(gl->cutbuf, gl->line + ca, cb + 1 - ca); + gl->cutbuf[cb + 1 - ca] = '\0'; + return 0; +} + +/*....................................................................... + * This is an action function which inserts the contents of the cut + * buffer at the current cursor location. + */ +static KT_KEY_FN(gl_yank) +{ + int i; +/* + * Set the mark at the current location. + */ + gl->buff_mark = gl->buff_curpos; +/* + * Do nothing else if the cut buffer is empty. + */ + if(gl->cutbuf[0] == '\0') + return gl_ring_bell(gl, 1, NULL); +/* + * If in vi command mode, preserve the current line for potential + * use by vi-undo. + */ + gl_save_for_undo(gl); +/* + * Insert the string count times. + */ + for(i=0; icutbuf)) + return 1; + }; +/* + * gl_add_string_to_line() leaves the cursor after the last character that + * was pasted, whereas vi leaves the cursor over the last character pasted. + */ + if(gl->editor == GL_VI_MODE && gl_cursor_left(gl, 1, NULL)) + return 1; + return 0; +} + +/*....................................................................... + * This is an action function which inserts the contents of the cut + * buffer one character beyond the current cursor location. + */ +static KT_KEY_FN(gl_append_yank) +{ + int was_command = gl->vi.command; + int i; +/* + * If the cut buffer is empty, ring the terminal bell. + */ + if(gl->cutbuf[0] == '\0') + return gl_ring_bell(gl, 1, NULL); +/* + * Set the mark at the current location + 1. + */ + gl->buff_mark = gl->buff_curpos + 1; +/* + * If in vi command mode, preserve the current line for potential + * use by vi-undo. + */ + gl_save_for_undo(gl); +/* + * Arrange to paste the text in insert mode after the current character. + */ + if(gl_vi_append(gl, 0, NULL)) + return 1; +/* + * Insert the string count times. + */ + for(i=0; icutbuf)) + return 1; + }; +/* + * Switch back to command mode if necessary. + */ + if(was_command) + gl_vi_command_mode(gl); + return 0; +} + +/*....................................................................... + * Attempt to ask the terminal for its current size. On systems that + * don't support the TIOCWINSZ ioctl() for querying the terminal size, + * the current values of gl->ncolumn and gl->nrow are returned. + * + * Input: + * gl GetLine * The resource object of gl_get_line(). + * Input/Output: + * ncolumn int * The number of columns will be assigned to *ncolumn. + * nline int * The number of lines will be assigned to *nline. + */ +static void gl_query_size(GetLine *gl, int *ncolumn, int *nline) +{ +#ifdef TIOCGWINSZ +/* + * Query the new terminal window size. Ignore invalid responses. + */ + struct winsize size; + if(ioctl(gl->output_fd, TIOCGWINSZ, &size) == 0 && + size.ws_row > 0 && size.ws_col > 0) { + *ncolumn = size.ws_col; + *nline = size.ws_row; + return; + }; +#endif +/* + * Return the existing values. + */ + *ncolumn = gl->ncolumn; + *nline = gl->nline; + return; +} + +/*....................................................................... + * Query the size of the terminal, and if it has changed, redraw the + * current input line accordingly. + * + * Input: + * gl GetLine * The resource object of gl_get_line(). + * Output: + * return int 0 - OK. + * 1 - Error. + */ +static int _gl_update_size(GetLine *gl) +{ + int ncolumn, nline; /* The new size of the terminal */ +/* + * Query the new terminal window size. + */ + gl_query_size(gl, &ncolumn, &nline); +/* + * Update gl and the displayed line to fit the new dimensions. + */ + return gl_handle_tty_resize(gl, ncolumn, nline); +} + +/*....................................................................... + * Redraw the current input line to account for a change in the terminal + * size. Also install the new size in gl. + * + * Input: + * gl GetLine * The resource object of gl_get_line(). + * ncolumn int The new number of columns. + * nline int The new number of lines. + * Output: + * return int 0 - OK. + * 1 - Error. + */ +static int gl_handle_tty_resize(GetLine *gl, int ncolumn, int nline) +{ +/* + * If the input device isn't a terminal, just record the new size. + */ + if(!gl->is_term) { + gl->nline = nline; + gl->ncolumn = ncolumn; +/* + * Has the size actually changed? + */ + } else if(ncolumn != gl->ncolumn || nline != gl->nline) { +/* + * If we are currently editing a line, erase it. + */ + if(gl_erase_line(gl)) + return 1; +/* + * Update the recorded window size. + */ + gl->nline = nline; + gl->ncolumn = ncolumn; +/* + * Arrange for the input line to be redrawn before the next character + * is read from the terminal. + */ + gl_queue_redisplay(gl); + }; + return 0; +} + +/*....................................................................... + * This is the action function that recalls the previous line in the + * history buffer. + */ +static KT_KEY_FN(gl_up_history) +{ +/* + * In vi mode, switch to command mode, since the user is very + * likely to want to move around newly recalled lines. + */ + gl_vi_command_mode(gl); +/* + * Forget any previous recall session. + */ + gl->preload_id = 0; +/* + * Record the key sequence number of this search action. + */ + gl->last_search = gl->keyseq_count; +/* + * We don't want a search prefix for this function. + */ + if(_glh_search_prefix(gl->glh, gl->line, 0)) { + _err_record_msg(gl->err, _glh_last_error(gl->glh), END_ERR_MSG); + return 1; + }; +/* + * Recall the count'th next older line in the history list. If the first one + * fails we can return since nothing has changed, otherwise we must continue + * and update the line state. + */ + if(_glh_find_backwards(gl->glh, gl->line, gl->linelen+1) == NULL) + return 0; + while(--count && _glh_find_backwards(gl->glh, gl->line, gl->linelen+1)) + ; +/* + * Accomodate the new contents of gl->line[]. + */ + gl_update_buffer(gl); +/* + * Arrange to have the cursor placed at the end of the new line. + */ + gl->buff_curpos = gl->ntotal; +/* + * Erase and display the new line. + */ + gl_queue_redisplay(gl); + return 0; +} + +/*....................................................................... + * This is the action function that recalls the next line in the + * history buffer. + */ +static KT_KEY_FN(gl_down_history) +{ +/* + * In vi mode, switch to command mode, since the user is very + * likely to want to move around newly recalled lines. + */ + gl_vi_command_mode(gl); +/* + * Record the key sequence number of this search action. + */ + gl->last_search = gl->keyseq_count; +/* + * If no search is currently in progress continue a previous recall + * session from a previous entered line if possible. + */ + if(_glh_line_id(gl->glh, 0) == 0 && gl->preload_id) { + _glh_recall_line(gl->glh, gl->preload_id, gl->line, gl->linelen+1); + gl->preload_id = 0; + } else { +/* + * We don't want a search prefix for this function. + */ + if(_glh_search_prefix(gl->glh, gl->line, 0)) { + _err_record_msg(gl->err, _glh_last_error(gl->glh), END_ERR_MSG); + return 1; + }; +/* + * Recall the count'th next newer line in the history list. If the first one + * fails we can return since nothing has changed otherwise we must continue + * and update the line state. + */ + if(_glh_find_forwards(gl->glh, gl->line, gl->linelen+1) == NULL) + return 0; + while(--count && _glh_find_forwards(gl->glh, gl->line, gl->linelen+1)) + ; + }; +/* + * Accomodate the new contents of gl->line[]. + */ + gl_update_buffer(gl); +/* + * Arrange to have the cursor placed at the end of the new line. + */ + gl->buff_curpos = gl->ntotal; +/* + * Erase and display the new line. + */ + gl_queue_redisplay(gl); + return 0; +} + +/*....................................................................... + * This is the action function that recalls the previous line in the + * history buffer whos prefix matches the characters that currently + * precede the cursor. By setting count=-1, this can be used internally + * to force searching for the prefix used in the last search. + */ +static KT_KEY_FN(gl_history_search_backward) +{ +/* + * In vi mode, switch to command mode, since the user is very + * likely to want to move around newly recalled lines. + */ + gl_vi_command_mode(gl); +/* + * Forget any previous recall session. + */ + gl->preload_id = 0; +/* + * Record the key sequence number of this search action. + */ + gl->last_search = gl->keyseq_count; +/* + * If a prefix search isn't already in progress, replace the search + * prefix to the string that precedes the cursor. In vi command mode + * include the character that is under the cursor in the string. If + * count<0 keep the previous search prefix regardless, so as to force + * a repeat search even if the last command wasn't a history command. + */ + if(count >= 0 && !_glh_search_active(gl->glh) && + _glh_search_prefix(gl->glh, gl->line, gl->buff_curpos + + (gl->editor==GL_VI_MODE && gl->ntotal>0))) { + _err_record_msg(gl->err, _glh_last_error(gl->glh), END_ERR_MSG); + return 1; + }; +/* + * Search backwards for a match to the part of the line which precedes the + * cursor. + */ + if(_glh_find_backwards(gl->glh, gl->line, gl->linelen+1) == NULL) + return 0; +/* + * Accomodate the new contents of gl->line[]. + */ + gl_update_buffer(gl); +/* + * Arrange to have the cursor placed at the end of the new line. + */ + gl->buff_curpos = gl->ntotal; +/* + * Erase and display the new line. + */ + gl_queue_redisplay(gl); + return 0; +} + +/*....................................................................... + * This is the action function that recalls the previous line in the + * history buffer who's prefix matches that specified in an earlier call + * to gl_history_search_backward() or gl_history_search_forward(). + */ +static KT_KEY_FN(gl_history_re_search_backward) +{ + return gl_history_search_backward(gl, -1, NULL); +} + +/*....................................................................... + * This is the action function that recalls the next line in the + * history buffer who's prefix matches that specified in the earlier call + * to gl_history_search_backward) which started the history search. + * By setting count=-1, this can be used internally to force searching + * for the prefix used in the last search. + */ +static KT_KEY_FN(gl_history_search_forward) +{ +/* + * In vi mode, switch to command mode, since the user is very + * likely to want to move around newly recalled lines. + */ + gl_vi_command_mode(gl); +/* + * Record the key sequence number of this search action. + */ + gl->last_search = gl->keyseq_count; +/* + * If a prefix search isn't already in progress, replace the search + * prefix to the string that precedes the cursor. In vi command mode + * include the character that is under the cursor in the string. If + * count<0 keep the previous search prefix regardless, so as to force + * a repeat search even if the last command wasn't a history command. + */ + if(count >= 0 && !_glh_search_active(gl->glh) && + _glh_search_prefix(gl->glh, gl->line, gl->buff_curpos + + (gl->editor==GL_VI_MODE && gl->ntotal>0))) { + _err_record_msg(gl->err, _glh_last_error(gl->glh), END_ERR_MSG); + return 1; + }; +/* + * Search forwards for the next matching line. + */ + if(_glh_find_forwards(gl->glh, gl->line, gl->linelen+1) == NULL) + return 0; +/* + * Accomodate the new contents of gl->line[]. + */ + gl_update_buffer(gl); +/* + * Arrange for the cursor to be placed at the end of the new line. + */ + gl->buff_curpos = gl->ntotal; +/* + * Erase and display the new line. + */ + gl_queue_redisplay(gl); + return 0; +} + +/*....................................................................... + * This is the action function that recalls the next line in the + * history buffer who's prefix matches that specified in an earlier call + * to gl_history_search_backward() or gl_history_search_forward(). + */ +static KT_KEY_FN(gl_history_re_search_forward) +{ + return gl_history_search_forward(gl, -1, NULL); +} + +#ifdef HIDE_FILE_SYSTEM +/*....................................................................... + * The following function is used as the default completion handler when + * the filesystem is to be hidden. It simply reports no completions. + */ +static CPL_MATCH_FN(gl_no_completions) +{ + return 0; +} +#endif + +/*....................................................................... + * This is the tab completion function that completes the filename that + * precedes the cursor position. Its callback data argument must be a + * pointer to a GlCplCallback containing the completion callback function + * and its callback data, or NULL to use the builtin filename completer. + */ +static KT_KEY_FN(gl_complete_word) +{ + CplMatches *matches; /* The possible completions */ + int suffix_len; /* The length of the completion extension */ + int cont_len; /* The length of any continuation suffix */ + int nextra; /* The number of characters being added to the */ + /* total length of the line. */ + int buff_pos; /* The buffer index at which the completion is */ + /* to be inserted. */ + int waserr = 0; /* True after errors */ +/* + * Get the container of the completion callback and its callback data. + */ + GlCplCallback *cb = data ? (GlCplCallback *) data : &gl->cplfn; +/* + * In vi command mode, switch to append mode so that the character under + * the cursor is included in the completion (otherwise people can't + * complete at the end of the line). + */ + if(gl->vi.command && gl_vi_append(gl, 0, NULL)) + return 1; +/* + * Get the cursor position at which the completion is to be inserted. + */ + buff_pos = gl->buff_curpos; +/* + * Perform the completion. + */ + matches = cpl_complete_word(gl->cpl, gl->line, gl->buff_curpos, cb->data, + cb->fn); +/* + * No matching completions? + */ + if(!matches) { + waserr = gl_print_info(gl, cpl_last_error(gl->cpl), GL_END_INFO); +/* + * Are there any completions? + */ + } else if(matches->nmatch >= 1) { +/* + * If there any ambiguous matches, report them, starting on a new line. + */ + if(matches->nmatch > 1 && gl->echo) { + if(_gl_normal_io(gl) || + _cpl_output_completions(matches, gl_write_fn, gl, gl->ncolumn)) + waserr = 1; + }; +/* + * Get the length of the suffix and any continuation suffix to add to it. + */ + suffix_len = strlen(matches->suffix); + cont_len = strlen(matches->cont_suffix); +/* + * If there is an unambiguous match, and the continuation suffix ends in + * a newline, strip that newline and arrange to have getline return + * after this action function returns. + */ + if(matches->nmatch==1 && cont_len > 0 && + matches->cont_suffix[cont_len - 1] == '\n') { + cont_len--; + if(gl_newline(gl, 1, NULL)) + waserr = 1; + }; +/* + * Work out the number of characters that are to be added. + */ + nextra = suffix_len + cont_len; +/* + * Is there anything to be added? + */ + if(!waserr && nextra) { +/* + * Will there be space for the expansion in the line buffer? + */ + if(gl->ntotal + nextra < gl->linelen) { +/* + * Make room to insert the filename extension. + */ + gl_make_gap_in_buffer(gl, gl->buff_curpos, nextra); +/* + * Insert the filename extension. + */ + gl_buffer_string(gl, matches->suffix, suffix_len, gl->buff_curpos); +/* + * Add the terminating characters. + */ + gl_buffer_string(gl, matches->cont_suffix, cont_len, + gl->buff_curpos + suffix_len); +/* + * Place the cursor position at the end of the completion. + */ + gl->buff_curpos += nextra; +/* + * If we don't have to redisplay the whole line, redisplay the part + * of the line which follows the original cursor position, and place + * the cursor at the end of the completion. + */ + if(gl->displayed) { + if(gl_truncate_display(gl) || + gl_print_string(gl, gl->line + buff_pos, '\0') || + gl_place_cursor(gl, gl->buff_curpos)) + waserr = 1; + }; + } else { + (void) gl_print_info(gl, + "Insufficient room in line for file completion.", + GL_END_INFO); + waserr = 1; + }; + }; + }; +/* + * If any output had to be written to the terminal, then editing will + * have been suspended, make sure that we are back in raw line editing + * mode before returning. + */ + if(_gl_raw_io(gl, 1)) + waserr = 1; + return 0; +} + +#ifndef HIDE_FILE_SYSTEM +/*....................................................................... + * This is the function that expands the filename that precedes the + * cursor position. It expands ~user/ expressions, $envvar expressions, + * and wildcards. + */ +static KT_KEY_FN(gl_expand_filename) +{ + char *start_path; /* The pointer to the start of the pathname in */ + /* gl->line[]. */ + FileExpansion *result; /* The results of the filename expansion */ + int pathlen; /* The length of the pathname being expanded */ + int length; /* The number of characters needed to display the */ + /* expanded files. */ + int nextra; /* The number of characters to be added */ + int i,j; +/* + * In vi command mode, switch to append mode so that the character under + * the cursor is included in the completion (otherwise people can't + * complete at the end of the line). + */ + if(gl->vi.command && gl_vi_append(gl, 0, NULL)) + return 1; +/* + * Locate the start of the filename that precedes the cursor position. + */ + start_path = _pu_start_of_path(gl->line, gl->buff_curpos); + if(!start_path) + return 1; +/* + * Get the length of the string that is to be expanded. + */ + pathlen = gl->buff_curpos - (start_path - gl->line); +/* + * Attempt to expand it. + */ + result = ef_expand_file(gl->ef, start_path, pathlen); +/* + * If there was an error, report the error on a new line. + */ + if(!result) + return gl_print_info(gl, ef_last_error(gl->ef), GL_END_INFO); +/* + * If no files matched, report this as well. + */ + if(result->nfile == 0 || !result->exists) + return gl_print_info(gl, "No files match.", GL_END_INFO); +/* + * If in vi command mode, preserve the current line for potential use by + * vi-undo. + */ + gl_save_for_undo(gl); +/* + * Work out how much space we will need to display all of the matching + * filenames, taking account of the space that we need to place between + * them, and the number of additional '\' characters needed to escape + * spaces, tabs and backslash characters in the individual filenames. + */ + length = 0; + for(i=0; infile; i++) { + char *file = result->files[i]; + while(*file) { + int c = *file++; + switch(c) { + case ' ': case '\t': case '\\': case '*': case '?': case '[': + length++; /* Count extra backslash characters */ + }; + length++; /* Count the character itself */ + }; + length++; /* Count the space that follows each filename */ + }; +/* + * Work out the number of characters that are to be added. + */ + nextra = length - pathlen; +/* + * Will there be space for the expansion in the line buffer? + */ + if(gl->ntotal + nextra >= gl->linelen) { + return gl_print_info(gl, "Insufficient room in line for file expansion.", + GL_END_INFO); + } else { +/* + * Do we need to move the part of the line that followed the unexpanded + * filename? + */ + if(nextra > 0) { + gl_make_gap_in_buffer(gl, gl->buff_curpos, nextra); + } else if(nextra < 0) { + gl->buff_curpos += nextra; + gl_remove_from_buffer(gl, gl->buff_curpos, -nextra); + }; +/* + * Insert the filenames, separated by spaces, and with internal spaces, + * tabs and backslashes escaped with backslashes. + */ + for(i=0,j=start_path - gl->line; infile; i++) { + char *file = result->files[i]; + while(*file) { + int c = *file++; + switch(c) { + case ' ': case '\t': case '\\': case '*': case '?': case '[': + gl_buffer_char(gl, '\\', j++); + }; + gl_buffer_char(gl, c, j++); + }; + gl_buffer_char(gl, ' ', j++); + }; + }; +/* + * Redisplay the part of the line which follows the start of + * the original filename. + */ + if(gl_place_cursor(gl, start_path - gl->line) || + gl_truncate_display(gl) || + gl_print_string(gl, start_path, start_path[length])) + return 1; +/* + * Move the cursor to the end of the expansion. + */ + return gl_place_cursor(gl, (start_path - gl->line) + length); +} +#endif + +#ifndef HIDE_FILE_SYSTEM +/*....................................................................... + * This is the action function that lists glob expansions of the + * filename that precedes the cursor position. It expands ~user/ + * expressions, $envvar expressions, and wildcards. + */ +static KT_KEY_FN(gl_list_glob) +{ + char *start_path; /* The pointer to the start of the pathname in */ + /* gl->line[]. */ + FileExpansion *result; /* The results of the filename expansion */ + int pathlen; /* The length of the pathname being expanded */ +/* + * Locate the start of the filename that precedes the cursor position. + */ + start_path = _pu_start_of_path(gl->line, gl->buff_curpos); + if(!start_path) + return 1; +/* + * Get the length of the string that is to be expanded. + */ + pathlen = gl->buff_curpos - (start_path - gl->line); +/* + * Attempt to expand it. + */ + result = ef_expand_file(gl->ef, start_path, pathlen); +/* + * If there was an error, report it. + */ + if(!result) { + return gl_print_info(gl, ef_last_error(gl->ef), GL_END_INFO); +/* + * If no files matched, report this as well. + */ + } else if(result->nfile == 0 || !result->exists) { + return gl_print_info(gl, "No files match.", GL_END_INFO); +/* + * List the matching expansions. + */ + } else if(gl->echo) { + if(gl_start_newline(gl, 1) || + _ef_output_expansions(result, gl_write_fn, gl, gl->ncolumn)) + return 1; + gl_queue_redisplay(gl); + }; + return 0; +} +#endif + +/*....................................................................... + * Return non-zero if a character should be considered a part of a word. + * + * Input: + * c int The character to be tested. + * Output: + * return int True if the character should be considered part of a word. + */ +static int gl_is_word_char(int c) +{ + return isalnum((int)(unsigned char)c) || strchr(GL_WORD_CHARS, c) != NULL; +} + +/*....................................................................... + * Override the builtin file-completion callback that is bound to the + * "complete_word" action function. + * + * Input: + * gl GetLine * The resource object of the command-line input + * module. + * data void * This is passed to match_fn() whenever it is + * called. It could, for example, point to a + * symbol table where match_fn() could look + * for possible completions. + * match_fn CplMatchFn * The function that will identify the prefix + * to be completed from the input line, and + * report matching symbols. + * Output: + * return int 0 - OK. + * 1 - Error. + */ +int gl_customize_completion(GetLine *gl, void *data, CplMatchFn *match_fn) +{ + sigset_t oldset; /* The signals that were blocked on entry to this function */ +/* + * Check the arguments. + */ + if(!gl || !match_fn) { + if(gl) + _err_record_msg(gl->err, "NULL argument", END_ERR_MSG); + errno = EINVAL; + return 1; + }; +/* + * Temporarily block all signals. + */ + gl_mask_signals(gl, &oldset); +/* + * Record the new completion function and its callback data. + */ + gl->cplfn.fn = match_fn; + gl->cplfn.data = data; +/* + * Restore the process signal mask before returning. + */ + gl_unmask_signals(gl, &oldset); + return 0; +} + +/*....................................................................... + * Change the terminal (or stream) that getline interacts with. + * + * Input: + * gl GetLine * The resource object of the command-line input + * module. + * input_fp FILE * The stdio stream to read from. + * output_fp FILE * The stdio stream to write to. + * term char * The terminal type. This can be NULL if + * either or both of input_fp and output_fp don't + * refer to a terminal. Otherwise it should refer + * to an entry in the terminal information database. + * Output: + * return int 0 - OK. + * 1 - Error. + */ +int gl_change_terminal(GetLine *gl, FILE *input_fp, FILE *output_fp, + const char *term) +{ + sigset_t oldset; /* The signals that were blocked on entry to this function */ + int status; /* The return status of _gl_change_terminal() */ +/* + * Check the arguments. + */ + if(!gl) { + errno = EINVAL; + return 1; + }; +/* + * Block all signals. + */ + if(gl_mask_signals(gl, &oldset)) + return 1; +/* + * Execute the private body of the function while signals are blocked. + */ + status = _gl_change_terminal(gl, input_fp, output_fp, term); +/* + * Restore the process signal mask. + */ + gl_unmask_signals(gl, &oldset); + return status; +} + +/*....................................................................... + * This is the private body of the gl_change_terminal() function. It + * assumes that the caller has checked its arguments and blocked the + * delivery of signals. + */ +static int _gl_change_terminal(GetLine *gl, FILE *input_fp, FILE *output_fp, + const char *term) +{ + int is_term = 0; /* True if both input_fd and output_fd are associated */ + /* with a terminal. */ +/* + * Require that input_fp and output_fp both be valid. + */ + if(!input_fp || !output_fp) { + gl_print_info(gl, "Can't change terminal. Bad input/output stream(s).", + GL_END_INFO); + return 1; + }; +/* + * Are we displacing an existing terminal (as opposed to setting the + * initial terminal)? + */ + if(gl->input_fd >= 0) { +/* + * Make sure to leave the previous terminal in a usable state. + */ + if(_gl_normal_io(gl)) + return 1; +/* + * Remove the displaced terminal from the list of fds to watch. + */ +#ifdef HAVE_SELECT + FD_CLR(gl->input_fd, &gl->rfds); +#endif + }; +/* + * Record the file descriptors and streams. + */ + gl->input_fp = input_fp; + gl->input_fd = fileno(input_fp); + gl->output_fp = output_fp; + gl->output_fd = fileno(output_fp); +/* + * If needed, expand the record of the maximum file-descriptor that might + * need to be monitored with select(). + */ +#ifdef HAVE_SELECT + if(gl->input_fd > gl->max_fd) + gl->max_fd = gl->input_fd; +#endif +/* + * Disable terminal interaction until we have enough info to interact + * with the terminal. + */ + gl->is_term = 0; +/* + * For terminal editing, we need both output_fd and input_fd to refer to + * a terminal. While we can't verify that they both point to the same + * terminal, we can verify that they point to terminals. If the user + * sets the TERM environment variable to "dumb", treat a terminal as + * a non-interactive I/O stream. + */ + is_term = (isatty(gl->input_fd) && isatty(gl->output_fd)) && + !(term && strcmp(term, "dumb")==0); +/* + * If we are interacting with a terminal and no terminal type has been + * specified, treat it as a generic ANSI terminal. + */ + if(is_term && !term) + term = "ansi"; +/* + * Make a copy of the terminal type string. + */ + if(term != gl->term) { +/* + * Delete any old terminal type string. + */ + if(gl->term) { + free(gl->term); + gl->term = NULL; + }; +/* + * Make a copy of the new terminal-type string, if any. + */ + if(term) { + gl->term = (char *) malloc(strlen(term)+1); + if(gl->term) + strcpy(gl->term, term); + }; + }; +/* + * Clear any terminal-specific key bindings that were taken from the + * settings of the last terminal. + */ + _kt_clear_bindings(gl->bindings, KTB_TERM); +/* + * If we have a terminal install new bindings for it. + */ + if(is_term) { +/* + * Get the current settings of the terminal. + */ + if(tcgetattr(gl->input_fd, &gl->oldattr)) { + _err_record_msg(gl->err, "tcgetattr error", END_ERR_MSG); + return 1; + }; +/* + * If we don't set this now, gl_control_strings() won't know + * that it is talking to a terminal. + */ + gl->is_term = 1; +/* + * Lookup the terminal control string and size information. + */ + if(gl_control_strings(gl, term)) { + gl->is_term = 0; + return 1; + }; +/* + * Bind terminal-specific keys. + */ + if(gl_bind_terminal_keys(gl)) + return 1; + }; +/* + * Assume that the caller has given us a terminal in a sane state. + */ + gl->io_mode = GL_NORMAL_MODE; +/* + * Switch into the currently configured I/O mode. + */ + if(_gl_io_mode(gl, gl->io_mode)) + return 1; + return 0; +} + +/*....................................................................... + * Set up terminal-specific key bindings. + * + * Input: + * gl GetLine * The resource object of the command-line input + * module. + * Output: + * return int 0 - OK. + * 1 - Error. + */ +static int gl_bind_terminal_keys(GetLine *gl) +{ +/* + * Install key-bindings for the special terminal characters. + */ + if(gl_bind_control_char(gl, KTB_TERM, gl->oldattr.c_cc[VINTR], + "user-interrupt") || + gl_bind_control_char(gl, KTB_TERM, gl->oldattr.c_cc[VQUIT], "abort") || + gl_bind_control_char(gl, KTB_TERM, gl->oldattr.c_cc[VSUSP], "suspend")) + return 1; +/* + * In vi-mode, arrange for the above characters to be seen in command + * mode. + */ + if(gl->editor == GL_VI_MODE) { + if(gl_bind_control_char(gl, KTB_TERM, MAKE_META(gl->oldattr.c_cc[VINTR]), + "user-interrupt") || + gl_bind_control_char(gl, KTB_TERM, MAKE_META(gl->oldattr.c_cc[VQUIT]), + "abort") || + gl_bind_control_char(gl, KTB_TERM, MAKE_META(gl->oldattr.c_cc[VSUSP]), + "suspend")) + return 1; + }; +/* + * Non-universal special keys. + */ +#ifdef VLNEXT + if(gl_bind_control_char(gl, KTB_TERM, gl->oldattr.c_cc[VLNEXT], + "literal-next")) + return 1; +#else + if(_kt_set_keybinding(gl->bindings, KTB_TERM, "^V", "literal-next")) { + _err_record_msg(gl->err, _kt_last_error(gl->bindings), END_ERR_MSG); + return 1; + }; +#endif +/* + * Bind action functions to the terminal-specific arrow keys + * looked up by gl_control_strings(). + */ + if(_gl_bind_arrow_keys(gl)) + return 1; + return 0; +} + +/*....................................................................... + * This function is normally bound to control-D. When it is invoked within + * a line it deletes the character which follows the cursor. When invoked + * at the end of the line it lists possible file completions, and when + * invoked on an empty line it causes gl_get_line() to return EOF. This + * function emulates the one that is normally bound to control-D by tcsh. + */ +static KT_KEY_FN(gl_del_char_or_list_or_eof) +{ +/* + * If we have an empty line arrange to return EOF. + */ + if(gl->ntotal < 1) { + gl_record_status(gl, GLR_EOF, 0); + return 1; +/* + * If we are at the end of the line list possible completions. + */ + } else if(gl->buff_curpos >= gl->ntotal) { + return gl_list_completions(gl, 1, NULL); +/* + * Within the line delete the character that follows the cursor. + */ + } else { +/* + * If in vi command mode, first preserve the current line for potential use + * by vi-undo. + */ + gl_save_for_undo(gl); +/* + * Delete 'count' characters. + */ + return gl_forward_delete_char(gl, count, NULL); + }; +} + +/*....................................................................... + * This function is normally bound to control-D in vi mode. When it is + * invoked within a line it lists possible file completions, and when + * invoked on an empty line it causes gl_get_line() to return EOF. This + * function emulates the one that is normally bound to control-D by tcsh. + */ +static KT_KEY_FN(gl_list_or_eof) +{ +/* + * If we have an empty line arrange to return EOF. + */ + if(gl->ntotal < 1) { + gl_record_status(gl, GLR_EOF, 0); + return 1; +/* + * Otherwise list possible completions. + */ + } else { + return gl_list_completions(gl, 1, NULL); + }; +} + +/*....................................................................... + * List possible completions of the word that precedes the cursor. The + * callback data argument must either be NULL to select the default + * file completion callback, or be a GlCplCallback object containing the + * completion callback function to call. + */ +static KT_KEY_FN(gl_list_completions) +{ + int waserr = 0; /* True after errors */ +/* + * Get the container of the completion callback and its callback data. + */ + GlCplCallback *cb = data ? (GlCplCallback *) data : &gl->cplfn; +/* + * Get the list of possible completions. + */ + CplMatches *matches = cpl_complete_word(gl->cpl, gl->line, gl->buff_curpos, + cb->data, cb->fn); +/* + * No matching completions? + */ + if(!matches) { + waserr = gl_print_info(gl, cpl_last_error(gl->cpl), GL_END_INFO); +/* + * List the matches. + */ + } else if(matches->nmatch > 0 && gl->echo) { + if(_gl_normal_io(gl) || + _cpl_output_completions(matches, gl_write_fn, gl, gl->ncolumn)) + waserr = 1; + }; +/* + * If any output had to be written to the terminal, then editing will + * have been suspended, make sure that we are back in raw line editing + * mode before returning. + */ + if(_gl_raw_io(gl, 1)) + waserr = 1; + return waserr; +} + +/*....................................................................... + * Where the user has used the symbolic arrow-key names to specify + * arrow key bindings, bind the specified action functions to the default + * and terminal specific arrow key sequences. + * + * Input: + * gl GetLine * The getline resource object. + * Output: + * return int 0 - OK. + * 1 - Error. + */ +static int _gl_bind_arrow_keys(GetLine *gl) +{ +/* + * Process each of the arrow keys. + */ + if(_gl_rebind_arrow_key(gl, "up", gl->u_arrow, "^[[A", "^[OA") || + _gl_rebind_arrow_key(gl, "down", gl->d_arrow, "^[[B", "^[OB") || + _gl_rebind_arrow_key(gl, "left", gl->l_arrow, "^[[D", "^[OD") || + _gl_rebind_arrow_key(gl, "right", gl->r_arrow, "^[[C", "^[OC")) + return 1; + return 0; +} + +/*....................................................................... + * Lookup the action function of a symbolic arrow-key binding, and bind + * it to the terminal-specific and default arrow-key sequences. Note that + * we don't trust the terminal-specified key sequences to be correct. + * The main reason for this is that on some machines the xterm terminfo + * entry is for hardware X-terminals, rather than xterm terminal emulators + * and the two terminal types emit different character sequences when the + * their cursor keys are pressed. As a result we also supply a couple + * of default key sequences. + * + * Input: + * gl GetLine * The resource object of gl_get_line(). + * name char * The symbolic name of the arrow key. + * term_seq char * The terminal-specific arrow-key sequence. + * def_seq1 char * The first default arrow-key sequence. + * def_seq2 char * The second arrow-key sequence. + * Output: + * return int 0 - OK. + * 1 - Error. + */ +static int _gl_rebind_arrow_key(GetLine *gl, const char *name, + const char *term_seq, const char *def_seq1, + const char *def_seq2) +{ + KeySym *keysym; /* The binding-table entry matching the arrow-key name */ + int nsym; /* The number of ambiguous matches */ +/* + * Lookup the key binding for the symbolic name of the arrow key. This + * will either be the default action, or a user provided one. + */ + if(_kt_lookup_keybinding(gl->bindings, name, strlen(name), &keysym, &nsym) + == KT_EXACT_MATCH) { +/* + * Get the action function. + */ + KtAction *action = keysym->actions + keysym->binder; + KtKeyFn *fn = action->fn; + void *data = action->data; +/* + * Bind this to each of the specified key sequences. + */ + if((term_seq && + _kt_set_keyfn(gl->bindings, KTB_TERM, term_seq, fn, data)) || + (def_seq1 && + _kt_set_keyfn(gl->bindings, KTB_NORM, def_seq1, fn, data)) || + (def_seq2 && + _kt_set_keyfn(gl->bindings, KTB_NORM, def_seq2, fn, data))) { + _err_record_msg(gl->err, _kt_last_error(gl->bindings), END_ERR_MSG); + return 1; + }; + }; + return 0; +} + +/*....................................................................... + * Read getline configuration information from a given file. + * + * Input: + * gl GetLine * The getline resource object. + * filename const char * The name of the file to read configuration + * information from. The contents of this file + * are as described in the gl_get_line(3) man + * page for the default ~/.teclarc configuration + * file. + * who KtBinder Who bindings are to be installed for. + * Output: + * return int 0 - OK. + * 1 - Irrecoverable error. + */ +static int _gl_read_config_file(GetLine *gl, const char *filename, KtBinder who) +{ +/* + * If filesystem access is to be excluded, configuration files can't + * be read. + */ +#ifdef WITHOUT_FILE_SYSTEM + _err_record_msg(gl->err, + "Can't read configuration files without filesystem access", + END_ERR_MSG); + errno = EINVAL; + return 1; +#else + FileExpansion *expansion; /* The expansion of the filename */ + FILE *fp; /* The opened file */ + int waserr = 0; /* True if an error occurred while reading */ + int lineno = 1; /* The line number being processed */ +/* + * Check the arguments. + */ + if(!gl || !filename) { + if(gl) + _err_record_msg(gl->err, "NULL argument(s)", END_ERR_MSG); + errno = EINVAL; + return 1; + }; +/* + * Expand the filename. + */ + expansion = ef_expand_file(gl->ef, filename, -1); + if(!expansion) { + gl_print_info(gl, "Unable to expand ", filename, " (", + ef_last_error(gl->ef), ").", GL_END_INFO); + return 1; + }; +/* + * Attempt to open the file. + */ + fp = fopen(expansion->files[0], "r"); +/* + * It isn't an error for there to be no configuration file. + */ + if(!fp) + return 0; +/* + * Parse the contents of the file. + */ + while(!waserr && !feof(fp)) + waserr = _gl_parse_config_line(gl, fp, glc_file_getc, filename, who, + &lineno); +/* + * Bind action functions to the terminal-specific arrow keys. + */ + if(_gl_bind_arrow_keys(gl)) + return 1; +/* + * Clean up. + */ + (void) fclose(fp); + return waserr; +#endif +} + +/*....................................................................... + * Read GetLine configuration information from a string. The contents of + * the string are the same as those described in the gl_get_line(3) + * man page for the contents of the ~/.teclarc configuration file. + */ +static int _gl_read_config_string(GetLine *gl, const char *buffer, KtBinder who) +{ + const char *bptr; /* A pointer into buffer[] */ + int waserr = 0; /* True if an error occurred while reading */ + int lineno = 1; /* The line number being processed */ +/* + * Check the arguments. + */ + if(!gl || !buffer) { + if(gl) + _err_record_msg(gl->err, "NULL argument(s)", END_ERR_MSG); + errno = EINVAL; + return 1; + }; +/* + * Get a pointer to the start of the buffer. + */ + bptr = buffer; +/* + * Parse the contents of the buffer. + */ + while(!waserr && *bptr) + waserr = _gl_parse_config_line(gl, &bptr, glc_buff_getc, "", who, &lineno); +/* + * Bind action functions to the terminal-specific arrow keys. + */ + if(_gl_bind_arrow_keys(gl)) + return 1; + return waserr; +} + +/*....................................................................... + * Parse the next line of a getline configuration file. + * + * Input: + * gl GetLine * The getline resource object. + * stream void * The pointer representing the stream to be read + * by getc_fn(). + * getc_fn GlcGetcFn * A callback function which when called with + * 'stream' as its argument, returns the next + * unread character from the stream. + * origin const char * The name of the entity being read (eg. a + * file name). + * who KtBinder Who bindings are to be installed for. + * Input/Output: + * lineno int * The line number being processed is to be + * maintained in *lineno. + * Output: + * return int 0 - OK. + * 1 - Irrecoverable error. + */ +static int _gl_parse_config_line(GetLine *gl, void *stream, GlcGetcFn *getc_fn, + const char *origin, KtBinder who, int *lineno) +{ + char buffer[GL_CONF_BUFLEN+1]; /* The input line buffer */ + char *argv[GL_CONF_MAXARG]; /* The argument list */ + int argc = 0; /* The number of arguments in argv[] */ + int c; /* A character from the file */ + int escaped = 0; /* True if the next character is escaped */ + int i; +/* + * Skip spaces and tabs. + */ + do c = getc_fn(stream); while(c==' ' || c=='\t'); +/* + * Comments extend to the end of the line. + */ + if(c=='#') + do c = getc_fn(stream); while(c != '\n' && c != EOF); +/* + * Ignore empty lines. + */ + if(c=='\n' || c==EOF) { + (*lineno)++; + return 0; + }; +/* + * Record the buffer location of the start of the first argument. + */ + argv[argc] = buffer; +/* + * Read the rest of the line, stopping early if a comment is seen, or + * the buffer overflows, and replacing sequences of spaces with a + * '\0', and recording the thus terminated string as an argument. + */ + i = 0; + while(i= GL_CONF_MAXARG) { + gl_report_config_error(gl, origin, *lineno, "Too many arguments."); + do c = getc_fn(stream); while(c!='\n' && c!=EOF); /* Skip past eol */ + return 0; + }; + argv[argc] = buffer + i; +/* + * The next character was preceded by spaces, so it isn't escaped. + */ + escaped = 0; + } else { +/* + * If we hit an unescaped backslash, this means that we should arrange + * to treat the next character like a simple alphabetical character. + */ + if(c=='\\' && !escaped) { + escaped = 1; +/* + * Splice lines where the newline is escaped. + */ + } else if(c=='\n' && escaped) { + (*lineno)++; +/* + * Record a normal character, preserving any preceding backslash. + */ + } else { + if(escaped) + buffer[i++] = '\\'; + if(i>=GL_CONF_BUFLEN) + break; + escaped = 0; + buffer[i++] = c; + }; +/* + * Get the next character. + */ + c = getc_fn(stream); + }; + }; +/* + * Did the buffer overflow? + */ + if(i>=GL_CONF_BUFLEN) { + gl_report_config_error(gl, origin, *lineno, "Line too long."); + return 0; + }; +/* + * The first argument should be a command name. + */ + if(strcmp(argv[0], "bind") == 0) { + const char *action = NULL; /* A NULL action removes a keybinding */ + const char *keyseq = NULL; + switch(argc) { + case 3: + action = argv[2]; + case 2: /* Note the intentional fallthrough */ + keyseq = argv[1]; +/* + * Attempt to record the new keybinding. + */ + if(_kt_set_keybinding(gl->bindings, who, keyseq, action)) { + gl_report_config_error(gl, origin, *lineno, + _kt_last_error(gl->bindings)); + }; + break; + default: + gl_report_config_error(gl, origin, *lineno, "Wrong number of arguments."); + }; + } else if(strcmp(argv[0], "edit-mode") == 0) { + if(argc == 2 && strcmp(argv[1], "emacs") == 0) { + gl_change_editor(gl, GL_EMACS_MODE); + } else if(argc == 2 && strcmp(argv[1], "vi") == 0) { + gl_change_editor(gl, GL_VI_MODE); + } else if(argc == 2 && strcmp(argv[1], "none") == 0) { + gl_change_editor(gl, GL_NO_EDITOR); + } else { + gl_report_config_error(gl, origin, *lineno, + "The argument of editor should be vi or emacs."); + }; + } else if(strcmp(argv[0], "nobeep") == 0) { + gl->silence_bell = 1; + } else { + gl_report_config_error(gl, origin, *lineno, "Unknown command name."); + }; +/* + * Skip any trailing comment. + */ + while(c != '\n' && c != EOF) + c = getc_fn(stream); + (*lineno)++; + return 0; +} + +/*....................................................................... + * This is a private function of _gl_parse_config_line() which prints + * out an error message about the contents of the line, prefixed by the + * name of the origin of the line and its line number. + * + * Input: + * gl GetLine * The resource object of gl_get_line(). + * origin const char * The name of the entity being read (eg. a + * file name). + * lineno int The line number at which the error occurred. + * errmsg const char * The error message. + * Output: + * return int 0 - OK. + * 1 - Error. + */ +static int gl_report_config_error(GetLine *gl, const char *origin, int lineno, + const char *errmsg) +{ + char lnum[20]; /* A buffer in which to render a single integer */ +/* + * Convert the line number into a string. + */ + sprintf(lnum, "%d", lineno); +/* + * Have the string printed on the terminal. + */ + return gl_print_info(gl, origin, ":", lnum, ": ", errmsg, GL_END_INFO); +} + +/*....................................................................... + * This is the _gl_parse_config_line() callback function which reads the + * next character from a configuration file. + */ +static GLC_GETC_FN(glc_file_getc) +{ + return fgetc((FILE *) stream); +} + +/*....................................................................... + * This is the _gl_parse_config_line() callback function which reads the + * next character from a buffer. Its stream argument is a pointer to a + * variable which is, in turn, a pointer into the buffer being read from. + */ +static GLC_GETC_FN(glc_buff_getc) +{ + const char **lptr = (char const **) stream; + return **lptr ? *(*lptr)++ : EOF; +} + +#ifndef HIDE_FILE_SYSTEM +/*....................................................................... + * When this action is triggered, it arranges to temporarily read command + * lines from the regular file whos name precedes the cursor. + * The current line is first discarded. + */ +static KT_KEY_FN(gl_read_from_file) +{ + char *start_path; /* The pointer to the start of the pathname in */ + /* gl->line[]. */ + FileExpansion *result; /* The results of the filename expansion */ + int pathlen; /* The length of the pathname being expanded */ +/* + * Locate the start of the filename that precedes the cursor position. + */ + start_path = _pu_start_of_path(gl->line, gl->buff_curpos); + if(!start_path) + return 1; +/* + * Get the length of the pathname string. + */ + pathlen = gl->buff_curpos - (start_path - gl->line); +/* + * Attempt to expand the pathname. + */ + result = ef_expand_file(gl->ef, start_path, pathlen); +/* + * If there was an error, report the error on a new line. + */ + if(!result) { + return gl_print_info(gl, ef_last_error(gl->ef), GL_END_INFO); +/* + * If no files matched, report this as well. + */ + } else if(result->nfile == 0 || !result->exists) { + return gl_print_info(gl, "No files match.", GL_END_INFO); +/* + * Complain if more than one file matches. + */ + } else if(result->nfile > 1) { + return gl_print_info(gl, "More than one file matches.", GL_END_INFO); +/* + * Disallow input from anything but normal files. In principle we could + * also support input from named pipes. Terminal files would be a problem + * since we wouldn't know the terminal type, and other types of files + * might cause the library to lock up. + */ + } else if(!_pu_path_is_file(result->files[0])) { + return gl_print_info(gl, "Not a normal file.", GL_END_INFO); + } else { +/* + * Attempt to open and install the specified file for reading. + */ + gl->file_fp = fopen(result->files[0], "r"); + if(!gl->file_fp) { + return gl_print_info(gl, "Unable to open: ", result->files[0], + GL_END_INFO); + }; +/* + * If needed, expand the record of the maximum file-descriptor that might + * need to be monitored with select(). + */ +#ifdef HAVE_SELECT + if(fileno(gl->file_fp) > gl->max_fd) + gl->max_fd = fileno(gl->file_fp); +#endif +/* + * Is non-blocking I/O needed? + */ + if(gl->raw_mode && gl->io_mode==GL_SERVER_MODE && + gl_nonblocking_io(gl, fileno(gl->file_fp))) { + gl_revert_input(gl); + return gl_print_info(gl, "Can't read file %s with non-blocking I/O", + result->files[0]); + }; +/* + * Inform the user what is happening. + */ + if(gl_print_info(gl, "files[0], ">", + GL_END_INFO)) + return 1; + }; + return 0; +} +#endif + +/*....................................................................... + * Close any temporary file that is being used for input. + * + * Input: + * gl GetLine * The getline resource object. + */ +static void gl_revert_input(GetLine *gl) +{ + if(gl->file_fp) + fclose(gl->file_fp); + gl->file_fp = NULL; + gl->endline = 1; +} + +/*....................................................................... + * This is the action function that recalls the oldest line in the + * history buffer. + */ +static KT_KEY_FN(gl_beginning_of_history) +{ +/* + * In vi mode, switch to command mode, since the user is very + * likely to want to move around newly recalled lines. + */ + gl_vi_command_mode(gl); +/* + * Forget any previous recall session. + */ + gl->preload_id = 0; +/* + * Record the key sequence number of this search action. + */ + gl->last_search = gl->keyseq_count; +/* + * Recall the next oldest line in the history list. + */ + if(_glh_oldest_line(gl->glh, gl->line, gl->linelen+1) == NULL) + return 0; +/* + * Accomodate the new contents of gl->line[]. + */ + gl_update_buffer(gl); +/* + * Arrange to have the cursor placed at the end of the new line. + */ + gl->buff_curpos = gl->ntotal; +/* + * Erase and display the new line. + */ + gl_queue_redisplay(gl); + return 0; +} + +/*....................................................................... + * If a history session is currently in progress, this action function + * recalls the line that was being edited when the session started. If + * no history session is in progress, it does nothing. + */ +static KT_KEY_FN(gl_end_of_history) +{ +/* + * In vi mode, switch to command mode, since the user is very + * likely to want to move around newly recalled lines. + */ + gl_vi_command_mode(gl); +/* + * Forget any previous recall session. + */ + gl->preload_id = 0; +/* + * Record the key sequence number of this search action. + */ + gl->last_search = gl->keyseq_count; +/* + * Recall the next oldest line in the history list. + */ + if(_glh_current_line(gl->glh, gl->line, gl->linelen+1) == NULL) + return 0; +/* + * Accomodate the new contents of gl->line[]. + */ + gl_update_buffer(gl); +/* + * Arrange to have the cursor placed at the end of the new line. + */ + gl->buff_curpos = gl->ntotal; +/* + * Erase and display the new line. + */ + gl_queue_redisplay(gl); + return 0; +} + +/*....................................................................... + * This action function is treated specially, in that its count argument + * is set to the end keystroke of the keysequence that activated it. + * It accumulates a numeric argument, adding one digit on each call in + * which the last keystroke was a numeric digit. + */ +static KT_KEY_FN(gl_digit_argument) +{ +/* + * Was the last keystroke a digit? + */ + int is_digit = isdigit((int)(unsigned char) count); +/* + * In vi command mode, a lone '0' means goto-start-of-line. + */ + if(gl->vi.command && gl->number < 0 && count == '0') + return gl_beginning_of_line(gl, count, NULL); +/* + * Are we starting to accumulate a new number? + */ + if(gl->number < 0 || !is_digit) + gl->number = 0; +/* + * Was the last keystroke a digit? + */ + if(is_digit) { +/* + * Read the numeric value of the digit, without assuming ASCII. + */ + int n; + char s[2]; s[0] = count; s[1] = '\0'; + n = atoi(s); +/* + * Append the new digit. + */ + gl->number = gl->number * 10 + n; + }; + return 0; +} + +/*....................................................................... + * The newline action function sets gl->endline to tell + * gl_get_input_line() that the line is now complete. + */ +static KT_KEY_FN(gl_newline) +{ + GlhLineID id; /* The last history line recalled while entering this line */ +/* + * Flag the line as ended. + */ + gl->endline = 1; +/* + * Record the next position in the history buffer, for potential + * recall by an action function on the next call to gl_get_line(). + */ + id = _glh_line_id(gl->glh, 1); + if(id) + gl->preload_id = id; + return 0; +} + +/*....................................................................... + * The 'repeat' action function sets gl->endline to tell + * gl_get_input_line() that the line is now complete, and records the + * ID of the next history line in gl->preload_id so that the next call + * to gl_get_input_line() will preload the line with that history line. + */ +static KT_KEY_FN(gl_repeat_history) +{ + gl->endline = 1; + gl->preload_id = _glh_line_id(gl->glh, 1); + gl->preload_history = 1; + return 0; +} + +/*....................................................................... + * Flush unwritten characters to the terminal. + * + * Input: + * gl GetLine * The getline resource object. + * Output: + * return int 0 - OK. + * 1 - Either an error occured, or the output + * blocked and non-blocking I/O is being used. + * See gl->rtn_status for details. + */ +static int gl_flush_output(GetLine *gl) +{ +/* + * Record the fact that we are about to write to the terminal. + */ + gl->pending_io = GLP_WRITE; +/* + * Attempt to flush the output to the terminal. + */ + errno = 0; + switch(_glq_flush_queue(gl->cq, gl->flush_fn, gl)) { + case GLQ_FLUSH_DONE: + return gl->redisplay && !gl->postpone && gl_redisplay(gl, 1, NULL); + break; + case GLQ_FLUSH_AGAIN: /* Output blocked */ + gl_record_status(gl, GLR_BLOCKED, BLOCKED_ERRNO); + return 1; + break; + default: /* Abort the line if an error occurs */ + gl_record_status(gl, errno==EINTR ? GLR_SIGNAL : GLR_ERROR, errno); + return 1; + break; + }; +} + +/*....................................................................... + * This is the callback which _glq_flush_queue() uses to write buffered + * characters to the terminal. + */ +static GL_WRITE_FN(gl_flush_terminal) +{ + int ndone = 0; /* The number of characters written so far */ +/* + * Get the line-editor resource object. + */ + GetLine *gl = (GetLine *) data; +/* + * Transfer the latest array of characters to stdio. + */ + while(ndone < n) { + int nnew = write(gl->output_fd, s, n-ndone); +/* + * If the write was successful, add to the recorded number of bytes + * that have now been written. + */ + if(nnew > 0) { + ndone += nnew; +/* + * If a signal interrupted the call, restart the write(), since all of + * the signals that gl_get_line() has been told to watch for are + * currently blocked. + */ + } else if(errno == EINTR) { + continue; +/* + * If we managed to write something before an I/O error occurred, or + * output blocked before anything was written, report the number of + * bytes that were successfully written before this happened. + */ + } else if(ndone > 0 +#if defined(EAGAIN) + || errno==EAGAIN +#endif +#if defined(EWOULDBLOCK) + || errno==EWOULDBLOCK +#endif + ) { + return ndone; + +/* + * To get here, an error must have occurred before anything new could + * be written. + */ + } else { + return -1; + }; + }; +/* + * To get here, we must have successfully written the number of + * bytes that was specified. + */ + return n; +} + +/*....................................................................... + * Change the style of editing to emulate a given editor. + * + * Input: + * gl GetLine * The getline resource object. + * editor GlEditor The type of editor to emulate. + * Output: + * return int 0 - OK. + * 1 - Error. + */ +static int gl_change_editor(GetLine *gl, GlEditor editor) +{ +/* + * Install the default key-bindings of the requested editor. + */ + switch(editor) { + case GL_EMACS_MODE: + _kt_clear_bindings(gl->bindings, KTB_NORM); + _kt_clear_bindings(gl->bindings, KTB_TERM); + (void) _kt_add_bindings(gl->bindings, KTB_NORM, gl_emacs_bindings, + sizeof(gl_emacs_bindings)/sizeof(gl_emacs_bindings[0])); + break; + case GL_VI_MODE: + _kt_clear_bindings(gl->bindings, KTB_NORM); + _kt_clear_bindings(gl->bindings, KTB_TERM); + (void) _kt_add_bindings(gl->bindings, KTB_NORM, gl_vi_bindings, + sizeof(gl_vi_bindings)/sizeof(gl_vi_bindings[0])); + break; + case GL_NO_EDITOR: + break; + default: + _err_record_msg(gl->err, "Unknown editor", END_ERR_MSG); + errno = EINVAL; + return 1; + }; +/* + * Record the new editing mode. + */ + gl->editor = editor; + gl->vi.command = 0; /* Start in input mode */ + gl->insert_curpos = 0; +/* + * Reinstate terminal-specific bindings. + */ + if(gl->editor != GL_NO_EDITOR && gl->input_fp) + (void) gl_bind_terminal_keys(gl); + return 0; +} + +/*....................................................................... + * This is an action function that switches to editing using emacs bindings + */ +static KT_KEY_FN(gl_emacs_editing_mode) +{ + return gl_change_editor(gl, GL_EMACS_MODE); +} + +/*....................................................................... + * This is an action function that switches to editing using vi bindings + */ +static KT_KEY_FN(gl_vi_editing_mode) +{ + return gl_change_editor(gl, GL_VI_MODE); +} + +/*....................................................................... + * This is the action function that switches to insert mode. + */ +static KT_KEY_FN(gl_vi_insert) +{ +/* + * If in vi command mode, preserve the current line for potential + * use by vi-undo. + */ + gl_save_for_undo(gl); +/* + * Switch to vi insert mode. + */ + gl->insert = 1; + gl->vi.command = 0; + gl->insert_curpos = gl->buff_curpos; + return 0; +} + +/*....................................................................... + * This is an action function that switches to overwrite mode. + */ +static KT_KEY_FN(gl_vi_overwrite) +{ +/* + * If in vi command mode, preserve the current line for potential + * use by vi-undo. + */ + gl_save_for_undo(gl); +/* + * Switch to vi overwrite mode. + */ + gl->insert = 0; + gl->vi.command = 0; + gl->insert_curpos = gl->buff_curpos; + return 0; +} + +/*....................................................................... + * This action function toggles the case of the character under the + * cursor. + */ +static KT_KEY_FN(gl_change_case) +{ + int i; +/* + * Keep a record of the current insert mode and the cursor position. + */ + int insert = gl->insert; +/* + * If in vi command mode, preserve the current line for potential + * use by vi-undo. + */ + gl_save_for_undo(gl); +/* + * We want to overwrite the modified word. + */ + gl->insert = 0; +/* + * Toggle the case of 'count' characters. + */ + for(i=0; ibuff_curpos < gl->ntotal; i++) { + char *cptr = gl->line + gl->buff_curpos++; +/* + * Convert the character to upper case? + */ + if(islower((int)(unsigned char) *cptr)) + gl_buffer_char(gl, toupper((int) *cptr), cptr - gl->line); + else if(isupper((int)(unsigned char) *cptr)) + gl_buffer_char(gl, tolower((int) *cptr), cptr - gl->line); +/* + * Write the possibly modified character back. Note that for non-modified + * characters we want to do this as well, so as to advance the cursor. + */ + if(gl_print_char(gl, *cptr, cptr[1])) + return 1; + }; +/* + * Restore the insertion mode. + */ + gl->insert = insert; + return gl_place_cursor(gl, gl->buff_curpos); /* bounds check */ +} + +/*....................................................................... + * This is the action function which implements the vi-style action which + * moves the cursor to the start of the line, then switches to insert mode. + */ +static KT_KEY_FN(gl_vi_insert_at_bol) +{ + gl_save_for_undo(gl); + return gl_beginning_of_line(gl, 0, NULL) || + gl_vi_insert(gl, 0, NULL); + +} + +/*....................................................................... + * This is the action function which implements the vi-style action which + * moves the cursor to the end of the line, then switches to insert mode + * to allow text to be appended to the line. + */ +static KT_KEY_FN(gl_vi_append_at_eol) +{ + gl_save_for_undo(gl); + gl->vi.command = 0; /* Allow cursor at EOL */ + return gl_end_of_line(gl, 0, NULL) || + gl_vi_insert(gl, 0, NULL); +} + +/*....................................................................... + * This is the action function which implements the vi-style action which + * moves the cursor to right one then switches to insert mode, thus + * allowing text to be appended after the next character. + */ +static KT_KEY_FN(gl_vi_append) +{ + gl_save_for_undo(gl); + gl->vi.command = 0; /* Allow cursor at EOL */ + return gl_cursor_right(gl, 1, NULL) || + gl_vi_insert(gl, 0, NULL); +} + +/*....................................................................... + * This action function moves the cursor to the column specified by the + * numeric argument. Column indexes start at 1. + */ +static KT_KEY_FN(gl_goto_column) +{ + return gl_place_cursor(gl, count - 1); +} + +/*....................................................................... + * Starting with the character under the cursor, replace 'count' + * characters with the next character that the user types. + */ +static KT_KEY_FN(gl_vi_replace_char) +{ + char c; /* The replacement character */ + int i; +/* + * Keep a record of the current insert mode. + */ + int insert = gl->insert; +/* + * Get the replacement character. + */ + if(gl->vi.repeat.active) { + c = gl->vi.repeat.input_char; + } else { + if(gl_read_terminal(gl, 1, &c)) + return 1; + gl->vi.repeat.input_char = c; + }; +/* + * Are there 'count' characters to be replaced? + */ + if(gl->ntotal - gl->buff_curpos >= count) { +/* + * If in vi command mode, preserve the current line for potential + * use by vi-undo. + */ + gl_save_for_undo(gl); +/* + * Temporarily switch to overwrite mode. + */ + gl->insert = 0; +/* + * Overwrite the current character plus count-1 subsequent characters + * with the replacement character. + */ + for(i=0; iinsert = insert; + }; + return gl_place_cursor(gl, gl->buff_curpos); /* bounds check */ +} + +/*....................................................................... + * This is an action function which changes all characters between the + * current cursor position and the end of the line. + */ +static KT_KEY_FN(gl_vi_change_rest_of_line) +{ + gl_save_for_undo(gl); + gl->vi.command = 0; /* Allow cursor at EOL */ + return gl_kill_line(gl, count, NULL) || gl_vi_insert(gl, 0, NULL); +} + +/*....................................................................... + * This is an action function which changes all characters between the + * start of the line and the current cursor position. + */ +static KT_KEY_FN(gl_vi_change_to_bol) +{ + return gl_backward_kill_line(gl,count,NULL) || gl_vi_insert(gl,0,NULL); +} + +/*....................................................................... + * This is an action function which deletes the entire contents of the + * current line and switches to insert mode. + */ +static KT_KEY_FN(gl_vi_change_line) +{ + return gl_delete_line(gl,count,NULL) || gl_vi_insert(gl,0,NULL); +} + +/*....................................................................... + * Starting from the cursor position and looking towards the end of the + * line, copy 'count' characters to the cut buffer. + */ +static KT_KEY_FN(gl_forward_copy_char) +{ +/* + * Limit the count to the number of characters available. + */ + if(gl->buff_curpos + count >= gl->ntotal) + count = gl->ntotal - gl->buff_curpos; + if(count < 0) + count = 0; +/* + * Copy the characters to the cut buffer. + */ + memcpy(gl->cutbuf, gl->line + gl->buff_curpos, count); + gl->cutbuf[count] = '\0'; + return 0; +} + +/*....................................................................... + * Starting from the character before the cursor position and looking + * backwards towards the start of the line, copy 'count' characters to + * the cut buffer. + */ +static KT_KEY_FN(gl_backward_copy_char) +{ +/* + * Limit the count to the number of characters available. + */ + if(count > gl->buff_curpos) + count = gl->buff_curpos; + if(count < 0) + count = 0; + gl_place_cursor(gl, gl->buff_curpos - count); +/* + * Copy the characters to the cut buffer. + */ + memcpy(gl->cutbuf, gl->line + gl->buff_curpos, count); + gl->cutbuf[count] = '\0'; + return 0; +} + +/*....................................................................... + * Starting from the cursor position copy to the specified column into the + * cut buffer. + */ +static KT_KEY_FN(gl_copy_to_column) +{ + if (--count >= gl->buff_curpos) + return gl_forward_copy_char(gl, count - gl->buff_curpos, NULL); + else + return gl_backward_copy_char(gl, gl->buff_curpos - count, NULL); +} + +/*....................................................................... + * Starting from the cursor position copy characters up to a matching + * parenthesis into the cut buffer. + */ +static KT_KEY_FN(gl_copy_to_parenthesis) +{ + int curpos = gl_index_of_matching_paren(gl); + if(curpos >= 0) { + gl_save_for_undo(gl); + if(curpos >= gl->buff_curpos) + return gl_forward_copy_char(gl, curpos - gl->buff_curpos + 1, NULL); + else + return gl_backward_copy_char(gl, ++gl->buff_curpos - curpos + 1, NULL); + }; + return 0; +} + +/*....................................................................... + * Starting from the cursor position copy the rest of the line into the + * cut buffer. + */ +static KT_KEY_FN(gl_copy_rest_of_line) +{ +/* + * Copy the characters to the cut buffer. + */ + memcpy(gl->cutbuf, gl->line + gl->buff_curpos, gl->ntotal - gl->buff_curpos); + gl->cutbuf[gl->ntotal - gl->buff_curpos] = '\0'; + return 0; +} + +/*....................................................................... + * Copy from the beginning of the line to the cursor position into the + * cut buffer. + */ +static KT_KEY_FN(gl_copy_to_bol) +{ +/* + * Copy the characters to the cut buffer. + */ + memcpy(gl->cutbuf, gl->line, gl->buff_curpos); + gl->cutbuf[gl->buff_curpos] = '\0'; + gl_place_cursor(gl, 0); + return 0; +} + +/*....................................................................... + * Copy the entire line into the cut buffer. + */ +static KT_KEY_FN(gl_copy_line) +{ +/* + * Copy the characters to the cut buffer. + */ + memcpy(gl->cutbuf, gl->line, gl->ntotal); + gl->cutbuf[gl->ntotal] = '\0'; + return 0; +} + +/*....................................................................... + * Search forwards for the next character that the user enters. + */ +static KT_KEY_FN(gl_forward_find_char) +{ + int pos = gl_find_char(gl, count, 1, 1, '\0'); + return pos >= 0 && gl_place_cursor(gl, pos); +} + +/*....................................................................... + * Search backwards for the next character that the user enters. + */ +static KT_KEY_FN(gl_backward_find_char) +{ + int pos = gl_find_char(gl, count, 0, 1, '\0'); + return pos >= 0 && gl_place_cursor(gl, pos); +} + +/*....................................................................... + * Search forwards for the next character that the user enters. Move up to, + * but not onto, the found character. + */ +static KT_KEY_FN(gl_forward_to_char) +{ + int pos = gl_find_char(gl, count, 1, 0, '\0'); + return pos >= 0 && gl_place_cursor(gl, pos); +} + +/*....................................................................... + * Search backwards for the next character that the user enters. Move back to, + * but not onto, the found character. + */ +static KT_KEY_FN(gl_backward_to_char) +{ + int pos = gl_find_char(gl, count, 0, 0, '\0'); + return pos >= 0 && gl_place_cursor(gl, pos); +} + +/*....................................................................... + * Searching in a given direction, return the index of a given (or + * read) character in the input line, or the character that precedes + * it in the specified search direction. Return -1 if not found. + * + * Input: + * gl GetLine * The getline resource object. + * count int The number of times to search. + * forward int True if searching forward. + * onto int True if the search should end on top of the + * character, false if the search should stop + * one character before the character in the + * specified search direction. + * c char The character to be sought, or '\0' if the + * character should be read from the user. + * Output: + * return int The index of the character in gl->line[], or + * -1 if not found. + */ +static int gl_find_char(GetLine *gl, int count, int forward, int onto, char c) +{ + int pos; /* The index reached in searching the input line */ + int i; +/* + * Get a character from the user? + */ + if(!c) { +/* + * If we are in the process of repeating a previous change command, substitute + * the last find character. + */ + if(gl->vi.repeat.active) { + c = gl->vi.find_char; + } else { + if(gl_read_terminal(gl, 1, &c)) + return -1; +/* + * Record the details of the new search, for use by repeat finds. + */ + gl->vi.find_forward = forward; + gl->vi.find_onto = onto; + gl->vi.find_char = c; + }; + }; +/* + * Which direction should we search? + */ + if(forward) { +/* + * Search forwards 'count' times for the character, starting with the + * character that follows the cursor. + */ + for(i=0, pos=gl->buff_curpos; intotal; i++) { +/* + * Advance past the last match (or past the current cursor position + * on the first search). + */ + pos++; +/* + * Search for the next instance of c. + */ + for( ; posntotal && c!=gl->line[pos]; pos++) + ; + }; +/* + * If the character was found and we have been requested to return the + * position of the character that precedes the desired character, then + * we have gone one character too far. + */ + if(!onto && posntotal) + pos--; + } else { +/* + * Search backwards 'count' times for the character, starting with the + * character that precedes the cursor. + */ + for(i=0, pos=gl->buff_curpos; i= gl->insert_curpos; i++) { +/* + * Step back one from the last match (or from the current cursor + * position on the first search). + */ + pos--; +/* + * Search for the next instance of c. + */ + for( ; pos>=gl->insert_curpos && c!=gl->line[pos]; pos--) + ; + }; +/* + * If the character was found and we have been requested to return the + * position of the character that precedes the desired character, then + * we have gone one character too far. + */ + if(!onto && pos>=gl->insert_curpos) + pos++; + }; +/* + * If found, return the cursor position of the count'th match. + * Otherwise ring the terminal bell. + */ + if(pos >= gl->insert_curpos && pos < gl->ntotal) { + return pos; + } else { + (void) gl_ring_bell(gl, 1, NULL); + return -1; + } +} + +/*....................................................................... + * Repeat the last character search in the same direction as the last + * search. + */ +static KT_KEY_FN(gl_repeat_find_char) +{ + int pos = gl->vi.find_char ? + gl_find_char(gl, count, gl->vi.find_forward, gl->vi.find_onto, + gl->vi.find_char) : -1; + return pos >= 0 && gl_place_cursor(gl, pos); +} + +/*....................................................................... + * Repeat the last character search in the opposite direction as the last + * search. + */ +static KT_KEY_FN(gl_invert_refind_char) +{ + int pos = gl->vi.find_char ? + gl_find_char(gl, count, !gl->vi.find_forward, gl->vi.find_onto, + gl->vi.find_char) : -1; + return pos >= 0 && gl_place_cursor(gl, pos); +} + +/*....................................................................... + * Search forward from the current position of the cursor for 'count' + * word endings, returning the index of the last one found, or the end of + * the line if there were less than 'count' words. + * + * Input: + * gl GetLine * The getline resource object. + * n int The number of word boundaries to search for. + * Output: + * return int The buffer index of the located position. + */ +static int gl_nth_word_end_forward(GetLine *gl, int n) +{ + int bufpos; /* The buffer index being checked. */ + int i; +/* + * In order to guarantee forward motion to the next word ending, + * we need to start from one position to the right of the cursor + * position, since this may already be at the end of a word. + */ + bufpos = gl->buff_curpos + 1; +/* + * If we are at the end of the line, return the index of the last + * real character on the line. Note that this will be -1 if the line + * is empty. + */ + if(bufpos >= gl->ntotal) + return gl->ntotal - 1; +/* + * Search 'n' times, unless the end of the input line is reached first. + */ + for(i=0; intotal; i++) { +/* + * If we are not already within a word, skip to the start of the next word. + */ + for( ; bufposntotal && !gl_is_word_char((int)gl->line[bufpos]); + bufpos++) + ; +/* + * Find the end of the next word. + */ + for( ; bufposntotal && gl_is_word_char((int)gl->line[bufpos]); + bufpos++) + ; + }; +/* + * We will have overshot. + */ + return bufpos > 0 ? bufpos-1 : bufpos; +} + +/*....................................................................... + * Search forward from the current position of the cursor for 'count' + * word starts, returning the index of the last one found, or the end of + * the line if there were less than 'count' words. + * + * Input: + * gl GetLine * The getline resource object. + * n int The number of word boundaries to search for. + * Output: + * return int The buffer index of the located position. + */ +static int gl_nth_word_start_forward(GetLine *gl, int n) +{ + int bufpos; /* The buffer index being checked. */ + int i; +/* + * Get the current cursor position. + */ + bufpos = gl->buff_curpos; +/* + * Search 'n' times, unless the end of the input line is reached first. + */ + for(i=0; intotal; i++) { +/* + * Find the end of the current word. + */ + for( ; bufposntotal && gl_is_word_char((int)gl->line[bufpos]); + bufpos++) + ; +/* + * Skip to the start of the next word. + */ + for( ; bufposntotal && !gl_is_word_char((int)gl->line[bufpos]); + bufpos++) + ; + }; + return bufpos; +} + +/*....................................................................... + * Search backward from the current position of the cursor for 'count' + * word starts, returning the index of the last one found, or the start + * of the line if there were less than 'count' words. + * + * Input: + * gl GetLine * The getline resource object. + * n int The number of word boundaries to search for. + * Output: + * return int The buffer index of the located position. + */ +static int gl_nth_word_start_backward(GetLine *gl, int n) +{ + int bufpos; /* The buffer index being checked. */ + int i; +/* + * Get the current cursor position. + */ + bufpos = gl->buff_curpos; +/* + * Search 'n' times, unless the beginning of the input line (or vi insertion + * point) is reached first. + */ + for(i=0; i gl->insert_curpos; i++) { +/* + * Starting one character back from the last search, so as not to keep + * settling on the same word-start, search backwards until finding a + * word character. + */ + while(--bufpos >= gl->insert_curpos && + !gl_is_word_char((int)gl->line[bufpos])) + ; +/* + * Find the start of the word. + */ + while(--bufpos >= gl->insert_curpos && + gl_is_word_char((int)gl->line[bufpos])) + ; +/* + * We will have gone one character too far. + */ + bufpos++; + }; + return bufpos >= gl->insert_curpos ? bufpos : gl->insert_curpos; +} + +/*....................................................................... + * Copy one or more words into the cut buffer without moving the cursor + * or deleting text. + */ +static KT_KEY_FN(gl_forward_copy_word) +{ +/* + * Find the location of the count'th start or end of a word + * after the cursor, depending on whether in emacs or vi mode. + */ + int next = gl->editor == GL_EMACS_MODE ? + gl_nth_word_end_forward(gl, count) : + gl_nth_word_start_forward(gl, count); +/* + * How many characters are to be copied into the cut buffer? + */ + int n = next - gl->buff_curpos; +/* + * Copy the specified segment and terminate the string. + */ + memcpy(gl->cutbuf, gl->line + gl->buff_curpos, n); + gl->cutbuf[n] = '\0'; + return 0; +} + +/*....................................................................... + * Copy one or more words preceding the cursor into the cut buffer, + * without moving the cursor or deleting text. + */ +static KT_KEY_FN(gl_backward_copy_word) +{ +/* + * Find the location of the count'th start of word before the cursor. + */ + int next = gl_nth_word_start_backward(gl, count); +/* + * How many characters are to be copied into the cut buffer? + */ + int n = gl->buff_curpos - next; + gl_place_cursor(gl, next); +/* + * Copy the specified segment and terminate the string. + */ + memcpy(gl->cutbuf, gl->line + next, n); + gl->cutbuf[n] = '\0'; + return 0; +} + +/*....................................................................... + * Copy the characters between the cursor and the count'th instance of + * a specified character in the input line, into the cut buffer. + * + * Input: + * gl GetLine * The getline resource object. + * count int The number of times to search. + * c char The character to be searched for, or '\0' if + * the character should be read from the user. + * forward int True if searching forward. + * onto int True if the search should end on top of the + * character, false if the search should stop + * one character before the character in the + * specified search direction. + * Output: + * return int 0 - OK. + * 1 - Error. + * + */ +static int gl_copy_find(GetLine *gl, int count, char c, int forward, int onto) +{ + int n; /* The number of characters in the cut buffer */ +/* + * Search for the character, and abort the operation if not found. + */ + int pos = gl_find_char(gl, count, forward, onto, c); + if(pos < 0) + return 0; +/* + * Copy the specified segment. + */ + if(forward) { + n = pos + 1 - gl->buff_curpos; + memcpy(gl->cutbuf, gl->line + gl->buff_curpos, n); + } else { + n = gl->buff_curpos - pos; + memcpy(gl->cutbuf, gl->line + pos, n); + if(gl->editor == GL_VI_MODE) + gl_place_cursor(gl, pos); + } +/* + * Terminate the copy. + */ + gl->cutbuf[n] = '\0'; + return 0; +} + +/*....................................................................... + * Copy a section up to and including a specified character into the cut + * buffer without moving the cursor or deleting text. + */ +static KT_KEY_FN(gl_forward_copy_find) +{ + return gl_copy_find(gl, count, '\0', 1, 1); +} + +/*....................................................................... + * Copy a section back to and including a specified character into the cut + * buffer without moving the cursor or deleting text. + */ +static KT_KEY_FN(gl_backward_copy_find) +{ + return gl_copy_find(gl, count, '\0', 0, 1); +} + +/*....................................................................... + * Copy a section up to and not including a specified character into the cut + * buffer without moving the cursor or deleting text. + */ +static KT_KEY_FN(gl_forward_copy_to) +{ + return gl_copy_find(gl, count, '\0', 1, 0); +} + +/*....................................................................... + * Copy a section back to and not including a specified character into the cut + * buffer without moving the cursor or deleting text. + */ +static KT_KEY_FN(gl_backward_copy_to) +{ + return gl_copy_find(gl, count, '\0', 0, 0); +} + +/*....................................................................... + * Copy to a character specified in a previous search into the cut + * buffer without moving the cursor or deleting text. + */ +static KT_KEY_FN(gl_copy_refind) +{ + return gl_copy_find(gl, count, gl->vi.find_char, gl->vi.find_forward, + gl->vi.find_onto); +} + +/*....................................................................... + * Copy to a character specified in a previous search, but in the opposite + * direction, into the cut buffer without moving the cursor or deleting text. + */ +static KT_KEY_FN(gl_copy_invert_refind) +{ + return gl_copy_find(gl, count, gl->vi.find_char, !gl->vi.find_forward, + gl->vi.find_onto); +} + +/*....................................................................... + * Set the position of the cursor in the line input buffer and the + * terminal. + * + * Input: + * gl GetLine * The getline resource object. + * buff_curpos int The new buffer cursor position. + * Output: + * return int 0 - OK. + * 1 - Error. + */ +static int gl_place_cursor(GetLine *gl, int buff_curpos) +{ +/* + * Don't allow the cursor position to go out of the bounds of the input + * line. + */ + if(buff_curpos >= gl->ntotal) + buff_curpos = gl->vi.command ? gl->ntotal-1 : gl->ntotal; + if(buff_curpos < 0) + buff_curpos = 0; +/* + * Record the new buffer position. + */ + gl->buff_curpos = buff_curpos; +/* + * Move the terminal cursor to the corresponding character. + */ + return gl_set_term_curpos(gl, gl->prompt_len + + gl_displayed_string_width(gl, gl->line, buff_curpos, gl->prompt_len)); +} + +/*....................................................................... + * In vi command mode, this function saves the current line to the + * historical buffer needed by the undo command. In emacs mode it does + * nothing. In order to allow action functions to call other action + * functions, gl_interpret_char() sets gl->vi.undo.saved to 0 before + * invoking an action, and thereafter once any call to this function + * has set it to 1, further calls are ignored. + * + * Input: + * gl GetLine * The getline resource object. + */ +static void gl_save_for_undo(GetLine *gl) +{ + if(gl->vi.command && !gl->vi.undo.saved) { + strcpy(gl->vi.undo.line, gl->line); + gl->vi.undo.buff_curpos = gl->buff_curpos; + gl->vi.undo.ntotal = gl->ntotal; + gl->vi.undo.saved = 1; + }; + if(gl->vi.command && !gl->vi.repeat.saved && + gl->current_action.fn != gl_vi_repeat_change) { + gl->vi.repeat.action = gl->current_action; + gl->vi.repeat.count = gl->current_count; + gl->vi.repeat.saved = 1; + }; + return; +} + +/*....................................................................... + * In vi mode, restore the line to the way it was before the last command + * mode operation, storing the current line in the buffer so that the + * undo operation itself can subsequently be undone. + */ +static KT_KEY_FN(gl_vi_undo) +{ +/* + * Get pointers into the two lines. + */ + char *undo_ptr = gl->vi.undo.line; + char *line_ptr = gl->line; +/* + * Swap the characters of the two buffers up to the length of the shortest + * line. + */ + while(*undo_ptr && *line_ptr) { + char c = *undo_ptr; + *undo_ptr++ = *line_ptr; + *line_ptr++ = c; + }; +/* + * Copy the rest directly. + */ + if(gl->ntotal > gl->vi.undo.ntotal) { + strcpy(undo_ptr, line_ptr); + *line_ptr = '\0'; + } else { + strcpy(line_ptr, undo_ptr); + *undo_ptr = '\0'; + }; +/* + * Record the length of the stored string. + */ + gl->vi.undo.ntotal = gl->ntotal; +/* + * Accomodate the new contents of gl->line[]. + */ + gl_update_buffer(gl); +/* + * Set both cursor positions to the leftmost of the saved and current + * cursor positions to emulate what vi does. + */ + if(gl->buff_curpos < gl->vi.undo.buff_curpos) + gl->vi.undo.buff_curpos = gl->buff_curpos; + else + gl->buff_curpos = gl->vi.undo.buff_curpos; +/* + * Since we have bipassed calling gl_save_for_undo(), record repeat + * information inline. + */ + gl->vi.repeat.action.fn = gl_vi_undo; + gl->vi.repeat.action.data = NULL; + gl->vi.repeat.count = 1; +/* + * Display the restored line. + */ + gl_queue_redisplay(gl); + return 0; +} + +/*....................................................................... + * Delete the following word and leave the user in vi insert mode. + */ +static KT_KEY_FN(gl_vi_forward_change_word) +{ + gl_save_for_undo(gl); + gl->vi.command = 0; /* Allow cursor at EOL */ + return gl_forward_delete_word(gl, count, NULL) || gl_vi_insert(gl, 0, NULL); +} + +/*....................................................................... + * Delete the preceding word and leave the user in vi insert mode. + */ +static KT_KEY_FN(gl_vi_backward_change_word) +{ + return gl_backward_delete_word(gl, count, NULL) || gl_vi_insert(gl, 0, NULL); +} + +/*....................................................................... + * Delete the following section and leave the user in vi insert mode. + */ +static KT_KEY_FN(gl_vi_forward_change_find) +{ + return gl_delete_find(gl, count, '\0', 1, 1, 1); +} + +/*....................................................................... + * Delete the preceding section and leave the user in vi insert mode. + */ +static KT_KEY_FN(gl_vi_backward_change_find) +{ + return gl_delete_find(gl, count, '\0', 0, 1, 1); +} + +/*....................................................................... + * Delete the following section and leave the user in vi insert mode. + */ +static KT_KEY_FN(gl_vi_forward_change_to) +{ + return gl_delete_find(gl, count, '\0', 1, 0, 1); +} + +/*....................................................................... + * Delete the preceding section and leave the user in vi insert mode. + */ +static KT_KEY_FN(gl_vi_backward_change_to) +{ + return gl_delete_find(gl, count, '\0', 0, 0, 1); +} + +/*....................................................................... + * Delete to a character specified by a previous search and leave the user + * in vi insert mode. + */ +static KT_KEY_FN(gl_vi_change_refind) +{ + return gl_delete_find(gl, count, gl->vi.find_char, gl->vi.find_forward, + gl->vi.find_onto, 1); +} + +/*....................................................................... + * Delete to a character specified by a previous search, but in the opposite + * direction, and leave the user in vi insert mode. + */ +static KT_KEY_FN(gl_vi_change_invert_refind) +{ + return gl_delete_find(gl, count, gl->vi.find_char, !gl->vi.find_forward, + gl->vi.find_onto, 1); +} + +/*....................................................................... + * Delete the following character and leave the user in vi insert mode. + */ +static KT_KEY_FN(gl_vi_forward_change_char) +{ + gl_save_for_undo(gl); + gl->vi.command = 0; /* Allow cursor at EOL */ + return gl_delete_chars(gl, count, 1) || gl_vi_insert(gl, 0, NULL); +} + +/*....................................................................... + * Delete the preceding character and leave the user in vi insert mode. + */ +static KT_KEY_FN(gl_vi_backward_change_char) +{ + return gl_backward_delete_char(gl, count, NULL) || gl_vi_insert(gl, 0, NULL); +} + +/*....................................................................... + * Starting from the cursor position change characters to the specified column. + */ +static KT_KEY_FN(gl_vi_change_to_column) +{ + if (--count >= gl->buff_curpos) + return gl_vi_forward_change_char(gl, count - gl->buff_curpos, NULL); + else + return gl_vi_backward_change_char(gl, gl->buff_curpos - count, NULL); +} + +/*....................................................................... + * Starting from the cursor position change characters to a matching + * parenthesis. + */ +static KT_KEY_FN(gl_vi_change_to_parenthesis) +{ + int curpos = gl_index_of_matching_paren(gl); + if(curpos >= 0) { + gl_save_for_undo(gl); + if(curpos >= gl->buff_curpos) + return gl_vi_forward_change_char(gl, curpos - gl->buff_curpos + 1, NULL); + else + return gl_vi_backward_change_char(gl, ++gl->buff_curpos - curpos + 1, + NULL); + }; + return 0; +} + +/*....................................................................... + * If in vi mode, switch to vi command mode. + * + * Input: + * gl GetLine * The getline resource object. + */ +static void gl_vi_command_mode(GetLine *gl) +{ + if(gl->editor == GL_VI_MODE && !gl->vi.command) { + gl->insert = 1; + gl->vi.command = 1; + gl->vi.repeat.input_curpos = gl->insert_curpos; + gl->vi.repeat.command_curpos = gl->buff_curpos; + gl->insert_curpos = 0; /* unrestrict left motion boundary */ + gl_cursor_left(gl, 1, NULL); /* Vi moves 1 left on entering command mode */ + }; +} + +/*....................................................................... + * This is an action function which rings the terminal bell. + */ +static KT_KEY_FN(gl_ring_bell) +{ + return gl->silence_bell ? 0 : + gl_print_control_sequence(gl, 1, gl->sound_bell); +} + +/*....................................................................... + * This is the action function which implements the vi-repeat-change + * action. + */ +static KT_KEY_FN(gl_vi_repeat_change) +{ + int status; /* The return status of the repeated action function */ + int i; +/* + * Nothing to repeat? + */ + if(!gl->vi.repeat.action.fn) + return gl_ring_bell(gl, 1, NULL); +/* + * Provide a way for action functions to know whether they are being + * called by us. + */ + gl->vi.repeat.active = 1; +/* + * Re-run the recorded function. + */ + status = gl->vi.repeat.action.fn(gl, gl->vi.repeat.count, + gl->vi.repeat.action.data); +/* + * Mark the repeat as completed. + */ + gl->vi.repeat.active = 0; +/* + * Is we are repeating a function that has just switched to input + * mode to allow the user to type, re-enter the text that the user + * previously entered. + */ + if(status==0 && !gl->vi.command) { +/* + * Make sure that the current line has been saved. + */ + gl_save_for_undo(gl); +/* + * Repeat a previous insertion or overwrite? + */ + if(gl->vi.repeat.input_curpos >= 0 && + gl->vi.repeat.input_curpos <= gl->vi.repeat.command_curpos && + gl->vi.repeat.command_curpos <= gl->vi.undo.ntotal) { +/* + * Using the current line which is saved in the undo buffer, plus + * the range of characters therein, as recorded by gl_vi_command_mode(), + * add the characters that the user previously entered, to the input + * line. + */ + for(i=gl->vi.repeat.input_curpos; ivi.repeat.command_curpos; i++) { + if(gl_add_char_to_line(gl, gl->vi.undo.line[i])) + return 1; + }; + }; +/* + * Switch back to command mode, now that the insertion has been repeated. + */ + gl_vi_command_mode(gl); + }; + return status; +} + +/*....................................................................... + * If the cursor is currently over a parenthesis character, return the + * index of its matching parenthesis. If not currently over a parenthesis + * character, return the next close parenthesis character to the right of + * the cursor. If the respective parenthesis character isn't found, + * ring the terminal bell and return -1. + * + * Input: + * gl GetLine * The getline resource object. + * Output: + * return int Either the index of the matching parenthesis, + * or -1 if not found. + */ +static int gl_index_of_matching_paren(GetLine *gl) +{ + int i; +/* + * List the recognized parentheses, and their matches. + */ + const char *o_paren = "([{"; + const char *c_paren = ")]}"; + const char *cptr; +/* + * Get the character that is currently under the cursor. + */ + char c = gl->line[gl->buff_curpos]; +/* + * If the character under the cursor is an open parenthesis, look forward + * for the matching close parenthesis. + */ + if((cptr=strchr(o_paren, c))) { + char match = c_paren[cptr - o_paren]; + int matches_needed = 1; + for(i=gl->buff_curpos+1; intotal; i++) { + if(gl->line[i] == c) + matches_needed++; + else if(gl->line[i] == match && --matches_needed==0) + return i; + }; +/* + * If the character under the cursor is an close parenthesis, look forward + * for the matching open parenthesis. + */ + } else if((cptr=strchr(c_paren, c))) { + char match = o_paren[cptr - c_paren]; + int matches_needed = 1; + for(i=gl->buff_curpos-1; i>=0; i--) { + if(gl->line[i] == c) + matches_needed++; + else if(gl->line[i] == match && --matches_needed==0) + return i; + }; +/* + * If not currently over a parenthesis character, search forwards for + * the first close parenthesis (this is what the vi % binding does). + */ + } else { + for(i=gl->buff_curpos+1; intotal; i++) + if(strchr(c_paren, gl->line[i]) != NULL) + return i; + }; +/* + * Not found. + */ + (void) gl_ring_bell(gl, 1, NULL); + return -1; +} + +/*....................................................................... + * If the cursor is currently over a parenthesis character, this action + * function moves the cursor to its matching parenthesis. + */ +static KT_KEY_FN(gl_find_parenthesis) +{ + int curpos = gl_index_of_matching_paren(gl); + if(curpos >= 0) + return gl_place_cursor(gl, curpos); + return 0; +} + +/*....................................................................... + * Handle the receipt of the potential start of a new key-sequence from + * the user. + * + * Input: + * gl GetLine * The resource object of this library. + * first_char char The first character of the sequence. + * Output: + * return int 0 - OK. + * 1 - Error. + */ +static int gl_interpret_char(GetLine *gl, char first_char) +{ + char keyseq[GL_KEY_MAX+1]; /* A special key sequence being read */ + int nkey=0; /* The number of characters in the key sequence */ + int count; /* The repeat count of an action function */ + int ret; /* The return value of an action function */ + int i; +/* + * Get the first character. + */ + char c = first_char; +/* + * If editing is disabled, just add newly entered characters to the + * input line buffer, and watch for the end of the line. + */ + if(gl->editor == GL_NO_EDITOR) { + gl_discard_chars(gl, 1); + if(gl->ntotal >= gl->linelen) + return 0; + if(c == '\n' || c == '\r') + return gl_newline(gl, 1, NULL); + gl_buffer_char(gl, c, gl->ntotal); + return 0; + }; +/* + * If the user is in the process of specifying a repeat count and the + * new character is a digit, increment the repeat count accordingly. + */ + if(gl->number >= 0 && isdigit((int)(unsigned char) c)) { + gl_discard_chars(gl, 1); + return gl_digit_argument(gl, c, NULL); +/* + * In vi command mode, all key-sequences entered need to be + * either implicitly or explicitly prefixed with an escape character. + */ + } else if(gl->vi.command && c != GL_ESC_CHAR) { + keyseq[nkey++] = GL_ESC_CHAR; +/* + * If the first character of the sequence is a printable character, + * then to avoid confusion with the special "up", "down", "left" + * or "right" cursor key bindings, we need to prefix the + * printable character with a backslash escape before looking it up. + */ + } else if(!IS_META_CHAR(c) && !IS_CTRL_CHAR(c)) { + keyseq[nkey++] = '\\'; + }; +/* + * Compose a potentially multiple key-sequence in gl->keyseq. + */ + while(nkey < GL_KEY_MAX) { + KtAction *action; /* An action function */ + KeySym *keysym; /* The symbol-table entry of a key-sequence */ + int nsym; /* The number of ambiguously matching key-sequences */ +/* + * If the character is an unprintable meta character, split it + * into two characters, an escape character and the character + * that was modified by the meta key. + */ + if(IS_META_CHAR(c)) { + keyseq[nkey++] = GL_ESC_CHAR; + c = META_TO_CHAR(c); + continue; + }; +/* + * Append the latest character to the key sequence. + */ + keyseq[nkey++] = c; +/* + * When doing vi-style editing, an escape at the beginning of any binding + * switches to command mode. + */ + if(keyseq[0] == GL_ESC_CHAR && !gl->vi.command) + gl_vi_command_mode(gl); +/* + * Lookup the key sequence. + */ + switch(_kt_lookup_keybinding(gl->bindings, keyseq, nkey, &keysym, &nsym)) { + case KT_EXACT_MATCH: +/* + * Get the matching action function. + */ + action = keysym->actions + keysym->binder; +/* + * Get the repeat count, passing the last keystroke if executing the + * digit-argument action. + */ + if(action->fn == gl_digit_argument) { + count = c; + } else { + count = gl->number >= 0 ? gl->number : 1; + }; +/* + * Record the function that is being invoked. + */ + gl->current_action = *action; + gl->current_count = count; +/* + * Mark the current line as not yet preserved for use by the vi undo command. + */ + gl->vi.undo.saved = 0; + gl->vi.repeat.saved = 0; +/* + * Execute the action function. Note the action function can tell + * whether the provided repeat count was defaulted or specified + * explicitly by looking at whether gl->number is -1 or not. If + * it is negative, then no repeat count was specified by the user. + */ + ret = action->fn(gl, count, action->data); +/* + * In server mode, the action will return immediately if it tries to + * read input from the terminal, and no input is currently available. + * If this happens, abort. Note that gl_get_input_line() will rewind + * the read-ahead buffer to allow the next call to redo the function + * from scratch. + */ + if(gl->rtn_status == GLR_BLOCKED && gl->pending_io==GLP_READ) + return 1; +/* + * Discard the now processed characters from the key sequence buffer. + */ + gl_discard_chars(gl, gl->nread); +/* + * If the latest action function wasn't a history action, cancel any + * current history search. + */ + if(gl->last_search != gl->keyseq_count) + _glh_cancel_search(gl->glh); +/* + * Reset the repeat count after running action functions. + */ + if(action->fn != gl_digit_argument) + gl->number = -1; + return ret ? 1 : 0; + break; + case KT_AMBIG_MATCH: /* Ambiguous match - so read the next character */ + if(gl_read_terminal(gl, 1, &c)) + return 1; + break; + case KT_NO_MATCH: +/* + * If the first character looked like it might be a prefix of a key-sequence + * but it turned out not to be, ring the bell to tell the user that it + * wasn't recognised. + */ + if(keyseq[0] != '\\' && keyseq[0] != '\t') { + gl_ring_bell(gl, 1, NULL); + } else { +/* + * The user typed a single printable character that doesn't match + * the start of any keysequence, so add it to the line in accordance + * with the current repeat count. + */ + count = gl->number >= 0 ? gl->number : 1; + for(i=0; inumber = -1; + }; + gl_discard_chars(gl, 1); + _glh_cancel_search(gl->glh); + return 0; + break; + case KT_BAD_MATCH: + gl_ring_bell(gl, 1, NULL); + gl_discard_chars(gl, gl->nread); + _glh_cancel_search(gl->glh); + return 1; + break; + }; + }; +/* + * If the key sequence was too long to match, ring the bell, then + * discard the first character, so that the next attempt to match a + * key-sequence continues with the next key press. In practice this + * shouldn't happen, since one isn't allowed to bind action functions + * to keysequences that are longer than GL_KEY_MAX. + */ + gl_ring_bell(gl, 1, NULL); + gl_discard_chars(gl, 1); + return 0; +} + +/*....................................................................... + * Configure the application and/or user-specific behavior of + * gl_get_line(). + * + * Note that calling this function between calling new_GetLine() and + * the first call to gl_get_line(), disables the otherwise automatic + * reading of ~/.teclarc on the first call to gl_get_line(). + * + * Input: + * gl GetLine * The resource object of this library. + * app_string const char * Either NULL, or a string containing one + * or more .teclarc command lines, separated + * by newline characters. This can be used to + * establish an application-specific + * configuration, without the need for an external + * file. This is particularly useful in embedded + * environments where there is no filesystem. + * app_file const char * Either NULL, or the pathname of an + * application-specific .teclarc file. The + * contents of this file, if provided, are + * read after the contents of app_string[]. + * user_file const char * Either NULL, or the pathname of a + * user-specific .teclarc file. Except in + * embedded applications, this should + * usually be "~/.teclarc". + * Output: + * return int 0 - OK. + * 1 - Bad argument(s). + */ +int gl_configure_getline(GetLine *gl, const char *app_string, + const char *app_file, const char *user_file) +{ + sigset_t oldset; /* The signals that were blocked on entry to this function */ + int status; /* The return status of _gl_configure_getline() */ +/* + * Check the arguments. + */ + if(!gl) { + errno = EINVAL; + return 1; + }; +/* + * Block all signals. + */ + if(gl_mask_signals(gl, &oldset)) + return 1; +/* + * Execute the private body of the function while signals are blocked. + */ + status = _gl_configure_getline(gl, app_string, app_file, user_file); +/* + * Restore the process signal mask. + */ + gl_unmask_signals(gl, &oldset); + return status; +} + +/*....................................................................... + * This is the private body of the gl_configure_getline() function. It + * assumes that the caller has checked its arguments and blocked the + * delivery of signals. + */ +static int _gl_configure_getline(GetLine *gl, const char *app_string, + const char *app_file, const char *user_file) +{ +/* + * Mark getline as having been explicitly configured. + */ + gl->configured = 1; +/* + * Start by parsing the configuration string, if provided. + */ + if(app_string) + (void) _gl_read_config_string(gl, app_string, KTB_NORM); +/* + * Now parse the application-specific configuration file, if provided. + */ + if(app_file) + (void) _gl_read_config_file(gl, app_file, KTB_NORM); +/* + * Finally, parse the user-specific configuration file, if provided. + */ + if(user_file) + (void) _gl_read_config_file(gl, user_file, KTB_USER); +/* + * Record the names of the configuration files to allow them to + * be re-read if requested at a later time. + */ + if(gl_record_string(&gl->app_file, app_file) || + gl_record_string(&gl->user_file, user_file)) { + errno = ENOMEM; + _err_record_msg(gl->err, + "Insufficient memory to record tecla configuration file names", + END_ERR_MSG); + return 1; + }; + return 0; +} + +/*....................................................................... + * Replace a malloc'd string (or NULL), with another malloc'd copy of + * a string (or NULL). + * + * Input: + * sptr char ** On input if *sptr!=NULL, *sptr will be + * free'd and *sptr will be set to NULL. Then, + * on output, if string!=NULL a malloc'd copy + * of this string will be assigned to *sptr. + * string const char * The string to be copied, or NULL to simply + * discard any existing string. + * Output: + * return int 0 - OK. + * 1 - Malloc failure (no error message is generated). + */ +static int gl_record_string(char **sptr, const char *string) +{ +/* + * If the original string is the same string, don't do anything. + */ + if(*sptr == string || (*sptr && string && strcmp(*sptr, string)==0)) + return 0; +/* + * Discard any existing cached string. + */ + if(*sptr) { + free(*sptr); + *sptr = NULL; + }; +/* + * Allocate memory for a copy of the specified string. + */ + if(string) { + *sptr = (char *) malloc(strlen(string) + 1); + if(!*sptr) + return 1; +/* + * Copy the string. + */ + strcpy(*sptr, string); + }; + return 0; +} + +#ifndef HIDE_FILE_SYSTEM +/*....................................................................... + * Re-read any application-specific and user-specific files previously + * specified via the gl_configure_getline() function. + */ +static KT_KEY_FN(gl_read_init_files) +{ + return _gl_configure_getline(gl, NULL, gl->app_file, gl->user_file); +} +#endif + +/*....................................................................... + * Save the contents of the history buffer to a given new file. + * + * Input: + * gl GetLine * The resource object of this library. + * filename const char * The name of the new file to write to. + * comment const char * Extra information such as timestamps will + * be recorded on a line started with this + * string, the idea being that the file can + * double as a command file. Specify "" if + * you don't care. + * max_lines int The maximum number of lines to save, or -1 + * to save all of the lines in the history + * list. + * Output: + * return int 0 - OK. + * 1 - Error. + */ +int gl_save_history(GetLine *gl, const char *filename, const char *comment, + int max_lines) +{ + sigset_t oldset; /* The signals that were blocked on entry to this function */ + int status; /* The return status of _gl_save_history() */ +/* + * Check the arguments. + */ + if(!gl || !filename || !comment) { + if(gl) + _err_record_msg(gl->err, "NULL argument(s)", END_ERR_MSG); + errno = EINVAL; + return 1; + }; +/* + * Block all signals. + */ + if(gl_mask_signals(gl, &oldset)) + return 1; +/* + * Execute the private body of the function while signals are blocked. + */ + status = _gl_save_history(gl, filename, comment, max_lines); +/* + * Restore the process signal mask. + */ + gl_unmask_signals(gl, &oldset); + return status; +} + +/*....................................................................... + * This is the private body of the gl_save_history() function. It + * assumes that the caller has checked its arguments and blocked the + * delivery of signals. + */ +static int _gl_save_history(GetLine *gl, const char *filename, + const char *comment, int max_lines) +{ +/* + * If filesystem access is to be excluded, then history files can't + * be written. + */ +#ifdef WITHOUT_FILE_SYSTEM + _err_record_msg(gl->err, "Can't save history without filesystem access", + END_ERR_MSG); + errno = EINVAL; + return 1; +#else + FileExpansion *expansion; /* The expansion of the filename */ +/* + * Expand the filename. + */ + expansion = ef_expand_file(gl->ef, filename, -1); + if(!expansion) { + gl_print_info(gl, "Unable to expand ", filename, " (", + ef_last_error(gl->ef), ").", GL_END_INFO); + return 1; + }; +/* + * Attempt to save to the specified file. + */ + if(_glh_save_history(gl->glh, expansion->files[0], comment, max_lines)) { + _err_record_msg(gl->err, _glh_last_error(gl->glh), END_ERR_MSG); + return 1; + }; + return 0; +#endif +} + +/*....................................................................... + * Restore the contents of the history buffer from a given new file. + * + * Input: + * gl GetLine * The resource object of this library. + * filename const char * The name of the new file to write to. + * comment const char * This must be the same string that was + * passed to gl_save_history() when the file + * was written. + * Output: + * return int 0 - OK. + * 1 - Error. + */ +int gl_load_history(GetLine *gl, const char *filename, const char *comment) +{ + sigset_t oldset; /* The signals that were blocked on entry to this function */ + int status; /* The return status of _gl_load_history() */ +/* + * Check the arguments. + */ + if(!gl || !filename || !comment) { + if(gl) + _err_record_msg(gl->err, "NULL argument(s)", END_ERR_MSG); + errno = EINVAL; + return 1; + }; +/* + * Block all signals. + */ + if(gl_mask_signals(gl, &oldset)) + return 1; +/* + * Execute the private body of the function while signals are blocked. + */ + status = _gl_load_history(gl, filename, comment); +/* + * Restore the process signal mask. + */ + gl_unmask_signals(gl, &oldset); + return status; +} + +/*....................................................................... + * This is the private body of the gl_load_history() function. It + * assumes that the caller has checked its arguments and blocked the + * delivery of signals. + */ +static int _gl_load_history(GetLine *gl, const char *filename, + const char *comment) +{ +/* + * If filesystem access is to be excluded, then history files can't + * be read. + */ +#ifdef WITHOUT_FILE_SYSTEM + _err_record_msg(gl->err, "Can't load history without filesystem access", + END_ERR_MSG); + errno = EINVAL; + return 1; +#else + FileExpansion *expansion; /* The expansion of the filename */ +/* + * Expand the filename. + */ + expansion = ef_expand_file(gl->ef, filename, -1); + if(!expansion) { + gl_print_info(gl, "Unable to expand ", filename, " (", + ef_last_error(gl->ef), ").", GL_END_INFO); + return 1; + }; +/* + * Attempt to load from the specified file. + */ + if(_glh_load_history(gl->glh, expansion->files[0], comment, + gl->cutbuf, gl->linelen+1)) { + _err_record_msg(gl->err, _glh_last_error(gl->glh), END_ERR_MSG); + gl->cutbuf[0] = '\0'; + return 1; + }; + gl->cutbuf[0] = '\0'; + return 0; +#endif +} + +/*....................................................................... + * Where possible, register a function and associated data to be called + * whenever a specified event is seen on a file descriptor. + * + * Input: + * gl GetLine * The resource object of the command-line input + * module. + * fd int The file descriptor to watch. + * event GlFdEvent The type of activity to watch for. + * callback GlFdEventFn * The function to call when the specified + * event occurs. Setting this to 0 removes + * any existing callback. + * data void * A pointer to arbitrary data to pass to the + * callback function. + * Output: + * return int 0 - OK. + * 1 - Either gl==NULL, or this facility isn't + * available on the the host system + * (ie. select() isn't available). No + * error message is generated in the latter + * case. + */ +int gl_watch_fd(GetLine *gl, int fd, GlFdEvent event, + GlFdEventFn *callback, void *data) +{ + sigset_t oldset; /* The signals that were blocked on entry to this function */ + int status; /* The return status of _gl_watch_fd() */ +/* + * Check the arguments. + */ + if(!gl) { + errno = EINVAL; + return 1; + }; + if(fd < 0) { + _err_record_msg(gl->err, "Error: fd < 0", END_ERR_MSG); + errno = EINVAL; + return 1; + }; +/* + * Block all signals. + */ + if(gl_mask_signals(gl, &oldset)) + return 1; +/* + * Execute the private body of the function while signals are blocked. + */ + status = _gl_watch_fd(gl, fd, event, callback, data); +/* + * Restore the process signal mask. + */ + gl_unmask_signals(gl, &oldset); + return status; +} + +/*....................................................................... + * This is the private body of the gl_watch_fd() function. It + * assumes that the caller has checked its arguments and blocked the + * delivery of signals. + */ +static int _gl_watch_fd(GetLine *gl, int fd, GlFdEvent event, + GlFdEventFn *callback, void *data) +#if !defined(HAVE_SELECT) +{return 1;} /* The facility isn't supported on this system */ +#else +{ + GlFdNode *prev; /* The node that precedes 'node' in gl->fd_nodes */ + GlFdNode *node; /* The file-descriptor node being checked */ +/* + * Search the list of already registered fd activity nodes for the specified + * file descriptor. + */ + for(prev=NULL,node=gl->fd_nodes; node && node->fd != fd; + prev=node, node=node->next) + ; +/* + * Hasn't a node been allocated for this fd yet? + */ + if(!node) { +/* + * If there is no callback to record, just ignore the call. + */ + if(!callback) + return 0; +/* + * Allocate the new node. + */ + node = (GlFdNode *) _new_FreeListNode(gl->fd_node_mem); + if(!node) { + errno = ENOMEM; + _err_record_msg(gl->err, "Insufficient memory", END_ERR_MSG); + return 1; + }; +/* + * Prepend the node to the list. + */ + node->next = gl->fd_nodes; + gl->fd_nodes = node; +/* + * Initialize the node. + */ + node->fd = fd; + node->rd.fn = 0; + node->rd.data = NULL; + node->ur = node->wr = node->rd; + }; +/* + * Record the new callback. + */ + switch(event) { + case GLFD_READ: + node->rd.fn = callback; + node->rd.data = data; + if(callback) + FD_SET(fd, &gl->rfds); + else + FD_CLR(fd, &gl->rfds); + break; + case GLFD_WRITE: + node->wr.fn = callback; + node->wr.data = data; + if(callback) + FD_SET(fd, &gl->wfds); + else + FD_CLR(fd, &gl->wfds); + break; + case GLFD_URGENT: + node->ur.fn = callback; + node->ur.data = data; + if(callback) + FD_SET(fd, &gl->ufds); + else + FD_CLR(fd, &gl->ufds); + break; + }; +/* + * Keep a record of the largest file descriptor being watched. + */ + if(fd > gl->max_fd) + gl->max_fd = fd; +/* + * If we are deleting an existing callback, also delete the parent + * activity node if no callbacks are registered to the fd anymore. + */ + if(!callback) { + if(!node->rd.fn && !node->wr.fn && !node->ur.fn) { + if(prev) + prev->next = node->next; + else + gl->fd_nodes = node->next; + node = (GlFdNode *) _del_FreeListNode(gl->fd_node_mem, node); + }; + }; + return 0; +} +#endif + +/*....................................................................... + * On systems with the select() system call, the gl_inactivity_timeout() + * function provides the option of setting (or cancelling) an + * inactivity timeout. Inactivity, in this case, refers both to + * terminal input received from the user, and to I/O on any file + * descriptors registered by calls to gl_watch_fd(). If at any time, + * no activity is seen for the requested time period, the specified + * timeout callback function is called. On returning, this callback + * returns a code which tells gl_get_line() what to do next. Note that + * each call to gl_inactivity_timeout() replaces any previously installed + * timeout callback, and that specifying a callback of 0, turns off + * inactivity timing. + * + * Beware that although the timeout argument includes a nano-second + * component, few computer clocks presently have resolutions finer + * than a few milliseconds, so asking for less than a few milliseconds + * is equivalent to zero on a lot of systems. + * + * Input: + * gl GetLine * The resource object of the command-line input + * module. + * callback GlTimeoutFn * The function to call when the inactivity + * timeout is exceeded. To turn off + * inactivity timeouts altogether, send 0. + * data void * A pointer to arbitrary data to pass to the + * callback function. + * sec unsigned long The number of whole seconds in the timeout. + * nsec unsigned long The fractional number of seconds in the + * timeout, expressed in nano-seconds (see + * the caveat above). + * Output: + * return int 0 - OK. + * 1 - Either gl==NULL, or this facility isn't + * available on the the host system + * (ie. select() isn't available). No + * error message is generated in the latter + * case. + */ +int gl_inactivity_timeout(GetLine *gl, GlTimeoutFn *timeout_fn, void *data, + unsigned long sec, unsigned long nsec) +#if !defined(HAVE_SELECT) +{return 1;} /* The facility isn't supported on this system */ +#else +{ + sigset_t oldset; /* The signals that were blocked on entry to this function */ +/* + * Check the arguments. + */ + if(!gl) { + errno = EINVAL; + return 1; + }; +/* + * Block all signals. + */ + if(gl_mask_signals(gl, &oldset)) + return 1; +/* + * Install a new timeout? + */ + if(timeout_fn) { + gl->timer.dt.tv_sec = sec; + gl->timer.dt.tv_usec = nsec / 1000; + gl->timer.fn = timeout_fn; + gl->timer.data = data; + } else { + gl->timer.fn = 0; + gl->timer.data = NULL; + }; +/* + * Restore the process signal mask. + */ + gl_unmask_signals(gl, &oldset); + return 0; +} +#endif + +/*....................................................................... + * When select() is available, this is a private function of + * gl_read_input() which responds to file-descriptor events registered by + * the caller. Note that it assumes that it is being called from within + * gl_read_input()'s sigsetjump() clause. + * + * Input: + * gl GetLine * The resource object of this module. + * fd int The file descriptor to be watched for user input. + * Output: + * return int 0 - OK. + * 1 - An error occurred. + */ +static int gl_event_handler(GetLine *gl, int fd) +#if !defined(HAVE_SELECT) +{return 0;} +#else +{ +/* + * Set up a zero-second timeout. + */ + struct timeval zero; + zero.tv_sec = zero.tv_usec = 0; +/* + * If at any time no external callbacks remain, quit the loop return, + * so that we can simply wait in read(). This is designed as an + * optimization for when no callbacks have been registered on entry to + * this function, but since callbacks can delete themselves, it can + * also help later. + */ + while(gl->fd_nodes || gl->timer.fn) { + int nready; /* The number of file descriptors that are ready for I/O */ +/* + * Get the set of descriptors to be watched. + */ + fd_set rfds = gl->rfds; + fd_set wfds = gl->wfds; + fd_set ufds = gl->ufds; +/* + * Get the appropriate timeout. + */ + struct timeval dt = gl->timer.fn ? gl->timer.dt : zero; +/* + * Add the specified user-input file descriptor tot he set that is to + * be watched. + */ + FD_SET(fd, &rfds); +/* + * Unblock the signals that we are watching, while select is blocked + * waiting for I/O. + */ + gl_catch_signals(gl); +/* + * Wait for activity on any of the file descriptors. + */ + nready = select(gl->max_fd+1, &rfds, &wfds, &ufds, + (gl->timer.fn || gl->io_mode==GL_SERVER_MODE) ? &dt : NULL); +/* + * We don't want to do a longjmp in the middle of a callback that + * might be modifying global or heap data, so block all the signals + * that we are trapping before executing callback functions. Note that + * the caller will unblock them again when it needs to, so there is + * no need to undo this before returning. + */ + gl_mask_signals(gl, NULL); +/* + * If select() returns but none of the file descriptors are reported + * to have activity, then select() timed out. + */ + if(nready == 0) { +/* + * Note that in non-blocking server mode, the inactivity timer is used + * to allow I/O to block for a specified amount of time, so in this + * mode we return the postponed blocked status when an abort is + * requested. + */ + if(gl_call_timeout_handler(gl)) { + return 1; + } else if(gl->io_mode == GL_SERVER_MODE) { + gl_record_status(gl, GLR_BLOCKED, BLOCKED_ERRNO); + return 1; + }; +/* + * If nready < 0, this means an error occurred. + */ + } else if(nready < 0) { + if(errno != EINTR) { + gl_record_status(gl, GLR_ERROR, errno); + return 1; + }; +/* + * If the user-input file descriptor has data available, return. + */ + } else if(FD_ISSET(fd, &rfds)) { + return 0; +/* + * Check for activity on any of the file descriptors registered by the + * calling application, and call the associated callback functions. + */ + } else { + GlFdNode *node; /* The fd event node being checked */ +/* + * Search the list for the file descriptor that caused select() to return. + */ + for(node=gl->fd_nodes; node; node=node->next) { +/* + * Is there urgent out of band data waiting to be read on fd? + */ + if(node->ur.fn && FD_ISSET(node->fd, &ufds)) { + if(gl_call_fd_handler(gl, &node->ur, node->fd, GLFD_URGENT)) + return 1; + break; /* The callback may have changed the list of nodes */ +/* + * Is the fd readable? + */ + } else if(node->rd.fn && FD_ISSET(node->fd, &rfds)) { + if(gl_call_fd_handler(gl, &node->rd, node->fd, GLFD_READ)) + return 1; + break; /* The callback may have changed the list of nodes */ +/* + * Is the fd writable? + */ + } else if(node->wr.fn && FD_ISSET(node->fd, &wfds)) { + if(gl_call_fd_handler(gl, &node->wr, node->fd, GLFD_WRITE)) + return 1; + break; /* The callback may have changed the list of nodes */ + }; + }; + }; +/* + * Just in case the above event handlers asked for the input line to + * be redrawn, flush any pending output. + */ + if(gl_flush_output(gl)) + return 1; + }; + return 0; +} +#endif + +#if defined(HAVE_SELECT) +/*....................................................................... + * This is a private function of gl_event_handler(), used to call a + * file-descriptor callback. + * + * Input: + * gl GetLine * The resource object of gl_get_line(). + * gfh GlFdHandler * The I/O handler. + * fd int The file-descriptor being reported. + * event GlFdEvent The I/O event being reported. + * Output: + * return int 0 - OK. + * 1 - Error. + */ +static int gl_call_fd_handler(GetLine *gl, GlFdHandler *gfh, int fd, + GlFdEvent event) +{ + Termios attr; /* The terminal attributes */ + int waserr = 0; /* True after any error */ +/* + * Re-enable conversion of newline characters to carriage-return/linefeed, + * so that the callback can write to the terminal without having to do + * anything special. + */ + if(tcgetattr(gl->input_fd, &attr)) { + _err_record_msg(gl->err, "tcgetattr error", END_ERR_MSG); + return 1; + }; + attr.c_oflag |= OPOST; + while(tcsetattr(gl->input_fd, TCSADRAIN, &attr)) { + if(errno != EINTR) { + _err_record_msg(gl->err, "tcsetattr error", END_ERR_MSG); + return 1; + }; + }; +/* + * Invoke the application's callback function. + */ + switch(gfh->fn(gl, gfh->data, fd, event)) { + default: + case GLFD_ABORT: + gl_record_status(gl, GLR_FDABORT, 0); + waserr = 1; + break; + case GLFD_REFRESH: + gl_queue_redisplay(gl); + break; + case GLFD_CONTINUE: + break; + }; +/* + * If the callback function called gl_normal_io(), restore raw mode, + * and queue a redisplay of the input line. + */ + if(!gl->raw_mode) + waserr = waserr || _gl_raw_io(gl, 1); +/* + * Disable conversion of newline characters to carriage-return/linefeed. + */ + attr.c_oflag &= ~(OPOST); + while(tcsetattr(gl->input_fd, TCSADRAIN, &attr)) { + if(errno != EINTR) { + _err_record_msg(gl->err, "tcsetattr error", END_ERR_MSG); + return 1; + }; + }; + return waserr; +} + +/*....................................................................... + * This is a private function of gl_event_handler(), used to call a + * inactivity timer callbacks. + * + * Input: + * gl GetLine * The resource object of gl_get_line(). + * Output: + * return int 0 - OK. + * 1 - Error. + */ +static int gl_call_timeout_handler(GetLine *gl) +{ + Termios attr; /* The terminal attributes */ + int waserr = 0; /* True after any error */ +/* + * Make sure that there is an inactivity timeout callback. + */ + if(!gl->timer.fn) + return 0; +/* + * Re-enable conversion of newline characters to carriage-return/linefeed, + * so that the callback can write to the terminal without having to do + * anything special. + */ + if(tcgetattr(gl->input_fd, &attr)) { + _err_record_msg(gl->err, "tcgetattr error", END_ERR_MSG); + return 1; + }; + attr.c_oflag |= OPOST; + while(tcsetattr(gl->input_fd, TCSADRAIN, &attr)) { + if(errno != EINTR) { + _err_record_msg(gl->err, "tcsetattr error", END_ERR_MSG); + return 1; + }; + }; +/* + * Invoke the application's callback function. + */ + switch(gl->timer.fn(gl, gl->timer.data)) { + default: + case GLTO_ABORT: + gl_record_status(gl, GLR_TIMEOUT, 0); + waserr = 1; + break; + case GLTO_REFRESH: + gl_queue_redisplay(gl); + break; + case GLTO_CONTINUE: + break; + }; +/* + * If the callback function called gl_normal_io(), restore raw mode, + * and queue a redisplay of the input line. + */ + if(!gl->raw_mode) + waserr = waserr || _gl_raw_io(gl, 1); +/* + * Disable conversion of newline characters to carriage-return/linefeed. + */ + attr.c_oflag &= ~(OPOST); + while(tcsetattr(gl->input_fd, TCSADRAIN, &attr)) { + if(errno != EINTR) { + _err_record_msg(gl->err, "tcsetattr error", END_ERR_MSG); + return 1; + }; + }; + return waserr; +} +#endif /* HAVE_SELECT */ + +/*....................................................................... + * Switch history groups. History groups represent separate history + * lists recorded within a single history buffer. Different groups + * are distinguished by integer identifiers chosen by the calling + * appplicaton. Initially new_GetLine() sets the group identifier to + * 0. Whenever a new line is appended to the history list, the current + * group identifier is recorded with it, and history lookups only + * consider lines marked with the current group identifier. + * + * Input: + * gl GetLine * The resource object of gl_get_line(). + * id unsigned The new history group identifier. + * Output: + * return int 0 - OK. + * 1 - Error. + */ +int gl_group_history(GetLine *gl, unsigned id) +{ + sigset_t oldset; /* The signals that were blocked on entry to this function */ + int status; /* The return status of this function */ +/* + * Check the arguments. + */ + if(!gl) { + errno = EINVAL; + return 1; + }; +/* + * Block all signals while we install the new configuration. + */ + if(gl_mask_signals(gl, &oldset)) + return 1; +/* + * If the group isn't being changed, do nothing. + */ + if(_glh_get_group(gl->glh) == id) { + status = 0; +/* + * Establish the new group. + */ + } else if(_glh_set_group(gl->glh, id)) { + _err_record_msg(gl->err, _glh_last_error(gl->glh), END_ERR_MSG); + status = 1; +/* + * Prevent history information from the previous group being + * inappropriately used by the next call to gl_get_line(). + */ + } else { + gl->preload_history = 0; + gl->last_search = -1; + status = 0; + }; +/* + * Restore the process signal mask. + */ + gl_unmask_signals(gl, &oldset); + return status; +} + +/*....................................................................... + * Display the contents of the history list. + * + * Input: + * gl GetLine * The resource object of gl_get_line(). + * fp FILE * The stdio output stream to write to. + * fmt const char * A format string. This containing characters to be + * written verbatim, plus any of the following + * format directives: + * %D - The date, formatted like 2001-11-20 + * %T - The time of day, formatted like 23:59:59 + * %N - The sequential entry number of the + * line in the history buffer. + * %G - The number of the history group that + * the line belongs to. + * %% - A literal % character. + * %H - The history line itself. + * Note that a '\n' newline character is not + * appended by default. + * all_groups int If true, display history lines from all + * history groups. Otherwise only display + * those of the current history group. + * max_lines int If max_lines is < 0, all available lines + * are displayed. Otherwise only the most + * recent max_lines lines will be displayed. + * Output: + * return int 0 - OK. + * 1 - Error. + */ +int gl_show_history(GetLine *gl, FILE *fp, const char *fmt, int all_groups, + int max_lines) +{ + sigset_t oldset; /* The signals that were blocked on entry to this function */ + int status; /* The return status of this function */ +/* + * Check the arguments. + */ + if(!gl || !fp || !fmt) { + if(gl) + _err_record_msg(gl->err, "NULL argument(s)", END_ERR_MSG); + errno = EINVAL; + return 1; + }; +/* + * Block all signals. + */ + if(gl_mask_signals(gl, &oldset)) + return 1; +/* + * Display the specified history group(s) while signals are blocked. + */ + status = _glh_show_history(gl->glh, _io_write_stdio, fp, fmt, all_groups, + max_lines) || fflush(fp)==EOF; + if(!status) + _err_record_msg(gl->err, _glh_last_error(gl->glh), END_ERR_MSG); +/* + * Restore the process signal mask. + */ + gl_unmask_signals(gl, &oldset); + return status; +} + +/*....................................................................... + * Update if necessary, and return the current size of the terminal. + * + * Input: + * gl GetLine * The resource object of gl_get_line(). + * def_ncolumn int If the number of columns in the terminal + * can't be determined, substitute this number. + * def_nline int If the number of lines in the terminal can't + * be determined, substitute this number. + * Output: + * return GlTerminalSize The current terminal size. + */ +GlTerminalSize gl_terminal_size(GetLine *gl, int def_ncolumn, int def_nline) +{ + GlTerminalSize size; /* The object to be returned */ + sigset_t oldset; /* The signals that were blocked on entry */ + /* to this function */ +/* + * Block all signals while accessing gl. + */ + gl_mask_signals(gl, &oldset); +/* + * Lookup/configure the terminal size. + */ + _gl_terminal_size(gl, def_ncolumn, def_nline, &size); +/* + * Restore the process signal mask before returning. + */ + gl_unmask_signals(gl, &oldset); + return size; +} + +/*....................................................................... + * This is the private body of the gl_terminal_size() function. It + * assumes that the caller has checked its arguments and blocked the + * delivery of signals. + */ +static void _gl_terminal_size(GetLine *gl, int def_ncolumn, int def_nline, + GlTerminalSize *size) +{ + const char *env; /* The value of an environment variable */ + int n; /* A number read from env[] */ +/* + * Set the number of lines and columns to non-sensical values so that + * we know later if they have been set. + */ + gl->nline = 0; + gl->ncolumn = 0; +/* + * Are we reading from a terminal? + */ + if(gl->is_term) { +/* + * Ask the terminal directly if possible. + */ + gl_query_size(gl, &gl->ncolumn, &gl->nline); +/* + * If gl_query_size() couldn't ask the terminal, it will have + * left gl->nrow and gl->ncolumn unchanged. If these values haven't + * been changed from their initial values of zero, we need to find + * a different method to get the terminal size. + * + * If the number of lines isn't known yet, first see if the + * LINES environment ariable exists and specifies a believable number. + * If this doesn't work, look up the default size in the terminal + * information database. + */ + if(gl->nline < 1) { + if((env = getenv("LINES")) && (n=atoi(env)) > 0) + gl->nline = n; +#ifdef USE_TERMINFO + else + gl->nline = tigetnum((char *)"lines"); +#elif defined(USE_TERMCAP) + else + gl->nline = tgetnum("li"); +#endif + }; +/* + * If the number of lines isn't known yet, first see if the COLUMNS + * environment ariable exists and specifies a believable number. If + * this doesn't work, look up the default size in the terminal + * information database. + */ + if(gl->ncolumn < 1) { + if((env = getenv("COLUMNS")) && (n=atoi(env)) > 0) + gl->ncolumn = n; +#ifdef USE_TERMINFO + else + gl->ncolumn = tigetnum((char *)"cols"); +#elif defined(USE_TERMCAP) + else + gl->ncolumn = tgetnum("co"); +#endif + }; + }; +/* + * If we still haven't been able to acquire reasonable values, substitute + * the default values specified by the caller. + */ + if(gl->nline <= 0) + gl->nline = def_nline; + if(gl->ncolumn <= 0) + gl->ncolumn = def_ncolumn; +/* + * Copy the new size into the return value. + */ + if(size) { + size->nline = gl->nline; + size->ncolumn = gl->ncolumn; + }; + return; +} + +/*....................................................................... + * Resize or delete the history buffer. + * + * Input: + * gl GetLine * The resource object of gl_get_line(). + * bufsize size_t The number of bytes in the history buffer, or 0 + * to delete the buffer completely. + * Output: + * return int 0 - OK. + * 1 - Insufficient memory (the previous buffer + * will have been retained). No error message + * will be displayed. + */ +int gl_resize_history(GetLine *gl, size_t bufsize) +{ + sigset_t oldset; /* The signals that were blocked on entry to this function */ + int status; /* The return status of this function */ +/* + * Check the arguments. + */ + if(!gl) + return 1; +/* + * Block all signals while modifying the contents of gl. + */ + if(gl_mask_signals(gl, &oldset)) + return 1; +/* + * Perform the resize while signals are blocked. + */ + status = _glh_resize_history(gl->glh, bufsize); + if(status) + _err_record_msg(gl->err, _glh_last_error(gl->glh), END_ERR_MSG); +/* + * Restore the process signal mask before returning. + */ + gl_unmask_signals(gl, &oldset); + return status; +} + +/*....................................................................... + * Set an upper limit to the number of lines that can be recorded in the + * history list, or remove a previously specified limit. + * + * Input: + * gl GetLine * The resource object of gl_get_line(). + * max_lines int The maximum number of lines to allow, or -1 to + * cancel a previous limit and allow as many lines + * as will fit in the current history buffer size. + */ +void gl_limit_history(GetLine *gl, int max_lines) +{ + if(gl) { + sigset_t oldset; /* The signals that were blocked on entry to this block */ +/* + * Temporarily block all signals. + */ + gl_mask_signals(gl, &oldset); +/* + * Apply the limit while signals are blocked. + */ + _glh_limit_history(gl->glh, max_lines); +/* + * Restore the process signal mask before returning. + */ + gl_unmask_signals(gl, &oldset); + }; +} + +/*....................................................................... + * Discard either all historical lines, or just those associated with the + * current history group. + * + * Input: + * gl GetLine * The resource object of gl_get_line(). + * all_groups int If true, clear all of the history. If false, + * clear only the stored lines associated with the + * currently selected history group. + */ +void gl_clear_history(GetLine *gl, int all_groups) +{ + if(gl) { + sigset_t oldset; /* The signals that were blocked on entry to this block */ +/* + * Temporarily block all signals. + */ + gl_mask_signals(gl, &oldset); +/* + * Clear the history buffer while signals are blocked. + */ + _glh_clear_history(gl->glh, all_groups); +/* + * Restore the process signal mask before returning. + */ + gl_unmask_signals(gl, &oldset); + }; +} + +/*....................................................................... + * Temporarily enable or disable the gl_get_line() history mechanism. + * + * Input: + * gl GetLine * The resource object of gl_get_line(). + * enable int If true, turn on the history mechanism. If + * false, disable it. + */ +void gl_toggle_history(GetLine *gl, int enable) +{ + if(gl) { + sigset_t oldset; /* The signals that were blocked on entry to this block */ +/* + * Temporarily block all signals. + */ + gl_mask_signals(gl, &oldset); +/* + * Change the history recording mode while signals are blocked. + */ + _glh_toggle_history(gl->glh, enable); +/* + * Restore the process signal mask before returning. + */ + gl_unmask_signals(gl, &oldset); + }; +} + +/*....................................................................... + * Lookup a history line by its sequential number of entry in the + * history buffer. + * + * Input: + * gl GetLine * The resource object of gl_get_line(). + * id unsigned long The identification number of the line to + * be returned, where 0 denotes the first line + * that was entered in the history list, and + * each subsequently added line has a number + * one greater than the previous one. For + * the range of lines currently in the list, + * see the gl_range_of_history() function. + * Input/Output: + * line GlHistoryLine * A pointer to the variable in which to + * return the details of the line. + * Output: + * return int 0 - The line is no longer in the history + * list, and *line has not been changed. + * 1 - The requested line can be found in + * *line. Note that line->line is part + * of the history buffer, so a + * private copy should be made if you + * wish to use it after subsequent calls + * to any functions that take *gl as an + * argument. + */ +int gl_lookup_history(GetLine *gl, unsigned long id, GlHistoryLine *line) +{ + sigset_t oldset; /* The signals that were blocked on entry to this function */ + int status; /* The return status of this function */ +/* + * Check the arguments. + */ + if(!gl) + return 0; +/* + * Block all signals while modifying the contents of gl. + */ + if(gl_mask_signals(gl, &oldset)) + return 1; +/* + * Perform the lookup while signals are blocked. + */ + status = _glh_lookup_history(gl->glh, (GlhLineID) id, &line->line, + &line->group, &line->timestamp); + if(status) + _err_record_msg(gl->err, _glh_last_error(gl->glh), END_ERR_MSG); +/* + * Restore the process signal mask before returning. + */ + gl_unmask_signals(gl, &oldset); + return status; +} + +/*....................................................................... + * Query the state of the history list. Note that any of the input/output + * pointers can be specified as NULL. + * + * Input: + * gl GetLine * The resource object of gl_get_line(). + * Input/Output: + * state GlHistoryState * A pointer to the variable in which to record + * the return values. + */ +void gl_state_of_history(GetLine *gl, GlHistoryState *state) +{ + if(gl && state) { + sigset_t oldset; /* The signals that were blocked on entry to this block */ +/* + * Temporarily block all signals. + */ + gl_mask_signals(gl, &oldset); +/* + * Lookup the status while signals are blocked. + */ + _glh_state_of_history(gl->glh, &state->enabled, &state->group, + &state->max_lines); +/* + * Restore the process signal mask before returning. + */ + gl_unmask_signals(gl, &oldset); + }; +} + +/*....................................................................... + * Query the number and range of lines in the history buffer. + * + * Input: + * gl GetLine * The resource object of gl_get_line(). + * range GlHistoryRange * A pointer to the variable in which to record + * the return values. If range->nline=0, the + * range of lines will be given as 0-0. + */ +void gl_range_of_history(GetLine *gl, GlHistoryRange *range) +{ + if(gl && range) { + sigset_t oldset; /* The signals that were blocked on entry to this block */ +/* + * Temporarily block all signals. + */ + gl_mask_signals(gl, &oldset); +/* + * Lookup the information while signals are blocked. + */ + _glh_range_of_history(gl->glh, &range->oldest, &range->newest, + &range->nlines); +/* + * Restore the process signal mask before returning. + */ + gl_unmask_signals(gl, &oldset); + }; +} + +/*....................................................................... + * Return the size of the history buffer and the amount of the + * buffer that is currently in use. + * + * Input: + * gl GetLine * The gl_get_line() resource object. + * Input/Output: + * GlHistorySize size * A pointer to the variable in which to return + * the results. + */ +void gl_size_of_history(GetLine *gl, GlHistorySize *size) +{ + if(gl && size) { + sigset_t oldset; /* The signals that were blocked on entry to this block */ +/* + * Temporarily block all signals. + */ + gl_mask_signals(gl, &oldset); +/* + * Lookup the information while signals are blocked. + */ + _glh_size_of_history(gl->glh, &size->size, &size->used); +/* + * Restore the process signal mask before returning. + */ + gl_unmask_signals(gl, &oldset); + }; +} + +/*....................................................................... + * This is the action function that lists the contents of the history + * list. + */ +static KT_KEY_FN(gl_list_history) +{ +/* + * Start a new line. + */ + if(gl_start_newline(gl, 1)) + return 1; +/* + * List history lines that belong to the current group. + */ + _glh_show_history(gl->glh, gl_write_fn, gl, "%N %T %H\r\n", 0, + count<=1 ? -1 : count); +/* + * Arrange for the input line to be redisplayed. + */ + gl_queue_redisplay(gl); + return 0; +} + +/*....................................................................... + * Specify whether text that users type should be displayed or hidden. + * In the latter case, only the prompt is displayed, and the final + * input line is not archived in the history list. + * + * Input: + * gl GetLine * The gl_get_line() resource object. + * enable int 0 - Disable echoing. + * 1 - Enable echoing. + * -1 - Just query the mode without changing it. + * Output: + * return int The echoing disposition that was in effect + * before this function was called: + * 0 - Echoing was disabled. + * 1 - Echoing was enabled. + */ +int gl_echo_mode(GetLine *gl, int enable) +{ + if(gl) { + sigset_t oldset; /* The signals that were blocked on entry to this block */ + int was_echoing; /* The echoing disposition on entry to this function */ +/* + * Temporarily block all signals. + */ + gl_mask_signals(gl, &oldset); +/* + * Install the new disposition while signals are blocked. + */ + was_echoing = gl->echo; + if(enable >= 0) + gl->echo = enable; +/* + * Restore the process signal mask before returning. + */ + gl_unmask_signals(gl, &oldset); +/* + * Return the original echoing disposition. + */ + return was_echoing; + }; + return 1; +} + +/*....................................................................... + * Display the prompt. + * + * Input: + * gl GetLine * The resource object of gl_get_line(). + * Output: + * return int 0 - OK. + * 1 - Error. + */ +static int gl_display_prompt(GetLine *gl) +{ + const char *pptr; /* A pointer into gl->prompt[] */ + unsigned old_attr=0; /* The current text display attributes */ + unsigned new_attr=0; /* The requested text display attributes */ +/* + * Temporarily switch to echoing output characters. + */ + int kept_echo = gl->echo; + gl->echo = 1; +/* + * In case the screen got messed up, send a carriage return to + * put the cursor at the beginning of the current terminal line. + */ + if(gl_print_control_sequence(gl, 1, gl->bol)) + return 1; +/* + * Mark the line as partially displayed. + */ + gl->displayed = 1; +/* + * Write the prompt, using the currently selected prompt style. + */ + switch(gl->prompt_style) { + case GL_LITERAL_PROMPT: + if(gl_print_string(gl, gl->prompt, '\0')) + return 1; + break; + case GL_FORMAT_PROMPT: + for(pptr=gl->prompt; *pptr; pptr++) { +/* + * Does the latest character appear to be the start of a directive? + */ + if(*pptr == '%') { +/* + * Check for and act on attribute changing directives. + */ + switch(pptr[1]) { +/* + * Add or remove a text attribute from the new set of attributes. + */ + case 'B': case 'U': case 'S': case 'P': case 'F': case 'V': + case 'b': case 'u': case 's': case 'p': case 'f': case 'v': + switch(*++pptr) { + case 'B': /* Switch to a bold font */ + new_attr |= GL_TXT_BOLD; + break; + case 'b': /* Switch to a non-bold font */ + new_attr &= ~GL_TXT_BOLD; + break; + case 'U': /* Start underlining */ + new_attr |= GL_TXT_UNDERLINE; + break; + case 'u': /* Stop underlining */ + new_attr &= ~GL_TXT_UNDERLINE; + break; + case 'S': /* Start highlighting */ + new_attr |= GL_TXT_STANDOUT; + break; + case 's': /* Stop highlighting */ + new_attr &= ~GL_TXT_STANDOUT; + break; + case 'P': /* Switch to a pale font */ + new_attr |= GL_TXT_DIM; + break; + case 'p': /* Switch to a non-pale font */ + new_attr &= ~GL_TXT_DIM; + break; + case 'F': /* Switch to a flashing font */ + new_attr |= GL_TXT_BLINK; + break; + case 'f': /* Switch to a steady font */ + new_attr &= ~GL_TXT_BLINK; + break; + case 'V': /* Switch to reverse video */ + new_attr |= GL_TXT_REVERSE; + break; + case 'v': /* Switch out of reverse video */ + new_attr &= ~GL_TXT_REVERSE; + break; + }; + continue; +/* + * A literal % is represented by %%. Skip the leading %. + */ + case '%': + pptr++; + break; + }; + }; +/* + * Many terminals, when asked to turn off a single text attribute, turn + * them all off, so the portable way to turn one off individually is to + * explicitly turn them all off, then specify those that we want from + * scratch. + */ + if(old_attr & ~new_attr) { + if(gl_print_control_sequence(gl, 1, gl->text_attr_off)) + return 1; + old_attr = 0; + }; +/* + * Install new text attributes? + */ + if(new_attr != old_attr) { + if(new_attr & GL_TXT_BOLD && !(old_attr & GL_TXT_BOLD) && + gl_print_control_sequence(gl, 1, gl->bold)) + return 1; + if(new_attr & GL_TXT_UNDERLINE && !(old_attr & GL_TXT_UNDERLINE) && + gl_print_control_sequence(gl, 1, gl->underline)) + return 1; + if(new_attr & GL_TXT_STANDOUT && !(old_attr & GL_TXT_STANDOUT) && + gl_print_control_sequence(gl, 1, gl->standout)) + return 1; + if(new_attr & GL_TXT_DIM && !(old_attr & GL_TXT_DIM) && + gl_print_control_sequence(gl, 1, gl->dim)) + return 1; + if(new_attr & GL_TXT_REVERSE && !(old_attr & GL_TXT_REVERSE) && + gl_print_control_sequence(gl, 1, gl->reverse)) + return 1; + if(new_attr & GL_TXT_BLINK && !(old_attr & GL_TXT_BLINK) && + gl_print_control_sequence(gl, 1, gl->blink)) + return 1; + old_attr = new_attr; + }; +/* + * Display the latest character. + */ + if(gl_print_char(gl, *pptr, pptr[1])) + return 1; + }; +/* + * Turn off all text attributes now that we have finished drawing + * the prompt. + */ + if(gl_print_control_sequence(gl, 1, gl->text_attr_off)) + return 1; + break; + }; +/* + * Restore the original echo mode. + */ + gl->echo = kept_echo; +/* + * The prompt has now been displayed at least once. + */ + gl->prompt_changed = 0; + return 0; +} + +/*....................................................................... + * This function can be called from gl_get_line() callbacks to have + * the prompt changed when they return. It has no effect if gl_get_line() + * is not currently being invoked. + * + * Input: + * gl GetLine * The resource object of gl_get_line(). + * prompt const char * The new prompt. + */ +void gl_replace_prompt(GetLine *gl, const char *prompt) +{ + if(gl) { + sigset_t oldset; /* The signals that were blocked on entry to this block */ +/* + * Temporarily block all signals. + */ + gl_mask_signals(gl, &oldset); +/* + * Replace the prompt. + */ + _gl_replace_prompt(gl, prompt); +/* + * Restore the process signal mask before returning. + */ + gl_unmask_signals(gl, &oldset); + }; +} + +/*....................................................................... + * This is the private body of the gl_replace_prompt() function. It + * assumes that the caller has checked its arguments and blocked the + * delivery of signals. + */ +static void _gl_replace_prompt(GetLine *gl, const char *prompt) +{ +/* + * Substitute an empty prompt? + */ + if(!prompt) + prompt = ""; +/* + * Gaurd against aliasing between prompt and gl->prompt. + */ + if(gl->prompt != prompt) { +/* + * Get the length of the new prompt string. + */ + size_t slen = strlen(prompt); +/* + * If needed, allocate a new buffer for the prompt string. + */ + if(!gl->prompt || slen > strlen(gl->prompt)) { + size_t size = sizeof(char) * (slen + 1); + char *new_prompt = gl->prompt ? realloc(gl->prompt, size) : malloc(size); + if(!new_prompt) + return; + gl->prompt = new_prompt; + }; +/* + * Make a copy of the new prompt. + */ + strcpy(gl->prompt, prompt); + }; +/* + * Record the statistics of the new prompt. + */ + gl->prompt_len = gl_displayed_prompt_width(gl); + gl->prompt_changed = 1; + gl_queue_redisplay(gl); + return; +} + +/*....................................................................... + * Work out the length of the current prompt on the terminal, according + * to the current prompt formatting style. + * + * Input: + * gl GetLine * The resource object of this library. + * Output: + * return int The number of displayed characters. + */ +static int gl_displayed_prompt_width(GetLine *gl) +{ + int slen=0; /* The displayed number of characters */ + const char *pptr; /* A pointer into prompt[] */ +/* + * The length differs according to the prompt display style. + */ + switch(gl->prompt_style) { + case GL_LITERAL_PROMPT: + return gl_displayed_string_width(gl, gl->prompt, -1, 0); + break; + case GL_FORMAT_PROMPT: +/* + * Add up the length of the displayed string, while filtering out + * attribute directives. + */ + for(pptr=gl->prompt; *pptr; pptr++) { +/* + * Does the latest character appear to be the start of a directive? + */ + if(*pptr == '%') { +/* + * Check for and skip attribute changing directives. + */ + switch(pptr[1]) { + case 'B': case 'b': case 'U': case 'u': case 'S': case 's': + pptr++; + continue; +/* + * A literal % is represented by %%. Skip the leading %. + */ + case '%': + pptr++; + break; + }; + }; + slen += gl_displayed_char_width(gl, *pptr, slen); + }; + break; + }; + return slen; +} + +/*....................................................................... + * Specify whether to heed text attribute directives within prompt + * strings. + * + * Input: + * gl GetLine * The resource object of gl_get_line(). + * style GlPromptStyle The style of prompt (see the definition of + * GlPromptStyle in libtecla.h for details). + */ +void gl_prompt_style(GetLine *gl, GlPromptStyle style) +{ + if(gl) { + sigset_t oldset; /* The signals that were blocked on entry to this block */ +/* + * Temporarily block all signals. + */ + gl_mask_signals(gl, &oldset); +/* + * Install the new style in gl while signals are blocked. + */ + if(style != gl->prompt_style) { + gl->prompt_style = style; + gl->prompt_len = gl_displayed_prompt_width(gl); + gl->prompt_changed = 1; + gl_queue_redisplay(gl); + }; +/* + * Restore the process signal mask before returning. + */ + gl_unmask_signals(gl, &oldset); + }; +} + +/*....................................................................... + * Tell gl_get_line() how to respond to a given signal. This can be used + * both to override the default responses to signals that gl_get_line() + * normally catches and to add new signals to the list that are to be + * caught. + * + * Input: + * gl GetLine * The resource object of gl_get_line(). + * signo int The number of the signal to be caught. + * flags unsigned A bitwise union of GlSignalFlags enumerators. + * after GlAfterSignal What to do after the application's signal + * handler has been called. + * errno_value int The value to set errno to. + * Output: + * return int 0 - OK. + * 1 - Error. + */ +int gl_trap_signal(GetLine *gl, int signo, unsigned flags, + GlAfterSignal after, int errno_value) +{ + sigset_t oldset; /* The signals that were blocked on entry to this function */ + int status; /* The return status of this function */ +/* + * Check the arguments. + */ + if(!gl) { + errno = EINVAL; + return 1; + }; +/* + * Block all signals while modifying the contents of gl. + */ + if(gl_mask_signals(gl, &oldset)) + return 1; +/* + * Perform the modification while signals are blocked. + */ + status = _gl_trap_signal(gl, signo, flags, after, errno_value); +/* + * Restore the process signal mask before returning. + */ + gl_unmask_signals(gl, &oldset); + return status; +} + +/*....................................................................... + * This is the private body of the gl_trap_signal() function. It + * assumes that the caller has checked its arguments and blocked the + * delivery of signals. + */ +static int _gl_trap_signal(GetLine *gl, int signo, unsigned flags, + GlAfterSignal after, int errno_value) +{ + GlSignalNode *sig; +/* + * Complain if an attempt is made to trap untrappable signals. + * These would otherwise cause errors later in gl_mask_signals(). + */ + if(0 +#ifdef SIGKILL + || signo==SIGKILL +#endif +#ifdef SIGBLOCK + || signo==SIGBLOCK +#endif + ) { + return 1; + }; +/* + * See if the signal has already been registered. + */ + for(sig=gl->sigs; sig && sig->signo != signo; sig = sig->next) + ; +/* + * If the signal hasn't already been registered, allocate a node for + * it. + */ + if(!sig) { + sig = (GlSignalNode *) _new_FreeListNode(gl->sig_mem); + if(!sig) + return 1; +/* + * Add the new node to the head of the list. + */ + sig->next = gl->sigs; + gl->sigs = sig; +/* + * Record the signal number. + */ + sig->signo = signo; +/* + * Create a signal set that includes just this signal. + */ + sigemptyset(&sig->proc_mask); + if(sigaddset(&sig->proc_mask, signo) == -1) { + _err_record_msg(gl->err, "sigaddset error", END_ERR_MSG); + sig = (GlSignalNode *) _del_FreeListNode(gl->sig_mem, sig); + return 1; + }; +/* + * Add the signal to the bit-mask of signals being trapped. + */ + sigaddset(&gl->all_signal_set, signo); + }; +/* + * Record the new signal attributes. + */ + sig->flags = flags; + sig->after = after; + sig->errno_value = errno_value; + return 0; +} + +/*....................................................................... + * Remove a signal from the list of signals that gl_get_line() traps. + * + * Input: + * gl GetLine * The resource object of gl_get_line(). + * signo int The number of the signal to be ignored. + * Output: + * return int 0 - OK. + * 1 - Error. + */ +int gl_ignore_signal(GetLine *gl, int signo) +{ + GlSignalNode *sig; /* The gl->sigs list node of the specified signal */ + GlSignalNode *prev; /* The node that precedes sig in the list */ + sigset_t oldset; /* The signals that were blocked on entry to this */ + /* function. */ +/* + * Check the arguments. + */ + if(!gl) { + errno = EINVAL; + return 1; + }; +/* + * Block all signals while modifying the contents of gl. + */ + if(gl_mask_signals(gl, &oldset)) + return 1; +/* + * Find the node of the gl->sigs list which records the disposition + * of the specified signal. + */ + for(prev=NULL,sig=gl->sigs; sig && sig->signo != signo; + prev=sig,sig=sig->next) + ; + if(sig) { +/* + * Remove the node from the list. + */ + if(prev) + prev->next = sig->next; + else + gl->sigs = sig->next; +/* + * Return the node to the freelist. + */ + sig = (GlSignalNode *) _del_FreeListNode(gl->sig_mem, sig); +/* + * Remove the signal from the bit-mask union of signals being trapped. + */ + sigdelset(&gl->all_signal_set, signo); + }; +/* + * Restore the process signal mask before returning. + */ + gl_unmask_signals(gl, &oldset); + return 0; +} + +/*....................................................................... + * This function is called when an input line has been completed. It + * appends the specified newline character, terminates the line, + * records the line in the history buffer if appropriate, and positions + * the terminal cursor at the start of the next line. + * + * Input: + * gl GetLine * The resource object of gl_get_line(). + * newline_char int The newline character to add to the end + * of the line. + * Output: + * return int 0 - OK. + * 1 - Error. + */ +static int gl_line_ended(GetLine *gl, int newline_char) +{ +/* + * If the newline character is printable, display it at the end of + * the line, and add it to the input line buffer. + */ + if(isprint((int)(unsigned char) newline_char)) { + if(gl_end_of_line(gl, 1, NULL) || gl_add_char_to_line(gl, newline_char)) + return 1; + } else { +/* + * Otherwise just append a newline character to the input line buffer. + */ + newline_char = '\n'; + gl_buffer_char(gl, newline_char, gl->ntotal); + }; +/* + * Add the line to the history buffer if it was entered with a + * newline character. + */ + if(gl->echo && gl->automatic_history && newline_char=='\n') + (void) _gl_append_history(gl, gl->line); +/* + * Except when depending on the system-provided line editing, start a new + * line after the end of the line that has just been entered. + */ + if(gl->editor != GL_NO_EDITOR && gl_start_newline(gl, 1)) + return 1; +/* + * Record the successful return status. + */ + gl_record_status(gl, GLR_NEWLINE, 0); +/* + * Attempt to flush any pending output. + */ + (void) gl_flush_output(gl); +/* + * The next call to gl_get_line() will write the prompt for a new line + * (or continue the above flush if incomplete), so if we manage to + * flush the terminal now, report that we are waiting to write to the + * terminal. + */ + gl->pending_io = GLP_WRITE; + return 0; +} + +/*....................................................................... + * Return the last signal that was caught by the most recent call to + * gl_get_line(), or -1 if no signals were caught. This is useful if + * gl_get_line() returns errno=EINTR and you need to find out what signal + * caused it to abort. + * + * Input: + * gl GetLine * The resource object of gl_get_line(). + * Output: + * return int The last signal caught by the most recent + * call to gl_get_line(), or -1 if no signals + * were caught. + */ +int gl_last_signal(GetLine *gl) +{ + int signo = -1; /* The requested signal number */ + if(gl) { + sigset_t oldset; /* The signals that were blocked on entry to this block */ +/* + * Temporarily block all signals. + */ + gl_mask_signals(gl, &oldset); +/* + * Access gl now that signals are blocked. + */ + signo = gl->last_signal; +/* + * Restore the process signal mask before returning. + */ + gl_unmask_signals(gl, &oldset); + }; + return signo; +} + +/*....................................................................... + * Prepare to edit a new line. + * + * Input: + * gl GetLine * The resource object of this library. + * prompt char * The prompt to prefix the line with, or NULL to + * use the same prompt that was used by the previous + * line. + * start_line char * The initial contents of the input line, or NULL + * if it should start out empty. + * start_pos int If start_line isn't NULL, this specifies the + * index of the character over which the cursor + * should initially be positioned within the line. + * If you just want it to follow the last character + * of the line, send -1. + * Output: + * return int 0 - OK. + * 1 - Error. + */ +static int gl_present_line(GetLine *gl, const char *prompt, + const char *start_line, int start_pos) +{ +/* + * Prepare the line-editing properties for a new editing session. + */ + gl_reset_editor(gl); +/* + * Record the new prompt and its displayed width. + */ + if(prompt) + _gl_replace_prompt(gl, prompt); +/* + * Reset the history search pointers. + */ + if(_glh_cancel_search(gl->glh)) { + _err_record_msg(gl->err, _glh_last_error(gl->glh), END_ERR_MSG); + return 1; + }; +/* + * If the previous line was entered via the repeat-history action, + * preload the specified history line. + */ + if(gl->preload_history) { + gl->preload_history = 0; + if(_glh_recall_line(gl->glh, gl->preload_id, gl->line, gl->linelen+1)) { + gl_update_buffer(gl); /* Compute gl->ntotal etc.. */ + gl->buff_curpos = gl->ntotal; + } else { + gl_truncate_buffer(gl, 0); + }; + gl->preload_id = 0; +/* + * Present a specified initial line? + */ + } else if(start_line) { + char *cptr; /* A pointer into gl->line[] */ +/* + * Measure the length of the starting line. + */ + int start_len = strlen(start_line); +/* + * If the length of the line is greater than the available space, + * truncate it. + */ + if(start_len > gl->linelen) + start_len = gl->linelen; +/* + * Load the line into the buffer. + */ + if(start_line != gl->line) + gl_buffer_string(gl, start_line, start_len, 0); +/* + * Strip off any trailing newline and carriage return characters. + */ + for(cptr=gl->line + gl->ntotal - 1; cptr >= gl->line && + (*cptr=='\n' || *cptr=='\r'); cptr--,gl->ntotal--) + ; + gl_truncate_buffer(gl, gl->ntotal < 0 ? 0 : gl->ntotal); +/* + * Where should the cursor be placed within the line? + */ + if(start_pos < 0 || start_pos > gl->ntotal) { + if(gl_place_cursor(gl, gl->ntotal)) + return 1; + } else { + if(gl_place_cursor(gl, start_pos)) + return 1; + }; +/* + * Clear the input line? + */ + } else { + gl_truncate_buffer(gl, 0); + }; +/* + * Arrange for the line to be displayed by gl_flush_output(). + */ + gl_queue_redisplay(gl); +/* + * Update the display. + */ + return gl_flush_output(gl); +} + +/*....................................................................... + * Reset all line-editing parameters for a new editing session. Note + * that this does not empty the input line, since that would prevent a + * gl_get_line() caller from specifying the returned line buffer as + * the start_line argument of the next call to gl_get_line(). + * + * Input: + * gl GetLine * The line editor resource object. + */ +static void gl_reset_editor(GetLine *gl) +{ +/* + * Warning: Don't clear gl->line[] and gl->ntotal here (see above). + */ + gl->buff_curpos = 0; + gl->term_curpos = 0; + gl->term_len = 0; + gl->insert_curpos = 0; + gl->number = -1; + gl->displayed = 0; + gl->endline = 0; + gl->redisplay = 0; + gl->postpone = 0; + gl->nbuf = 0; + gl->nread = 0; + gl->vi.command = 0; + gl->vi.undo.line[0] = '\0'; + gl->vi.undo.ntotal = 0; + gl->vi.undo.buff_curpos = 0; + gl->vi.repeat.action.fn = 0; + gl->vi.repeat.action.data = 0; + gl->last_signal = -1; +} + +/*....................................................................... + * Print an informational message to the terminal, after starting a new + * line. + * + * Input: + * gl GetLine * The line editor resource object. + * ... const char * Zero or more strings to be printed. + * ... void * The last argument must always be GL_END_INFO. + * Output: + * return int 0 - OK. + * 1 - Error. + */ +static int gl_print_info(GetLine *gl, ...) +{ + va_list ap; /* The variable argument list */ + const char *s; /* The string being printed */ + int waserr = 0; /* True after an error */ +/* + * Only display output when echoing is on. + */ + if(gl->echo) { +/* + * Skip to the start of the next empty line before displaying the message. + */ + if(gl_start_newline(gl, 1)) + return 1; +/* + * Display the list of provided messages. + */ + va_start(ap, gl); + while(!waserr && (s = va_arg(ap, const char *)) != GL_END_INFO) + waserr = gl_print_raw_string(gl, 1, s, -1); + va_end(ap); +/* + * Start a newline. + */ + waserr = waserr || gl_print_raw_string(gl, 1, "\n\r", -1); +/* + * Arrange for the input line to be redrawn. + */ + gl_queue_redisplay(gl); + }; + return waserr; +} + +/*....................................................................... + * Go to the start of the next empty line, ready to output miscellaneous + * text to the screen. + * + * Note that when async-signal safety is required, the 'buffered' + * argument must be 0. + * + * Input: + * gl GetLine * The line editor resource object. + * buffered int If true, used buffered I/O when writing to + * the terminal. Otherwise use async-signal-safe + * unbuffered I/O. + * Output: + * return int 0 - OK. + * 1 - Error. + */ +static int gl_start_newline(GetLine *gl, int buffered) +{ + int waserr = 0; /* True after any I/O error */ +/* + * Move the cursor to the start of the terminal line that follows the + * last line of the partially enterred line. In order that this + * function remain async-signal safe when write_fn is signal safe, we + * can't call our normal output functions, since they call tputs(), + * who's signal saftey isn't defined. Fortunately, we can simply use + * \r and \n to move the cursor to the right place. + */ + if(gl->displayed) { /* Is an input line currently displayed? */ +/* + * On which terminal lines are the cursor and the last character of the + * input line? + */ + int curs_line = gl->term_curpos / gl->ncolumn; + int last_line = gl->term_len / gl->ncolumn; +/* + * Move the cursor to the start of the line that follows the last + * terminal line that is occupied by the input line. + */ + for( ; curs_line < last_line + 1; curs_line++) + waserr = waserr || gl_print_raw_string(gl, buffered, "\n", 1); + waserr = waserr || gl_print_raw_string(gl, buffered, "\r", 1); +/* + * Mark the line as no longer displayed. + */ + gl_line_erased(gl); + }; + return waserr; +} + +/*....................................................................... + * The callback through which all terminal output is routed. + * This simply appends characters to a queue buffer, which is + * subsequently flushed to the output channel by gl_flush_output(). + * + * Input: + * data void * The pointer to a GetLine line editor resource object + * cast to (void *). + * s const char * The string to be written. + * n int The number of characters to write from s[]. + * Output: + * return int The number of characters written. This will always + * be equal to 'n' unless an error occurs. + */ +static GL_WRITE_FN(gl_write_fn) +{ + GetLine *gl = (GetLine *) data; + int ndone = _glq_append_chars(gl->cq, s, n, gl->flush_fn, gl); + if(ndone != n) + _err_record_msg(gl->err, _glq_last_error(gl->cq), END_ERR_MSG); + return ndone; +} + +/*....................................................................... + * Ask gl_get_line() what caused it to return. + * + * Input: + * gl GetLine * The line editor resource object. + * Output: + * return GlReturnStatus The return status of the last call to + * gl_get_line(). + */ +GlReturnStatus gl_return_status(GetLine *gl) +{ + GlReturnStatus rtn_status = GLR_ERROR; /* The requested status */ + if(gl) { + sigset_t oldset; /* The signals that were blocked on entry to this block */ +/* + * Temporarily block all signals. + */ + gl_mask_signals(gl, &oldset); +/* + * Access gl while signals are blocked. + */ + rtn_status = gl->rtn_status; +/* + * Restore the process signal mask before returning. + */ + gl_unmask_signals(gl, &oldset); + }; + return rtn_status; +} + +/*....................................................................... + * In non-blocking server-I/O mode, this function should be called + * from the application's external event loop to see what type of + * terminal I/O is being waited for by gl_get_line(), and thus what + * direction of I/O to wait for with select() or poll(). + * + * Input: + * gl GetLine * The resource object of gl_get_line(). + * Output: + * return GlPendingIO The type of pending I/O being waited for. + */ +GlPendingIO gl_pending_io(GetLine *gl) +{ + GlPendingIO pending_io = GLP_WRITE; /* The requested information */ + if(gl) { + sigset_t oldset; /* The signals that were blocked on entry to this block */ +/* + * Temporarily block all signals. + */ + gl_mask_signals(gl, &oldset); +/* + * Access gl while signals are blocked. + */ + pending_io = gl->pending_io; +/* + * Restore the process signal mask before returning. + */ + gl_unmask_signals(gl, &oldset); + }; + return pending_io; +} + +/*....................................................................... + * In server mode, this function configures the terminal for non-blocking + * raw terminal I/O. In normal I/O mode it does nothing. + * + * Callers of this function must be careful to trap all signals that + * terminate or suspend the program, and call gl_normal_io() + * from the corresponding signal handlers in order to restore the + * terminal to its original settings before the program is terminated + * or suspended. They should also trap the SIGCONT signal to detect + * when the program resumes, and ensure that its signal handler + * call gl_raw_io() to redisplay the line and resume editing. + * + * This function is async signal safe. + * + * Input: + * gl GetLine * The line editor resource object. + * Output: + * return int 0 - OK. + * 1 - Error. + */ +int gl_raw_io(GetLine *gl) +{ + sigset_t oldset; /* The signals that were blocked on entry to this function */ + int status; /* The return status of _gl_raw_io() */ +/* + * Check the arguments. + */ + if(!gl) { + errno = EINVAL; + return 1; + }; +/* + * Block all signals. + */ + if(gl_mask_signals(gl, &oldset)) + return 1; +/* + * Don't allow applications to switch into raw mode unless in server mode. + */ + if(gl->io_mode != GL_SERVER_MODE) { + _err_record_msg(gl->err, "Can't switch to raw I/O unless in server mode", + END_ERR_MSG); + errno = EPERM; + status = 1; + } else { +/* + * Execute the private body of the function while signals are blocked. + */ + status = _gl_raw_io(gl, 1); + }; +/* + * Restore the process signal mask. + */ + gl_unmask_signals(gl, &oldset); + return status; +} + +/*....................................................................... + * This is the private body of the public function, gl_raw_io(). + * It assumes that the caller has checked its arguments and blocked the + * delivery of signals. + * + * This function is async signal safe. + */ +static int _gl_raw_io(GetLine *gl, int redisplay) +{ +/* + * If we are already in the correct mode, do nothing. + */ + if(gl->raw_mode) + return 0; +/* + * Switch the terminal to raw mode. + */ + if(gl->is_term && gl_raw_terminal_mode(gl)) + return 1; +/* + * Switch to non-blocking I/O mode? + */ + if(gl->io_mode==GL_SERVER_MODE && + (gl_nonblocking_io(gl, gl->input_fd) || + gl_nonblocking_io(gl, gl->output_fd) || + (gl->file_fp && gl_nonblocking_io(gl, fileno(gl->file_fp))))) { + if(gl->is_term) + gl_restore_terminal_attributes(gl); + return 1; + }; +/* + * If an input line is being entered, arrange for it to be + * displayed. + */ + if(redisplay) { + gl->postpone = 0; + gl_queue_redisplay(gl); + }; + return 0; +} + +/*....................................................................... + * Restore the terminal to the state that it had when + * gl_raw_io() was last called. After calling + * gl_raw_io(), this function must be called before + * terminating or suspending the program, and before attempting other + * uses of the terminal from within the program. See gl_raw_io() + * for more details. + * + * Input: + * gl GetLine * The line editor resource object. + * Output: + * return int 0 - OK. + * 1 - Error. + */ +int gl_normal_io(GetLine *gl) +{ + sigset_t oldset; /* The signals that were blocked on entry to this function */ + int status; /* The return status of _gl_normal_io() */ +/* + * Check the arguments. + */ + if(!gl) { + errno = EINVAL; + return 1; + }; +/* + * Block all signals. + */ + if(gl_mask_signals(gl, &oldset)) + return 1; +/* + * Execute the private body of the function while signals are blocked. + */ + status = _gl_normal_io(gl); +/* + * Restore the process signal mask. + */ + gl_unmask_signals(gl, &oldset); + return status; +} + +/*....................................................................... + * This is the private body of the public function, gl_normal_io(). + * It assumes that the caller has checked its arguments and blocked the + * delivery of signals. + */ +static int _gl_normal_io(GetLine *gl) +{ +/* + * If we are already in normal mode, do nothing. + */ + if(!gl->raw_mode) + return 0; +/* + * Postpone subsequent redisplays until after _gl_raw_io(gl, 1) + * is next called. + */ + gl->postpone = 1; +/* + * Switch back to blocking I/O. Note that this is essential to do + * here, because when using non-blocking I/O, the terminal output + * buffering code can't always make room for new output without calling + * malloc(), and a call to malloc() would mean that this function + * couldn't safely be called from signal handlers. + */ + if(gl->io_mode==GL_SERVER_MODE && + (gl_blocking_io(gl, gl->input_fd) || + gl_blocking_io(gl, gl->output_fd) || + (gl->file_fp && gl_blocking_io(gl, fileno(gl->file_fp))))) + return 1; +/* + * Move the cursor to the next empty terminal line. Note that + * unbuffered I/O is requested, to ensure that gl_start_newline() be + * async-signal-safe. + */ + if(gl->is_term && gl_start_newline(gl, 0)) + return 1; +/* + * Switch the terminal to normal mode. + */ + if(gl->is_term && gl_restore_terminal_attributes(gl)) { +/* + * On error, revert to non-blocking I/O if needed, so that on failure + * we remain in raw mode. + */ + if(gl->io_mode==GL_SERVER_MODE) { + gl_nonblocking_io(gl, gl->input_fd); + gl_nonblocking_io(gl, gl->output_fd); + if(gl->file_fp) + gl_nonblocking_io(gl, fileno(gl->file_fp)); + }; + return 1; + }; + return 0; +} + +/*....................................................................... + * This function allows you to install an additional completion + * action, or to change the completion function of an existing + * one. This should be called before the first call to gl_get_line() + * so that the name of the action be defined before the user's + * configuration file is read. + * + * Input: + * gl GetLine * The resource object of the command-line input + * module. + * data void * This is passed to match_fn() whenever it is + * called. It could, for example, point to a + * symbol table that match_fn() would look up + * matches in. + * match_fn CplMatchFn * The function that will identify the prefix + * to be completed from the input line, and + * report matching symbols. + * list_only int If non-zero, install an action that only lists + * possible completions, rather than attempting + * to perform the completion. + * name const char * The name with which users can refer to the + * binding in tecla configuration files. + * keyseq const char * Either NULL, or a key sequence with which + * to invoke the binding. This should be + * specified in the same manner as key-sequences + * in tecla configuration files (eg. "M-^I"). + * Output: + * return int 0 - OK. + * 1 - Error. + */ +int gl_completion_action(GetLine *gl, void *data, CplMatchFn *match_fn, + int list_only, const char *name, const char *keyseq) +{ + sigset_t oldset; /* The signals that were blocked on entry to this function */ + int status; /* The return status of _gl_completion_action() */ +/* + * Check the arguments. + */ + if(!gl || !name || !match_fn) { + errno = EINVAL; + return 1; + }; +/* + * Block all signals. + */ + if(gl_mask_signals(gl, &oldset)) + return 1; +/* + * Install the new action while signals are blocked. + */ + status = _gl_completion_action(gl, data, match_fn, list_only, name, keyseq); +/* + * Restore the process signal mask. + */ + gl_unmask_signals(gl, &oldset); + return status; +} + +/*....................................................................... + * This is the private body of the public function, gl_completion_action(). + * It assumes that the caller has checked its arguments and blocked the + * delivery of signals. + */ +static int _gl_completion_action(GetLine *gl, void *data, CplMatchFn *match_fn, + int list_only, const char *name, + const char *keyseq) +{ + KtKeyFn *current_fn; /* An existing action function */ + void *current_data; /* The action-function callback data */ +/* + * Which action function is desired? + */ + KtKeyFn *action_fn = list_only ? gl_list_completions : gl_complete_word; +/* + * Is there already an action of the specified name? + */ + if(_kt_lookup_action(gl->bindings, name, ¤t_fn, ¤t_data) == 0) { +/* + * If the action has the same type as the one being requested, + * simply change the contents of its GlCplCallback callback data. + */ + if(current_fn == action_fn) { + GlCplCallback *cb = (GlCplCallback *) current_data; + cb->fn = match_fn; + cb->data = data; + } else { + errno = EINVAL; + _err_record_msg(gl->err, + "Illegal attempt to change the type of an existing completion action", + END_ERR_MSG); + return 1; + }; +/* + * No existing action has the specified name. + */ + } else { +/* + * Allocate a new GlCplCallback callback object. + */ + GlCplCallback *cb = (GlCplCallback *) _new_FreeListNode(gl->cpl_mem); + if(!cb) { + errno = ENOMEM; + _err_record_msg(gl->err, "Insufficient memory to add completion action", + END_ERR_MSG); + return 1; + }; +/* + * Record the completion callback data. + */ + cb->fn = match_fn; + cb->data = data; +/* + * Attempt to register the new action. + */ + if(_kt_set_action(gl->bindings, name, action_fn, cb)) { + _err_record_msg(gl->err, _kt_last_error(gl->bindings), END_ERR_MSG); + _del_FreeListNode(gl->cpl_mem, (void *) cb); + return 1; + }; + }; +/* + * Bind the action to a given key-sequence? + */ + if(keyseq && _kt_set_keybinding(gl->bindings, KTB_NORM, keyseq, name)) { + _err_record_msg(gl->err, _kt_last_error(gl->bindings), END_ERR_MSG); + return 1; + }; + return 0; +} + +/*....................................................................... + * Register an application-provided function as an action function. + * This should preferably be called before the first call to gl_get_line() + * so that the name of the action becomes defined before the user's + * configuration file is read. + * + * Input: + * gl GetLine * The resource object of the command-line input + * module. + * data void * Arbitrary application-specific callback + * data to be passed to the callback + * function, fn(). + * fn GlActionFn * The application-specific function that + * implements the action. This will be invoked + * whenever the user presses any + * key-sequence which is bound to this action. + * name const char * The name with which users can refer to the + * binding in tecla configuration files. + * keyseq const char * The key sequence with which to invoke + * the binding. This should be specified in the + * same manner as key-sequences in tecla + * configuration files (eg. "M-^I"). + * Output: + * return int 0 - OK. + * 1 - Error. + */ +int gl_register_action(GetLine *gl, void *data, GlActionFn *fn, + const char *name, const char *keyseq) +{ + sigset_t oldset; /* The signals that were blocked on entry to this function */ + int status; /* The return status of _gl_register_action() */ +/* + * Check the arguments. + */ + if(!gl || !name || !fn) { + errno = EINVAL; + return 1; + }; +/* + * Block all signals. + */ + if(gl_mask_signals(gl, &oldset)) + return 1; +/* + * Install the new action while signals are blocked. + */ + status = _gl_register_action(gl, data, fn, name, keyseq); +/* + * Restore the process signal mask. + */ + gl_unmask_signals(gl, &oldset); + return status; +} + +/*....................................................................... + * This is the private body of the public function, gl_register_action(). + * It assumes that the caller has checked its arguments and blocked the + * delivery of signals. + */ +static int _gl_register_action(GetLine *gl, void *data, GlActionFn *fn, + const char *name, const char *keyseq) +{ + KtKeyFn *current_fn; /* An existing action function */ + void *current_data; /* The action-function callback data */ +/* + * Get the action function which actually runs the application-provided + * function. + */ + KtKeyFn *action_fn = gl_run_external_action; +/* + * Is there already an action of the specified name? + */ + if(_kt_lookup_action(gl->bindings, name, ¤t_fn, ¤t_data) == 0) { +/* + * If the action has the same type as the one being requested, + * simply change the contents of its GlCplCallback callback data. + */ + if(current_fn == action_fn) { + GlExternalAction *a = (GlExternalAction *) current_data; + a->fn = fn; + a->data = data; + } else { + errno = EINVAL; + _err_record_msg(gl->err, + "Illegal attempt to change the type of an existing action", + END_ERR_MSG); + return 1; + }; +/* + * No existing action has the specified name. + */ + } else { +/* + * Allocate a new GlCplCallback callback object. + */ + GlExternalAction *a = + (GlExternalAction *) _new_FreeListNode(gl->ext_act_mem); + if(!a) { + errno = ENOMEM; + _err_record_msg(gl->err, "Insufficient memory to add completion action", + END_ERR_MSG); + return 1; + }; +/* + * Record the completion callback data. + */ + a->fn = fn; + a->data = data; +/* + * Attempt to register the new action. + */ + if(_kt_set_action(gl->bindings, name, action_fn, a)) { + _err_record_msg(gl->err, _kt_last_error(gl->bindings), END_ERR_MSG); + _del_FreeListNode(gl->cpl_mem, (void *) a); + return 1; + }; + }; +/* + * Bind the action to a given key-sequence? + */ + if(keyseq && _kt_set_keybinding(gl->bindings, KTB_NORM, keyseq, name)) { + _err_record_msg(gl->err, _kt_last_error(gl->bindings), END_ERR_MSG); + return 1; + }; + return 0; +} + +/*....................................................................... + * Invoke an action function previously registered by a call to + * gl_register_action(). + */ +static KT_KEY_FN(gl_run_external_action) +{ + GlAfterAction status; /* The return value of the action function */ +/* + * Get the container of the action function and associated callback data. + */ + GlExternalAction *a = (GlExternalAction *) data; +/* + * Invoke the action function. + */ + status = a->fn(gl, a->data, count, gl->buff_curpos, gl->line); +/* + * If the callback took us out of raw (possibly non-blocking) input + * mode, restore this mode, and queue a redisplay of the input line. + */ + if(_gl_raw_io(gl, 1)) + return 1; +/* + * Finally, check to see what the action function wants us to do next. + */ + switch(status) { + default: + case GLA_ABORT: + gl_record_status(gl, GLR_ERROR, errno); + return 1; + break; + case GLA_RETURN: + return gl_newline(gl, 1, NULL); + break; + case GLA_CONTINUE: + break; + }; + return 0; +} + +/*....................................................................... + * In server-I/O mode the terminal is left in raw mode between calls + * to gl_get_line(), so it is necessary for the application to install + * terminal restoring signal handlers for signals that could terminate + * or suspend the process, plus a terminal reconfiguration handler to + * be called when a process resumption signal is received, and finally + * a handler to be called when a terminal-resize signal is received. + * + * Since there are many signals that by default terminate or suspend + * processes, and different systems support different sub-sets of + * these signals, this function provides a convenient wrapper around + * sigaction() for assigning the specified handlers to all appropriate + * signals. It also arranges that when any one of these signals is + * being handled, all other catchable signals are blocked. This is + * necessary so that the specified signal handlers can safely call + * gl_raw_io(), gl_normal_io() and gl_update_size() without + * reentrancy issues. + * + * Input: + * term_handler void (*)(int) The signal handler to invoke when + * a process-terminating signal is + * received. + * susp_handler void (*)(int) The signal handler to invoke when + * a process-suspending signal is + * received. + * cont_handler void (*)(int) The signal handler to invoke when + * a process-resumption signal is + * received (ie. SIGCONT). + * size_handler void (*)(int) The signal handler to invoke when + * a terminal-resize signal (ie. SIGWINCH) + * is received. + * Output: + * return int 0 - OK. + * 1 - Error. + */ +int gl_tty_signals(void (*term_handler)(int), void (*susp_handler)(int), + void (*cont_handler)(int), void (*size_handler)(int)) +{ + int i; +/* + * Search for signals of the specified classes, and assign the + * associated signal handler to them. + */ + for(i=0; iattr & GLSA_SUSP) { + if(gl_set_tty_signal(sig->signo, susp_handler)) + return 1; + } else if(sig->attr & GLSA_TERM) { + if(gl_set_tty_signal(sig->signo, term_handler)) + return 1; + } else if(sig->attr & GLSA_CONT) { + if(gl_set_tty_signal(sig->signo, cont_handler)) + return 1; + } else if(sig->attr & GLSA_SIZE) { + if(gl_set_tty_signal(sig->signo, size_handler)) + return 1; + }; + }; + return 0; +} + +/*....................................................................... + * This is a private function of gl_tty_signals(). It installs a given + * signal handler, and arranges that when that signal handler is being + * invoked other signals are blocked. The latter is important to allow + * functions like gl_normal_io(), gl_raw_io() and gl_update_size() + * to be called from signal handlers. + * + * Input: + * signo int The signal to be trapped. + * handler void (*)(int) The signal handler to assign to the signal. + */ +static int gl_set_tty_signal(int signo, void (*handler)(int)) +{ + SigAction act; /* The signal handler configuation */ +/* + * Arrange to block all trappable signals except the one that is being + * assigned (the trapped signal will be blocked automatically by the + * system). + */ + gl_list_trappable_signals(&act.sa_mask); + sigdelset(&act.sa_mask, signo); +/* + * Assign the signal handler. + */ + act.sa_handler = handler; +/* + * There is only one portable signal handling flag, and it isn't + * relevant to us, so don't specify any flags. + */ + act.sa_flags = 0; +/* + * Register the signal handler. + */ + if(sigaction(signo, &act, NULL)) + return 1; + return 0; +} + +/*....................................................................... + * Display a left-justified string over multiple terminal lines, + * taking account of the current width of the terminal. Optional + * indentation and an optional prefix string can be specified to be + * displayed at the start of each new terminal line used. Similarly, + * an optional suffix can be specified to be displayed at the end of + * each terminal line. If needed, a single paragraph can be broken + * across multiple calls. Note that literal newlines in the input + * string can be used to force a newline at any point and that you + * should use this feature to explicitly end all paragraphs, including + * at the end of the last string that you write. Note that when a new + * line is started between two words that are separated by spaces, + * those spaces are not output, whereas when a new line is started + * because a newline character was found in the string, only the + * spaces before the newline character are discarded. + * + * Input: + * gl GetLine * The resource object of gl_get_line(). + * indentation int The number of spaces of indentation to write + * at the beginning of each new terminal line. + * prefix const char * An optional prefix string to write after the + * indentation margin at the start of each new + * terminal line. You can specify NULL if no + * prefix is required. + * suffix const char * An optional suffix string to draw at the end + * of the terminal line. Spaces will be added + * where necessary to ensure that the suffix ends + * in the last column of the terminal line. If + * no suffix is desired, specify NULL. + * fill_char int The padding character to use when indenting + * the line or padding up to the suffix. + * def_width int If the terminal width isn't known, such as when + * writing to a pipe or redirecting to a file, + * this number specifies what width to assume. + * start int The number of characters already written to + * the start of the current terminal line. This + * is primarily used to allow individual + * paragraphs to be written over multiple calls + * to this function, but can also be used to + * allow you to start the first line of a + * paragraph with a different prefix or + * indentation than those specified above. + * string const char * The string to be written. + * Output: + * return int On error -1 is returned. Otherwise the + * return value is the terminal column index at + * which the cursor was left after writing the + * final word in the string. Successful return + * values can thus be passed verbatim to the + * 'start' arguments of subsequent calls to + * gl_display_text() to allow the printing of a + * paragraph to be broken across multiple calls + * to gl_display_text(). + */ +int gl_display_text(GetLine *gl, int indentation, const char *prefix, + const char *suffix, int fill_char, + int def_width, int start, const char *string) +{ + sigset_t oldset; /* The signals that were blocked on entry to this function */ + int status; /* The return status of _gl_completion_action() */ +/* + * Check the arguments? + */ + if(!gl || !string) { + errno = EINVAL; + return -1; + }; +/* + * Block all signals. + */ + if(gl_mask_signals(gl, &oldset)) + return -1; +/* + * Display the text while signals are blocked. + */ + status = _io_display_text(_io_write_stdio, gl->output_fp, indentation, + prefix, suffix, fill_char, + gl->ncolumn > 0 ? gl->ncolumn : def_width, + start, string); +/* + * Restore the process signal mask. + */ + gl_unmask_signals(gl, &oldset); + return status; +} + +/*....................................................................... + * Block all of the signals that we are currently trapping. + * + * Input: + * gl GetLine * The resource object of gl_get_line(). + * Input/Output: + * oldset sigset_t * The superseded process signal mask + * will be return in *oldset unless oldset is + * NULL. + * Output: + * return int 0 - OK. + * 1 - Error. + */ +static int gl_mask_signals(GetLine *gl, sigset_t *oldset) +{ +/* + * Block all signals in all_signal_set, along with any others that are + * already blocked by the application. + */ + if(sigprocmask(SIG_BLOCK, &gl->all_signal_set, oldset) >= 0) { + gl->signals_masked = 1; + return 0; + }; +/* + * On error attempt to query the current process signal mask, so + * that oldset be the correct process signal mask to restore later + * if the caller of this function ignores the error return value. + */ + if(oldset) + (void) sigprocmask(SIG_SETMASK, NULL, oldset); + gl->signals_masked = 0; + return 1; +} + +/*....................................................................... + * Restore a process signal mask that was previously returned via the + * oldset argument of gl_mask_signals(). + * + * Input: + * gl GetLine * The resource object of gl_get_line(). + * Input/Output: + * oldset sigset_t * The process signal mask to be restored. + * Output: + * return int 0 - OK. + * 1 - Error. + */ +static int gl_unmask_signals(GetLine *gl, sigset_t *oldset) +{ + gl->signals_masked = 0; + return sigprocmask(SIG_SETMASK, oldset, NULL) < 0; +} + +/*....................................................................... + * Arrange to temporarily catch the signals marked in gl->use_signal_set. + * + * Input: + * gl GetLine * The resource object of gl_get_line(). + * Output: + * return int 0 - OK. + * 1 - Error. + */ +static int gl_catch_signals(GetLine *gl) +{ + return sigprocmask(SIG_UNBLOCK, &gl->use_signal_set, NULL) < 0; +} + +/*....................................................................... + * Select the I/O mode to be used by gl_get_line(). + * + * Input: + * gl GetLine * The resource object of gl_get_line(). + * mode GlIOMode The I/O mode to establish. + * Output: + * return int 0 - OK. + * 1 - Error. + */ +int gl_io_mode(GetLine *gl, GlIOMode mode) +{ + sigset_t oldset; /* The signals that were blocked on entry to this function */ + int status; /* The return status of _gl_io_mode() */ +/* + * Check the arguments. + */ + if(!gl) { + errno = EINVAL; + return 1; + }; +/* + * Check that the requested mode is known. + */ + switch(mode) { + case GL_NORMAL_MODE: + case GL_SERVER_MODE: + break; + default: + errno = EINVAL; + _err_record_msg(gl->err, "Unknown gl_get_line() I/O mode requested.", + END_ERR_MSG); + return 1; + }; +/* + * Block all signals. + */ + if(gl_mask_signals(gl, &oldset)) + return 1; +/* + * Invoke the private body of this function. + */ + status = _gl_io_mode(gl, mode); +/* + * Restore the process signal mask. + */ + gl_unmask_signals(gl, &oldset); + return status; +} + +/*....................................................................... + * This is the private body of the public function, gl_io_mode(). + * It assumes that the caller has checked its arguments and blocked the + * delivery of signals. + */ +static int _gl_io_mode(GetLine *gl, GlIOMode mode) +{ +/* + * Are we already in the specified mode? + */ + if(mode == gl->io_mode) + return 0; +/* + * First revert to normal I/O in the current I/O mode. + */ + _gl_normal_io(gl); +/* + * Record the new mode. + */ + gl->io_mode = mode; +/* + * Perform any actions needed by the new mode. + */ + if(mode==GL_SERVER_MODE) { + if(_gl_raw_io(gl, 1)) + return 1; + }; + return 0; +} + +/*....................................................................... + * Return extra information (ie. in addition to that provided by errno) + * about the last error to occur in either gl_get_line() or its + * associated public functions. + * + * Input: + * gl GetLine * The resource object of gl_get_line(). + * Input/Output: + * buff char * An optional output buffer. Note that if the + * calling application calls any gl_*() + * functions from signal handlers, it should + * provide a buffer here, so that a copy of + * the latest error message can safely be made + * while signals are blocked. + * n size_t The allocated size of buff[]. + * Output: + * return const char * A pointer to the error message. This will + * be the buff argument, unless buff==NULL, in + * which case it will be a pointer to an + * internal error buffer. In the latter case, + * note that the contents of the returned buffer + * will change on subsequent calls to any gl_*() + * functions. + */ +const char *gl_error_message(GetLine *gl, char *buff, size_t n) +{ + if(!gl) { + static const char *msg = "NULL GetLine argument"; + if(buff) { + strncpy(buff, msg, n); + buff[n-1] = '\0'; + } else { + return msg; + }; + } else if(buff) { + sigset_t oldset; /* The signals that were blocked on entry to this block */ +/* + * Temporarily block all signals. + */ + gl_mask_signals(gl, &oldset); +/* + * Copy the error message into the specified buffer. + */ + if(buff && n > 0) { + strncpy(buff, _err_get_msg(gl->err), n); + buff[n-1] = '\0'; + }; +/* + * Restore the process signal mask before returning. + */ + gl_unmask_signals(gl, &oldset); + } else { + return _err_get_msg(gl->err); + }; + return buff; +} + +/*....................................................................... + * Return the signal mask used by gl_get_line(). This is the set of + * signals that gl_get_line() is currently configured to trap. + * + * Input: + * gl GetLine * The resource object of gl_get_line(). + * Input/Output: + * set sigset_t * The set of signals will be returned in *set, + * in the form of a signal process mask, as + * used by sigaction(), sigprocmask(), + * sigpending(), sigsuspend(), sigsetjmp() and + * other standard POSIX signal-aware + * functions. + * Output: + * return int 0 - OK. + * 1 - Error (examine errno for reason). + */ +int gl_list_signals(GetLine *gl, sigset_t *set) +{ +/* + * Check the arguments. + */ + if(!gl || !set) { + if(gl) + _err_record_msg(gl->err, "NULL argument(s)", END_ERR_MSG); + errno = EINVAL; + return 1; + }; +/* + * Copy the signal mask into *set. + */ + memcpy(set, &gl->all_signal_set, sizeof(*set)); + return 0; +} + +/*....................................................................... + * By default, gl_get_line() doesn't trap signals that are blocked + * when it is called. This default can be changed either on a + * per-signal basis by calling gl_trap_signal(), or on a global basis + * by calling this function. What this function does is add the + * GLS_UNBLOCK_SIG flag to all signals that are currently configured + * to be trapped by gl_get_line(), such that when subsequent calls to + * gl_get_line() wait for I/O, these signals are temporarily + * unblocked. This behavior is useful in non-blocking server-I/O mode, + * where it is used to avoid race conditions related to handling these + * signals externally to gl_get_line(). See the demonstration code in + * demo3.c, or the gl_handle_signal() man page for further + * information. + * + * Input: + * gl GetLine * The resource object of gl_get_line(). + */ +void gl_catch_blocked(GetLine *gl) +{ + sigset_t oldset; /* The process signal mask to restore */ + GlSignalNode *sig; /* A signal node in gl->sigs */ +/* + * Check the arguments. + */ + if(!gl) { + errno = EINVAL; + return; + }; +/* + * Temporarily block all signals while we modify the contents of gl. + */ + gl_mask_signals(gl, &oldset); +/* + * Add the GLS_UNBLOCK_SIG flag to all configured signals. + */ + for(sig=gl->sigs; sig; sig=sig->next) + sig->flags |= GLS_UNBLOCK_SIG; +/* + * Restore the process signal mask that was superseded by the call + * to gl_mask_signals(). + */ + gl_unmask_signals(gl, &oldset); + return; +} + +/*....................................................................... + * Respond to signals who's default effects have important + * consequences to gl_get_line(). This is intended for use in + * non-blocking server mode, where the external event loop is + * responsible for catching signals. Signals that are handled include + * those that by default terminate or suspend the process, and the + * signal that indicates that the terminal size has changed. Note that + * this function is not signal safe and should thus not be called from + * a signal handler itself. See the gl_io_mode() man page for how it + * should be used. + * + * In the case of signals that by default terminate or suspend + * processes, command-line editing will be suspended, the terminal + * returned to a usable state, then the default disposition of the + * signal restored and the signal resent, in order to suspend or + * terminate the process. If the process subsequently resumes, + * command-line editing is resumed. + * + * In the case of signals that indicate that the terminal has been + * resized, the new size will be queried, and any input line that is + * being edited will be redrawn to fit the new dimensions of the + * terminal. + * + * Input: + * signo int The number of the signal to respond to. + * gl GetLine * The first element of an array of 'ngl' GetLine + * objects. + * ngl int The number of elements in the gl[] array. Normally + * this will be one. + */ +void gl_handle_signal(int signo, GetLine *gl, int ngl) +{ + int attr; /* The attributes of the specified signal */ + sigset_t all_signals; /* The set of trappable signals */ + sigset_t oldset; /* The process signal mask to restore */ + int i; +/* + * NULL operation? + */ + if(ngl < 1 || !gl) + return; +/* + * Look up the default attributes of the specified signal. + */ + attr = gl_classify_signal(signo); +/* + * If the signal isn't known, we are done. + */ + if(!attr) + return; +/* + * Temporarily block all signals while we modify the gl objects. + */ + gl_list_trappable_signals(&all_signals); + sigprocmask(SIG_BLOCK, &all_signals, &oldset); +/* + * Suspend or terminate the process? + */ + if(attr & (GLSA_SUSP | GLSA_TERM)) { + gl_suspend_process(signo, gl, ngl); +/* + * Resize the terminal? Note that ioctl() isn't defined as being + * signal safe, so we can't call gl_update_size() here. However, + * gl_get_line() checks for resizes on each call, so simply arrange + * for the application's event loop to call gl_get_line() as soon as + * it becomes possible to write to the terminal. Note that if the + * caller is calling select() or poll when this happens, these functions + * get interrupted, since a signal has been caught. + */ + } else if(attr & GLSA_SIZE) { + for(i=0; iraw_mode) { + _gl_normal_io(obj); + if(!obj->raw_mode) /* Check that gl_normal_io() succeded */ + obj->raw_mode = -1; /* Flag raw mode as needing to be restored */ + }; + }; +/* + * Restore the system default disposition of the signal that we + * caught. Note that this signal is currently blocked. Note that we + * don't use memcpy() to copy signal sets here, because the signal safety + * of memcpy() is undefined. + */ + def_action.sa_handler = SIG_DFL; + { + char *orig = (char *) &all_signals; + char *dest = (char *) &def_action.sa_mask; + for(i=0; iraw_mode == -1) { /* Did we flag the need to restore raw mode? */ + obj->raw_mode = 0; /* gl_raw_io() does nothing unless raw_mode==0 */ + _gl_raw_io(obj, 1); + }; + }; +/* + * Restore the process signal mask to the way it was when this function + * was called. + */ + sigprocmask(SIG_SETMASK, &oldset, NULL); + return; +} + +/*....................................................................... + * Return the information about the default attributes of a given signal. + * The attributes that are returned are as defined by the standards that + * created them, including POSIX, SVR4 and 4.3+BSD, and are taken from a + * table in Richard Steven's book, "Advanced programming in the UNIX + * environment". + * + * Input: + * signo int The signal to be characterized. + * Output: + * return int A bitwise union of GlSigAttr enumerators, or 0 + * if the signal isn't known. + */ +static int gl_classify_signal(int signo) +{ + int i; +/* + * Search for the specified signal in the gl_signal_list[] table. + */ + for(i=0; isigno == signo) + return sig->attr; + }; +/* + * Signal not known. + */ + return 0; +} + +/*....................................................................... + * When in non-blocking server mode, this function can be used to abandon + * the current incompletely entered input line, and prepare to start + * editing a new line on the next call to gl_get_line(). + * + * Input: + * gl GetLine * The line editor resource object. + */ +void gl_abandon_line(GetLine *gl) +{ + sigset_t oldset; /* The process signal mask to restore */ +/* + * Check the arguments. + */ + if(!gl) { + errno = EINVAL; + return; + }; +/* + * Temporarily block all signals while we modify the contents of gl. + */ + gl_mask_signals(gl, &oldset); +/* + * Mark the input line as discarded. + */ + _gl_abandon_line(gl); +/* + * Restore the process signal mask that was superseded by the call + * to gl_mask_signals(). + */ + gl_unmask_signals(gl, &oldset); + return; +} + +/*....................................................................... + * This is the private body of the gl_abandon_line() function. It + * assumes that the caller has checked its arguments and blocked the + * delivery of signals. + */ +void _gl_abandon_line(GetLine *gl) +{ + gl->endline = 1; + gl->pending_io = GLP_WRITE; +} + +/*....................................................................... + * How many characters are needed to write a number as an octal string? + * + * Input: + * num unsigned The to be measured. + * Output: + * return int The number of characters needed. + */ +static int gl_octal_width(unsigned num) +{ + int n; /* The number of characters needed to render the number */ + for(n=1; num /= 8; n++) + ; + return n; +} + +/*....................................................................... + * Tell gl_get_line() the current terminal size. Note that this is only + * necessary on systems where changes in terminal size aren't reported + * via SIGWINCH. + * + * Input: + * gl GetLine * The resource object of gl_get_line(). + * ncolumn int The number of columns in the terminal. + * nline int The number of lines in the terminal. + * Output: + * return int 0 - OK. + * 1 - Error. + */ +int gl_set_term_size(GetLine *gl, int ncolumn, int nline) +{ + sigset_t oldset; /* The signals that were blocked on entry */ + /* to this function */ + int status; /* The return status */ +/* + * Block all signals while accessing gl. + */ + gl_mask_signals(gl, &oldset); +/* + * Install the new terminal size. + */ + status = _gl_set_term_size(gl, ncolumn, nline); +/* + * Restore the process signal mask before returning. + */ + gl_unmask_signals(gl, &oldset); + return status; +} + +/*....................................................................... + * This is the private body of the gl_set_term_size() function. It + * assumes that the caller has checked its arguments and blocked the + * delivery of signals. + */ +static int _gl_set_term_size(GetLine *gl, int ncolumn, int nline) +{ +/* + * Check the arguments. + */ + if(!gl) { + errno = EINVAL; + return 1; + }; +/* + * Reject non-sensical dimensions. + */ + if(ncolumn <= 0 || nline <= 0) { + _err_record_msg(gl->err, "Invalid terminal size", END_ERR_MSG); + errno = EINVAL; + return 1; + }; +/* + * Install the new dimensions in the terminal driver if possible, so + * that future calls to gl_query_size() get the new value. + */ +#ifdef TIOCSWINSZ + if(gl->is_term) { + struct winsize size; + size.ws_row = nline; + size.ws_col = ncolumn; + size.ws_xpixel = 0; + size.ws_ypixel = 0; + if(ioctl(gl->output_fd, TIOCSWINSZ, &size) == -1) { + _err_record_msg(gl->err, "Can't change terminal size", END_ERR_MSG); + return 1; + }; + }; +#endif +/* + * If an input line is in the process of being edited, redisplay it to + * accomodate the new dimensions, and record the new dimensions in + * gl->nline and gl->ncolumn. + */ + return gl_handle_tty_resize(gl, ncolumn, nline); +} + +/*....................................................................... + * Record a character in the input line buffer at a given position. + * + * Input: + * gl GetLine * The resource object of gl_get_line(). + * c char The character to be recorded. + * bufpos int The index in the buffer at which to record the + * character. + * Output: + * return int 0 - OK. + * 1 - Insufficient room. + */ +static int gl_buffer_char(GetLine *gl, char c, int bufpos) +{ +/* + * Guard against buffer overruns. + */ + if(bufpos >= gl->linelen) + return 1; +/* + * Record the new character. + */ + gl->line[bufpos] = c; +/* + * If the new character was placed beyond the end of the current input + * line, update gl->ntotal to reflect the increased number of characters + * that are in gl->line, and terminate the string. + */ + if(bufpos >= gl->ntotal) { + gl->ntotal = bufpos+1; + gl->line[gl->ntotal] = '\0'; + }; + return 0; +} + +/*....................................................................... + * Copy a given string into the input buffer, overwriting the current + * contents. + * + * Input: + * gl GetLine * The resource object of gl_get_line(). + * s const char * The string to be recorded. + * n int The number of characters to be copied from the + * string. + * bufpos int The index in the buffer at which to place the + * the first character of the string. + * Output: + * return int 0 - OK. + * 1 - String truncated to fit. + */ +static int gl_buffer_string(GetLine *gl, const char *s, int n, int bufpos) +{ + int nnew; /* The number of characters actually recorded */ + int i; +/* + * How many of the characters will fit within the buffer? + */ + nnew = bufpos + n <= gl->linelen ? n : (gl->linelen - bufpos); +/* + * Record the first nnew characters of s[] in the buffer. + */ + for(i=0; intotal + n > gl->linelen) + return 1; +/* + * Move everything including and beyond the character at 'start' + * towards the end of the string. + */ + memmove(gl->line + start + n, gl->line + start, gl->ntotal - start + 1); +/* + * Update the recorded size of the line. + */ + gl->ntotal += n; + return 1; +} + +/*....................................................................... + * Remove a given number of characters from the input buffer. This + * involves moving the characters that follow the removed characters to + * where the removed sub-string started in the input buffer. + * + * Input: + * gl GetLine * The resource object of gl_get_line(). + * start int The first character to be removed. + * n int The number of characters to remove. + */ +static void gl_remove_from_buffer(GetLine *gl, int start, int n) +{ + memmove(gl->line + start, gl->line + start + n, gl->ntotal - start - n + 1); +/* + * Update the recorded size of the line. + */ + gl->ntotal -= n; +} + +/*....................................................................... + * Truncate the string in the input line buffer after a given number of + * characters. + * + * Input: + * gl GetLine * The resource object of gl_get_line(). + * n int The new length of the line. + * Output: + * return int 0 - OK. + * 1 - n > gl->linelen. + */ +static int gl_truncate_buffer(GetLine *gl, int n) +{ + if(n > gl->linelen) + return 1; + gl->line[n] = '\0'; + gl->ntotal = n; + return 0; +} + +/*....................................................................... + * When the contents of gl->line[] are changed without calling any of the + * gl_ buffer manipulation functions, this function must be called to + * compute the length of this string, and ancillary information. + * + * Input: + * gl GetLine * The resource object of gl_get_line(). + */ +static void gl_update_buffer(GetLine *gl) +{ + int len; /* The length of the line */ +/* + * Measure the length of the input line. + */ + for(len=0; len <= gl->linelen && gl->line[len]; len++) + ; +/* + * Just in case the string wasn't correctly terminated, do so here. + */ + gl->line[len] = '\0'; +/* + * Record the number of characters that are now in gl->line[]. + */ + gl->ntotal = len; +/* + * Ensure that the cursor stays within the bounds of the modified + * input line. + */ + if(gl->buff_curpos > gl->ntotal) + gl->buff_curpos = gl->ntotal; +/* + * Arrange for the input line to be redrawn. + */ + gl_queue_redisplay(gl); + return; +} + +/*....................................................................... + * Erase the displayed input line, including its prompt, and leave the + * cursor where the erased line started. Note that to allow this + * function to be used when responding to a terminal resize, this + * function is designed to work even if the horizontal cursor position + * doesn't match the internally recorded position. + * + * Input: + * gl GetLine * The resource object of gl_get_line(). + * Output: + * return int 0 - OK. + * 1 - Error. + */ +static int gl_erase_line(GetLine *gl) +{ +/* + * Is a line currently displayed? + */ + if(gl->displayed) { +/* + * Relative the the start of the input line, which terminal line of + * the current input line is the cursor currently on? + */ + int cursor_line = gl->term_curpos / gl->ncolumn; +/* + * Move the cursor to the start of the line. + */ + for( ; cursor_line > 0; cursor_line--) { + if(gl_print_control_sequence(gl, 1, gl->up)) + return 1; + }; + if(gl_print_control_sequence(gl, 1, gl->bol)) + return 1; +/* + * Clear from the start of the line to the end of the terminal. + */ + if(gl_print_control_sequence(gl, gl->nline, gl->clear_eod)) + return 1; +/* + * Mark the line as no longer displayed. + */ + gl_line_erased(gl); + }; + return 0; +} + +/*....................................................................... + * Arrange for the input line to be redisplayed by gl_flush_output(), + * as soon as the output queue becomes empty. + * + * Input: + * gl GetLine * The resource object of gl_get_line(). + */ +static void gl_queue_redisplay(GetLine *gl) +{ + gl->redisplay = 1; + gl->pending_io = GLP_WRITE; +} + +/*....................................................................... + * Truncate the displayed input line starting from the current + * terminal cursor position, and leave the cursor at the end of the + * truncated line. The input-line buffer is not affected. + * + * Input: + * gl GetLine * The resource object of gl_get_line(). + * Output: + * return int 0 - OK. + * 1 - Error. + */ +static int gl_truncate_display(GetLine *gl) +{ +/* + * Keep a record of the current terminal cursor position. + */ + int term_curpos = gl->term_curpos; +/* + * First clear from the cursor to the end of the current input line. + */ + if(gl_print_control_sequence(gl, 1, gl->clear_eol)) + return 1; +/* + * If there is more than one line displayed, go to the start of the + * next line and clear from there to the end of the display. Note that + * we can't use clear_eod to do the whole job of clearing from the + * current cursor position to the end of the terminal because + * clear_eod is only defined when used at the start of a terminal line + * (eg. with gnome terminals, clear_eod clears from the start of the + * current terminal line, rather than from the current cursor + * position). + */ + if(gl->term_len / gl->ncolumn > gl->term_curpos / gl->ncolumn) { + if(gl_print_control_sequence(gl, 1, gl->down) || + gl_print_control_sequence(gl, 1, gl->bol) || + gl_print_control_sequence(gl, gl->nline, gl->clear_eod)) + return 1; +/* + * Where is the cursor now? + */ + gl->term_curpos = gl->ncolumn * (term_curpos / gl->ncolumn + 1); +/* + * Restore the cursor position. + */ + gl_set_term_curpos(gl, term_curpos); + }; +/* + * Update the recorded position of the final character. + */ + gl->term_len = gl->term_curpos; + return 0; +} + +/*....................................................................... + * Return the set of all trappable signals. + * + * Input: + * signals sigset_t * The set of signals will be recorded in + * *signals. + */ +static void gl_list_trappable_signals(sigset_t *signals) +{ +/* + * Start with the set of all signals. + */ + sigfillset(signals); +/* + * Remove un-trappable signals from this set. + */ +#ifdef SIGKILL + sigdelset(signals, SIGKILL); +#endif +#ifdef SIGSTOP + sigdelset(signals, SIGSTOP); +#endif +} + +/*....................................................................... + * Read an input line from a non-interactive input stream. + * + * Input: + * gl GetLine * The resource object of gl_get_line(). + * Output: + * return int 0 - OK + * 1 - Error. + */ +static int gl_read_stream_line(GetLine *gl) +{ + char c = '\0'; /* The latest character read from fp */ +/* + * Record the fact that we are about to read input. + */ + gl->pending_io = GLP_READ; +/* + * If we are starting a new line, reset the line-editing parameters, + * and discard the previous input line. + */ + if(gl->endline) { + gl_reset_editor(gl); + gl_truncate_buffer(gl, 0); + }; +/* + * Read one character at a time. + */ + while(gl->ntotal < gl->linelen && c != '\n') { +/* + * Attempt to read one more character. + */ + switch(gl_read_input(gl, &c)) { + case GL_READ_OK: + break; + case GL_READ_EOF: /* Reached end-of-file? */ +/* + * If any characters were read before the end-of-file condition, + * interpolate a newline character, so that the caller sees a + * properly terminated line. Otherwise return an end-of-file + * condition. + */ + if(gl->ntotal > 0) { + c = '\n'; + } else { + gl_record_status(gl, GLR_EOF, 0); + return 1; + }; + break; + case GL_READ_BLOCKED: /* Input blocked? */ + gl_record_status(gl, GLR_BLOCKED, BLOCKED_ERRNO); + return 1; + break; + case GL_READ_ERROR: /* I/O error? */ + return 1; + break; + }; +/* + * Append the character to the line buffer. + */ + if(gl_buffer_char(gl, c, gl->ntotal)) + return 1; + }; +/* + * Was the end of the input line reached before running out of buffer space? + */ + gl->endline = (c == '\n'); + return 0; +} + +/*....................................................................... + * Read a single character from a non-interactive input stream. + * + * Input: + * gl GetLine * The resource object of gl_get_line(). + * Output: + * return int The character, or EOF on error. + */ +static int gl_read_stream_char(GetLine *gl) +{ + char c = '\0'; /* The latest character read from fp */ + int retval = EOF; /* The return value of this function */ +/* + * Arrange to discard any incomplete input line. + */ + _gl_abandon_line(gl); +/* + * Record the fact that we are about to read input. + */ + gl->pending_io = GLP_READ; +/* + * Attempt to read one more character. + */ + switch(gl_read_input(gl, &c)) { + case GL_READ_OK: /* Success */ + retval = c; + break; + case GL_READ_BLOCKED: /* The read blocked */ + gl_record_status(gl, GLR_BLOCKED, BLOCKED_ERRNO); + retval = EOF; /* Failure */ + break; + case GL_READ_EOF: /* End of file reached */ + gl_record_status(gl, GLR_EOF, 0); + retval = EOF; /* Failure */ + break; + case GL_READ_ERROR: + retval = EOF; /* Failure */ + break; + }; + return retval; +} + +/*....................................................................... + * Bind a key sequence to a given action. + * + * Input: + * gl GetLine * The resource object of gl_get_line(). + * origin GlKeyOrigin The originator of the key binding. + * key const char * The key-sequence to be bound (or unbound). + * action const char * The name of the action to bind the key to, + * or either NULL or "" to unbind the + * key-sequence. + * Output: + * return int 0 - OK + * 1 - Error. + */ +int gl_bind_keyseq(GetLine *gl, GlKeyOrigin origin, const char *keyseq, + const char *action) +{ + KtBinder binder; /* The private internal equivalent of 'origin' */ +/* + * Check the arguments. + */ + if(!gl || !keyseq) { + errno = EINVAL; + if(gl) + _err_record_msg(gl->err, "NULL argument(s)", END_ERR_MSG); + return 1; + }; +/* + * An empty action string requests that the key-sequence be unbound. + * This is indicated to _kt_set_keybinding() by passing a NULL action + * string, so convert an empty string to a NULL action pointer. + */ + if(action && *action=='\0') + action = NULL; +/* + * Translate the public originator enumeration to the private equivalent. + */ + binder = origin==GL_USER_KEY ? KTB_USER : KTB_NORM; +/* + * Bind the action to a given key-sequence? + */ + if(keyseq && _kt_set_keybinding(gl->bindings, binder, keyseq, action)) { + _err_record_msg(gl->err, _kt_last_error(gl->bindings), END_ERR_MSG); + return 1; + }; + return 0; +} + +/*....................................................................... + * This is the public wrapper around the gl_clear_termina() function. + * It clears the terminal and leaves the cursor at the home position. + * In server I/O mode, the next call to gl_get_line() will also + * redisplay the current input line. + * + * Input: + * gl GetLine * The resource object of gl_get_line(). + * Output: + * return int 0 - OK. + * 1 - Error. + */ +int gl_erase_terminal(GetLine *gl) +{ + sigset_t oldset; /* The signals that were blocked on entry */ + /* to this function */ + int status; /* The return status */ +/* + * Block all signals while accessing gl. + */ + gl_mask_signals(gl, &oldset); +/* + * Clear the terminal. + */ + status = gl_clear_screen(gl, 1, NULL); +/* + * Attempt to flush the clear-screen control codes to the terminal. + * If this doesn't complete the job, the next call to gl_get_line() + * will. + */ + (void) gl_flush_output(gl); +/* + * Restore the process signal mask before returning. + */ + gl_unmask_signals(gl, &oldset); + return status; +} + +/*....................................................................... + * This function must be called by any function that erases the input + * line. + * + * Input: + * gl GetLine * The resource object of gl_get_line(). + */ +static void gl_line_erased(GetLine *gl) +{ + gl->displayed = 0; + gl->term_curpos = 0; + gl->term_len = 0; +} + +/*....................................................................... + * Append a specified line to the history list. + * + * Input: + * gl GetLine * The resource object of gl_get_line(). + * line const char * The line to be added. + * Output: + * return int 0 - OK. + * 1 - Error. + */ +int gl_append_history(GetLine *gl, const char *line) +{ + sigset_t oldset; /* The signals that were blocked on entry */ + /* to this function */ + int status; /* The return status */ +/* + * Check the arguments. + */ + if(!gl || !line) { + errno = EINVAL; + return 1; + }; +/* + * Block all signals. + */ + if(gl_mask_signals(gl, &oldset)) + return 1; +/* + * Execute the private body of the function while signals are blocked. + */ + status = _gl_append_history(gl, line); +/* + * Restore the process signal mask. + */ + gl_unmask_signals(gl, &oldset); + return status; +} + +/*....................................................................... + * This is the private body of the public function, gl_append_history(). + * It assumes that the caller has checked its arguments and blocked the + * delivery of signals. + */ +static int _gl_append_history(GetLine *gl, const char *line) +{ + int status =_glh_add_history(gl->glh, line, 0); + if(status) + _err_record_msg(gl->err, _glh_last_error(gl->glh), END_ERR_MSG); + return status; +} + +/*....................................................................... + * Enable or disable the automatic addition of newly entered lines to the + * history list. + * + * Input: + * gl GetLine * The resource object of gl_get_line(). + * enable int If true, subsequently entered lines will + * automatically be added to the history list + * before they are returned to the caller of + * gl_get_line(). If 0, the choice of how and + * when to archive lines in the history list, + * is left up to the calling application, which + * can do so via calls to gl_append_history(). + * Output: + * return int 0 - OK. + * 1 - Error. + */ +int gl_automatic_history(GetLine *gl, int enable) +{ + sigset_t oldset; /* The signals that were blocked on entry */ + /* to this function */ +/* + * Check the arguments. + */ + if(!gl) { + errno = EINVAL; + return 1; + }; +/* + * Block all signals. + */ + if(gl_mask_signals(gl, &oldset)) + return 1; +/* + * Execute the private body of the function while signals are blocked. + */ + gl->automatic_history = enable; +/* + * Restore the process signal mask. + */ + gl_unmask_signals(gl, &oldset); + return 0; +} + +/*....................................................................... + * This is a public function that reads a single uninterpretted + * character from the user, without displaying anything. + * + * Input: + * gl GetLine * A resource object previously returned by + * new_GetLine(). + * Output: + * return int The character that was read, or EOF if the read + * had to be aborted (in which case you can call + * gl_return_status() to find out why). + */ +int gl_read_char(GetLine *gl) +{ + int retval; /* The return value of _gl_read_char() */ +/* + * This function can be called from application callback functions, + * so check whether signals have already been masked, so that we don't + * do it again, and overwrite gl->old_signal_set. + */ + int was_masked = gl->signals_masked; +/* + * Check the arguments. + */ + if(!gl) { + errno = EINVAL; + return EOF; + }; +/* + * Temporarily block all of the signals that we have been asked to trap. + */ + if(!was_masked && gl_mask_signals(gl, &gl->old_signal_set)) + return EOF; +/* + * Perform the character reading task. + */ + retval = _gl_read_char(gl); +/* + * Restore the process signal mask to how it was when this function was + * first called. + */ + if(!was_masked) + gl_unmask_signals(gl, &gl->old_signal_set); + return retval; +} + +/*....................................................................... + * This is the main body of the public function gl_read_char(). + */ +static int _gl_read_char(GetLine *gl) +{ + int retval = EOF; /* The return value */ + int waserr = 0; /* True if an error occurs */ + char c; /* The character read */ +/* + * This function can be called from application callback functions, + * so check whether signals have already been overriden, so that we don't + * overwrite the preserved signal handlers with gl_get_line()s. Also + * record whether we are currently in raw I/O mode or not, so that this + * can be left in the same state on leaving this function. + */ + int was_overriden = gl->signals_overriden; + int was_raw = gl->raw_mode; +/* + * Also keep a record of the direction of any I/O that gl_get_line() + * is awaiting, so that we can restore this status on return. + */ + GlPendingIO old_pending_io = gl->pending_io; +/* + * Assume that this call will successfully complete the input operation + * until proven otherwise. + */ + gl_clear_status(gl); +/* + * If this is the first call to this function or gl_get_line(), + * since new_GetLine(), complete any postponed configuration. + */ + if(!gl->configured) { + (void) _gl_configure_getline(gl, NULL, NULL, TECLA_CONFIG_FILE); + gl->configured = 1; + }; +/* + * Before installing our signal handler functions, record the fact + * that there are no pending signals. + */ + gl_pending_signal = -1; +/* + * Temporarily override the signal handlers of the calling program, + * so that we can intercept signals that would leave the terminal + * in a bad state. + */ + if(!was_overriden) + waserr = gl_override_signal_handlers(gl); +/* + * After recording the current terminal settings, switch the terminal + * into raw input mode, without redisplaying any partially entered input + * line. + */ + if(!was_raw) + waserr = waserr || _gl_raw_io(gl, 0); +/* + * Attempt to read the line. This will require more than one attempt if + * either a current temporary input file is opened by gl_get_input_line() + * or the end of a temporary input file is reached by gl_read_stream_line(). + */ + while(!waserr) { +/* + * Read a line from a non-interactive stream? + */ + if(gl->file_fp || !gl->is_term) { + retval = gl_read_stream_char(gl); + if(retval != EOF) { /* Success? */ + break; + } else if(gl->file_fp) { /* End of temporary input file? */ + gl_revert_input(gl); + gl_record_status(gl, GLR_NEWLINE, 0); + } else { /* An error? */ + waserr = 1; + break; + }; + }; +/* + * Read from the terminal? Note that the above if() block may have + * changed gl->file_fp, so it is necessary to retest it here, rather + * than using an else statement. + */ + if(!gl->file_fp && gl->is_term) { +/* + * Flush any pending output to the terminal before waiting + * for the user to type a character. + */ + if(_glq_char_count(gl->cq) > 0 && gl_flush_output(gl)) { + retval = EOF; +/* + * Read one character. Don't append it to the key buffer, since + * this would subseuqnely appear as bogus input to the line editor. + */ + } else if(gl_read_terminal(gl, 0, &c) == 0) { +/* + * Record the character for return. + */ + retval = c; +/* + * In this mode, count each character as being a new key-sequence. + */ + gl->keyseq_count++; +/* + * Delete the character that was read, from the key-press buffer. + */ + gl_discard_chars(gl, 1); + }; + if(retval==EOF) + waserr = 1; + else + break; + }; + }; +/* + * If an error occurred, but gl->rtn_status is still set to + * GLR_NEWLINE, change the status to GLR_ERROR. Otherwise + * leave it at whatever specific value was assigned by the function + * that aborted input. This means that only functions that trap + * non-generic errors have to remember to update gl->rtn_status + * themselves. + */ + if(waserr && gl->rtn_status == GLR_NEWLINE) + gl_record_status(gl, GLR_ERROR, errno); +/* + * Restore terminal settings, if they were changed by this function. + */ + if(!was_raw && gl->io_mode != GL_SERVER_MODE) + _gl_normal_io(gl); +/* + * Restore the signal handlers, if they were overriden by this function. + */ + if(!was_overriden) + gl_restore_signal_handlers(gl); +/* + * If this function gets aborted early, the errno value associated + * with the event that caused this to happen is recorded in + * gl->rtn_errno. Since errno may have been overwritten by cleanup + * functions after this, restore its value to the value that it had + * when the error condition occured, so that the caller can examine it + * to find out what happened. + */ + errno = gl->rtn_errno; +/* + * Error conditions are signalled to the caller, by setting the returned + * character to EOF. + */ + if(gl->rtn_status != GLR_NEWLINE) + retval = EOF; +/* + * Restore the indication of what direction of I/O gl_get_line() + * was awaiting before this call. + */ + gl->pending_io = old_pending_io; +/* + * Return the acquired character. + */ + return retval; +} + +/*....................................................................... + * Reset the GetLine completion status. This function should be called + * at the start of gl_get_line(), gl_read_char() and gl_query_char() + * to discard the completion status and non-zero errno value of any + * preceding calls to these functions. + * + * Input: + * gl GetLine * The resource object of this module. + */ +static void gl_clear_status(GetLine *gl) +{ + gl_record_status(gl, GLR_NEWLINE, 0); +} + +/*....................................................................... + * When an error or other event causes gl_get_line() to return, this + * function should be called to record information about what + * happened, including the value of errno and the value that + * gl_return_status() should return. + * + * Input: + * gl GetLine * The resource object of this module. + * rtn_status GlReturnStatus The completion status. To clear a + * previous abnormal completion status, + * specify GLR_NEWLINE (this is what + * gl_clear_status() does). + * rtn_errno int The associated value of errno. + */ +static void gl_record_status(GetLine *gl, GlReturnStatus rtn_status, + int rtn_errno) +{ +/* + * If rtn_status==GLR_NEWLINE, then this resets the completion status, so we + * should always heed this. Otherwise, only record the first abnormal + * condition that occurs after such a reset. + */ + if(rtn_status == GLR_NEWLINE || gl->rtn_status == GLR_NEWLINE) { + gl->rtn_status = rtn_status; + gl->rtn_errno = rtn_errno; + }; +} + diff --git a/libtecla-1.6.1/getline.h b/libtecla-1.6.1/getline.h new file mode 100644 index 0000000..60a9c33 --- /dev/null +++ b/libtecla-1.6.1/getline.h @@ -0,0 +1,88 @@ +#ifndef getline_h +#define getline_h + +/* + * Copyright (c) 2000, 2001, 2002, 2003, 2004 by Martin C. Shepherd. + * + * All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, and/or sell copies of the Software, and to permit persons + * to whom the Software is furnished to do so, provided that the above + * copyright notice(s) and this permission notice appear in all copies of + * the Software and that both the above copyright notice(s) and this + * permission notice appear in supporting documentation. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT + * OF THIRD PARTY RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR + * HOLDERS INCLUDED IN THIS NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL + * INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING + * FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, + * NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION + * WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + * Except as contained in this notice, the name of a copyright holder + * shall not be used in advertising or otherwise to promote the sale, use + * or other dealings in this Software without prior written authorization + * of the copyright holder. + */ + +/* + * Set the name of the getline configuration file. + */ +#define TECLA_CONFIG_FILE "~/.teclarc" + +/* + * The following macro returns non-zero if a character is + * a control character. + */ +#define IS_CTRL_CHAR(c) ((unsigned char)(c) < ' ' || (unsigned char)(c)=='\177') + +/* + * The following macro returns non-zero if a character is + * a meta character. + */ +#define IS_META_CHAR(c) (((unsigned char)(c) & 0x80) && !isprint((int)(unsigned char)(c))) + +/* + * Return the character that would be produced by pressing the + * specified key plus the control key. + */ +#define MAKE_CTRL(c) ((c)=='?' ? '\177' : ((unsigned char)toupper(c) & ~0x40)) + +/* + * Return the character that would be produced by pressing the + * specified key plus the meta key. + */ +#define MAKE_META(c) ((unsigned char)(c) | 0x80) + +/* + * Given a binary control character, return the character that + * had to be pressed at the same time as the control key. + */ +#define CTRL_TO_CHAR(c) (toupper((unsigned char)(c) | 0x40)) + +/* + * Given a meta character, return the character that was pressed + * at the same time as the meta key. + */ +#define META_TO_CHAR(c) ((unsigned char)(c) & ~0x80) + +/* + * Specify the string of characters other than the alphanumeric characters, + * that are to be considered parts of words. + */ +#define GL_WORD_CHARS "_*\?\\[]" + +/* + * Define the escape character, both as a string and as a character. + */ +#define GL_ESC_STR "\033" +#define GL_ESC_CHAR '\033' + +#endif diff --git a/libtecla-1.6.1/hash.c b/libtecla-1.6.1/hash.c new file mode 100644 index 0000000..acaaff6 --- /dev/null +++ b/libtecla-1.6.1/hash.c @@ -0,0 +1,737 @@ +/* + * Copyright (c) 2000, 2001, 2002, 2003, 2004 by Martin C. Shepherd. + * + * All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, and/or sell copies of the Software, and to permit persons + * to whom the Software is furnished to do so, provided that the above + * copyright notice(s) and this permission notice appear in all copies of + * the Software and that both the above copyright notice(s) and this + * permission notice appear in supporting documentation. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT + * OF THIRD PARTY RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR + * HOLDERS INCLUDED IN THIS NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL + * INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING + * FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, + * NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION + * WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + * Except as contained in this notice, the name of a copyright holder + * shall not be used in advertising or otherwise to promote the sale, use + * or other dealings in this Software without prior written authorization + * of the copyright holder. + */ + +#include +#include +#include +#include +#include + +#include "hash.h" +#include "strngmem.h" +#include "freelist.h" + +/* + * The following container object contains free-lists to be used + * for allocation of HashTable containers and nodes. + */ +struct HashMemory { + FreeList *hash_memory; /* HashTable free-list */ + FreeList *node_memory; /* HashNode free-list */ + StringMem *string_memory; /* Memory used to allocate hash strings */ +}; + +/* + * Define a hash symbol-table entry. + * See symbol.h for the definition of the Symbol container type. + */ +typedef struct HashNode HashNode; +struct HashNode { + Symbol symbol; /* The symbol stored in the hash-entry */ + HashNode *next; /* The next hash-table entry in a bucket list */ +}; + +/* + * Each hash-table bucket contains a linked list of entries that + * hash to the same bucket. + */ +typedef struct { + HashNode *head; /* The head of the bucket hash-node list */ + int count; /* The number of entries in the list */ +} HashBucket; + +/* + * A hash-table consists of 'size' hash buckets. + * Note that the HashTable typedef for this struct is contained in hash.h. + */ +struct HashTable { + HashMemory *mem; /* HashTable free-list */ + int internal_mem; /* True if 'mem' was allocated by _new_HashTable() */ + int case_sensitive; /* True if case is significant in lookup keys */ + int size; /* The number of hash buckets */ + HashBucket *bucket; /* An array of 'size' hash buckets */ + int (*keycmp)(const char *, const char *); /* Key comparison function */ + void *app_data; /* Application-provided data */ + HASH_DEL_FN(*del_fn); /* Application-provided 'app_data' destructor */ +}; + +static HashNode *_del_HashNode(HashTable *hash, HashNode *node); +static HashNode *_new_HashNode(HashTable *hash, const char *name, int code, + void (*fn)(void), void *data, SYM_DEL_FN(*del_fn)); +static HashNode *_find_HashNode(HashTable *hash, HashBucket *bucket, + const char *name, HashNode **prev); +static HashBucket *_find_HashBucket(HashTable *hash, const char *name); +static int _ht_lower_strcmp(const char *node_key, const char *look_key); +static int _ht_strcmp(const char *node_key, const char *look_key); + +/*....................................................................... + * Allocate a free-list for use in allocating hash tables and their nodes. + * + * Input: + * list_count int The number of HashTable containers per free-list + * block. + * node_count int The number of HashTable nodes per free-list block. + * Output: + * return HashMemory * The new free-list for use in allocating hash tables + * and their nodes. + */ +HashMemory *_new_HashMemory(int hash_count, int node_count) +{ + HashMemory *mem; +/* + * Allocate the free-list container. + */ + mem = (HashMemory *) malloc(sizeof(HashMemory)); + if(!mem) { + errno = ENOMEM; + return NULL; + }; +/* + * Initialize the container at least up to the point at which it can + * safely be passed to _del_HashMemory(). + */ + mem->hash_memory = NULL; + mem->node_memory = NULL; + mem->string_memory = NULL; +/* + * Allocate the two free-lists. + */ + mem->hash_memory = _new_FreeList(sizeof(HashTable), hash_count); + if(!mem->hash_memory) + return _del_HashMemory(mem, 1); + mem->node_memory = _new_FreeList(sizeof(HashNode), node_count); + if(!mem->node_memory) + return _del_HashMemory(mem, 1); + mem->string_memory = _new_StringMem(64); + if(!mem->string_memory) + return _del_HashMemory(mem, 1); +/* + * Return the free-list container. + */ + return mem; +} + +/*....................................................................... + * Delete a HashTable free-list. An error will be displayed if the list is + * still in use and the deletion will be aborted. + * + * Input: + * mem HashMemory * The free-list container to be deleted. + * force int If force==0 then _del_HashMemory() will complain + * and refuse to delete the free-list if any + * of nodes have not been returned to the free-list. + * If force!=0 then _del_HashMemory() will not check + * whether any nodes are still in use and will + * always delete the list. + * Output: + * return HashMemory * Always NULL (even if the memory could not be + * deleted). + */ +HashMemory *_del_HashMemory(HashMemory *mem, int force) +{ + if(mem) { + if(!force && (_busy_FreeListNodes(mem->hash_memory) > 0 || + _busy_FreeListNodes(mem->node_memory) > 0)) { + errno = EBUSY; + return NULL; + }; + mem->hash_memory = _del_FreeList(mem->hash_memory, force); + mem->node_memory = _del_FreeList(mem->node_memory, force); + mem->string_memory = _del_StringMem(mem->string_memory, force); + free(mem); + }; + return NULL; +} + +/*....................................................................... + * Create a new hash table. + * + * Input: + * mem HashMemory * An optional free-list for use in allocating + * HashTable containers and nodes. See explanation + * in hash.h. If you are going to allocate more + * than one hash table, then it will be more + * efficient to allocate a single free-list for + * all of them than to force each hash table + * to allocate its own private free-list. + * size int The size of the hash table. Best performance + * will be acheived if this is a prime number. + * hcase HashCase Specify how symbol case is considered when + * looking up symbols, from: + * IGNORE_CASE - Upper and lower case versions + * of a letter are treated as + * being identical. + * HONOUR_CASE - Upper and lower case versions + * of a letter are treated as + * being distinct. + * characters in a lookup name is significant. + * app_data void * Optional application data to be registered + * to the table. This is presented to user + * provided SYM_DEL_FN() symbol destructors along + * with the symbol data. + * del_fn() HASH_DEL_FN(*) If you want app_data to be free'd when the + * hash-table is destroyed, register a suitable + * destructor function here. + * Output: + * return HashTable * The new hash table, or NULL on error. + */ +HashTable *_new_HashTable(HashMemory *mem, int size, HashCase hcase, + void *app_data, HASH_DEL_FN(*del_fn)) +{ + HashTable *hash; /* The table to be returned */ + int allocate_mem = !mem; /* True if mem should be internally allocated */ + int i; +/* + * Check arguments. + */ + if(size <= 0) { + errno = EINVAL; + return NULL; + }; +/* + * Allocate an internal free-list? + */ + if(allocate_mem) { + mem = _new_HashMemory(1, 100); + if(!mem) + return NULL; + }; +/* + * Allocate the container. + */ + hash = (HashTable *) _new_FreeListNode(mem->hash_memory); + if(!hash) { + errno = ENOMEM; + if(allocate_mem) + mem = _del_HashMemory(mem, 1); + return NULL; + }; +/* + * Before attempting any operation that might fail, initialize + * the container at least up to the point at which it can safely + * be passed to _del_HashTable(). + */ + hash->mem = mem; + hash->internal_mem = allocate_mem; + hash->case_sensitive = hcase==HONOUR_CASE; + hash->size = size; + hash->bucket = NULL; + hash->keycmp = hash->case_sensitive ? _ht_strcmp : _ht_lower_strcmp; + hash->app_data = app_data; + hash->del_fn = del_fn; +/* + * Allocate the array of 'size' hash buckets. + */ + hash->bucket = (HashBucket *) malloc(sizeof(HashBucket) * size); + if(!hash->bucket) { + errno = ENOMEM; + return _del_HashTable(hash); + }; +/* + * Initialize the bucket array. + */ + for(i=0; ibucket + i; + b->head = NULL; + b->count = 0; + }; +/* + * The table is ready for use - albeit currently empty. + */ + return hash; +} + +/*....................................................................... + * Delete a hash-table. + * + * Input: + * hash HashTable * The hash table to be deleted. + * Output: + * return HashTable * The deleted hash table (always NULL). + */ +HashTable *_del_HashTable(HashTable *hash) +{ + if(hash) { +/* + * Clear and delete the bucket array. + */ + if(hash->bucket) { + _clear_HashTable(hash); + free(hash->bucket); + hash->bucket = NULL; + }; +/* + * Delete application data. + */ + if(hash->del_fn) + hash->del_fn(hash->app_data); +/* + * If the hash table was allocated from an internal free-list, delete + * it and the hash table by deleting the free-list. Otherwise just + * return the hash-table to the external free-list. + */ + if(hash->internal_mem) + _del_HashMemory(hash->mem, 1); + else + hash = (HashTable *) _del_FreeListNode(hash->mem->hash_memory, hash); + }; + return NULL; +} + +/*....................................................................... + * Create and install a new entry in a hash table. If an entry with the + * same name already exists, replace its contents with the new data. + * + * Input: + * hash HashTable * The hash table to insert the symbol into. + * name const char * The name to tag the entry with. + * code int An application-specific code to be stored in + * the entry. + * fn void (*)(void) An application-specific function to be stored + * in the entry. + * data void * An application-specific pointer to data to be + * associated with the entry, or NULL if not + * relevant. + * del_fn SYM_DEL_FN(*) An optional destructor function. When the + * symbol is deleted this function will be called + * with the 'code' and 'data' arguments given + * above. Any application data that was registered + * to the table via the app_data argument of + * _new_HashTable() will also be passed. + * Output: + * return HashNode * The new entry, or NULL if there was insufficient + * memory or the arguments were invalid. + */ +Symbol *_new_HashSymbol(HashTable *hash, const char *name, int code, + void (*fn)(void), void *data, SYM_DEL_FN(*del_fn)) +{ + HashBucket *bucket; /* The hash-bucket associated with the name */ + HashNode *node; /* The new node */ +/* + * Check arguments. + */ + if(!hash || !name) { + errno = EINVAL; + return NULL; + }; +/* + * Get the hash bucket of the specified name. + */ + bucket = _find_HashBucket(hash, name); +/* + * See if a node with the same name already exists. + */ + node = _find_HashNode(hash, bucket, name, NULL); +/* + * If found, delete its contents by calling the user-supplied + * destructor function, if provided. + */ + if(node) { + if(node->symbol.data && node->symbol.del_fn) { + node->symbol.data = node->symbol.del_fn(hash->app_data, node->symbol.code, + node->symbol.data); + }; +/* + * Allocate a new node if necessary. + */ + } else { + node = _new_HashNode(hash, name, code, fn, data, del_fn); + if(!node) + return NULL; + }; +/* + * Install the node at the head of the hash-bucket list. + */ + node->next = bucket->head; + bucket->head = node; + bucket->count++; + return &node->symbol; +} + +/*....................................................................... + * Remove and delete a given hash-table entry. + * + * Input: + * hash HashTable * The hash table to find the symbol in. + * name const char * The name of the entry. + * Output: + * return HashNode * The deleted hash node (always NULL). + */ +Symbol *_del_HashSymbol(HashTable *hash, const char *name) +{ + if(hash && name) { + HashBucket *bucket = _find_HashBucket(hash, name); + HashNode *prev; /* The node preceding the located node */ + HashNode *node = _find_HashNode(hash, bucket, name, &prev); +/* + * Node found? + */ + if(node) { +/* + * Remove the node from the bucket list. + */ + if(prev) { + prev->next = node->next; + } else { + bucket->head = node->next; + }; +/* + * Record the loss of a node. + */ + bucket->count--; +/* + * Delete the node. + */ + (void) _del_HashNode(hash, node); + }; + }; + return NULL; +} + +/*....................................................................... + * Look up a symbol in the hash table. + * + * Input: + * hash HashTable * The table to look up the string in. + * name const char * The name of the symbol to look up. + * Output: + * return Symbol * The located hash-table symbol, or NULL if not + * found. + */ +Symbol *_find_HashSymbol(HashTable *hash, const char *name) +{ + HashBucket *bucket; /* The hash-table bucket associated with name[] */ + HashNode *node; /* The hash-table node of the requested symbol */ +/* + * Check arguments. + */ + if(!hash) + return NULL; +/* + * Nothing to lookup? + */ + if(!name) + return NULL; +/* + * Hash the name to a hash-table bucket. + */ + bucket = _find_HashBucket(hash, name); +/* + * Find the bucket entry that exactly matches the name. + */ + node = _find_HashNode(hash, bucket, name, NULL); + if(!node) + return NULL; + return &node->symbol; +} + +/*....................................................................... + * Private function used to allocate a hash-table node. + * The caller is responsible for checking that the specified symbol + * is unique and for installing the returned entry in the table. + * + * Input: + * hash HashTable * The table to allocate the node for. + * name const char * The name of the new entry. + * code int A user-supplied context code. + * fn void (*)(void) A user-supplied function pointer. + * data void * A user-supplied data pointer. + * del_fn SYM_DEL_FN(*) An optional 'data' destructor function. + * Output: + * return HashNode * The new node, or NULL on error. + */ +static HashNode *_new_HashNode(HashTable *hash, const char *name, int code, + void (*fn)(void), void *data, SYM_DEL_FN(*del_fn)) +{ + HashNode *node; /* The new node */ +/* + * Allocate the new node from the free list. + */ + node = (HashNode *) _new_FreeListNode(hash->mem->node_memory); + if(!node) + return NULL; +/* + * Before attempting any operation that might fail, initialize the + * contents of 'node' at least up to the point at which it can be + * safely passed to _del_HashNode(). + */ + node->symbol.name = NULL; + node->symbol.code = code; + node->symbol.fn = fn; + node->symbol.data = data; + node->symbol.del_fn = del_fn; + node->next = NULL; +/* + * Allocate a copy of 'name'. + */ + node->symbol.name = _new_StringMemString(hash->mem->string_memory, + strlen(name) + 1); + if(!node->symbol.name) + return _del_HashNode(hash, node); +/* + * If character-case is insignificant in the current table, convert the + * name to lower case while copying it. + */ + if(hash->case_sensitive) { + strcpy(node->symbol.name, name); + } else { + const char *src = name; + char *dst = node->symbol.name; + for( ; *src; src++,dst++) + *dst = tolower(*src); + *dst = '\0'; + }; + return node; +} + +/*....................................................................... + * Private function used to delete a hash-table node. + * The node must have been removed from its list before calling this + * function. + * + * Input: + * hash HashTable * The table for which the node was originally + * allocated. + * node HashNode * The node to be deleted. + * Output: + * return HashNode * The deleted node (always NULL). + */ +static HashNode *_del_HashNode(HashTable *hash, HashNode *node) +{ + if(node) { + node->symbol.name = _del_StringMemString(hash->mem->string_memory, + node->symbol.name); +/* + * Call the user-supplied data-destructor if provided. + */ + if(node->symbol.data && node->symbol.del_fn) + node->symbol.data = node->symbol.del_fn(hash->app_data, + node->symbol.code, + node->symbol.data); +/* + * Return the node to the free-list. + */ + node->next = NULL; + node = (HashNode *) _del_FreeListNode(hash->mem->node_memory, node); + }; + return NULL; +} + +/*....................................................................... + * Private function to locate the hash bucket associated with a given + * name. + * + * This uses a hash-function described in the dragon-book + * ("Compilers - Principles, Techniques and Tools", by Aho, Sethi and + * Ullman; pub. Adison Wesley) page 435. + * + * Input: + * hash HashTable * The table to look up the string in. + * name const char * The name of the symbol to look up. + * Output: + * return HashBucket * The located hash-bucket. + */ +static HashBucket *_find_HashBucket(HashTable *hash, const char *name) +{ + unsigned const char *kp; + unsigned long h = 0L; + if(hash->case_sensitive) { + for(kp=(unsigned const char *) name; *kp; kp++) + h = 65599UL * h + *kp; /* 65599 is a prime close to 2^16 */ + } else { + for(kp=(unsigned const char *) name; *kp; kp++) + h = 65599UL * h + tolower((int)*kp); /* 65599 is a prime close to 2^16 */ + }; + return hash->bucket + (h % hash->size); +} + +/*....................................................................... + * Search for a given name in the entries of a given bucket. + * + * Input: + * hash HashTable * The hash-table being searched. + * bucket HashBucket * The bucket to search (use _find_HashBucket()). + * name const char * The name to search for. + * Output: + * prev HashNode ** If prev!=NULL then the pointer to the node + * preceding the located node in the list will + * be recorded in *prev. This will be NULL either + * if the name is not found or the located node is + * at the head of the list of entries. + * return HashNode * The located hash-table node, or NULL if not + * found. + */ +static HashNode *_find_HashNode(HashTable *hash, HashBucket *bucket, + const char *name, HashNode **prev) +{ + HashNode *last; /* The previously searched node */ + HashNode *node; /* The node that is being searched */ +/* + * Search the list for a node containing the specified name. + */ + for(last=NULL, node=bucket->head; + node && hash->keycmp(node->symbol.name, name)!=0; + last = node, node=node->next) + ; + if(prev) + *prev = node ? last : NULL; + return node; +} + +/*....................................................................... + * When hash->case_sensitive is zero this function is called + * in place of strcmp(). In such cases the hash-table names are stored + * as lower-case versions of the original strings so this function + * performs the comparison against lower-case copies of the characters + * of the string being compared. + * + * Input: + * node_key const char * The lower-case hash-node key being compared + * against. + * look_key const char * The lookup key. + * Output: + * return int <0 if node_key < look_key. + * 0 if node_key == look_key. + * >0 if node_key > look_key. + */ +static int _ht_lower_strcmp(const char *node_key, const char *look_key) +{ + int cn; /* The latest character from node_key[] */ + int cl; /* The latest character from look_key[] */ + do { + cn = *node_key++; + cl = *look_key++; + } while(cn && cn==tolower(cl)); + return cn - tolower(cl); +} + +/*....................................................................... + * This is a wrapper around strcmp for comparing hash-keys in a case + * sensitive manner. The reason for having this wrapper, instead of using + * strcmp() directly, is to make some C++ compilers happy. The problem + * is that when the library is compiled with a C++ compiler, the + * declaration of the comparison function is a C++ declaration, whereas + * strcmp() is a pure C function and thus although it appears to have the + * same declaration, the compiler disagrees. + * + * Input: + * node_key char * The lower-case hash-node key being compared against. + * look_key char * The lookup key. + * Output: + * return int <0 if node_key < look_key. + * 0 if node_key == look_key. + * >0 if node_key > look_key. + */ +static int _ht_strcmp(const char *node_key, const char *look_key) +{ + return strcmp(node_key, look_key); +} + +/*....................................................................... + * Empty a hash-table by deleting all of its entries. + * + * Input: + * hash HashTable * The hash table to clear. + * Output: + * return int 0 - OK. + * 1 - Invalid arguments. + */ +int _clear_HashTable(HashTable *hash) +{ + int i; +/* + * Check the arguments. + */ + if(!hash) + return 1; +/* + * Clear the contents of the bucket array. + */ + for(i=0; isize; i++) { + HashBucket *bucket = hash->bucket + i; +/* + * Delete the list of active hash nodes from the bucket. + */ + HashNode *node = bucket->head; + while(node) { + HashNode *next = node->next; + (void) _del_HashNode(hash, node); + node = next; + }; +/* + * Mark the bucket as empty. + */ + bucket->head = NULL; + bucket->count = 0; + }; + return 0; +} + +/*....................................................................... + * Execute a given function on each entry of a hash table, returning + * before completion if the the specified function returns non-zero. + * + * Input: + * hash HashTable * The table to traverse. + * scan_fn HASH_SCAN_FN(*) The function to call. + * context void * Optional caller-specific context data + * to be passed to scan_fn(). + * Output: + * return int 0 - OK. + * 1 - Either the arguments were invalid, or + * scan_fn() returned non-zero at some + * point. + */ +int _scan_HashTable(HashTable *hash, HASH_SCAN_FN(*scan_fn), void *context) +{ + int i; +/* + * Check the arguments. + */ + if(!hash || !scan_fn) + return 1; +/* + * Iterate through the buckets of the table. + */ + for(i=0; isize; i++) { + HashBucket *bucket = hash->bucket + i; + HashNode *node; +/* + * Iterate through the list of symbols that fall into bucket i, + * passing each one to the caller-specified function. + */ + for(node=bucket->head; node; node=node->next) { + if(scan_fn(&node->symbol, context)) + return 1; + }; + }; + return 0; +} diff --git a/libtecla-1.6.1/hash.h b/libtecla-1.6.1/hash.h new file mode 100644 index 0000000..6cee6a2 --- /dev/null +++ b/libtecla-1.6.1/hash.h @@ -0,0 +1,157 @@ +#ifndef hash_h +#define hash_h + +/* + * Copyright (c) 2000, 2001, 2002, 2003, 2004 by Martin C. Shepherd. + * + * All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, and/or sell copies of the Software, and to permit persons + * to whom the Software is furnished to do so, provided that the above + * copyright notice(s) and this permission notice appear in all copies of + * the Software and that both the above copyright notice(s) and this + * permission notice appear in supporting documentation. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT + * OF THIRD PARTY RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR + * HOLDERS INCLUDED IN THIS NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL + * INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING + * FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, + * NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION + * WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + * Except as contained in this notice, the name of a copyright holder + * shall not be used in advertising or otherwise to promote the sale, use + * or other dealings in this Software without prior written authorization + * of the copyright holder. + */ + +/* + * The following macro can be used to prototype or define a + * function that deletes the data of a symbol-table entry. + * + * Input: + * app_data void * The _new_HashTable() app_data argument. + * code int The Symbol::code argument. + * sym_data void * The Symbol::data argument to be deleted. + * Output: + * return void * The deleted data (always return NULL). + */ +#define SYM_DEL_FN(fn) void *(fn)(void *app_data, int code, void *sym_data) + +/* + * The following macro can be used to prototype or define a + * function that deletes the application-data of a hash-table. + * + * Input: + * data void * The _new_HashTable() 'app_data' argument to be + * deleted. + * Output: + * return void * The deleted data (always return NULL). + */ +#define HASH_DEL_FN(fn) void *(fn)(void *app_data) + +/* + * The following is a container for recording the context + * of a symbol in a manner that is independant of the particular + * symbol-table implementation. Each hash-table entry contains + * the following user supplied parameters: + * + * 1. An optional integral parameter 'code'. This is useful for + * enumerating a symbol or for describing what type of data + * or function is stored in the symbol. + * + * 2. An optional generic function pointer. This is useful for + * associating functions with names. The user is responsible + * for casting between the generic function type and the + * actual function type. The code field could be used to + * enumerate what type of function to cast to. + * + * 3. An optional generic pointer to a static or heap-allocated + * object. It is up to the user to cast this back to the + * appropriate object type. Again, the code field could be used + * to describe what type of object is stored there. + * If the object is dynamically allocated and should be discarded + * when the symbol is deleted from the symbol table, send a + * destructor function to have it deleted automatically. + */ +typedef struct { + char *name; /* The name of the symbol */ + int code; /* Application supplied integral code */ + void (*fn)(void); /* Application supplied generic function */ + void *data; /* Application supplied context data */ + SYM_DEL_FN(*del_fn); /* Data destructor function */ +} Symbol; + +/* + * HashNode's and HashTable's are small objects. Separately allocating + * many such objects would normally cause memory fragmentation. To + * counter this, HashMemory objects are used. These contain + * dedicated free-lists formed from large dynamically allocated arrays + * of objects. One HashMemory object can be shared between multiple hash + * tables (within a single thread). + */ +typedef struct HashMemory HashMemory; + + /* Create a free-list for allocation of hash tables and their nodes */ + +HashMemory *_new_HashMemory(int hash_count, int node_count); + + /* Delete a redundant free-list if not being used */ + +HashMemory *_del_HashMemory(HashMemory *mem, int force); + +/* + * Declare an alias for the private HashTable structure defined in + * hash.c. + */ +typedef struct HashTable HashTable; + +/* + * Enumerate case-sensitivity options. + */ +typedef enum { + IGNORE_CASE, /* Ignore case when looking up symbols */ + HONOUR_CASE /* Honor case when looking up symbols */ +} HashCase; + + /* Create a new hash-table */ + +HashTable *_new_HashTable(HashMemory *mem, int size, HashCase hcase, + void *app_data, HASH_DEL_FN(*del_fn)); + + /* Delete a reference to a hash-table */ + +HashTable *_del_HashTable(HashTable *hash); + + /* Add an entry to a hash table */ + +Symbol *_new_HashSymbol(HashTable *hash, const char *key, int code, + void (*fn)(void), void *data, SYM_DEL_FN(*del_fn)); + + /* Remove and delete all the entries in a given hash table */ + +int _clear_HashTable(HashTable *hash); + + /* Remove and delete a given hash-table entry */ + +Symbol *_del_HashSymbol(HashTable *hash, const char *key); + + /* Lookup a given hash-table entry */ + +Symbol *_find_HashSymbol(HashTable *hash, const char *key); + + /* Execute a given function on each entry of a hash table, returning */ + /* before completion if the specified function returns non-zero. */ + +#define HASH_SCAN_FN(fn) int (fn)(Symbol *sym, void *context) + +int _scan_HashTable(HashTable *hash, HASH_SCAN_FN(*scan_fn), void *context); + +#endif diff --git a/libtecla-1.6.1/history.c b/libtecla-1.6.1/history.c new file mode 100644 index 0000000..cffa33e --- /dev/null +++ b/libtecla-1.6.1/history.c @@ -0,0 +1,2842 @@ +/* + * Copyright (c) 2000, 2001, 2002, 2003, 2004 by Martin C. Shepherd. + * + * All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, and/or sell copies of the Software, and to permit persons + * to whom the Software is furnished to do so, provided that the above + * copyright notice(s) and this permission notice appear in all copies of + * the Software and that both the above copyright notice(s) and this + * permission notice appear in supporting documentation. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT + * OF THIRD PARTY RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR + * HOLDERS INCLUDED IN THIS NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL + * INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING + * FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, + * NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION + * WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + * Except as contained in this notice, the name of a copyright holder + * shall not be used in advertising or otherwise to promote the sale, use + * or other dealings in this Software without prior written authorization + * of the copyright holder. + */ + +#include +#include +#include +#include +#include +#include + +#include "ioutil.h" +#include "history.h" +#include "freelist.h" +#include "errmsg.h" + +/* + * History lines are split into sub-strings of GLH_SEG_SIZE + * characters. To avoid wasting space in the GlhLineSeg structure, + * this should be a multiple of the size of a pointer. + */ +#define GLH_SEG_SIZE 16 + +/* + * GlhLineSeg structures contain fixed sized segments of a larger + * string. These are linked into lists to record strings, with all but + * the last segment having GLH_SEG_SIZE characters. The last segment + * of a string is terminated within the GLH_SEG_SIZE characters with a + * '\0'. + */ +typedef struct GlhLineSeg GlhLineSeg; +struct GlhLineSeg { + GlhLineSeg *next; /* The next sub-string of the history line */ + char s[GLH_SEG_SIZE]; /* The sub-string. Beware that only the final */ + /* substring of a line, as indicated by 'next' */ + /* being NULL, is '\0' terminated. */ +}; + +/* + * History lines are recorded in a hash table, such that repeated + * lines are stored just once. + * + * Start by defining the size of the hash table. This should be a + * prime number. + */ +#define GLH_HASH_SIZE 113 + +typedef struct GlhHashBucket GlhHashBucket; + +/* + * Each history line will be represented in the hash table by a + * structure of the following type. + */ +typedef struct GlhHashNode GlhHashNode; +struct GlhHashNode { + GlhHashBucket *bucket; /* The parent hash-table bucket of this node */ + GlhHashNode *next; /* The next in the list of nodes within the */ + /* parent hash-table bucket. */ + GlhLineSeg *head; /* The list of sub-strings which make up a line */ + int len; /* The length of the line, excluding any '\0' */ + int used; /* The number of times this string is pointed to by */ + /* the time-ordered list of history lines. */ + int reported; /* A flag that is used when searching to ensure that */ + /* a line isn't reported redundantly. */ +}; + +/* + * How many new GlhHashNode elements should be allocated at a time? + */ +#define GLH_HASH_INCR 50 + +static int _glh_is_line(GlhHashNode *hash, const char *line, size_t n); +static int _glh_line_matches_prefix(GlhHashNode *line, GlhHashNode *prefix); +static void _glh_return_line(GlhHashNode *hash, char *line, size_t dim); + +/* + * All history lines which hash to a given bucket in the hash table, are + * recorded in a structure of the following type. + */ +struct GlhHashBucket { + GlhHashNode *lines; /* The list of history lines which fall in this bucket */ +}; + +static GlhHashBucket *glh_find_bucket(GlHistory *glh, const char *line, + size_t n); +static GlhHashNode *glh_find_hash_node(GlhHashBucket *bucket, const char *line, + size_t n); + +typedef struct { + FreeList *node_mem; /* A free-list of GlhHashNode structures */ + GlhHashBucket bucket[GLH_HASH_SIZE]; /* The buckets of the hash table */ +} GlhLineHash; + +/* + * GlhLineNode's are used to record history lines in time order. + */ +typedef struct GlhLineNode GlhLineNode; +struct GlhLineNode { + long id; /* The unique identifier of this history line */ + time_t timestamp; /* The time at which the line was archived */ + unsigned group; /* The identifier of the history group to which the */ + /* the line belongs. */ + GlhLineNode *next; /* The next youngest line in the list */ + GlhLineNode *prev; /* The next oldest line in the list */ + GlhHashNode *line; /* The hash-table entry of the history line */ +}; + +/* + * The number of GlhLineNode elements per freelist block. + */ +#define GLH_LINE_INCR 100 + +/* + * Encapsulate the time-ordered list of historical lines. + */ +typedef struct { + FreeList *node_mem; /* A freelist of GlhLineNode objects */ + GlhLineNode *head; /* The oldest line in the list */ + GlhLineNode *tail; /* The newest line in the list */ +} GlhLineList; + +/* + * The _glh_lookup_history() returns copies of history lines in a + * dynamically allocated array. This array is initially allocated + * GLH_LOOKUP_SIZE bytes. If subsequently this size turns out to be + * too small, realloc() is used to increase its size to the required + * size plus GLH_LOOKUP_MARGIN. The idea of the later parameter is to + * reduce the number of realloc() operations needed. + */ +#define GLH_LBUF_SIZE 300 +#define GLH_LBUF_MARGIN 100 + +/* + * Encapsulate all of the resources needed to store historical input lines. + */ +struct GlHistory { + ErrMsg *err; /* The error-reporting buffer */ + GlhLineSeg *buffer; /* An array of sub-line nodes to be partitioned */ + /* into lists of sub-strings recording input lines. */ + int nbuff; /* The allocated dimension of buffer[] */ + GlhLineSeg *unused; /* The list of free nodes in buffer[] */ + GlhLineList list; /* A time ordered list of history lines */ + GlhLineNode *recall; /* The last line recalled, or NULL if no recall */ + /* session is currently active. */ + GlhLineNode *id_node;/* The node at which the last ID search terminated */ + GlhLineHash hash; /* A hash-table of reference-counted history lines */ + GlhHashNode *prefix; /* A pointer to a line containing the prefix that */ + /* is being searched for. Note that if prefix==NULL */ + /* and prefix_len>0, this means that no line in */ + /* the buffer starts with the requested prefix. */ + int prefix_len; /* The length of the prefix being searched for. */ + char *lbuf; /* The array in which _glh_lookup_history() returns */ + /* history lines */ + int lbuf_dim; /* The allocated size of lbuf[] */ + int nbusy; /* The number of line segments in buffer[] that are */ + /* currently being used to record sub-lines */ + int nfree; /* The number of line segments in buffer that are */ + /* not currently being used to record sub-lines */ + unsigned long seq; /* The next ID to assign to a line node */ + unsigned group; /* The identifier of the current history group */ + int nline; /* The number of lines currently in the history list */ + int max_lines; /* Either -1 or a ceiling on the number of lines */ + int enable; /* If false, ignore history additions and lookups */ +}; + +#ifndef WITHOUT_FILE_SYSTEM +static int _glh_cant_load_history(GlHistory *glh, const char *filename, + int lineno, const char *message, FILE *fp); +static int _glh_cant_save_history(GlHistory *glh, const char *message, + const char *filename, FILE *fp); +static int _glh_write_timestamp(FILE *fp, time_t timestamp); +static int _glh_decode_timestamp(char *string, char **endp, time_t *timestamp); +#endif +static void _glh_discard_line(GlHistory *glh, GlhLineNode *node); +static GlhLineNode *_glh_find_id(GlHistory *glh, GlhLineID id); +static GlhHashNode *_glh_acquire_copy(GlHistory *glh, const char *line, + size_t n); +static GlhHashNode *_glh_discard_copy(GlHistory *glh, GlhHashNode *hnode); +static int _glh_prepare_for_recall(GlHistory *glh, char *line); + +/* + * The following structure and functions are used to iterate through + * the characters of a segmented history line. + */ +typedef struct { + GlhLineSeg *seg; /* The line segment that the next character will */ + /* be returned from. */ + int posn; /* The index in the above line segment, containing */ + /* the next unread character. */ + char c; /* The current character in the input line */ +} GlhLineStream; +static void glh_init_stream(GlhLineStream *str, GlhHashNode *line); +static void glh_step_stream(GlhLineStream *str); + +/* + * See if search prefix contains any globbing characters. + */ +static int glh_contains_glob(GlhHashNode *prefix); +/* + * Match a line against a search pattern. + */ +static int glh_line_matches_glob(GlhLineStream *lstr, GlhLineStream *pstr); +static int glh_matches_range(char c, GlhLineStream *pstr); + +/*....................................................................... + * Create a line history maintenance object. + * + * Input: + * buflen size_t The number of bytes to allocate to the + * buffer that is used to record all of the + * most recent lines of user input that will fit. + * If buflen==0, no buffer will be allocated. + * Output: + * return GlHistory * The new object, or NULL on error. + */ +GlHistory *_new_GlHistory(size_t buflen) +{ + GlHistory *glh; /* The object to be returned */ + int i; +/* + * Allocate the container. + */ + glh = (GlHistory *) malloc(sizeof(GlHistory)); + if(!glh) { + errno = ENOMEM; + return NULL; + }; +/* + * Before attempting any operation that might fail, initialize the + * container at least up to the point at which it can safely be passed + * to _del_GlHistory(). + */ + glh->err = NULL; + glh->buffer = NULL; + glh->nbuff = (buflen+GLH_SEG_SIZE-1) / GLH_SEG_SIZE; + glh->unused = NULL; + glh->list.node_mem = NULL; + glh->list.head = glh->list.tail = NULL; + glh->recall = NULL; + glh->id_node = NULL; + glh->hash.node_mem = NULL; + for(i=0; ihash.bucket[i].lines = NULL; + glh->prefix = NULL; + glh->lbuf = NULL; + glh->lbuf_dim = 0; + glh->nbusy = 0; + glh->nfree = glh->nbuff; + glh->seq = 0; + glh->group = 0; + glh->nline = 0; + glh->max_lines = -1; + glh->enable = 1; +/* + * Allocate a place to record error messages. + */ + glh->err = _new_ErrMsg(); + if(!glh->err) + return _del_GlHistory(glh); +/* + * Allocate the buffer, if required. + */ + if(glh->nbuff > 0) { + glh->nbuff = glh->nfree; + glh->buffer = (GlhLineSeg *) malloc(sizeof(GlhLineSeg) * glh->nbuff); + if(!glh->buffer) { + errno = ENOMEM; + return _del_GlHistory(glh); + }; +/* + * All nodes of the buffer are currently unused, so link them all into + * a list and make glh->unused point to the head of this list. + */ + glh->unused = glh->buffer; + for(i=0; inbuff-1; i++) { + GlhLineSeg *seg = glh->unused + i; + seg->next = seg + 1; + }; + glh->unused[i].next = NULL; + }; +/* + * Allocate the GlhLineNode freelist. + */ + glh->list.node_mem = _new_FreeList(sizeof(GlhLineNode), GLH_LINE_INCR); + if(!glh->list.node_mem) + return _del_GlHistory(glh); +/* + * Allocate the GlhHashNode freelist. + */ + glh->hash.node_mem = _new_FreeList(sizeof(GlhLineNode), GLH_HASH_INCR); + if(!glh->hash.node_mem) + return _del_GlHistory(glh); +/* + * Allocate the array that _glh_lookup_history() uses to return a + * copy of a given history line. This will be resized when necessary. + */ + glh->lbuf_dim = GLH_LBUF_SIZE; + glh->lbuf = (char *) malloc(glh->lbuf_dim); + if(!glh->lbuf) { + errno = ENOMEM; + return _del_GlHistory(glh); + }; + return glh; +} + +/*....................................................................... + * Delete a GlHistory object. + * + * Input: + * glh GlHistory * The object to be deleted. + * Output: + * return GlHistory * The deleted object (always NULL). + */ +GlHistory *_del_GlHistory(GlHistory *glh) +{ + if(glh) { +/* + * Delete the error-message buffer. + */ + glh->err = _del_ErrMsg(glh->err); +/* + * Delete the buffer. + */ + if(glh->buffer) { + free(glh->buffer); + glh->buffer = NULL; + glh->unused = NULL; + }; +/* + * Delete the freelist of GlhLineNode's. + */ + glh->list.node_mem = _del_FreeList(glh->list.node_mem, 1); +/* + * The contents of the list were deleted by deleting the freelist. + */ + glh->list.head = NULL; + glh->list.tail = NULL; +/* + * Delete the freelist of GlhHashNode's. + */ + glh->hash.node_mem = _del_FreeList(glh->hash.node_mem, 1); +/* + * Delete the lookup buffer. + */ + if(glh->lbuf) + free(glh->lbuf); +/* + * Delete the container. + */ + free(glh); + }; + return NULL; +} + +/*....................................................................... + * Append a new line to the history list, deleting old lines to make + * room, if needed. + * + * Input: + * glh GlHistory * The input-line history maintenance object. + * line char * The line to be archived. + * force int Unless this flag is non-zero, empty lines aren't + * archived. This flag requests that the line be + * archived regardless. + * Output: + * return int 0 - OK. + * 1 - Error. + */ +int _glh_add_history(GlHistory *glh, const char *line, int force) +{ + int slen; /* The length of the line to be recorded (minus the '\0') */ + int empty; /* True if the string is empty */ + const char *nlptr; /* A pointer to a newline character in line[] */ + GlhHashNode *hnode; /* The hash-table node of the line */ + GlhLineNode *lnode; /* A node in the time-ordered list of lines */ + int i; +/* + * Check the arguments. + */ + if(!glh || !line) { + errno = EINVAL; + return 1; + }; +/* + * Is history enabled? + */ + if(!glh->enable || !glh->buffer || glh->max_lines == 0) + return 0; +/* + * Cancel any ongoing search. + */ + if(_glh_cancel_search(glh)) + return 1; +/* + * How long is the string to be recorded, being careful not to include + * any terminating '\n' character. + */ + nlptr = strchr(line, '\n'); + if(nlptr) + slen = (nlptr - line); + else + slen = strlen(line); +/* + * Is the line empty? + */ + empty = 1; + for(i=0; imax_lines >= 0) { +/* + * If necessary, remove old lines until there is room to add one new + * line without exceeding the specified line limit. + */ + while(glh->nline > 0 && glh->nline >= glh->max_lines) + _glh_discard_line(glh, glh->list.head); +/* + * We can't archive the line if the maximum number of lines allowed is + * zero. + */ + if(glh->max_lines == 0) + return 0; + }; +/* + * Unless already stored, store a copy of the line in the history buffer, + * then return a reference-counted hash-node pointer to this copy. + */ + hnode = _glh_acquire_copy(glh, line, slen); + if(!hnode) { + _err_record_msg(glh->err, "No room to store history line", END_ERR_MSG); + errno = ENOMEM; + return 1; + }; +/* + * Allocate a new node in the time-ordered list of lines. + */ + lnode = (GlhLineNode *) _new_FreeListNode(glh->list.node_mem); +/* + * If a new line-node couldn't be allocated, discard our copy of the + * stored line before reporting the error. + */ + if(!lnode) { + hnode = _glh_discard_copy(glh, hnode); + _err_record_msg(glh->err, "No room to store history line", END_ERR_MSG); + errno = ENOMEM; + return 1; + }; +/* + * Record a pointer to the hash-table record of the line in the new + * list node. + */ + lnode->id = glh->seq++; + lnode->timestamp = time(NULL); + lnode->group = glh->group; + lnode->line = hnode; +/* + * Append the new node to the end of the time-ordered list. + */ + if(glh->list.head) + glh->list.tail->next = lnode; + else + glh->list.head = lnode; + lnode->next = NULL; + lnode->prev = glh->list.tail; + glh->list.tail = lnode; +/* + * Record the addition of a line to the list. + */ + glh->nline++; + return 0; +} + +/*....................................................................... + * Recall the next oldest line that has the search prefix last recorded + * by _glh_search_prefix(). + * + * Input: + * glh GlHistory * The input-line history maintenance object. + * line char * The input line buffer. On input this should contain + * the current input line, and on output, if anything + * was found, its contents will have been replaced + * with the matching line. + * dim size_t The allocated dimension of the line buffer. + * Output: + * return char * A pointer to line[0], or NULL if not found. + */ +char *_glh_find_backwards(GlHistory *glh, char *line, size_t dim) +{ + GlhLineNode *node; /* The line location node being checked */ + GlhHashNode *old_line; /* The previous recalled line */ +/* + * Check the arguments. + */ + if(!glh || !line) { + if(glh) + _err_record_msg(glh->err, "NULL argument(s)", END_ERR_MSG); + errno = EINVAL; + return NULL; + }; +/* + * Is history enabled? + */ + if(!glh->enable || !glh->buffer || glh->max_lines == 0) + return NULL; +/* + * Check the line dimensions. + */ + if(dim < strlen(line) + 1) { + _err_record_msg(glh->err, "'dim' argument inconsistent with strlen(line)", + END_ERR_MSG); + errno = EINVAL; + return NULL; + }; +/* + * Preserve the input line if needed. + */ + if(_glh_prepare_for_recall(glh, line)) + return NULL; +/* + * From where should we start the search? + */ + if(glh->recall) { + node = glh->recall->prev; + old_line = glh->recall->line; + } else { + node = glh->list.tail; + old_line = NULL; + }; +/* + * Search backwards through the list for the first match with the + * prefix string that differs from the last line that was recalled. + */ + while(node && (node->group != glh->group || node->line == old_line || + !_glh_line_matches_prefix(node->line, glh->prefix))) + node = node->prev; +/* + * Was a matching line found? + */ + if(node) { +/* + * Recall the found node as the starting point for subsequent + * searches. + */ + glh->recall = node; +/* + * Copy the matching line into the provided line buffer. + */ + _glh_return_line(node->line, line, dim); +/* + * Return it. + */ + return line; + }; +/* + * No match was found. + */ + return NULL; +} + +/*....................................................................... + * Recall the next newest line that has the search prefix last recorded + * by _glh_search_prefix(). + * + * Input: + * glh GlHistory * The input-line history maintenance object. + * line char * The input line buffer. On input this should contain + * the current input line, and on output, if anything + * was found, its contents will have been replaced + * with the matching line. + * dim size_t The allocated dimensions of the line buffer. + * Output: + * return char * The line requested, or NULL if no matching line + * was found. + */ +char *_glh_find_forwards(GlHistory *glh, char *line, size_t dim) +{ + GlhLineNode *node; /* The line location node being checked */ + GlhHashNode *old_line; /* The previous recalled line */ +/* + * Check the arguments. + */ + if(!glh || !line) { + if(glh) + _err_record_msg(glh->err, "NULL argument(s)", END_ERR_MSG); + errno = EINVAL; + return NULL; + }; +/* + * Is history enabled? + */ + if(!glh->enable || !glh->buffer || glh->max_lines == 0) + return NULL; +/* + * Check the line dimensions. + */ + if(dim < strlen(line) + 1) { + _err_record_msg(glh->err, "'dim' argument inconsistent with strlen(line)", + END_ERR_MSG); + errno = EINVAL; + return NULL; + }; +/* + * From where should we start the search? + */ + if(glh->recall) { + node = glh->recall->next; + old_line = glh->recall->line; + } else { + return NULL; + }; +/* + * Search forwards through the list for the first match with the + * prefix string. + */ + while(node && (node->group != glh->group || node->line == old_line || + !_glh_line_matches_prefix(node->line, glh->prefix))) + node = node->next; +/* + * Was a matching line found? + */ + if(node) { +/* + * Copy the matching line into the provided line buffer. + */ + _glh_return_line(node->line, line, dim); +/* + * Record the starting point of the next search. + */ + glh->recall = node; +/* + * If we just returned the line that was being entered when the search + * session first started, cancel the search. + */ + if(node == glh->list.tail) + _glh_cancel_search(glh); +/* + * Return the matching line to the user. + */ + return line; + }; +/* + * No match was found. + */ + return NULL; +} + +/*....................................................................... + * If a search is in progress, cancel it. + * + * This involves discarding the line that was temporarily saved by + * _glh_find_backwards() when the search was originally started, + * and reseting the search iteration pointer to NULL. + * + * Input: + * glh GlHistory * The input-line history maintenance object. + * Output: + * return int 0 - OK. + * 1 - Error. + */ +int _glh_cancel_search(GlHistory *glh) +{ +/* + * Check the arguments. + */ + if(!glh) { + errno = EINVAL; + return 1; + }; +/* + * If there wasn't a search in progress, do nothing. + */ + if(!glh->recall) + return 0; +/* + * Reset the search pointers. Note that it is essential to set + * glh->recall to NULL before calling _glh_discard_line(), to avoid an + * infinite recursion. + */ + glh->recall = NULL; +/* + * Delete the node of the preserved line. + */ + _glh_discard_line(glh, glh->list.tail); + return 0; +} + +/*....................................................................... + * Set the prefix of subsequent history searches. + * + * Input: + * glh GlHistory * The input-line history maintenance object. + * line const char * The command line who's prefix is to be used. + * prefix_len int The length of the prefix. + * Output: + * return int 0 - OK. + * 1 - Error. + */ +int _glh_search_prefix(GlHistory *glh, const char *line, int prefix_len) +{ +/* + * Check the arguments. + */ + if(!glh) { + errno = EINVAL; + return 1; + }; +/* + * Is history enabled? + */ + if(!glh->enable || !glh->buffer || glh->max_lines == 0) + return 0; +/* + * Discard any existing prefix. + */ + glh->prefix = _glh_discard_copy(glh, glh->prefix); +/* + * Only store a copy of the prefix string if it isn't a zero-length string. + */ + if(prefix_len > 0) { +/* + * Get a reference-counted copy of the prefix from the history cache buffer. + */ + glh->prefix = _glh_acquire_copy(glh, line, prefix_len); +/* + * Was there insufficient buffer space? + */ + if(!glh->prefix) { + _err_record_msg(glh->err, "The search prefix is too long to store", + END_ERR_MSG); + errno = ENOMEM; + return 1; + }; + }; + return 0; +} + +/*....................................................................... + * Recall the oldest recorded line. + * + * Input: + * glh GlHistory * The input-line history maintenance object. + * line char * The input line buffer. On input this should contain + * the current input line, and on output, its contents + * will have been replaced with the oldest line. + * dim size_t The allocated dimensions of the line buffer. + * Output: + * return char * A pointer to line[0], or NULL if not found. + */ +char *_glh_oldest_line(GlHistory *glh, char *line, size_t dim) +{ + GlhLineNode *node; /* The line location node being checked */ +/* + * Check the arguments. + */ + if(!glh || !line) { + if(glh) + _err_record_msg(glh->err, "NULL argument(s)", END_ERR_MSG); + errno = EINVAL; + return NULL; + }; +/* + * Is history enabled? + */ + if(!glh->enable || !glh->buffer || glh->max_lines == 0) + return NULL; +/* + * Check the line dimensions. + */ + if(dim < strlen(line) + 1) { + _err_record_msg(glh->err, "'dim' argument inconsistent with strlen(line)", + END_ERR_MSG); + errno = EINVAL; + return NULL; + }; +/* + * Preserve the input line if needed. + */ + if(_glh_prepare_for_recall(glh, line)) + return NULL; +/* + * Locate the oldest line that belongs to the current group. + */ + for(node=glh->list.head; node && node->group != glh->group; + node = node->next) + ; +/* + * No line found? + */ + if(!node) + return NULL; +/* + * Record the above node as the starting point for subsequent + * searches. + */ + glh->recall = node; +/* + * Copy the recalled line into the provided line buffer. + */ + _glh_return_line(node->line, line, dim); +/* + * If we just returned the line that was being entered when the search + * session first started, cancel the search. + */ + if(node == glh->list.tail) + _glh_cancel_search(glh); + return line; +} + +/*....................................................................... + * Recall the line that was being entered when the search started. + * + * Input: + * glh GlHistory * The input-line history maintenance object. + * line char * The input line buffer. On input this should contain + * the current input line, and on output, its contents + * will have been replaced with the line that was + * being entered when the search was started. + * dim size_t The allocated dimensions of the line buffer. + * Output: + * return char * A pointer to line[0], or NULL if not found. + */ +char *_glh_current_line(GlHistory *glh, char *line, size_t dim) +{ +/* + * Check the arguments. + */ + if(!glh || !line) { + if(glh) + _err_record_msg(glh->err, "NULL argument(s)", END_ERR_MSG); + errno = EINVAL; + return NULL; + }; +/* + * If history isn't enabled, or no history search has yet been started, + * ignore the call. + */ + if(!glh->enable || !glh->buffer || glh->max_lines == 0 || !glh->recall) + return NULL; +/* + * Check the line dimensions. + */ + if(dim < strlen(line) + 1) { + _err_record_msg(glh->err, "'dim' argument inconsistent with strlen(line)", + END_ERR_MSG); + errno = EINVAL; + return NULL; + }; +/* + * Copy the recalled line into the provided line buffer. + */ + _glh_return_line(glh->list.tail->line, line, dim); +/* + * Since we have returned to the starting point of the search, cancel it. + */ + _glh_cancel_search(glh); + return line; +} + +/*....................................................................... + * Query the id of a history line offset by a given number of lines from + * the one that is currently being recalled. If a recall session isn't + * in progress, or the offset points outside the history list, 0 is + * returned. + * + * Input: + * glh GlHistory * The input-line history maintenance object. + * offset int The line offset (0 for the current line, < 0 + * for an older line, > 0 for a newer line. + * Output: + * return GlhLineID The identifier of the line that is currently + * being recalled, or 0 if no recall session is + * currently in progress. + */ +GlhLineID _glh_line_id(GlHistory *glh, int offset) +{ + GlhLineNode *node; /* The line location node being checked */ +/* + * Is history enabled? + */ + if(!glh->enable || !glh->buffer || glh->max_lines == 0) + return 0; +/* + * Search forward 'offset' lines to find the required line. + */ + if(offset >= 0) { + for(node=glh->recall; node && offset != 0; node=node->next) { + if(node->group == glh->group) + offset--; + }; + } else { + for(node=glh->recall; node && offset != 0; node=node->prev) { + if(node->group == glh->group) + offset++; + }; + }; + return node ? node->id : 0; +} + +/*....................................................................... + * Recall a line by its history buffer ID. If the line is no longer + * in the buffer, or the id is zero, NULL is returned. + * + * Input: + * glh GlHistory * The input-line history maintenance object. + * id GlhLineID The ID of the line to be returned. + * line char * The input line buffer. On input this should contain + * the current input line, and on output, its contents + * will have been replaced with the saved line. + * dim size_t The allocated dimensions of the line buffer. + * Output: + * return char * A pointer to line[0], or NULL if not found. + */ +char *_glh_recall_line(GlHistory *glh, GlhLineID id, char *line, size_t dim) +{ + GlhLineNode *node; /* The line location node being checked */ +/* + * Is history enabled? + */ + if(!glh->enable || !glh->buffer || glh->max_lines == 0) + return NULL; +/* + * Preserve the input line if needed. + */ + if(_glh_prepare_for_recall(glh, line)) + return NULL; +/* + * Search for the specified line. + */ + node = _glh_find_id(glh, id); +/* + * Not found? + */ + if(!node || node->group != glh->group) + return NULL; +/* + * Record the node of the matching line as the starting point + * for subsequent searches. + */ + glh->recall = node; +/* + * Copy the recalled line into the provided line buffer. + */ + _glh_return_line(node->line, line, dim); + return line; +} + +/*....................................................................... + * Save the current history in a specified file. + * + * Input: + * glh GlHistory * The input-line history maintenance object. + * filename const char * The name of the new file to record the + * history in. + * comment const char * Extra information such as timestamps will + * be recorded on a line started with this + * string, the idea being that the file can + * double as a command file. Specify "" if + * you don't care. + * max_lines int The maximum number of lines to save, or -1 + * to save all of the lines in the history + * list. + * Output: + * return int 0 - OK. + * 1 - Error. + */ +int _glh_save_history(GlHistory *glh, const char *filename, const char *comment, + int max_lines) +{ +#ifdef WITHOUT_FILE_SYSTEM + _err_record_msg(glh->err, "Can't save history without filesystem access", + END_ERR_MSG); + errno = EINVAL; + return 1; +#else + FILE *fp; /* The output file */ + GlhLineNode *node; /* The line being saved */ + GlhLineNode *head; /* The head of the list of lines to be saved */ + GlhLineSeg *seg; /* One segment of a line being saved */ +/* + * Check the arguments. + */ + if(!glh || !filename || !comment) { + if(glh) + _err_record_msg(glh->err, "NULL argument(s)", END_ERR_MSG); + errno = EINVAL; + return 1; + }; +/* + * Attempt to open the specified file. + */ + fp = fopen(filename, "w"); + if(!fp) + return _glh_cant_save_history(glh, "Can't open", filename, NULL); +/* + * If a ceiling on the number of lines to save was specified, count + * that number of lines backwards, to find the first line to be saved. + */ + head = NULL; + if(max_lines >= 0) { + for(head=glh->list.tail; head && --max_lines > 0; head=head->prev) + ; + }; + if(!head) + head = glh->list.head; +/* + * Write the contents of the history buffer to the history file, writing + * associated data such as timestamps, to a line starting with the + * specified comment string. + */ + for(node=head; node; node=node->next) { +/* + * Write peripheral information associated with the line, as a comment. + */ + if(fprintf(fp, "%s ", comment) < 0 || + _glh_write_timestamp(fp, node->timestamp) || + fprintf(fp, " %u\n", node->group) < 0) { + return _glh_cant_save_history(glh, "Error writing", filename, fp); + }; +/* + * Write the history line. + */ + for(seg=node->line->head; seg; seg=seg->next) { + size_t slen = seg->next ? GLH_SEG_SIZE : strlen(seg->s); + if(fwrite(seg->s, sizeof(char), slen, fp) != slen) + return _glh_cant_save_history(glh, "Error writing", filename, fp); + }; + fputc('\n', fp); + }; +/* + * Close the history file. + */ + if(fclose(fp) == EOF) + return _glh_cant_save_history(glh, "Error writing", filename, NULL); + return 0; +#endif +} + +#ifndef WITHOUT_FILE_SYSTEM +/*....................................................................... + * This is a private error return function of _glh_save_history(). It + * composes an error report in the error buffer, composed using + * sprintf("%s %s (%s)", message, filename, strerror(errno)). It then + * closes fp and returns the error return code of _glh_save_history(). + * + * Input: + * glh GlHistory * The input-line history maintenance object. + * message const char * A message to be followed by the filename. + * filename const char * The name of the offending output file. + * fp FILE * The stream to be closed (send NULL if not + * open). + * Output: + * return int Always 1. + */ +static int _glh_cant_save_history(GlHistory *glh, const char *message, + const char *filename, FILE *fp) +{ + _err_record_msg(glh->err, message, filename, " (", + strerror(errno), ")", END_ERR_MSG); + if(fp) + (void) fclose(fp); + return 1; +} + +/*....................................................................... + * Write a timestamp to a given stdio stream, in the format + * yyyymmddhhmmss + * + * Input: + * fp FILE * The stream to write to. + * timestamp time_t The timestamp to be written. + * Output: + * return int 0 - OK. + * 1 - Error. + */ +static int _glh_write_timestamp(FILE *fp, time_t timestamp) +{ + struct tm *t; /* THe broken-down calendar time */ +/* + * Get the calendar components corresponding to the given timestamp. + */ + if(timestamp < 0 || (t = localtime(×tamp)) == NULL) { + if(fprintf(fp, "?") < 0) + return 1; + return 0; + }; +/* + * Write the calendar time as yyyymmddhhmmss. + */ + if(fprintf(fp, "%04d%02d%02d%02d%02d%02d", t->tm_year + 1900, t->tm_mon + 1, + t->tm_mday, t->tm_hour, t->tm_min, t->tm_sec) < 0) + return 1; + return 0; +} + +#endif + +/*....................................................................... + * Restore previous history lines from a given file. + * + * Input: + * glh GlHistory * The input-line history maintenance object. + * filename const char * The name of the file to read from. + * comment const char * The same comment string that was passed to + * _glh_save_history() when this file was + * written. + * line char * A buffer into which lines can be read. + * dim size_t The allocated dimension of line[]. + * Output: + * return int 0 - OK. + * 1 - Error. + */ +int _glh_load_history(GlHistory *glh, const char *filename, const char *comment, + char *line, size_t dim) +{ +#ifdef WITHOUT_FILE_SYSTEM + _err_record_msg(glh->err, "Can't load history without filesystem access", + END_ERR_MSG); + errno = EINVAL; + return 1; +#else + FILE *fp; /* The output file */ + size_t comment_len; /* The length of the comment string */ + time_t timestamp; /* The timestamp of the history line */ + unsigned group; /* The identifier of the history group to which */ + /* the line belongs. */ + int lineno; /* The line number being read */ +/* + * Check the arguments. + */ + if(!glh || !filename || !comment || !line) { + if(glh) + _err_record_msg(glh->err, "NULL argument(s)", END_ERR_MSG); + errno = EINVAL; + return 1; + }; +/* + * Measure the length of the comment string. + */ + comment_len = strlen(comment); +/* + * Clear the history list. + */ + _glh_clear_history(glh, 1); +/* + * Attempt to open the specified file. Don't treat it as an error + * if the file doesn't exist. + */ + fp = fopen(filename, "r"); + if(!fp) + return 0; +/* + * Attempt to read each line and preceding peripheral info, and add these + * to the history list. + */ + for(lineno=1; fgets(line, dim, fp) != NULL; lineno++) { + char *lptr; /* A pointer into the input line */ +/* + * Check that the line starts with the comment string. + */ + if(strncmp(line, comment, comment_len) != 0) { + return _glh_cant_load_history(glh, filename, lineno, + "Corrupt history parameter line", fp); + }; +/* + * Skip spaces and tabs after the comment. + */ + for(lptr=line+comment_len; *lptr && (*lptr==' ' || *lptr=='\t'); lptr++) + ; +/* + * The next word must be a timestamp. + */ + if(_glh_decode_timestamp(lptr, &lptr, ×tamp)) { + return _glh_cant_load_history(glh, filename, lineno, + "Corrupt timestamp", fp); + }; +/* + * Skip spaces and tabs. + */ + while(*lptr==' ' || *lptr=='\t') + lptr++; +/* + * The next word must be an unsigned integer group number. + */ + group = (int) strtoul(lptr, &lptr, 10); + if(*lptr != ' ' && *lptr != '\n') { + return _glh_cant_load_history(glh, filename, lineno, + "Corrupt group id", fp); + }; +/* + * Skip spaces and tabs. + */ + while(*lptr==' ' || *lptr=='\t') + lptr++; +/* + * There shouldn't be anything left on the line. + */ + if(*lptr != '\n') { + return _glh_cant_load_history(glh, filename, lineno, + "Corrupt parameter line", fp); + }; +/* + * Now read the history line itself. + */ + lineno++; + if(fgets(line, dim, fp) == NULL) + return _glh_cant_load_history(glh, filename, lineno, "Read error", fp); +/* + * Append the line to the history buffer. + */ + if(_glh_add_history(glh, line, 1)) { + return _glh_cant_load_history(glh, filename, lineno, + "Insufficient memory to record line", fp); + }; +/* + * Record the group and timestamp information along with the line. + */ + if(glh->list.tail) { + glh->list.tail->timestamp = timestamp; + glh->list.tail->group = group; + }; + }; +/* + * Close the file. + */ + (void) fclose(fp); + return 0; +#endif +} + +#ifndef WITHOUT_FILE_SYSTEM +/*....................................................................... + * This is a private error return function of _glh_load_history(). + */ +static int _glh_cant_load_history(GlHistory *glh, const char *filename, + int lineno, const char *message, FILE *fp) +{ + char lnum[20]; +/* + * Convert the line number to a string. + */ + sprintf(lnum, "%d", lineno); +/* + * Render an error message. + */ + _err_record_msg(glh->err, filename, ":", lnum, ":", message, END_ERR_MSG); +/* + * Close the file. + */ + if(fp) + (void) fclose(fp); + return 1; +} + +/*....................................................................... + * Read a timestamp from a string. + * + * Input: + * string char * The string to read from. + * Input/Output: + * endp char ** On output *endp will point to the next unprocessed + * character in string[]. + * timestamp time_t * The timestamp will be assigned to *t. + * Output: + * return int 0 - OK. + * 1 - Error. + */ +static int _glh_decode_timestamp(char *string, char **endp, time_t *timestamp) +{ + unsigned year,month,day,hour,min,sec; /* Calendar time components */ + struct tm t; +/* + * There are 14 characters in the date format yyyymmddhhmmss. + */ + enum {TSLEN=14}; + char timestr[TSLEN+1]; /* The timestamp part of the string */ +/* + * If the time wasn't available at the time that the line was recorded + * it will have been written as "?". Check for this before trying + * to read the timestamp. + */ + if(string[0] == '\?') { + *endp = string+1; + *timestamp = -1; + return 0; + }; +/* + * The timestamp is expected to be written in the form yyyymmddhhmmss. + */ + if(strlen(string) < TSLEN) { + *endp = string; + return 1; + }; +/* + * Copy the timestamp out of the string. + */ + strncpy(timestr, string, TSLEN); + timestr[TSLEN] = '\0'; +/* + * Decode the timestamp. + */ + if(sscanf(timestr, "%4u%2u%2u%2u%2u%2u", &year, &month, &day, &hour, &min, + &sec) != 6) { + *endp = string; + return 1; + }; +/* + * Advance the string pointer over the successfully read timestamp. + */ + *endp = string + TSLEN; +/* + * Copy the read values into a struct tm. + */ + t.tm_sec = sec; + t.tm_min = min; + t.tm_hour = hour; + t.tm_mday = day; + t.tm_wday = 0; + t.tm_yday = 0; + t.tm_mon = month - 1; + t.tm_year = year - 1900; + t.tm_isdst = -1; +/* + * Convert the contents of the struct tm to a time_t. + */ + *timestamp = mktime(&t); + return 0; +} +#endif + +/*....................................................................... + * Switch history groups. + * + * Input: + * glh GlHistory * The input-line history maintenance object. + * group unsigned The new group identifier. This will be recorded + * with subsequent history lines, and subsequent + * history searches will only return lines with + * this group identifier. This allows multiple + * separate history lists to exist within + * a single GlHistory object. Note that the + * default group identifier is 0. + * Output: + * return int 0 - OK. + * 1 - Error. + */ +int _glh_set_group(GlHistory *glh, unsigned group) +{ +/* + * Check the arguments. + */ + if(!glh) { + if(glh) + _err_record_msg(glh->err, "NULL argument(s)", END_ERR_MSG); + errno = EINVAL; + return 1; + }; +/* + * Is the group being changed? + */ + if(group != glh->group) { +/* + * Cancel any ongoing search. + */ + if(_glh_cancel_search(glh)) + return 1; +/* + * Record the new group. + */ + glh->group = group; + }; + return 0; +} + +/*....................................................................... + * Query the current history group. + * + * Input: + * glh GlHistory * The input-line history maintenance object. + * Output: + * return unsigned The group identifier. + */ +int _glh_get_group(GlHistory *glh) +{ + return glh ? glh->group : 0; +} + +/*....................................................................... + * Display the contents of the history list. + * + * Input: + * glh GlHistory * The input-line history maintenance object. + * write_fn GlWriteFn * The function to call to write the line, or + * 0 to discard the output. + * data void * Anonymous data to pass to write_fn(). + * fmt const char * A format string. This can contain arbitrary + * characters, which are written verbatim, plus + * any of the following format directives: + * %D - The date, like 2001-11-20 + * %T - The time of day, like 23:59:59 + * %N - The sequential entry number of the + * line in the history buffer. + * %G - The history group number of the line. + * %% - A literal % character. + * %H - The history line. + * all_groups int If true, display history lines from all + * history groups. Otherwise only display + * those of the current history group. + * max_lines int If max_lines is < 0, all available lines + * are displayed. Otherwise only the most + * recent max_lines lines will be displayed. + * Output: + * return int 0 - OK. + * 1 - Error. + */ +int _glh_show_history(GlHistory *glh, GlWriteFn *write_fn, void *data, + const char *fmt, int all_groups, int max_lines) +{ + GlhLineNode *node; /* The line being displayed */ + GlhLineNode *oldest; /* The oldest line to display */ + GlhLineSeg *seg; /* One segment of a line being displayed */ + enum {TSMAX=32}; /* The maximum length of the date and time string */ + char buffer[TSMAX+1]; /* The buffer in which to write the date and time */ + int idlen; /* The length of displayed ID strings */ + unsigned grpmax; /* The maximum group number in the buffer */ + int grplen; /* The number of characters needed to print grpmax */ + int len; /* The length of a string to be written */ +/* + * Check the arguments. + */ + if(!glh || !write_fn || !fmt) { + if(glh) + _err_record_msg(glh->err, "NULL argument(s)", END_ERR_MSG); + errno = EINVAL; + return 1; + }; +/* + * Is history enabled? + */ + if(!glh->enable || !glh->list.head) + return 0; +/* + * Work out the length to display ID numbers, choosing the length of + * the biggest number in the buffer. Smaller numbers will be padded + * with leading zeroes if needed. + */ + sprintf(buffer, "%lu", (unsigned long) glh->list.tail->id); + idlen = strlen(buffer); +/* + * Find the largest group number. + */ + grpmax = 0; + for(node=glh->list.head; node; node=node->next) { + if(node->group > grpmax) + grpmax = node->group; + }; +/* + * Find out how many characters are needed to display the group number. + */ + sprintf(buffer, "%u", (unsigned) grpmax); + grplen = strlen(buffer); +/* + * Find the node that follows the oldest line to be displayed. + */ + if(max_lines < 0) { + oldest = glh->list.head; + } else if(max_lines==0) { + return 0; + } else { + for(oldest=glh->list.tail; oldest; oldest=oldest->prev) { + if((all_groups || oldest->group == glh->group) && --max_lines <= 0) + break; + }; +/* + * If the number of lines in the buffer doesn't exceed the specified + * maximum, start from the oldest line in the buffer. + */ + if(!oldest) + oldest = glh->list.head; + }; +/* + * List the history lines in increasing time order. + */ + for(node=oldest; node; node=node->next) { +/* + * Only display lines from the current history group, unless + * told otherwise. + */ + if(all_groups || node->group == glh->group) { + const char *fptr; /* A pointer into the format string */ + struct tm *t = NULL; /* The broken time version of the timestamp */ +/* + * Work out the calendar representation of the node timestamp. + */ + if(node->timestamp != (time_t) -1) + t = localtime(&node->timestamp); +/* + * Parse the format string. + */ + fptr = fmt; + while(*fptr) { +/* + * Search for the start of the next format directive or the end of the string. + */ + const char *start = fptr; + while(*fptr && *fptr != '%') + fptr++; +/* + * Display any literal characters that precede the located directive. + */ + if(fptr > start) { + len = (int) (fptr - start); + if(write_fn(data, start, len) != len) + return 1; + }; +/* + * Did we hit a new directive before the end of the line? + */ + if(*fptr) { +/* + * Obey the directive. Ignore unknown directives. + */ + switch(*++fptr) { + case 'D': /* Display the date */ + if(t && strftime(buffer, TSMAX, "%Y-%m-%d", t) != 0) { + len = strlen(buffer); + if(write_fn(data, buffer, len) != len) + return 1; + }; + break; + case 'T': /* Display the time of day */ + if(t && strftime(buffer, TSMAX, "%H:%M:%S", t) != 0) { + len = strlen(buffer); + if(write_fn(data, buffer, len) != len) + return 1; + }; + break; + case 'N': /* Display the sequential entry number */ + sprintf(buffer, "%*lu", idlen, (unsigned long) node->id); + len = strlen(buffer); + if(write_fn(data, buffer, len) != len) + return 1; + break; + case 'G': + sprintf(buffer, "%*u", grplen, (unsigned) node->group); + len = strlen(buffer); + if(write_fn(data, buffer, len) != len) + return 1; + break; + case 'H': /* Display the history line */ + for(seg=node->line->head; seg; seg=seg->next) { + len = seg->next ? GLH_SEG_SIZE : strlen(seg->s); + if(write_fn(data, seg->s, len) != len) + return 1; + }; + break; + case '%': /* A literal % symbol */ + if(write_fn(data, "%", 1) != 1) + return 1; + break; + }; +/* + * Skip the directive. + */ + if(*fptr) + fptr++; + }; + }; + }; + }; + return 0; +} + +/*....................................................................... + * Change the size of the history buffer. + * + * Input: + * glh GlHistory * The input-line history maintenance object. + * bufsize size_t The number of bytes in the history buffer, or 0 + * to delete the buffer completely. + * Output: + * return int 0 - OK. + * 1 - Insufficient memory (the previous buffer + * will have been retained). No error message + * will be displayed. + */ +int _glh_resize_history(GlHistory *glh, size_t bufsize) +{ + int nbuff; /* The number of segments in the new buffer */ + int i; +/* + * Check the arguments. + */ + if(!glh) { + errno = EINVAL; + return 1; + }; +/* + * How many buffer segments does the requested buffer size correspond + * to? + */ + nbuff = (bufsize+GLH_SEG_SIZE-1) / GLH_SEG_SIZE; +/* + * Has a different size than the current size been requested? + */ + if(glh->nbuff != nbuff) { +/* + * Cancel any ongoing search. + */ + (void) _glh_cancel_search(glh); +/* + * Create a wholly new buffer? + */ + if(glh->nbuff == 0 && nbuff>0) { + glh->buffer = (GlhLineSeg *) malloc(sizeof(GlhLineSeg) * nbuff); + if(!glh->buffer) + return 1; + glh->nbuff = nbuff; + glh->nfree = glh->nbuff; + glh->nbusy = 0; + glh->nline = 0; +/* + * Link the currently unused nodes of the buffer into a list. + */ + glh->unused = glh->buffer; + for(i=0; inbuff-1; i++) { + GlhLineSeg *seg = glh->unused + i; + seg->next = seg + 1; + }; + glh->unused[i].next = NULL; +/* + * Delete an existing buffer? + */ + } else if(nbuff == 0) { + _glh_clear_history(glh, 1); + free(glh->buffer); + glh->buffer = NULL; + glh->unused = NULL; + glh->nbuff = 0; + glh->nfree = 0; + glh->nbusy = 0; + glh->nline = 0; +/* + * Change from one finite buffer size to another? + */ + } else { + GlhLineSeg *buffer; /* The resized buffer */ + int nbusy; /* The number of used line segments in the new buffer */ +/* + * Starting from the oldest line in the buffer, discard lines until + * the buffer contains at most 'nbuff' used line segments. + */ + while(glh->list.head && glh->nbusy > nbuff) + _glh_discard_line(glh, glh->list.head); +/* + * Attempt to allocate a new buffer. + */ + buffer = (GlhLineSeg *) malloc(nbuff * sizeof(GlhLineSeg)); + if(!buffer) { + errno = ENOMEM; + return 1; + }; +/* + * Copy the used segments of the old buffer to the start of the new buffer. + */ + nbusy = 0; + for(i=0; ihash.bucket + i; + GlhHashNode *hnode; + for(hnode=b->lines; hnode; hnode=hnode->next) { + GlhLineSeg *seg = hnode->head; + hnode->head = buffer + nbusy; + for( ; seg; seg=seg->next) { + buffer[nbusy] = *seg; + buffer[nbusy].next = seg->next ? &buffer[nbusy+1] : NULL; + nbusy++; + }; + }; + }; +/* + * Make a list of the new buffer's unused segments. + */ + for(i=nbusy; ibuffer); +/* + * Install the new buffer. + */ + glh->buffer = buffer; + glh->nbuff = nbuff; + glh->nbusy = nbusy; + glh->nfree = nbuff - nbusy; + glh->unused = glh->nfree > 0 ? (buffer + nbusy) : NULL; + }; + }; + return 0; +} + +/*....................................................................... + * Set an upper limit to the number of lines that can be recorded in the + * history list, or remove a previously specified limit. + * + * Input: + * glh GlHistory * The input-line history maintenance object. + * max_lines int The maximum number of lines to allow, or -1 to + * cancel a previous limit and allow as many lines + * as will fit in the current history buffer size. + */ +void _glh_limit_history(GlHistory *glh, int max_lines) +{ + if(!glh) + return; +/* + * Apply a new limit? + */ + if(max_lines >= 0 && max_lines != glh->max_lines) { +/* + * Count successively older lines until we reach the start of the + * list, or until we have seen max_lines lines (at which point 'node' + * will be line number max_lines+1). + */ + int nline = 0; + GlhLineNode *node; + for(node=glh->list.tail; node && ++nline <= max_lines; node=node->prev) + ; +/* + * Discard any lines that exceed the limit. + */ + if(node) { + GlhLineNode *oldest = node->next; /* The oldest line to be kept */ +/* + * Delete nodes from the head of the list until we reach the node that + * is to be kept. + */ + while(glh->list.head && glh->list.head != oldest) + _glh_discard_line(glh, glh->list.head); + }; + }; +/* + * Record the new limit. + */ + glh->max_lines = max_lines; + return; +} + +/*....................................................................... + * Discard either all history, or the history associated with the current + * history group. + * + * Input: + * glh GlHistory * The input-line history maintenance object. + * all_groups int If true, clear all of the history. If false, + * clear only the stored lines associated with the + * currently selected history group. + */ +void _glh_clear_history(GlHistory *glh, int all_groups) +{ + int i; +/* + * Check the arguments. + */ + if(!glh) + return; +/* + * Cancel any ongoing search. + */ + (void) _glh_cancel_search(glh); +/* + * Delete all history lines regardless of group? + */ + if(all_groups) { +/* + * Claer the time-ordered list of lines. + */ + _rst_FreeList(glh->list.node_mem); + glh->list.head = glh->list.tail = NULL; + glh->nline = 0; + glh->id_node = NULL; +/* + * Clear the hash table. + */ + for(i=0; ihash.bucket[i].lines = NULL; + _rst_FreeList(glh->hash.node_mem); +/* + * Move all line segment nodes back onto the list of unused segments. + */ + if(glh->buffer) { + glh->unused = glh->buffer; + for(i=0; inbuff-1; i++) { + GlhLineSeg *seg = glh->unused + i; + seg->next = seg + 1; + }; + glh->unused[i].next = NULL; + glh->nfree = glh->nbuff; + glh->nbusy = 0; + } else { + glh->unused = NULL; + glh->nbusy = glh->nfree = 0; + }; +/* + * Just delete lines of the current group? + */ + } else { + GlhLineNode *node; /* The line node being checked */ + GlhLineNode *next; /* The line node that follows 'node' */ +/* + * Search out and delete the line nodes of the current group. + */ + for(node=glh->list.head; node; node=next) { +/* + * Keep a record of the following node before we delete the current + * node. + */ + next = node->next; +/* + * Discard this node? + */ + if(node->group == glh->group) + _glh_discard_line(glh, node); + }; + }; + return; +} + +/*....................................................................... + * Temporarily enable or disable the history list. + * + * Input: + * glh GlHistory * The input-line history maintenance object. + * enable int If true, turn on the history mechanism. If + * false, disable it. + */ +void _glh_toggle_history(GlHistory *glh, int enable) +{ + if(glh) + glh->enable = enable; +} + +/*....................................................................... + * Discard a given archived input line. + * + * Input: + * glh GlHistory * The history container object. + * node GlhLineNode * The line to be discarded, specified via its + * entry in the time-ordered list of historical + * input lines. + */ +static void _glh_discard_line(GlHistory *glh, GlhLineNode *node) +{ +/* + * Remove the node from the linked list. + */ + if(node->prev) + node->prev->next = node->next; + else + glh->list.head = node->next; + if(node->next) + node->next->prev = node->prev; + else + glh->list.tail = node->prev; +/* + * If we are deleting the node that is marked as the start point of the + * last ID search, remove the cached starting point. + */ + if(node == glh->id_node) + glh->id_node = NULL; +/* + * If we are deleting the node that is marked as the start point of the + * next prefix search, cancel the search. + */ + if(node == glh->recall) + _glh_cancel_search(glh); +/* + * Delete our copy of the line. + */ + node->line = _glh_discard_copy(glh, node->line); +/* + * Return the node to the freelist. + */ + (void) _del_FreeListNode(glh->list.node_mem, node); +/* + * Record the removal of a line from the list. + */ + glh->nline--; + return; +} + +/*....................................................................... + * Lookup the details of a given history line, given its id. + * + * Input: + * glh GlHistory * The input-line history maintenance object. + * id GlLineID The sequential number of the line. + * Input/Output: + * line const char ** A pointer to a copy of the history line will be + * assigned to *line. Beware that this pointer may + * be invalidated by the next call to any public + * history function. + * group unsigned * The group membership of the line will be assigned + * to *group. + * timestamp time_t * The timestamp of the line will be assigned to + * *timestamp. + * Output: + * return int 0 - The requested line wasn't found. + * 1 - The line was found. + */ +int _glh_lookup_history(GlHistory *glh, GlhLineID id, const char **line, + unsigned *group, time_t *timestamp) +{ + GlhLineNode *node; /* The located line location node */ +/* + * Check the arguments. + */ + if(!glh) + return 0; +/* + * Search for the line that has the specified ID. + */ + node = _glh_find_id(glh, id); +/* + * Not found? + */ + if(!node) + return 0; +/* + * Has the history line been requested? + */ + if(line) { +/* + * If necessary, reallocate the lookup buffer to accomodate the size of + * a copy of the located line. + */ + if(node->line->len + 1 > glh->lbuf_dim) { + int lbuf_dim = node->line->len + 1; + char *lbuf = realloc(glh->lbuf, lbuf_dim); + if(!lbuf) { + errno = ENOMEM; + return 0; + }; + glh->lbuf_dim = lbuf_dim; + glh->lbuf = lbuf; + }; +/* + * Copy the history line into the lookup buffer. + */ + _glh_return_line(node->line, glh->lbuf, glh->lbuf_dim); +/* + * Assign the lookup buffer as the returned line pointer. + */ + *line = glh->lbuf; + }; +/* + * Does the caller want to know the group of the line? + */ + if(group) + *group = node->group; +/* + * Does the caller want to know the timestamp of the line? + */ + if(timestamp) + *timestamp = node->timestamp; + return 1; +} + +/*....................................................................... + * Lookup a node in the history list by its ID. + * + * Input: + * glh GlHistory * The input-line history maintenance object. + * id GlhLineID The ID of the line to be returned. + * Output: + * return GlhLIneNode * The located node, or NULL if not found. + */ +static GlhLineNode *_glh_find_id(GlHistory *glh, GlhLineID id) +{ + GlhLineNode *node; /* The node being checked */ +/* + * Is history enabled? + */ + if(!glh->enable || !glh->list.head) + return NULL; +/* + * If possible, start at the end point of the last ID search. + * Otherwise start from the head of the list. + */ + node = glh->id_node; + if(!node) + node = glh->list.head; +/* + * Search forwards from 'node'? + */ + if(node->id < id) { + while(node && node->id != id) + node = node->next; + glh->id_node = node ? node : glh->list.tail; +/* + * Search backwards from 'node'? + */ + } else { + while(node && node->id != id) + node = node->prev; + glh->id_node = node ? node : glh->list.head; + }; +/* + * Return the located node (this will be NULL if the ID wasn't found). + */ + return node; +} + +/*....................................................................... + * Query the state of the history list. Note that any of the input/output + * pointers can be specified as NULL. + * + * Input: + * glh GlHistory * The input-line history maintenance object. + * Input/Output: + * enabled int * If history is enabled, *enabled will be + * set to 1. Otherwise it will be assigned 0. + * group unsigned * The current history group ID will be assigned + * to *group. + * max_lines int * The currently requested limit on the number + * of history lines in the list, or -1 if + * unlimited. + */ +void _glh_state_of_history(GlHistory *glh, int *enabled, unsigned *group, + int *max_lines) +{ + if(glh) { + if(enabled) + *enabled = glh->enable; + if(group) + *group = glh->group; + if(max_lines) + *max_lines = glh->max_lines; + }; +} + +/*....................................................................... + * Get the range of lines in the history buffer. + * + * Input: + * glh GlHistory * The input-line history maintenance object. + * Input/Output: + * oldest unsigned long * The sequential entry number of the oldest + * line in the history list will be assigned + * to *oldest, unless there are no lines, in + * which case 0 will be assigned. + * newest unsigned long * The sequential entry number of the newest + * line in the history list will be assigned + * to *newest, unless there are no lines, in + * which case 0 will be assigned. + * nlines int * The number of lines currently in the history + * list. + */ +void _glh_range_of_history(GlHistory *glh, unsigned long *oldest, + unsigned long *newest, int *nlines) +{ + if(glh) { + if(oldest) + *oldest = glh->list.head ? glh->list.head->id : 0; + if(newest) + *newest = glh->list.tail ? glh->list.tail->id : 0; + if(nlines) + *nlines = glh->nline; + }; +} + +/*....................................................................... + * Return the size of the history buffer and the amount of the + * buffer that is currently in use. + * + * Input: + * glh GlHistory * The input-line history maintenance object. + * Input/Output: + * buff_size size_t * The size of the history buffer (bytes). + * buff_used size_t * The amount of the history buffer that + * is currently occupied (bytes). + */ +void _glh_size_of_history(GlHistory *glh, size_t *buff_size, size_t *buff_used) +{ + if(glh) { + if(buff_size) + *buff_size = (glh->nbusy + glh->nfree) * GLH_SEG_SIZE; +/* + * Determine the amount of buffer space that is currently occupied. + */ + if(buff_used) + *buff_used = glh->nbusy * GLH_SEG_SIZE; + }; +} + +/*....................................................................... + * Return extra information (ie. in addition to that provided by errno) + * about the last error to occur in any of the public functions of this + * module. + * + * Input: + * glh GlHistory * The container of the history list. + * Output: + * return const char * A pointer to the internal buffer in which + * the error message is temporarily stored. + */ +const char *_glh_last_error(GlHistory *glh) +{ + return glh ? _err_get_msg(glh->err) : "NULL GlHistory argument"; +} + +/*....................................................................... + * Unless already stored, store a copy of the line in the history buffer, + * then return a reference-counted hash-node pointer to this copy. + * + * Input: + * glh GlHistory * The history maintenance buffer. + * line const char * The history line to be recorded. + * n size_t The length of the string, excluding any '\0' + * terminator. + * Output: + * return GlhHashNode * The hash-node containing the stored line, or + * NULL on error. + */ +static GlhHashNode *_glh_acquire_copy(GlHistory *glh, const char *line, + size_t n) +{ + GlhHashBucket *bucket; /* The hash-table bucket of the line */ + GlhHashNode *hnode; /* The hash-table node of the line */ + int i; +/* + * In which bucket should the line be recorded? + */ + bucket = glh_find_bucket(glh, line, n); +/* + * Is the line already recorded there? + */ + hnode = glh_find_hash_node(bucket, line, n); +/* + * If the line isn't recorded in the buffer yet, make room for it. + */ + if(!hnode) { + GlhLineSeg *seg; /* A line segment */ + int offset; /* An offset into line[] */ +/* + * How many string segments will be needed to record the new line, + * including space for a '\0' terminator? + */ + int nseg = ((n+1) + GLH_SEG_SIZE-1) / GLH_SEG_SIZE; +/* + * Discard the oldest history lines in the buffer until at least + * 'nseg' segments have been freed up, or until we run out of buffer + * space. + */ + while(glh->nfree < nseg && glh->nbusy > 0) + _glh_discard_line(glh, glh->list.head); +/* + * If the buffer is smaller than the new line, don't attempt to truncate + * it to fit. Simply don't archive it. + */ + if(glh->nfree < nseg) + return NULL; +/* + * Record the line in the first 'nseg' segments of the list of unused segments. + */ + offset = 0; + for(i=0,seg=glh->unused; inext, offset+=GLH_SEG_SIZE) + memcpy(seg->s, line + offset, GLH_SEG_SIZE); + memcpy(seg->s, line + offset, n-offset); + seg->s[n-offset] = '\0'; +/* + * Create a new hash-node for the line. + */ + hnode = (GlhHashNode *) _new_FreeListNode(glh->hash.node_mem); + if(!hnode) + return NULL; +/* + * Move the copy of the line from the list of unused segments to + * the hash node. + */ + hnode->head = glh->unused; + glh->unused = seg->next; + seg->next = NULL; + glh->nbusy += nseg; + glh->nfree -= nseg; +/* + * Prepend the new hash node to the list within the associated bucket. + */ + hnode->next = bucket->lines; + bucket->lines = hnode; +/* + * Initialize the rest of the members of the hash node. + */ + hnode->len = n; + hnode->reported = 0; + hnode->used = 0; + hnode->bucket = bucket; + }; +/* + * Increment the reference count of the line. + */ + hnode->used++; + return hnode; +} + +/*....................................................................... + * Decrement the reference count of the history line of a given hash-node, + * and if the count reaches zero, delete both the hash-node and the + * buffered copy of the line. + * + * Input: + * glh GlHistory * The history container object. + * hnode GlhHashNode * The node to be removed. + * Output: + * return GlhHashNode * The deleted hash-node (ie. NULL). + */ +static GlhHashNode *_glh_discard_copy(GlHistory *glh, GlhHashNode *hnode) +{ + if(hnode) { + GlhHashBucket *bucket = hnode->bucket; +/* + * If decrementing the reference count of the hash-node doesn't reduce + * the reference count to zero, then the line is still in use in another + * object, so don't delete it yet. Return NULL to indicate that the caller's + * access to the hash-node copy has been deleted. + */ + if(--hnode->used >= 1) + return NULL; +/* + * Remove the hash-node from the list in its parent bucket. + */ + if(bucket->lines == hnode) { + bucket->lines = hnode->next; + } else { + GlhHashNode *prev; /* The node which precedes hnode in the bucket */ + for(prev=bucket->lines; prev && prev->next != hnode; prev=prev->next) + ; + if(prev) + prev->next = hnode->next; + }; + hnode->next = NULL; +/* + * Return the line segments of the hash-node to the list of unused segments. + */ + if(hnode->head) { + GlhLineSeg *tail; /* The last node in the list of line segments */ + int nseg; /* The number of segments being discarded */ +/* + * Get the last node of the list of line segments referenced in the hash-node, + * while counting the number of line segments used. + */ + for(nseg=1,tail=hnode->head; tail->next; nseg++,tail=tail->next) + ; +/* + * Prepend the list of line segments used by the hash node to the + * list of unused line segments. + */ + tail->next = glh->unused; + glh->unused = hnode->head; + glh->nbusy -= nseg; + glh->nfree += nseg; + }; +/* + * Return the container of the hash-node to the freelist. + */ + hnode = (GlhHashNode *) _del_FreeListNode(glh->hash.node_mem, hnode); + }; + return NULL; +} + +/*....................................................................... + * Private function to locate the hash bucket associated with a given + * history line. + * + * This uses a hash-function described in the dragon-book + * ("Compilers - Principles, Techniques and Tools", by Aho, Sethi and + * Ullman; pub. Adison Wesley) page 435. + * + * Input: + * glh GlHistory * The history container object. + * line const char * The historical line to look up. + * n size_t The length of the line in line[], excluding + * any '\0' terminator. + * Output: + * return GlhHashBucket * The located hash-bucket. + */ +static GlhHashBucket *glh_find_bucket(GlHistory *glh, const char *line, + size_t n) +{ + unsigned long h = 0L; + int i; + for(i=0; ihash.bucket + (h % GLH_HASH_SIZE); +} + +/*....................................................................... + * Find a given history line within a given hash-table bucket. + * + * Input: + * bucket GlhHashBucket * The hash-table bucket in which to search. + * line const char * The historical line to lookup. + * n size_t The length of the line in line[], excluding + * any '\0' terminator. + * Output: + * return GlhHashNode * The hash-table entry of the line, or NULL + * if not found. + */ +static GlhHashNode *glh_find_hash_node(GlhHashBucket *bucket, const char *line, + size_t n) +{ + GlhHashNode *node; /* A node in the list of lines in the bucket */ +/* + * Compare each of the lines in the list of lines, against 'line'. + */ + for(node=bucket->lines; node; node=node->next) { + if(_glh_is_line(node, line, n)) + return node; + }; + return NULL; +} + +/*....................................................................... + * Return non-zero if a given string is equal to a given segmented line + * node. + * + * Input: + * hash GlhHashNode * The hash-table entry of the line. + * line const char * The string to be compared to the segmented + * line. + * n size_t The length of the line in line[], excluding + * any '\0' terminator. + * Output: + * return int 0 - The lines differ. + * 1 - The lines are the same. + */ +static int _glh_is_line(GlhHashNode *hash, const char *line, size_t n) +{ + GlhLineSeg *seg; /* A node in the list of line segments */ + int i; +/* + * Do the two lines have the same length? + */ + if(n != hash->len) + return 0; +/* + * Compare the characters of the segmented and unsegmented versions + * of the line. + */ + for(seg=hash->head; n>0 && seg; seg=seg->next) { + const char *s = seg->s; + for(i=0; n>0 && ilen > line->len) + return 0; +/* + * Compare the line to the prefix. + */ + while(pstr.c != '\0' && pstr.c == lstr.c) { + glh_step_stream(&lstr); + glh_step_stream(&pstr); + }; +/* + * Did we reach the end of the prefix string before finding + * any differences? + */ + return pstr.c == '\0'; +} + +/*....................................................................... + * Copy a given history line into a specified output string. + * + * Input: + * hash GlhHashNode The hash-table entry of the history line to + * be copied. + * line char * A copy of the history line. + * dim size_t The allocated dimension of the line buffer. + */ +static void _glh_return_line(GlhHashNode *hash, char *line, size_t dim) +{ + GlhLineSeg *seg; /* A node in the list of line segments */ + int i; + for(seg=hash->head; dim>0 && seg; seg=seg->next) { + const char *s = seg->s; + for(i=0; dim>0 && irecall && glh->recall == glh->list.tail && + !_glh_is_line(glh->recall->line, line, strlen(line))) { + _glh_cancel_search(glh); + }; +/* + * If this is the first line recall of a new recall session, save the + * current line for potential recall later, and mark it as the last + * line recalled. + */ + if(!glh->recall) { + if(_glh_add_history(glh, line, 1)) + return 1; + glh->recall = glh->list.tail; +/* + * The above call to _glh_add_history() will have incremented the line + * sequence number, after adding the line. Since we only want this to + * to be incremented for permanently entered lines, decrement it again. + */ + glh->seq--; + }; + return 0; +} + +/*....................................................................... + * Return non-zero if a history search session is currently in progress. + * + * Input: + * glh GlHistory * The input-line history maintenance object. + * Output: + * return int 0 - No search is currently in progress. + * 1 - A search is in progress. + */ +int _glh_search_active(GlHistory *glh) +{ + return glh && glh->recall; +} + +/*....................................................................... + * Initialize a character iterator object to point to the start of a + * given history line. The first character of the line will be placed + * in str->c, and subsequent characters can be placed there by calling + * glh_strep_stream(). + * + * Input: + * str GlhLineStream * The iterator object to be initialized. + * line GlhHashNode * The history line to be iterated over (a + * NULL value here, is interpretted as an + * empty string by glh_step_stream()). + */ +static void glh_init_stream(GlhLineStream *str, GlhHashNode *line) +{ + str->seg = line ? line->head : NULL; + str->posn = 0; + str->c = str->seg ? str->seg->s[0] : '\0'; +} + +/*....................................................................... + * Copy the next unread character in the line being iterated, in str->c. + * Once the end of the history line has been reached, all futher calls + * set str->c to '\0'. + * + * Input: + * str GlhLineStream * The history-line iterator to read from. + */ +static void glh_step_stream(GlhLineStream *str) +{ +/* + * Get the character from the current iterator position within the line. + */ + str->c = str->seg ? str->seg->s[str->posn] : '\0'; +/* + * Unless we have reached the end of the string, move the iterator + * to the position of the next character in the line. + */ + if(str->c != '\0' && ++str->posn >= GLH_SEG_SIZE) { + str->posn = 0; + str->seg = str->seg->next; + }; +} + +/*....................................................................... + * Return non-zero if the specified search prefix contains any glob + * wildcard characters. + * + * Input: + * prefix GlhHashNode * The search prefix. + * Output: + * return int 0 - The prefix doesn't contain any globbing + * characters. + * 1 - The prefix contains at least one + * globbing character. + */ +static int glh_contains_glob(GlhHashNode *prefix) +{ + GlhLineStream pstr; /* The stream that is used to traverse 'prefix' */ +/* + * Wrap a stream iterator around the prefix, so that we can traverse it + * without worrying about line-segmentation. + */ + glh_init_stream(&pstr, prefix); +/* + * Search for unescaped wildcard characters. + */ + while(pstr.c != '\0') { + switch(pstr.c) { + case '\\': /* Skip escaped characters */ + glh_step_stream(&pstr); + break; + case '*': case '?': case '[': /* A wildcard character? */ + return 1; + break; + }; + glh_step_stream(&pstr); + }; +/* + * No wildcard characters were found. + */ + return 0; +} + +/*....................................................................... + * Return non-zero if the history line matches a search prefix containing + * a glob pattern. + * + * Input: + * lstr GlhLineStream * The iterator stream being used to traverse + * the history line that is being matched. + * pstr GlhLineStream * The iterator stream being used to traverse + * the pattern. + * Output: + * return int 0 - Doesn't match. + * 1 - The line matches the pattern. + */ +static int glh_line_matches_glob(GlhLineStream *lstr, GlhLineStream *pstr) +{ +/* + * Match each character of the pattern until we reach the end of the + * pattern. + */ + while(pstr->c != '\0') { +/* + * Handle the next character of the pattern. + */ + switch(pstr->c) { +/* + * A match zero-or-more characters wildcard operator. + */ + case '*': +/* + * Skip the '*' character in the pattern. + */ + glh_step_stream(pstr); +/* + * If the pattern ends with the '*' wildcard, then the + * rest of the line matches this. + */ + if(pstr->c == '\0') + return 1; +/* + * Using the wildcard to match successively longer sections of + * the remaining characters of the line, attempt to match + * the tail of the line against the tail of the pattern. + */ + while(lstr->c) { + GlhLineStream old_lstr = *lstr; + GlhLineStream old_pstr = *pstr; + if(glh_line_matches_glob(lstr, pstr)) + return 1; +/* + * Restore the line and pattern iterators for a new try. + */ + *lstr = old_lstr; + *pstr = old_pstr; +/* + * Prepare to try again, one character further into the line. + */ + glh_step_stream(lstr); + }; + return 0; /* The pattern following the '*' didn't match */ + break; +/* + * A match-one-character wildcard operator. + */ + case '?': +/* + * If there is a character to be matched, skip it and advance the + * pattern pointer. + */ + if(lstr->c) { + glh_step_stream(lstr); + glh_step_stream(pstr); +/* + * If we hit the end of the line, there is no character + * matching the operator, so the pattern doesn't match. + */ + } else { + return 0; + }; + break; +/* + * A character range operator, with the character ranges enclosed + * in matching square brackets. + */ + case '[': + glh_step_stream(pstr); /* Skip the '[' character */ + if(!lstr->c || !glh_matches_range(lstr->c, pstr)) + return 0; + glh_step_stream(lstr); /* Skip the character that matched */ + break; +/* + * A backslash in the pattern prevents the following character as + * being seen as a special character. + */ + case '\\': + glh_step_stream(pstr); /* Skip the backslash */ + /* Note fallthrough to default */ +/* + * A normal character to be matched explicitly. + */ + default: + if(lstr->c == pstr->c) { + glh_step_stream(lstr); + glh_step_stream(pstr); + } else { + return 0; + }; + break; + }; + }; +/* + * To get here, pattern must have been exhausted. The line only + * matches the pattern if the line as also been exhausted. + */ + return pstr->c == '\0' && lstr->c == '\0'; +} + +/*....................................................................... + * Match a character range expression terminated by an unescaped close + * square bracket. + * + * Input: + * c char The character to be matched with the range + * pattern. + * pstr GlhLineStream * The iterator stream being used to traverse + * the pattern. + * Output: + * return int 0 - Doesn't match. + * 1 - The character matched. + */ +static int glh_matches_range(char c, GlhLineStream *pstr) +{ + int invert = 0; /* True to invert the sense of the match */ + int matched = 0; /* True if the character matched the pattern */ + char lastc = '\0'; /* The previous character in the pattern */ +/* + * If the first character is a caret, the sense of the match is + * inverted and only if the character isn't one of those in the + * range, do we say that it matches. + */ + if(pstr->c == '^') { + glh_step_stream(pstr); + invert = 1; + }; +/* + * The hyphen is only a special character when it follows the first + * character of the range (not including the caret). + */ + if(pstr->c == '-') { + glh_step_stream(pstr); + if(c == '-') + matched = 1; +/* + * Skip other leading '-' characters since they make no sense. + */ + while(pstr->c == '-') + glh_step_stream(pstr); + }; +/* + * The hyphen is only a special character when it follows the first + * character of the range (not including the caret or a hyphen). + */ + if(pstr->c == ']') { + glh_step_stream(pstr); + if(c == ']') + matched = 1; + }; +/* + * Having dealt with the characters that have special meanings at + * the beginning of a character range expression, see if the + * character matches any of the remaining characters of the range, + * up until a terminating ']' character is seen. + */ + while(!matched && pstr->c && pstr->c != ']') { +/* + * Is this a range of characters signaled by the two end characters + * separated by a hyphen? + */ + if(pstr->c == '-') { + glh_step_stream(pstr); /* Skip the hyphen */ + if(pstr->c != ']') { + if(c >= lastc && c <= pstr->c) + matched = 1; + }; +/* + * A normal character to be compared directly. + */ + } else if(pstr->c == c) { + matched = 1; + }; +/* + * Record and skip the character that we just processed. + */ + lastc = pstr->c; + if(pstr->c != ']') + glh_step_stream(pstr); + }; +/* + * Find the terminating ']'. + */ + while(pstr->c && pstr->c != ']') + glh_step_stream(pstr); +/* + * Did we find a terminating ']'? + */ + if(pstr->c == ']') { +/* + * Skip the terminating ']'. + */ + glh_step_stream(pstr); +/* + * If the pattern started with a caret, invert the sense of the match. + */ + if(invert) + matched = !matched; +/* + * If the pattern didn't end with a ']', then it doesn't match, + * regardless of the value of the required sense of the match. + */ + } else { + matched = 0; + }; + return matched; +} + diff --git a/libtecla-1.6.1/history.h b/libtecla-1.6.1/history.h new file mode 100644 index 0000000..d32ee8f --- /dev/null +++ b/libtecla-1.6.1/history.h @@ -0,0 +1,169 @@ +#ifndef history_h +#define history_h + +/* + * Copyright (c) 2000, 2001, 2002, 2003, 2004 by Martin C. Shepherd. + * + * All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, and/or sell copies of the Software, and to permit persons + * to whom the Software is furnished to do so, provided that the above + * copyright notice(s) and this permission notice appear in all copies of + * the Software and that both the above copyright notice(s) and this + * permission notice appear in supporting documentation. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT + * OF THIRD PARTY RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR + * HOLDERS INCLUDED IN THIS NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL + * INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING + * FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, + * NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION + * WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + * Except as contained in this notice, the name of a copyright holder + * shall not be used in advertising or otherwise to promote the sale, use + * or other dealings in this Software without prior written authorization + * of the copyright holder. + */ + +#include /* FILE * */ + +/*----------------------------------------------------------------------- + * This module is used to record and traverse historical lines of user input. + */ + +typedef struct GlHistory GlHistory; + +/* + * Create a new history maintenance object. + */ +GlHistory *_new_GlHistory(size_t buflen); + +/* + * Delete a history maintenance object. + */ +GlHistory *_del_GlHistory(GlHistory *glh); + +int _glh_add_history(GlHistory *glh, const char *line, int force); + +int _glh_search_prefix(GlHistory *glh, const char *line, int prefix_len); + +char *_glh_find_backwards(GlHistory *glh, char *line, size_t dim); +char *_glh_find_forwards(GlHistory *glh, char *line, size_t dim); + +int _glh_cancel_search(GlHistory *glh); + +char *_glh_oldest_line(GlHistory *glh, char *line, size_t dim); +char *_glh_current_line(GlHistory *glh, char *line, size_t dim); + +/* + * Whenever a new line is added to the history buffer, it is given + * a unique ID, recorded in an object of the following type. + */ +typedef unsigned long GlhLineID; + +/* + * Query the id of a history line offset by a given number of lines from + * the one that is currently being recalled. If a recall session isn't + * in progress, or the offset points outside the history list, 0 is + * returned. + */ +GlhLineID _glh_line_id(GlHistory *glh, int offset); + +/* + * Recall a line by its history buffer ID. If the line is no longer + * in the buffer, or the specified id is zero, NULL is returned. + */ +char *_glh_recall_line(GlHistory *glh, GlhLineID id, char *line, size_t dim); + +/* + * Write the contents of the history buffer to a given file. Note that + * ~ and $ expansion are not performed on the filename. + */ +int _glh_save_history(GlHistory *glh, const char *filename, + const char *comment, int max_lines); + +/* + * Restore the contents of the history buffer from a given file. + * Note that ~ and $ expansion are not performed on the filename. + */ +int _glh_load_history(GlHistory *glh, const char *filename, const char *comment, + char *line, size_t dim); + +/* + * Set and query the current history group. + */ +int _glh_set_group(GlHistory *glh, unsigned group); +int _glh_get_group(GlHistory *glh); + +/* + * Display the contents of the history list to the specified stdio + * output group. + */ +int _glh_show_history(GlHistory *glh, GlWriteFn *write_fn, void *data, + const char *fmt, int all_groups, int max_lines); + +/* + * Change the size of the history buffer. + */ +int _glh_resize_history(GlHistory *glh, size_t bufsize); + +/* + * Set an upper limit to the number of lines that can be recorded in the + * history list, or remove a previously specified limit. + */ +void _glh_limit_history(GlHistory *glh, int max_lines); + +/* + * Discard either all history, or the history associated with the current + * history group. + */ +void _glh_clear_history(GlHistory *glh, int all_groups); + +/* + * Temporarily enable or disable the history facility. + */ +void _glh_toggle_history(GlHistory *glh, int enable); + +/* + * Lookup a history line by its sequential number of entry in the + * history buffer. + */ +int _glh_lookup_history(GlHistory *glh, GlhLineID id, const char **line, + unsigned *group, time_t *timestamp); + +/* + * Query the state of the history list. + */ +void _glh_state_of_history(GlHistory *glh, int *enabled, unsigned *group, + int *max_lines); + +/* + * Get the range of lines in the history buffer. + */ +void _glh_range_of_history(GlHistory *glh, unsigned long *oldest, + unsigned long *newest, int *nlines); + +/* + * Return the size of the history buffer and the amount of the + * buffer that is currently in use. + */ +void _glh_size_of_history(GlHistory *glh, size_t *buff_size, size_t *buff_used); + +/* + * Get information about the last error in this module. + */ +const char *_glh_last_error(GlHistory *glh); + +/* + * Return non-zero if a history search session is currently in progress. + */ +int _glh_search_active(GlHistory *glh); + +#endif diff --git a/libtecla-1.6.1/homedir.c b/libtecla-1.6.1/homedir.c new file mode 100644 index 0000000..eb666c3 --- /dev/null +++ b/libtecla-1.6.1/homedir.c @@ -0,0 +1,470 @@ +/* + * Copyright (c) 2000, 2001, 2002, 2003, 2004 by Martin C. Shepherd. + * + * All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, and/or sell copies of the Software, and to permit persons + * to whom the Software is furnished to do so, provided that the above + * copyright notice(s) and this permission notice appear in all copies of + * the Software and that both the above copyright notice(s) and this + * permission notice appear in supporting documentation. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT + * OF THIRD PARTY RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR + * HOLDERS INCLUDED IN THIS NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL + * INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING + * FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, + * NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION + * WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + * Except as contained in this notice, the name of a copyright holder + * shall not be used in advertising or otherwise to promote the sale, use + * or other dealings in this Software without prior written authorization + * of the copyright holder. + */ + +/* + * If file-system access is to be excluded, this module has no function, + * so all of its code should be excluded. + */ +#ifndef WITHOUT_FILE_SYSTEM + +#include +#include +#include +#include + +#include +#include +#include +#include + +#include "pathutil.h" +#include "homedir.h" +#include "errmsg.h" + +/* + * Use the reentrant POSIX threads versions of the password lookup functions? + */ +#if defined(PREFER_REENTRANT) && defined(_POSIX_C_SOURCE) && _POSIX_C_SOURCE >= 199506L +#define THREAD_COMPATIBLE 1 +/* + * Under Solaris we can use thr_main() to determine whether + * threads are actually running, and thus when it is necessary + * to avoid non-reentrant features. + */ +#if defined __sun && defined __SVR4 +#include /* Solaris thr_main() */ +#endif +#endif + +/* + * Provide a password buffer size fallback in case the max size reported + * by sysconf() is said to be indeterminate. + */ +#define DEF_GETPW_R_SIZE_MAX 1024 + +/* + * The resources needed to lookup and record a home directory are + * maintained in objects of the following type. + */ +struct HomeDir { + ErrMsg *err; /* The error message report buffer */ + char *buffer; /* A buffer for reading password entries and */ + /* directory paths. */ + int buflen; /* The allocated size of buffer[] */ +#ifdef THREAD_COMPATIBLE + struct passwd pwd; /* The password entry of a user */ +#endif +}; + +static const char *hd_getpwd(HomeDir *home); + +/*....................................................................... + * Create a new HomeDir object. + * + * Output: + * return HomeDir * The new object, or NULL on error. + */ +HomeDir *_new_HomeDir(void) +{ + HomeDir *home; /* The object to be returned */ + size_t pathlen; /* The estimated maximum size of a pathname */ +/* + * Allocate the container. + */ + home = (HomeDir *) malloc(sizeof(HomeDir)); + if(!home) { + errno = ENOMEM; + return NULL; + }; +/* + * Before attempting any operation that might fail, initialize the + * container at least up to the point at which it can safely be passed + * to _del_HomeDir(). + */ + home->err = NULL; + home->buffer = NULL; + home->buflen = 0; +/* + * Allocate a place to record error messages. + */ + home->err = _new_ErrMsg(); + if(!home->err) + return _del_HomeDir(home); +/* + * Allocate the buffer that is used by the reentrant POSIX password-entry + * lookup functions. + */ +#ifdef THREAD_COMPATIBLE +/* + * Get the length of the buffer needed by the reentrant version + * of getpwnam(). + */ +#ifndef _SC_GETPW_R_SIZE_MAX + home->buflen = DEF_GETPW_R_SIZE_MAX; +#else + errno = 0; + home->buflen = sysconf(_SC_GETPW_R_SIZE_MAX); +/* + * If the limit isn't available, substitute a suitably large fallback value. + */ + if(home->buflen < 0 || errno) + home->buflen = DEF_GETPW_R_SIZE_MAX; +#endif +#endif +/* + * If the existing buffer length requirement is too restrictive to record + * a pathname, increase its length. + */ + pathlen = _pu_pathname_dim(); + if(pathlen > home->buflen) + home->buflen = pathlen; +/* + * Allocate a work buffer. + */ + home->buffer = (char *) malloc(home->buflen); + if(!home->buffer) { + errno = ENOMEM; + return _del_HomeDir(home); + }; + return home; +} + +/*....................................................................... + * Delete a HomeDir object. + * + * Input: + * home HomeDir * The object to be deleted. + * Output: + * return HomeDir * The deleted object (always NULL). + */ +HomeDir *_del_HomeDir(HomeDir *home) +{ + if(home) { + home->err = _del_ErrMsg(home->err); + if(home->buffer) + free(home->buffer); + free(home); + }; + return NULL; +} + +/*....................................................................... + * Lookup the home directory of a given user in the password file. + * + * Input: + * home HomeDir * The resources needed to lookup the home directory. + * user const char * The name of the user to lookup, or "" to lookup + * the home directory of the person running the + * program. + * Output: + * return const char * The home directory. If the library was compiled + * with threads, this string is part of the HomeDir + * object and will change on subsequent calls. If + * the library wasn't compiled to be reentrant, + * then the string is a pointer into a static string + * in the C library and will change not only on + * subsequent calls to this function, but also if + * any calls are made to the C library password + * file lookup functions. Thus to be safe, you should + * make a copy of this string before calling any + * other function that might do a password file + * lookup. + * + * On error, NULL is returned and a description + * of the error can be acquired by calling + * _hd_last_home_dir_error(). + */ +const char *_hd_lookup_home_dir(HomeDir *home, const char *user) +{ + const char *home_dir; /* A pointer to the home directory of the user */ +/* + * If no username has been specified, arrange to lookup the current + * user. + */ + int login_user = !user || *user=='\0'; +/* + * Check the arguments. + */ + if(!home) { + errno = EINVAL; + return NULL; + }; +/* + * Handle the ksh "~+". This expands to the absolute path of the + * current working directory. + */ + if(!login_user && strcmp(user, "+") == 0) { + home_dir = hd_getpwd(home); + if(!home_dir) { + _err_record_msg(home->err, "Can't determine current directory", + END_ERR_MSG); + return NULL; + } + return home_dir; + }; +/* + * When looking up the home directory of the current user, see if the + * HOME environment variable is set, and if so, return its value. + */ + if(login_user) { + home_dir = getenv("HOME"); + if(home_dir) + return home_dir; + }; +/* + * Look up the password entry of the user. + * First the POSIX threads version - this is painful! + */ +#ifdef THREAD_COMPATIBLE + { + struct passwd *ret; /* The returned pointer to pwd */ + int status; /* The return value of getpwnam_r() */ +/* + * Look up the password entry of the specified user. + */ + if(login_user) + status = getpwuid_r(geteuid(), &home->pwd, home->buffer, home->buflen, + &ret); + else + status = getpwnam_r(user, &home->pwd, home->buffer, home->buflen, &ret); + if(status || !ret) { + _err_record_msg(home->err, "User '", user, "' doesn't exist.", + END_ERR_MSG); + return NULL; + }; +/* + * Get a pointer to the string that holds the home directory. + */ + home_dir = home->pwd.pw_dir; + }; +/* + * Now the classic unix version. + */ +#else + { + struct passwd *pwd = login_user ? getpwuid(geteuid()) : getpwnam(user); + if(!pwd) { + _err_record_msg(home->err, "User '", user, "' doesn't exist.", + END_ERR_MSG); + return NULL; + }; +/* + * Get a pointer to the home directory. + */ + home_dir = pwd->pw_dir; + }; +#endif + return home_dir; +} + +/*....................................................................... + * Return a description of the last error that caused _hd_lookup_home_dir() + * to return NULL. + * + * Input: + * home HomeDir * The resources needed to record the home directory. + * Output: + * return char * The description of the last error. + */ +const char *_hd_last_home_dir_error(HomeDir *home) +{ + return home ? _err_get_msg(home->err) : "NULL HomeDir argument"; +} + +/*....................................................................... + * The _hd_scan_user_home_dirs() function calls a user-provided function + * for each username known by the system, passing the function both + * the name and the home directory of the user. + * + * Input: + * home HomeDir * The resource object for reading home + * directories. + * prefix const char * Only information for usernames that + * start with this prefix will be + * returned. Note that the empty + & string "", matches all usernames. + * data void * Anonymous data to be passed to the + * callback function. + * callback_fn HOME_DIR_FN(*) The function to call for each user. + * Output: + * return int 0 - Successful completion. + * 1 - An error occurred. A description + * of the error can be obtained by + * calling _hd_last_home_dir_error(). + */ +int _hd_scan_user_home_dirs(HomeDir *home, const char *prefix, + void *data, HOME_DIR_FN(*callback_fn)) +{ + int waserr = 0; /* True after errors */ + int prefix_len; /* The length of prefix[] */ +/* + * Check the arguments. + */ + if(!home || !prefix || !callback_fn) { + if(home) { + _err_record_msg(home->err, + "_hd_scan_user_home_dirs: Missing callback function", + END_ERR_MSG); + }; + return 1; + }; +/* + * Get the length of the username prefix. + */ + prefix_len = strlen(prefix); +/* + * There are no reentrant versions of getpwent() etc for scanning + * the password file, so disable username completion when the + * library is compiled to be reentrant. + */ +#if defined(PREFER_REENTRANT) && defined(_POSIX_C_SOURCE) && _POSIX_C_SOURCE >= 199506L +#if defined __sun && defined __SVR4 + if(thr_main() >= 0) /* thread library is linked in */ +#else + if(1) +#endif + { + struct passwd pwd_buffer; /* A returned password entry */ + struct passwd *pwd; /* A pointer to pwd_buffer */ + char buffer[512]; /* The buffer in which the string members of */ + /* pwd_buffer are stored. */ +/* + * See if the prefix that is being completed is a complete username. + */ + if(!waserr && getpwnam_r(prefix, &pwd_buffer, buffer, sizeof(buffer), + &pwd) == 0 && pwd != NULL) { + waserr = callback_fn(data, pwd->pw_name, pwd->pw_dir, + _err_get_msg(home->err), ERR_MSG_LEN); + }; +/* + * See if the username of the current user minimally matches the prefix. + */ + if(!waserr && getpwuid_r(getuid(), &pwd_buffer, buffer, sizeof(buffer), + &pwd) == 0 && pwd != NULL && + strncmp(prefix, pwd->pw_name, prefix_len)==0) { + waserr = callback_fn(data, pwd->pw_name, pwd->pw_dir, + _err_get_msg(home->err), ERR_MSG_LEN); + }; +/* + * Reentrancy not required? + */ + } else +#endif + { + struct passwd *pwd; /* The pointer to the latest password entry */ +/* + * Open the password file. + */ + setpwent(); +/* + * Read the contents of the password file, looking for usernames + * that start with the specified prefix, and adding them to the + * list of matches. + */ + while((pwd = getpwent()) != NULL && !waserr) { + if(strncmp(prefix, pwd->pw_name, prefix_len) == 0) { + waserr = callback_fn(data, pwd->pw_name, pwd->pw_dir, + _err_get_msg(home->err), ERR_MSG_LEN); + }; + }; +/* + * Close the password file. + */ + endpwent(); + }; +/* + * Under ksh ~+ stands for the absolute pathname of the current working + * directory. + */ + if(!waserr && strncmp(prefix, "+", prefix_len) == 0) { + const char *pwd = hd_getpwd(home); + if(pwd) { + waserr = callback_fn(data, "+", pwd, _err_get_msg(home->err),ERR_MSG_LEN); + } else { + waserr = 1; + _err_record_msg(home->err, "Can't determine current directory.", + END_ERR_MSG); + }; + }; + return waserr; +} + +/*....................................................................... + * Return the value of getenv("PWD") if this points to the current + * directory, or the return value of getcwd() otherwise. The reason for + * prefering PWD over getcwd() is that the former preserves the history + * of symbolic links that have been traversed to reach the current + * directory. This function is designed to provide the equivalent + * expansion of the ksh ~+ directive, which normally returns its value + * of PWD. + * + * Input: + * home HomeDir * The resource object for reading home directories. + * Output: + * return const char * A pointer to either home->buffer, where the + * pathname is recorded, the string returned by + * getenv("PWD"), or NULL on error. + */ +static const char *hd_getpwd(HomeDir *home) +{ +/* + * Get the absolute path of the current working directory. + */ + char *cwd = getcwd(home->buffer, home->buflen); +/* + * Some shells set PWD with the path of the current working directory. + * This will differ from cwd in that it won't have had symbolic links + * expanded. + */ + const char *pwd = getenv("PWD"); +/* + * If PWD was set, and it points to the same directory as cwd, return + * its value. Note that it won't be the same if the current shell or + * the current program has changed directories, after inheriting PWD + * from a parent shell. + */ + struct stat cwdstat, pwdstat; + if(pwd && cwd && stat(cwd, &cwdstat)==0 && stat(pwd, &pwdstat)==0 && + cwdstat.st_dev == pwdstat.st_dev && cwdstat.st_ino == pwdstat.st_ino) + return pwd; +/* + * Also return pwd if getcwd() failed, since it represents the best + * information that we have access to. + */ + if(!cwd) + return pwd; +/* + * In the absence of a valid PWD, return cwd. + */ + return cwd; +} + +#endif /* ifndef WITHOUT_FILE_SYSTEM */ diff --git a/libtecla-1.6.1/homedir.h b/libtecla-1.6.1/homedir.h new file mode 100644 index 0000000..0ad3e94 --- /dev/null +++ b/libtecla-1.6.1/homedir.h @@ -0,0 +1,81 @@ +#ifndef homedir_h +#define homedir_h + +/* + * Copyright (c) 2000, 2001, 2002, 2003, 2004 by Martin C. Shepherd. + * + * All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, and/or sell copies of the Software, and to permit persons + * to whom the Software is furnished to do so, provided that the above + * copyright notice(s) and this permission notice appear in all copies of + * the Software and that both the above copyright notice(s) and this + * permission notice appear in supporting documentation. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT + * OF THIRD PARTY RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR + * HOLDERS INCLUDED IN THIS NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL + * INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING + * FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, + * NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION + * WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + * Except as contained in this notice, the name of a copyright holder + * shall not be used in advertising or otherwise to promote the sale, use + * or other dealings in this Software without prior written authorization + * of the copyright holder. + */ + +typedef struct HomeDir HomeDir; + +/* + * The following constructor and destructor functions create and + * delete the resources needed to look up home directories. + */ +HomeDir *_new_HomeDir(void); +HomeDir *_del_HomeDir(HomeDir *home); + +/* + * Return the home directory of a specified user, or NULL if unknown. + */ +const char *_hd_lookup_home_dir(HomeDir *home, const char *user); + +/* + * Get the description of the that occured when _hd_lookup_home_dir() was + * last called. + */ +const char *_hd_last_home_dir_error(HomeDir *home); + +/* + * The _hd_scan_user_home_dirs() function calls a user-provided function + * for each username known by the system, passing the function both + * the name and the home directory of the user. + * + * The following macro can be used to declare both pointers and + * prototypes for the callback functions. The 'data' argument is + * a copy of the 'data' argument passed to _hd_scan_user_home_dirs() + * and is intended for the user of _hd_scan_user_home_dirs() to use + * to pass anonymous context data to the callback function. + * The username and home directories are passed to the callback function + * in the *usrnam and *homedir arguments respectively. + * To abort the scan, and have _hd_scan_user_home_dirs() return 1, the + * callback function can return 1. A description of up to maxerr + * characters before the terminating '\0', can be written to errmsg[]. + * This can then be examined by calling _hd_last_home_dir_error(). + * To indicate success and continue the scan, the callback function + * should return 0. _hd_scan_user_home_dirs() returns 0 on successful + * completion of the scan, or 1 if an error occurred or a call to the + * callback function returned 1. + */ +#define HOME_DIR_FN(fn) int (fn)(void *data, const char *usrnam, const char *homedir, char *errmsg, int maxerr) + +int _hd_scan_user_home_dirs(HomeDir *home, const char *prefix, void *data, + HOME_DIR_FN(*callback_fn)); + +#endif diff --git a/libtecla-1.6.1/html/changes.html b/libtecla-1.6.1/html/changes.html new file mode 100644 index 0000000..9be5251 --- /dev/null +++ b/libtecla-1.6.1/html/changes.html @@ -0,0 +1,2761 @@ +The tecla library change log +
+In the following log, modification dates are listed using the European
+convention in which the day comes before the month (ie. DD/MM/YYYY).
+The most recent modifications are listed first.
+
+31/10/2004 mcs@astro.caltech.edu (problem reported by Godfrey van der Linden) 
+           getline.c
+             The gl_event_handler() function had the endif of a
+             conditional compilation clause in the wrong place. This
+             only upset the compiler on unusual systems that don't
+             have select(). The problem was seen under Mac OS X, due
+             to the configuration problem in 1.6.0 that caused the
+             configure script to mistakenly report that select wasn't
+             available.
+
+31/10/2004 mcs@astro.caltech.edu (info provided by Ivan Rayner)
+           configure.in configure Makefile.in
+             Ivan reported that under IRIX 6.5 it is necessary to add
+             -D_XOPEN_SOURCE=500 to the compiler flags, when compiling
+             the reentrant version of the library. Thus, whereas
+             previously I hardwired the value of DEFINES_R in
+             Makefile.in, I have now made this a variable in the
+             configure script, which is augmented with the above
+             addition, within an IRIX-specific switch clause.
+
+             Also apparently configure leaves the RANLIB variable
+             blank, instead of setting it to ":", so I have now
+             explicitly set this to ":", within the new IRIX clause of
+             the configure script.
+
+31/10/2004 mcs@astro.caltech.edu (info provided by Ivan Rayner)
+           getline.c
+             Under IRIX, the compiler warned that gl_read_unmasked()
+             was returning an int, which was then being assigned to an
+             enumeration type. This is techically fine, but it
+             highlighted the fact that I had meant to declare
+             gl_read_unmasked() to directly return the enumerated
+             type. I have now done so.
+
+26/09/2004 mcs@astro.caltech.edu
+           getline.c
+             Users can now turn off interactive command-line editing
+             by setting the TERM environment variable to the word "dumb".
+
+18/07/2004 mcs@astro.caltech.edu (problem noted by Michael MacFaden)
+           getline.c
+             Calling gl_terminal_size() on a system without support
+             for SIGWINCH caused a divide-by-zero error in an unintended
+             call to gl_erase_line(), because gl_update_size() was
+             incorrectly being called to query the terminal size,
+             instead of gl_query_size().
+
+18/07/2004 Padraig Brady  (documented here by mcs@astro.caltech.edu)
+           getline.c
+             The suspend and termination signal-handlers installed by
+             gl_tty_signals(), were being installed swapped.
+
+03/06/2004 Mike Meaney  (documented here by mcs@astro.caltech.edu)
+           getline.c
+             Mike pointed out the fact that the curses setupterm()
+             function is actually documented to exit the application
+             if an error occurs while its optional errret argument is
+             NULL. I hadn't noticed this, and because I didn't need
+             the extra information returned in the errret argument, I
+             was passing it a NULL. As suggested by Mike, I now pass
+             this argument a pointer to a dummy errret variable.
+
+23/05/2004 mcs@astro.caltech.edu (problem noted by John Beck)
+           man/func/cpl_complete_word.in
+             Some of the prototypes of functions and types documented
+             by the cpl_complete_word man page, weren't listed in the
+             Synopsis section of this man page. They are now listed
+             there.
+            
+23/05/2004 mcs@astro.caltech.edu
+           getline.c man/func/gl_get_line.in
+             I have now added support for calling gl_normal_io() from
+             any callback functions that the application installs by
+             calling either gl_inactivity_timeout(), or gl_watch_fd().
+             Previously, if one of these callback functions called
+             gl_normal_io(), then after returning to gl_get_line(),
+             gl_get_line() would incorrectly assume that the terminal
+             was still in raw I/O mode. Now, gl_get_line() checks to
+             see if gl_normal_io() was called by the callback, and
+             if so, calls _gl_raw_io() to reinstate raw I/O mode.
+
+21/05/2004 mcs@astro.caltech.edu
+           configure.in configure
+             On Mac OS X the code that the configure script used to
+             check for select() failed due to missing symbols in
+             sys/select.h. Moving the inclusion of sys/select.h to
+             after the inclusion of sys/time.h, sys/types.h and
+             sys/unistd.h fixed this.
+
+11/05/2004 mcs@astro.caltech.edu
+           getline.c man/func/gl_get_line.in
+             If the line buffer returned by one call to gl_get_line()
+             was passed as the start_line argument of the next call to
+             gl_get_line(), then instead of the just-entered line
+             being presented back to the user for further editing, the
+             start_line argument was effectively ignored, because the
+             line buffer whose pointer was being passed back, was
+             being cleared before the start_line pointer was examined.
+             This appears to have been a case of me incorrectly
+             thinking that I had forgotten to initialize gl->line[]
+             and gl->ntotal in the gl_reset_input_line() function, and
+             then "fixing" this supposed omission. Removing this
+             erroneous fix, restored things to how they were meant to
+             be. To make it unlikely that I will make the same mistake
+             again, I have renamed the function from
+             gl_reset_input_line() to gl_reset_editor(), to stop it
+             looking as though it is meant to reset the contents of
+             the input line (that is what gl_truncate_buffer() is
+             for), explicitly stated that it doesn't clear the input
+             line, in the header comments of the function, and added a
+             prominent warning comment in the body of the function.
+
+             Also, since support for passing back the returned line
+             pointer via the start_line argument of the next call to
+             gl_get_line(), wasn't documented in the man page, but was
+             meant to be supported, and definitely used to work, I
+             have now amended the man page documentation of
+             gl_get_line() to explicitly state that this feature is
+             officially supported.
+
+2?/04/2004 Released 1.6.0
+
+22/04/2004 mcs@astro.caltech.edu  (Fixed a bug reported by John Beck)
+           getline.c
+             When an error, signal, or other abnormal event aborted
+             gl_get_line(), the cleanup code that restored the
+             terminal to a sane state, also overwrote the value of
+             errno that was associated with the aborting event. An
+             I/O error occurring in the cleanup code would have also
+             overwritten the value to be returned by
+             gl_return_status(), and thus remove any possibility of
+             the caller finding out what really caused gl_get_line()
+             to abort. I have now written a new internal function
+             called, gl_record_status(), which records the completion
+             status to be returned by gl_return_status(), and the
+             value to assign to errno just before gl_get_line()
+             returns. This is called wherever code detects conditions
+             that require gl_get_line() to return early. The function
+             ensures that once an abnormal completion status has been
+             recorded for return, subsequent completions statuses
+             aren't recorded. This ensures that the caller sees the
+             original cause of the abnormal return, rather than any
+             error that occurs during cleaning up from this before
+             return.
+
+17/04/2004 mcs@astro.caltech.edu
+           getline.c
+             If an application's callback called gl_read_char() after
+             calling gl_normal_io(), it would inappropriately
+             redisplay the input line, when it called _gl_raw_io() to
+             temporarily switch the terminal back into raw mode.
+
+             To fix this, _gl_raw_io() now takes a new 'redisplay'
+             argument, which specifies whether or not to queue a
+             redisplay of the input line. I also created a new
+             gl->postpone flag, which is set by gl_normal_io(), and
+             cleared by _gl_raw_io() (when its redisplay argument is
+             true). When this flag is set, gl_flush_output() ignores
+             queued redisplays, as it generally should between calls
+             to gl_normal_io() and gl_raw_io(). Thus its effect is to
+             postpone redisplays while line editing is suspended.
+
+11/04/2004 mcs@astro.caltech.edu
+           history.c man/misc/tecla.in
+             History searches can now include the globbing operators
+             *, ?, []. When a search prefix is found to have at least
+             one of these characters, then only history lines that
+             completely match that pattern are returned.
+
+11/04/2004 mcs@astro.caltech.edu  (issue raised by Mark Coiley)
+           getline.c ioutil.c
+             There appears to be a bug in Solaris's terminal I/O.
+             When the terminal file descriptor is placed in
+             non-blocking I/O mode, and the terminal is switched from
+             canonical to raw mode, characters that were previously
+             entered in canonical I/O mode don't become available to
+             be read until the user types one character more. Select()
+             incorrectly says that there are no characters available,
+             and read() returns EAGAIN. This is only a problem for
+             gl_get_line() when gl_get_line() is in non-blocking
+             server I/O mode, so most users won't have experienced any
+             problems with this.
+
+             The only way that I have found to get read() to return
+             the characters, without the user first having to type
+             another character, is to turn off non-blocking I/O before
+             calling read(). Select() still claims that there are no
+             characters available to be read, but read happily returns
+             them anyway. Fortunately, one can perform non-blocking
+             terminal reads without setting the non-blocking I/O flag
+             of the file descriptor, simply by setting the VTIME
+             terminal attribute to zero (which I already was
+             doing). Thus, when in non-blocking server I/O, I now turn
+             off the non-blocking I/O flag, attempt to read one
+             character and only if this fails, do I then call the
+             select() based event handler to implement any configured
+             non-zero timeout, before attempting the read again. Of
+             course the non-blocking I/O flag is still needed for
+             writing, so I only turn it off temporarily while reading.
+
+25/03/2004 mcs@astro.caltech.edu  (bug reported by Gregory Harris)
+           Makefile.in
+             It appears that when in February, I patched Makefile.in
+             to add abolute paths to the install-sh shell-script,
+             I accidentally replaced install-sh with install.sh. I
+             corrected the name in the Makefile.
+
+25/03/2004 Gregory Harris  (documented here by mcs)
+           configure.in configure
+             Greg added the configuration parameters needed to build
+             the shared version of the libtecla library under FreeBSD.
+
+25/03/2004 mcs@astro.caltech.edu
+           getline.c libtecla.h libtecla.map man/func/gl_get_line.in
+           man/func/gl_read_char.in
+             I wrote a public function called gl_read_char(). Unlike
+             gl_query_char(), this function neither prompts the user
+             for input, nor displays the character that was entered.
+             In fact it doesn't write anything to the terminal, and
+             takes pains not to disturb any incompletely entered
+             input line, and can safely be called from application
+             callback functions.
+
+21/03/2004 mcs@astro.caltech.edu
+           getline.c libtecla.h libtecla.map man/func/gl_get_line.in
+           man/func/gl_query_char.in
+             I wrote a public function called gl_query_char(), which
+             prompts the user and awaits a single-character reply,
+             without the user having to hit return.
+
+23/02/2004 mcs@astro.caltech.edu (bug reported by Gregory Harris)
+           configure.in configure getline.c enhance.c demo3.c
+             The configure script now checks for the sys/select.h
+             header file, and arranges for a C macro called
+             HAVE_SYS_SELECT_H to be set if it exists. Thus the files
+             that use select() now use this macro to conditionally
+             include sys/select.h where available. Apparently this
+             header is required under FreeBSD 5.1.
+
+23/02/2004 mcs@astro.caltech.edu
+           getline.c libtecla.h man/func/gl_get_line.in
+             I wrote two new public functions, gl_append_history() and
+             gl_automatic_history(). Together these allow the
+             application to take over the responsibility of adding
+             lines to the history list from gl_get_line(). I then
+             documented their functionality in the gl_get_line man
+             page.
+           Version 1.6.0
+             I incremented the minor version number of the library, to
+             comply with the requirement to do so when additions are
+             made to the public interface. See libtecla.map for
+             details.
+           libtecla.map
+             I added a new 1.6.0 group for the new minor version, and
+             added the above pair of functions to it.
+
+15/02/2004 mcs@astro.caltech.edu (fixes a bug reported by Satya Sahoo)
+           history.c
+             Calling gl_load_history() multiple times, eventually led
+             to a segmentation fault. This was due to the head of the
+             list of unused history string segments not getting
+             reset when the history buffer was cleared. While
+             debugging this problem I also noticed that the history
+             resizing function was way too complicated to verify, so
+             after fixing the above bug, I heavily simplified the
+             history resizing function, trading off a small reduction
+             in memory efficiency, for greatly improved clarity, and
+             thus made it much more verifiable and maintainable.
+
+14/02/2004 mcs@astro.caltech.edu (fixes a bug reported by Tim Burress).
+           getline.c
+             If gl_change_terminal() was first used to tell
+             gl_get_line to read input from a file, then called later
+             to tell it to read subsequent input from a terminal, no
+             prompt would be displayed for the first line of
+             interactive input. The problem was that on reaching the
+             end of the input file, gl_get_line() should have called
+             gl_abandon_line(), to tell the next call to gl_get_line()
+             to start inputting a new line from scratch. I have added
+             this now.
+
+14/02/2004 Krister Walfridsson (documented here by mcs@astro.caltech.edu)
+           Makefile.in
+             Krister noticed that I had failed to put $(srcdir)/ in front
+             of some invokations of install.sh. I have remedied this.
+           config.guess config.sub
+             I hadn't updated these for a long time, so apparently they
+             didn't recognise the BSD system that Krister was using.
+             I have now updated them to the versions that come with
+             autoconf-2.59. 
+
+22/01/2004 mcs@astro.caltech.edu
+           keytab.c
+             When parsing key-binding specifications, backslash escaped
+             characters following ^ characters were not being expanded.
+             Thus ^\\ got interpretted as a control-\ character followed
+             by a \ character, rather than simply as a control-\
+             character.
+
+12/01/2004 mcs@astro.caltech.edu
+           cplfile.c cplmatch.c demo2.c demo3.c demo.c direader.c
+           expand.c getline.c history.c homedir.c pathutil.c pcache.c
+           configure.in configure INSTALL
+             The configuration script now takes a
+             "--without-file-system" argument. This is primarily for
+             intended for embedded systems that either don't have
+             filesystems, or where the file-system code in libtecla is
+             unwanted bloat. It sets the WITHOUT_FILE_SYSTEM
+             macro. This removes all code related to filesystem
+             access, including the entire public file-expansion,
+             file-completion and path-lookup facilities. Note that the
+             general word completion facility is still included, but
+             without the normally bundled file completion
+             callback. Actually the callback is still there, but it
+             reports no completions, regardless of what string you ask
+             it to complete.
+
+             This option is described in the INSTALL document.
+
+12/01/2004 mcs@astro.caltech.edu
+           getline.c configure.in configure INSTALL
+             The configuration script now takes a
+             "--without-file-actions" argument. This allows an
+             application author/installer to prevent users of
+             gl_get_line() from accessing the filesystem from the
+             builtin actions of gl_get_line(). It defines a macro
+             called HIDE_FILE_SYSTEM. This causes the
+             "expand-filename", "read-from-file", "read-init-files",
+             and "list-glob" action functions to be completely
+             removed. It also changes the default behavior of actions
+             such as "complete-word" and "list-or-eof" to show no
+             completions, instead of the normal default of showing
+             filename completions.
+
+             This option is described in the INSTALL document.
+
+11/01/2004 mcs@astro.caltech.edu
+           getline.c man/func/gl_get_line.in
+             In case an application's customized completion handler
+             needs to write to the terminal for some unforseen reason,
+             there needs to be a way for the it to cleanly suspend raw
+             line editing, before writing to the terminal, and the
+             caller then needs to be aware that it may need to
+             resurrect the input line when the callback returns. I
+             have now arranged that the completion callback functions
+             can call the gl_normal_io() function for this purpose,
+             and documented this in the gl_get_line() man page.
+
+11/01/2004 mcs@astro.caltech.edu  (In response to a bug report by Satya Sahoo)
+           getline.c
+             The gl_configure_getline() function makes a malloc'd copy
+             of the names of the configuration files that it is asked
+             to read. Before the bug fix, if the application made one
+             or more calls to this function, the memory allocated by
+             the final call that it made before calling del_GetLine(),
+             wasn't being freed. Note that memory allocated in all but
+             the final call was being correctly freed, so the maximum
+             extent of the memory leak was the length of the file
+             name(s) passed in the final call to
+             gl_configure_getline(), and an application that didn't
+             call gl_configure_getline() didn't suffer any leak.
+
+20/12/2003 mcs@astro.caltech.edu
+           history.c
+             Ellen tested the history fix that I reported below, and
+             pointed out that it still had a problem. This turned out
+             to be because getline.c was making some incorrect
+             assumptions about the new behavior of history.c. This
+             problem and the previous one both revolved around how
+             search prefixes were stored and discarded, so I have now
+             re-written this part of the code. Previously the search
+             prefix was retained by looking for a line with that
+             prefix, and keeping a pointer to that line. This saved
+             memory, compared to storing a separate copy of the
+             prefix, but it led to all kinds of hairy
+             interdependencies, so I have now changed the code to keep
+             a separate copy of search prefixes. To keep the memory
+             requirements constant, the search prefix is stored in the
+             history buffer, like normal history lines, but not
+             referenced by the time-ordered history list. The prefix
+             can now be kept around indefinitely, until a new search
+             prefix is specified, regardless of changes to the
+             archived lines in the history buffer. This is actually
+             necessary to make the vi-mode re-search actions work
+             correctly. In particular, I no longer discard the search
+             prefix whenever a history search session ends. Also,
+             rather than have getline.c keep its own record of when a
+             history session is in progress, it now consults
+             history.c, so that failed assumptions can't cause the
+             kind of discrepancy that occurred before. For this to
+             work, getline.c now explicitly tells history.c to cancel
+             search sessions whenever it executes any non-history
+             action.
+
+14/12/2003 mcs@astro.caltech.edu (bug reported by Ellen Oschmann)
+           history.c
+             If one searched backwards for a prefix, then returned to
+             the original line, changed that line, then started
+             another backwards prefix search, getline incorrectly
+             discarded the new search prefix in the process of
+             throwing away its cached copy of the previous pre-search
+             input line. In other words getline was belatedly
+             cancelling a previous search, after a new search had
+             already partially begun, and thus messed up the new
+             search. The obvious fix was to arrange for the current
+             search to be cancelled whenever the history pointer
+             returns to its starting point, rather than waiting for
+             the next search to begin from there.
+
+14/12/2003 mcs@astro.caltech.edu
+           history.c
+             _glh_recall_line() was returning the last line in the
+             history buffer instead of the line requested by the
+             caller. This only affected the obscure "repeat-history"
+             action-function, which probably isn't used by anybody.
+
+09/12/2003 Version 1.5.0 released.
+
+28/09/2003 mcs@astro.caltech.edu
+           homedir.c
+             When the home directory of the login user is requested,
+             see if the HOME environment variable exists, and if so
+             return its value, rather than looking up the user's home
+             directory in the password file. This seems to be the
+             convention adopted by other unix programs that perform
+             tilde expansion, and it works around a strange problem,
+             where a third-party libtecla program, statically compiled
+             under an old version of RedHat, unexpectedly complained
+             that getpwd() returned an error when the program was run
+             under RedHat 9.
+
+01/09/2003 mcs@astro.caltech.edu
+           getline.c libtecla.h libtecla.map man/func/gl_get_line.in
+           man/func/gl_register_action.in.
+             It is now possible for an application to register
+             external functions as action functions. These actions are
+             initially bound to specified key-sequences, but if they
+             are registered before the user's configuration file is
+             loaded, they can also be re-bound by the user to
+             different key-sequences. The function used to register a
+             new action, is called gl_register_action().  Action
+             functions are passed a readonly copy of the input line
+             and the cursor position. They can display text to the
+             terminal, or perform other operations on the application
+             environment. Currently, they can't edit the input line or
+             move the cursor. This will require the future addition of
+             functions to queue the invokation of the built-in action
+             functions.
+
+26/08/2003 mcs@astro.caltech.edu
+           getline.c
+             I modified gl_update_buffer() to ensure that the cursor
+             stays within the input line after external line
+             modifications, and to queue a redisplay of the
+             potentially modified input line.
+
+21/07/2003 mcs@astro.caltech.edu
+           configure.in configure Makefile.in Makefile.stub INSTALL
+             By specifying --without-man-pages or --with-man-pages=no
+             as command-line arguments to the configure script, it is
+             now possible to have the configure script skip the
+             man-page preprocessing step, and arrange for the man-page
+             installation targets in the Makefile to do nothing. This
+             option is designed for people who embed libtecla within
+             other packages. It is also used by Makefile.stub when
+             the distclean target is specified.
+
+21/07/2003 mcs@astro.caltech.edu
+           configure.in configure
+             The previous workaround for recent versions of gcc
+             placing /usr/local/include at the start of the system
+             inlcude-file search path, broke something else.  The fix
+             placed /usr/include before gcc's include area, which
+             meant that gcc's modified version of stdarg.h was being
+             ignored in deference to the version in /usr/include. I
+             have changed the fix to have gcc report the search path,
+             then have awk add options to CFLAGS to reorder this path,
+             plaing /usr/local/include at the end.
+
+             Also, under Solaris 9, including term.h without first
+             including curses.h results in complaints about undefined
+             symbols, such as bool. As a result the configure script's
+             test for term.h was failing. I have now modified it to
+             include curses.h in the test code that it uses to check
+             for term.h. In the process I also improved the tests for
+             curses.h and term.h to prevent an ncurses version of
+             term.h from being used with the system-default version of
+             curses.h.
+
+29/06/2003 mcs@astro.caltech.edu
+           Makefile.in direader.c homedir.c
+             On some systems (eg. linux) the _POSIX_C_SOURCE
+             feature-test macro is set by system headers, rather than
+             being an option set by a project's Makefile at
+             compilation time.  In software, such as tecla, where the
+             definition of this macro is used as an indication of
+             whether to use the non-reentrant or reentrant versions of
+             system functions, this means that the reentrant functions
+             are always used, regardless of whether this macro is set
+             or not by the project Makefile. Thus, on such systems the
+             reentrant and non-reentrant versions of the tecla library
+             are essentially identical. This has a couple of
+             drawbacks.  First, since thread-safe functions for
+             traversing the password file don't exist, the supposedly
+             non-reentrant version of the tecla library can't support
+             ambiguous tab-completion of usernames in ~username/
+             constructions. Secondly, on some systems the use of
+             reentrant system functions dictates the use of a shared
+             library that isn't needed for the non-reentrant
+             functions, thus making it more difficult to distribute
+             binary versions of the library.
+
+             To remedy this situation I have modified the DEFINES_R
+             variable in Makefile.in to arrange for the compiler to
+             define a C macro called PREFER_REENTRANT when it is
+             compiling the reentrant version of the tecla library.
+             This macro is now used in the source code to determine
+             when to require reentrant code. Whithin the source code,
+             wherever a potentially non-reentrant interface is used,
+             the existance of both this macro and a suitably valued
+             _POSIX_C_SOURCE macro, are tested for to see if a
+             reentrant alternative to the problem code should be used.
+
+22/06/2003 mcs@astro.caltech.edu
+           getline.c
+             I changed the way that redisplays are requested and
+             performed.  Redisplays are now queued by calling
+             gl_queue_redisplay(), and subsequently performed by
+             gl_flush_output(), when the queue of already pending
+             output has been completely dispatched. This was necessary
+             to prevent event handlers from filling up the output
+             queue with redisplays, and it also simplifies a number of
+             things. In the process I removed the gl_queue_display()
+             function. I also wrote a gl_line_erased() function, which
+             is now called by all functions that erase the input
+             line. I also split the gl_abandon_line() function into
+             public and private callable parts, and used the private
+             version internally to arrange to discard the input line
+             after errors.
+
+             The raw_mode flag was not being initialized by new_GetLine().
+             It is now initialized to zero.
+
+             I removed the zapline flag, since using the endline flag to
+             communicate the desire to terminate the line, did the same
+             thing.
+
+             gl_terminal_move_cursor() now does nothing when the input
+             line isn't displayed.
+
+18/03/2003 mcs@astro.caltech.edu
+           getline.c
+             Fixed bug which was causing newlines not to be output
+             at the end of each newly entered line. I was
+             interpreting the gl->endline flag in conflicting ways in
+             two places. To fix this I have created a gl->displayed
+             flag. This flags whether an input line is currently
+             displayed.
+
+17/03/2003 mcs@astro.caltech.edu
+           getline.c libtecla.h man/func/gl_get_line.in
+           man/func/gl_erase_terminal.in libtecla.map
+             I added a new function that programs can call to clear
+             the terminal between calls to gl_get_line().
+
+11/03/2003 mcs@astro.caltech.edu
+           configure.in configure
+             Under linux when _POSIX_C_SOURCE is defined, getpwent()
+             and associated functions become undefined, because
+             _SVID_SOURCE and _BSD_SOURCE become undefined. Adding
+             these feature macros back to CFLAGS resolves this.
+
+06/03/2003 mcs@astro.caltech.edu
+           getline.c libtecla.map man/func/gl_get_line.in
+             Following the lead of Edward Chien, I wrote a function
+             called gl_bind_keyseq(), which binds a specified
+             key-sequence to a given action, or unbinds the
+             key-sequence.
+
+24/02/2003 mcs@astro.caltech.edu
+           getline.c libtecla.map man/func/cpl_complete_word.in
+             I implemented a simple function called
+             cpl_recall_matches().  This recalls the return value of
+             the last call to cpl_complete_word().
+
+19/01/2003 mcs@astro.caltech.edu
+           getline.c
+             The documented signal handling, fd event-handling,
+             inactivity timeout handling, and server-mode non-blocking
+             I/O features are now implemented for non-interactive
+             input streams, such as pipes and files.
+
+19/01/2003 mcs@astro.caltech.edu
+           getline.c libtecla.h man/func/gl_get_line.in demo3.c
+             I added a new return status enumerator to report
+             when an end-of-file condition causes gl_get_line()
+             to return NULL.
+
+13/01/2003 mcs@astro.caltech.edu
+           history.c
+             I rewrote the history facility. The previous
+             circular buffer implementation was a nightmare to change,
+             and it couldn't efficiently support certain newly
+             requested features. The new implementation stores history
+             lines in linked lists of fixed sized string segments,
+             taken from the buffer, with each line being reference
+             counted and recorded in a hash table. If the user enters
+             a line multiple times, only one copy of the line is now
+             stored. Not only does this make better use of the
+             available buffer space, but it also makes it easy to
+             ensure that a line whose prefix matches the current
+             search prefix, isn't returned more than once in sequence,
+             since we can simply see if the latest search result has
+             the same hash-table pointer as the previous one, rather
+             than having to compare strings. Another plus is that due
+             to the use of linked lists of nodes of fixed size line
+             segments, there is no longer any need to continually
+             shuffle the contents of the buffer in order to defragment
+             it. As far as the user is concerned, the visible
+             differences are as follows:
+
+             1. If the user enters a given line multiple times in a
+                row, each one will be recorded in the history list,
+                and will thus be listed by gl_show_history(), and
+                saved in the history file. Previously only one line
+                was recorded when consecutive duplicates were entered.
+                This was a kludge to prevent history recall from
+                recalling the same line multiple times in a row. This
+                only achieved the desired result when not recalling by
+                prefix.
+
+             2. Not only simple recall, but prefix-based history line
+                recalls now don't return the same line multiple times
+                in a row. As mentioned in (1) above, previously this
+                only worked when performing a simple recall, without a
+                search prefix.
+
+28/12/2002 mcs@astro.caltech.edu
+           getline.c
+             The one-line function, gl_buff_curpos_to_term_curpos()
+             was only being used by gl_place_cursor(), so I inlined it
+             in that function, and removed it.
+
+28/12/2002 mcs@astro.caltech.edu
+           getline.c
+             gl_suspend_process() was calling the application-level
+             gl_normal_io() and gl_raw_io() functions, where it should
+             have been calling the internal versions _gl_normal_io()
+             and _gl_raw_io().
+             Also gl_handle_signal() was masking and unmasking just
+             the signals of the first element of the gl[] array
+             argument. It now masks and unmasks all trappable signals.
+
+28/12/2002 mcs@astro.caltech.edu
+           getline.c
+             Now that the number of terminal characters used to
+             display the current input line, is recorded, the relative
+             line on which the last character of the input line
+             resides can be determined without having to call
+             gl_buff_curpos_to_term_curpos(). This is now used by
+             gl_normal_io() via gl_start_newline(), so there is now no
+             need for gl_buff_curpos_to_term_curpos() to be
+             async-signal safe. I have thus removed the annoying
+             gl->cwidth[] array, and gl_buff_curpos_to_term_curpos()
+             now calls gl_width_of_char() directly again. There is
+             also now no need for the gl_line_of_char_start() and
+             gl_line_of_char_end() functions, so I have removed them.
+
+28/12/2002 mcs@astro.caltech.edu
+           getline.c
+             Unfortunately it turns out that the terminfo/termcap
+             control sequence which is defined to delete everything
+             from the current position to the end of the terminal, is
+             only defined to work when at the start of a terminal
+             line. In gnome terminals in RedHat 8.0, if it is used
+             within a terminal line, it erases the whole terminal
+             line, rather than just what follows the cursor. Thus to
+             portably truncate the displayed input line it is
+             necessary to first use the control sequence which deletes
+             from the cursor position to the end of the line, then if
+             there are more terminal lines, move to the start of the
+             next line, and use the delete to end-of-terminal control
+             sequence, then restore the cursor position. This requires
+             that one know how many physical terminal lines are used
+             by the current input line, so I now keep a record of the
+             number of characters so far displayed to the terminal
+             following the start of the prompt, and the new
+             gl_truncate_display() function uses this information to
+             truncate the displayed input line from the current cursor
+             position.
+
+28/12/2002 mcs@astro.caltech.edu
+           getline.c
+             gl_start_newline() now moves to an empty line following
+             the input line, rather than just to the next line. It
+             also arranges for the input line to be redisplayed before
+             editing resumes. A major user of this is gl_print_info(),
+             which now need not be followed by an explicit call to
+             gl_redisplay(), since the terminal input loop in
+             gl_get_input_line() ensures that gl_redisplay() is called
+             after any action function that asserts gl->redisplay.
+             Also, all functions that erase the displayed input line
+             can now call the gl_erase_line() function, which is
+             designed to work correctly even when a terminal resize
+             invalidates the horizontal cursor position.  Finally, the
+             new gl_queue_display() function is now used by functions
+             that need to arrange for the input line to be displayed
+             from scratch after the displayed line has been erased or
+             invalidated by other text being written to the terminal.
+             All of these changes are aimed at reducing the number of
+             places that directly modify gl->term_curpos and
+             gl->redisplay.
+
+22/12/2002 Markus Gyger   (logged here by mcs)
+           Makefile.in update_html
+	     In places where echo and sed were being used to extract
+	     the base names of files, Markus substituted the basename
+	     command. He also replaced explicit cp and chmod commands
+	     with invokations of the install-sh script.
+           configure.in
+             Use $target_os and $target_cpu, where appropriate,
+	     instead of $target.
+           configure.in
+             The Solaris man function and library man pages should
+	     be in sections 3lib and 3tecla respectively, only in
+	     Solaris version 2.8 and above.
+           configure.in
+             Markus provided values for the man page configuration
+             variables for HPUX.
+           man/*/*.in
+             I had missed parameterizing man page section numbers in
+	     the man page titles, Markus corrected this.
+           man/func/libtecla_version.in
+             Fixed incorrect section number in the link to the
+             libtecla man page.
+           homedir.c
+             When compiled to be reentrant, although one can't use the
+	     non-reentrant getpwent() function to scan the password
+	     file for username completions, one can at least see if
+	     the prefix being completed is a valid username, and if
+	     the username of the current user minimally matches the
+	     prefix, and if so list them. I simplified Markus'
+	     modification by adding a prefix argument to the
+             _hd_scan_user_home_dirs() function, and redefining the
+	     function description accordingly, such that now it
+	     reports only those password file entries who's usernames
+	     minimally match the specified prefix. Without this, it
+	     would have been necessary to peak inside the private data
+	     argument passed in by cf_complete_username().
+             Markus also provided code which under Solaris uses the
+             non-reentrant interfaces if the reentrant version of the
+             library isn't linked with the threads library.
+
+19/12/2002 mcs@astro.caltech.edu
+           Makefile.in
+             Markus pointed out that LDFLAGS was being picked up by
+             the configure script, but not then being interpolated
+             into te Makefile. I have thus added the necessary
+             assignment to Makefile.in and arranged for the value of
+             LDFLAGS to be passed on to recursive make's. I also did
+             the same for CPPFLAGS, which had also been omitted.
+
+18/12/2002 mcs@astro.caltech.edu
+           man/* man/*/* configure.in configure Makefile.in
+           update_html
+             It turns out that the assignment of man page sections to
+             topics differs somewhat from system to system, so this is
+             another thing that needs to be configured by the main
+             configuration script, rather than being hardwired. All
+             man pages have now been moved into suitably named
+             topic-specific sub-directories of the top-level man
+             directory, and instead of having a numeric suffix, now
+             have the .in suffix, since they are now preprocessed by
+             the configure script, in the same fashion as Makefile.in.
+             Whithin these *.in versions of the man pages, and within
+             Makefile.in, the installation subdirectory (eg. man1) and
+             the file-name suffix (eg. 1), are written using
+             configuration macros, so that they get expanded to the
+             appropriate tokens when the configure script is run. In
+             principle, the man pages could also take advantage of
+             other configuration macros, such as the one which expands
+             to the library installation directory, to include full
+             path names to installed files in the documentation, so in
+             the future this feature could have more uses than just
+             that of parameterizing man page sections.
+
+18/12/2002 mcs@astro.caltech.edu
+           man3 man3/* Makefile.in html/index.html update_html
+             Markus suggested splitting the gl_get_line(3) man page
+             into user and developer sections, and also pointed out
+             that the enhance man page should be in section 1, not
+             section 3. I have thus created a top-level man
+             directory in which to place the various sections, and
+             moved the man3 directory into it. The enhance.3 man page
+             is now in man/man1/enhance.1. I have extracted all
+             user-oriented sections from the gl_get_line(3) man page
+             and placed them in a new man7/tecla.7 man page.
+
+18/12/2002 mcs@astro.caltech.edu
+           getline.c
+             Terminal resizing was broken in normal mode, due to
+             me forcing the terminal cursor position to zero in the
+             wrong place in gl_check_caught_signal().
+
+14/12/2002 Markus Gyger  (logged here by mcs)
+           configure.in configure
+             Under Solaris, recent versions of gcc search
+             /usr/local/include for header files before the system
+             directories. This caused a problem if ncurses was
+             installed under Solaris, since the termcap.h include file
+             in /usr/local/include ended up being used at compile
+             time, whereas the system default version of the curses
+             library was used at link time. Since the two libraries
+             declare tputs() differently, this evoked a complaint from
+             gcc. Markus came up with a way to force Gnu cpp to move
+             /usr/local/include to the end of the system-include-file
+             search path, where it belongs.
+
+13/12/2002 mcs@astro.caltech.edu
+           man3/gl_io_mode.3
+             I rewrote the man page which documents the new non-blocking
+             server I/O mode.
+
+12/12/2002 mcs@astro.caltech.edu
+           demo3.c
+             I wrote a new version of demo3.c, using signal handlers
+             that call gl_handle_signal() and gl_abandon_line(), where
+             previously in this demo, these functions were called from
+             the application code.
+
+05/12/2002 mcs@astro.caltech.edu
+           getline.c
+             gl_normal_io(), gl_raw_io() and gl_handle_signal() and
+             gl_abandon_line() are now signal safe, provided that
+             signal handlers that call them are installed with sa_mask's
+             that block all other signals who's handlers call them.
+             This is the case if gl_tty_signals() is used to install
+             signal handlers that call any of these functions.
+
+             A major stumbling block that had to be overcome was that
+             gl_displayed_char_width() calls isprint(), which can't
+             safely be called from a signal handler (eg. under linux,
+             the is*() functions all use thread-specific data
+             facilities to support per-thread locales, and the
+             thread-specific data facilities aren't signal safe). To
+             work around this, all functions that modify the
+             input-line buffer, now do so via accessor functions which
+             also maintain a parallel array of character widths, for
+             use by gl_buff_curpos_to_term_curpos() in place of
+             gl_displayed_char_width(). Other minor problems were the
+             need to avoid tputs(), who's signal safety isn't defined.
+
+05/12/2002 Eric Norum        (logged here by mcs@astro.caltech.edu)
+           configure.in
+             Eric provided the configuration information needed
+             to build shared libraries under Darwin (Max OS X).
+
+05/12/2002 Richard Mlynarik  (logged here by mcs@astro.caltech.edu)
+           configure.in
+             AC_PROG_RANLIB gets the wrong version of ranlib when
+             cross compiling, so has now been replaced by an
+             invokation of AC_CHECK_TOOL. In addition, AC_CHECK_TOOL
+             is also now used to find an appropriate version of LD.
+
+05/12/2002 mcs@astro.caltech.edu (based on patch by Pankaj Rathore)
+           getline.c libtecla.h libtecla.map man3/gl_get_line.3
+             The new gl_set_term_size() function provides a way
+             to tell gl_get_line() about changes in the size of
+             the terminal in cases where the values returned by
+             ioctl(TIOCGWINSZ) isn't correct.
+
+05/12/2002 mcs@astro.caltech.edu
+           getline.c
+             Rather than calling sprintf() to see how much space would
+             be needed to print a given number in octal, I wrote a
+             gl_octal_width() function, for use by
+             gl_displayed_char_width().  This makes the latter
+             function async signal safe.
+
+05/12/2002 mcs@astro.caltech.edu
+           chrqueue.c
+             Whenever the buffer is exhausted, and getting a new
+             buffer node would require a call to malloc(), attempt
+             to flush the buffer to the terminal. In blocking I/O
+             mode this means that the buffer never grows. In
+             non-blocking I/O mode, it just helps keep the buffer
+             size down.
+
+05/12/2002 mcs@astro.caltech.edu
+           freelist.h freelist.c
+             The new _idle_FreeListNodes() function queries the
+             number of nodes in the freelist which aren't currently
+             in use.
+
+05/12/2002 mcs@astro.caltech.edu
+           Makefile.stub
+             This now accepts all of the targets that the configured
+             makefile does, and after configuring the latter makefile,
+             it invokes it with the same options.
+
+03/12/2002 mcs@astro.caltech.edu
+           mans3/gl_io_mode.3
+             I completed the man page for all of the new functions
+             related to non-blocking I/O.
+
+01/12/2002 mcs@astro.caltech.edu
+           man3/gl_get_line.3
+             I wrote a long section on reliable signal handling,
+             explaining how gl_get_line() does this, how to make
+             use of this in a program, and how to handle signals
+             reliably when faced with other blocking functions.
+             This basically documents what I have learnt about
+             signal handling while working on this library.
+
+01/12/2002 mcs@astro.caltech.edu
+           getline.c man3/gl_get_line.3
+             In non-blocking server mode, the gl_replace_prompt()
+             function can now be used between calls to gl_get_line()
+             if the application wants to change the prompt of the
+             line that is being edited.
+
+01/12/2002 mcs@astro.caltech.edu
+           man3/gl_get_line.3
+             I documented the new gl_return_status() and
+             gl_error_message() functions.
+
+01/12/2002 mcs@astro.caltech.edu
+           getline.c man3/gl_get_line.3
+             Added SIGPOLL and SIGXFSZ to the list of signals that
+             are trapped by default. These are process termination
+             signals, so the terminal needs to be restored to a
+             usable state before they terminate the process.
+
+27/11/2002 mcs@astro.caltech.edu
+           getline.c libtecla.h
+             Completed the essential changes needed to support
+             non-blocking server-I/O mode.
+
+             The new gl_io_mode() function allows one to switch to
+             and from non-blocking server-I/O mode.
+
+             The new gl_raw_io() function is used in non-blocking
+             server-I/O mode to switch the terminal into non-blocking
+             raw I/O mode.
+
+             The new gl_normal_io() function is used in non-blocking
+             server-I/O mode to switch the restore the terminal to
+             a normal, blocking state. This is used to suspend line
+             input before suspending the process or writing messages
+             to the terminal.
+
+             The new gl_tty_signals() function installs specified
+             signals handlers for all signals that suspend, terminate
+             or resume processes, and also for signals that indicate
+             that the terminal has been resized. This not only saves
+             the application from having to keep its own ifdef'd list
+             of such signals, of which there are many, but it also
+             makes sure that these signal handlers are registered
+             correctly. This includes using the sa_mask member of each
+             sigaction structure to ensure that only one of these
+             handlers runs at a time. This is essential to avoid the
+             signal handlers all trying to simultaneously modify
+             shared global data.
+
+             The new gl_handle_signal() function is provided for
+             responding (from application level) to signals caught by
+             the application. It handles process suspension, process
+             termination and terminal resize signals.
+
+             The new gl_pending_io() function tells the application
+             what direction of I/O gl_get_line() is currently waiting
+             for.
+
+             In non-blocking server I/O mode, the new
+             gl_abandon_line() function can be called between calls to
+             gl_get_line() to discard an input line and force the next
+             call to gl_get_line() to start the input of a new line.
+
+             Also, in non-blocking server-I/O gl_get_line() doesn't
+             attempt to do anything but return when one of the signals
+             that it is configured to catch is caught. This is
+             necessary because when in this mode, the application is
+             required to handle these signals when gl_get_line() is
+             running, and the default configuration of most of these
+             signals in gl_get_line() is to restore the terminal then
+             call the application signal handlers. This would be a
+             case of too many cooks spoiling the broth, so in this
+             mode, gl_get_line() always defers to the application's
+             signal handlers.
+             
+26/11/2002 mcs@astro.caltech.edu
+           getline.c libtecla.h
+             I implemented a couple of new functions to support
+             reliable signal handling, as now documented
+             (see above) in the gl_get_line(3) man page.
+
+             The new gl_catch_blocked() function tells gl_get_line()
+             to unblock all configured signals around calls to
+             long-running functions, not only those that aren't
+             blocked when gl_get_line() is called. This allows
+             the caller to implement reliable signal handling,
+             since the unblocking is only done from within code
+             protected by sigsetjmp(), which avoids race conditions.
+
+             The new gl_list_signals() function fills a provided
+             sigset_t with the set of signals that gl_get_line() is
+             currently configured to catch. This allows callers to
+             block said signals, such that they are only unblocked by
+             gl_get_line() when it is waiting for I/O. When used in
+             conjunction with the gl_catch_blocked() function, this
+             removes the potential for race conditions.
+
+             Also, when gl_get_line() installs its signal handler,
+             it uses the sa_mask member of the sigaction structure
+             to ensure that only one instance of this signal handler
+             will ever be executing at a time.
+
+25/11/2002 mcs@astro.caltech.edu (bug reported by Pankaj Rathore)
+           getline.c
+             When any history recall action was invoked when the
+             input line buffer was full, an error message would be
+             displayed complaining about the length of the string
+             in the line input buffer being inconsistent with the
+             specified allocated size. This was because instead of
+             sending the allocated size of the input line, I was
+             sending the length excluding the element that is
+             reserved for the '\0' terminator. Sending it the
+             correct size corrected the problem.
+
+24/11/2002 mcs@astro.caltech.edu
+           getline.c
+             All public functions which take GetLine objects as
+             arguments now block signals on entry and restore the
+             signal mask on return. This was an attempt to make it
+             safe to call getline functions from signal handlers, but
+             the fact is that the functions that I really wanted this
+             to apply to, potentially call malloc(), so this currently
+             isn't the case.
+
+23/11/2002 mcs@astro.caltech.edu
+           getline.c libtecla.h
+             The new gl_return_status() function returns an enumerated
+             return status which can be used to query what caused
+             gl_get_line() to return.
+
+22/11/2002 mcs@astro.caltech.edu
+           Most existing .c and .h files, plus errmsg.c errmsg.h
+           Makefile.rules
+             Until now, many library functions would report error
+             messages to stderr. This isn't appropriate for library
+             functions, so in place of this behavior, error messages
+             are now recorded in internal ErrMsg objects, and passed
+             between modules via new module-specific error querying
+             functions. In addition, errno is now set appropriately.
+             Thus when gl_get_line() and related functions return an
+             error, strerror() can be used to look up system errors,
+             and gl_error_message() can be used to recover a higher level
+             error message. Note that error messages that are
+             responses to user actions continue to be reported to the
+             terminal, as before.
+
+21/11/2002 mcs@astro.caltech.edu
+           getline.c keytab.h keytab.c Makefile.rules
+             I wrote a new version of _kt_lookup_binding() that didn't
+             require the caller to have access to the innards of a
+             KeyTab object. This then enabled me to move the definition
+             of KeyTab objects into keytab.c and make the typedef in
+             keytab.h opaque. Many nested includes were also moved from
+             keytab.h into keytab.c.
+
+05/11/2002 mcs@astro.caltech.edu
+           getline.c libtecla.map libtecla.h demo3.c
+             I split the old gl_resize_terminal() function into
+             two parts, gl_query_size() and gl_update_size(), with
+             the latter calling the former to get the new terminal
+             size.
+
+05/11/2002 mcs@astro.caltech.edu
+           getline.c
+             I fixed a long time bug in the terminal resizing code.
+             When the cursor wasn't on the last terminal line of the
+             input line, the resizing code would redisplay the
+             the line one or more lines above where it should be
+             restored. This was due to an error in the calculation of
+             the number of lines above the cursor position.
+
+04/11/2002 mcs@astro.caltech.edu
+           demo.c demo2.c demo3.c
+             I used the new gl_display_text() function to display
+             introductory text at the startup of each of the demo
+             programs. The text is enclosed within a box of asterixes,
+             drawn dynamically to fit within the confines of the
+             available terminal width.
+
+04/11/2002 mcs@astro.caltech.edu
+           libtecla.h getline.c ioutil.c ioutil.h Makefile.rules
+           libtecla.map man3/gl_get_line.3 man3/gl_display_text.3
+             Needing a way to display introductory text intelligently
+             in the demo programs, I wrote and documented the
+             gl_display_text() function. This justifies arbitrary
+             length text within the bounds of the terminal width,
+             with or without optional indentation, prefixes and
+             suffixes.
+
+03/11/2002 mcs@astro.caltech.edu
+           demo3.c Makefile.rules
+             I wrote a new demonstration program. This program acts
+             exactly like the main demonstration program, except that
+             it uses an external event loop instead of using the
+             gl_get_line() internal event loop. This is thus an example
+             of the new non-blocking server I/O facility.
+
+02/11/2002 mcs@astro.caltech.edu
+           getline.c keytab.c keytab.h libtecla.h man3/gl_get_line.3
+           man3/gl_completion_action.3
+             I added the ability to register additional word
+             completion actions via the new function
+             gl_completion_action().  All action functions now take a
+             new (void *data) argument, which is stored with the
+             function in the symbol table of actions. The new
+             gl_completion_action() function uses this feature to
+             record dynamically allocated objects containing the
+             specified completion function and callback data along
+             with either the gl_complete_word() action function, or
+             the gl_list_completions() action function.  These two
+             actions continue to use the builtin completion functions
+             when their data pointer is NULL.
+
+20/10/2002 mcs@astro.caltech.edu
+           The following are changes merged from the non-blocking
+           gl_get_line() development branch.
+
+           getline.c
+             I wrote a gl_start_newline() function, to replace all of
+             the explicit calls to output \r\n to stdout.
+
+             Informational messages are now written to the terminal
+             using a new variadic function called gl_print_info().
+             This starts a newline, writes string arguments until a
+             special argument, GL_END_INFO, is seen, then starts
+             another newline.
+
+             Changed _output_ to _print_ in the following function
+             names gl_output_control_sequence(), gl_output_char(),
+             gl_output_string() and gl_output_raw_string().
+
+             gl_print_raw_string() now has a length argument, so that
+             strings that aren't terminated with '\0' can be printed.
+
+             The display of the initial contents of a new line to be
+             edited has been moved into a new function called
+             gl_present_line().
+
+             The gl_get_input_line() function now takes the prompt
+             string as an argument so that gl_replace_prompt() can be
+             called from within this function instead of from
+             gl_get_line().
+
+             Keyboard input is now buffered in a persistent buffer in
+             the parent GetLine object. gl_read_character() checks
+             this for unprocessed characters in preference to calling
+             gl_read_terminal() to append characters to it.  A new
+             function, gl_discard_chars(), removes processed
+             characters from this buffer. This change is in
+             preparation for a non-blocking version of gl_get_line(),
+             where partially input key-sequences must be stored
+             between calls to gl_get_line().
+
+           getline.c getline.h history.c history.h cplmatch.c \
+           cplmatch.h expand.c expand.h
+             All terminal output from gl_get_line() is now routed
+             through a GL_WRITE_FN() callback function called
+             gl_write_fn. Internal functions in cplmatch.c,
+             expand.c and history.c have been created which take
+             such callbacks to write output. These are used both
+             by functions in getline.c, to display file completions,
+             expansions, history etc, and as the internals of existing
+             public functions in these files that print to stdio
+             streams. In the latter case an internal stdio
+             GL_WRITE_FN() callback is substituted, so that the
+             functions behave as before.
+
+           getline.c chrqueue.c chrqueue.h
+             The gl_write_fn() callback used by gl_get_line() now
+             writes to a queue, implemented in chrqueue.c. This queue
+             is implemented as a list of blocks of buffer segments,
+             the number of which shrink and grow as
+             needed. The contents of the queue are flushed to the
+             terminal via another GL_WRITE_FN() callback passed to the
+             queue object. Currently gl_get_line() passes an internal
+             function assigned to gl->flush_fn, called
+             gl_flush_terminal(), which writes the contents of the
+             queue to the terminal, and knows how to handle both
+             blocking and non-blocking I/O. The output queue is
+             designed to be flushed to the terminal incrementally, and
+             thereby also facilitates non-blocking I/O.
+
+           getline.c getline.h
+             gl_get_line() now reads all input via the GL_READ_FN()
+             callback, assigned to gl->read_fn. Currently this is
+             set to an internal function called gl_read_terminal(),
+             which knows how to handle both blocking and
+             non-blocking I/O.
+
+           getline.c libtecla.h
+             The new gl_set_nonblocking() function can be used to
+             enable or disable non-blocking I/O. The default is still
+             blocking I/O. In non-blocking mode, the terminal is told
+             not to wait when either reading or writing would block.
+             gl_get_line() then returns, with a return value of NULL,
+             but with the terminal left in raw mode, so that the
+             caller's event loop can detect key presses. The caller
+             should call gl_return_status() to check whether the NULL
+             return value was due to an error, lack of input, or
+             inability to write to the terminal without waiting. If
+             either reading or writing was said to have blocked, the
+             user then should check for I/O readiness in the specified
+             direction before calling gl_get_line() again to
+             incrementally build up the input line.
+
+05/08/2002 mcs@astro.caltech.edu
+           man3/gl_get_line.3 man3/gl_inactivity_timeout.3
+             I documented the new gl_inactivity_timeout() function.
+
+08/07/2002 mcs@astro.caltech.edu
+           libtecla.h getline.c libtecla.map
+             I added a new gl_inactivity_timeout() function. On
+             systems that have the select system call, this provides
+             the option of registering a function that is then called
+             whenever no I/O activity has been seen for more than a
+             specified period of time. Like the gl_watch_fd()
+             facility, timeout callbacks return a code which tells
+             gl_get_line() how to proceed after the timeout has been
+             handled.
+             
+04/07/2002 mcs@astro.caltech.edu  (based on a bug report from Michael MacFaden)
+           getline.c
+             The internal event handler wasn't responding to write
+             events on client file descriptors, due to a typo which
+             resulted in read events being checked for twice, and
+             writes not checked for at all.
+           pathutil.c
+             The amount of space to allocate for pathnames is supposed
+             to come from PATH_MAX in limits.h, but I had neglected to
+             include limits.h. This went unnoticed because on most
+             systems the equivalent number is deduced by calling
+             pathconf(). Apparently under NetBSD this function doesn't
+             work correctly over NFS mounts.
+
+30/05/2002 Version 1.4.1 released.
+
+25/05/2002 mcs@astro.caltech.edu  (based on suggestions by Paul Smith)
+           pathutil.c
+             Apparently, under QNX pathconf("/",_PC_PATH_MAX) returns
+             EINVAL. At Paul's suggestion I have modified the code to
+             silently substitute the existing MAX_PATHLEN_FALLBACK
+             value if pathconf() returns an error of any kind.
+           homedir.c
+             Under QNX, sysconf(_SC_GETPW_R_SIZE_MAX) also apparently
+             returns EINVAL, so as with pathconf() I modified the code
+             to substitute a fallback default, rather than
+             complaining and failing.
+           enhance.c
+             Paul told me that the inclusion of sys/termios.h was
+             causing compilation of enhance.c to fail under QNX. This
+             line is a bug.  The correct thing to do is include
+             termios.h without a sub-directory prefix, as I was
+             already doing futher up in the file, so I have just
+             removed the errant include line.
+
+07/05/2002 mcs@astro.caltech.edu  (async development branch only)
+           getline.c
+             gl_read_character() now caches and reads unprocessed
+             characters from a key-press lookahead buffer. Whenever
+             gl_intepret_char() receives a new character which makes
+             an initially promising key-sequence no longer match the
+             prefix of any binding, it now simply discards the first
+             character from the key-press buffer and resets the buffer
+             pointer so that the next call to gl_read_character()
+             returns the character that followed it, from the buffer.
+           getline.c
+             The part of gl_get_input_line() which preloads, displays
+             and prepares to edit a new input line, has now been moved
+             into a function called gl_present_line().
+
+12/02/2002 mcs@astro.caltech.edu
+           getline.c configure.in configure
+             Mac OS X doesn't have a term.h or termcap.h, but it does
+             define prototypes for tputs() and setupterm(), so the
+             default prototypes that I was including if no headers
+             where available, upset it. I've removed these prototypes.
+             I also now conditionally include whichever is found of
+             curses.h and ncurses/curses.h for both termcap and
+             terminfo (before I wasn't including curses.h when
+             termcap was selected).
+
+12/02/2002 mcs@astro.caltech.edu
+           Updated version number to 1.4.1, ready for a micro
+           release.
+
+12/02/2002 mcs@astro.caltech.edu
+           html/index.html
+             Added Mac OS X and Cygwin to the list of systems that
+             can compile libtecla.
+
+12/02/2002 mcs@astro.caltech.edu
+           getline.c
+             Under Mac OS X, the tputs() callback function returns
+             void, instead of the int return value used by other
+             systems. This declaration is now used if both __MACH__
+             and __APPLE__ are defined. Hopefully these are the
+             correct system macros to check. Thanks for Stephan
+             Fiedler for providing information on Mac OS X.
+
+11/02/2002 mcs@astro.caltech.edu
+           configure.in configure getline.c
+             Some systems don't have term.h, and others have it hidden
+             in an ncurses sub-directory of the standard system include
+             directory. If term.h can't be found, simply don't include
+             it. If it is in an ncurses sub-directory, include
+             ncurses/term.h instead of term.h.
+
+04/02/2002 mcs@astro.caltech.edu
+           configure.in configure Makefile.in Makefile.rules
+             Use ranlib on systems that need it (Mac OS X).  Also,
+             make all components of the installation directories where
+             needed, instead of assuming that they exist.
+
+04/02/2002 mcs@astro.caltech.edu
+           getline.c
+             When the tab completion binding was unbound from the tab
+             key, hitting the tab key caused gl_get_line() to ring the
+             bell instead of inserting a tab character. This is
+             problematic when using the 'enhance' program with
+             Jython, since tabs are important in Python. I have
+             corrected this.
+
+10/12/2001 Version 1.4.0 released.
+
+10/12/2001 mcs@astro.caltech.edu
+           getline.c
+             If the TIOCGWINSZ ioctl doesn't work, as is the case when
+             running in an emacs shell, leave the size unchanged, rather
+             than returning a fatal error.
+
+07/12/2001 mcs@astro.caltech.edu
+           configure.in configure
+             Now that the configure version of CFLAGS is included in
+             the makefile, I noticed that the optimization flags -g
+             and -O2 had been added. It turns out that if CFLAGS isn't
+             already set, the autoconf AC_PROG_CC macro initializes it
+             with these two optimization flags. Since this would break
+             backwards compatibility in embedded distributions that
+             already use the OPT= makefile argument, and because
+             turning debugging on needlessly bloats the library, I now
+             make sure that CFLAGS is set before calling this macro.
+
+07/12/2001 mcs@astro.caltech.edu
+           enhance.c
+             Use argv[0] in error reports instead of using a
+             hardcoded macro.
+
+07/12/2001 mcs@astro.caltech.edu
+           getline.c
+             The cut buffer wasn't being cleared after being
+             used as a work buffer by gl_load_history().
+
+06/12/2001 mcs@astro.caltech.edu
+           configure.in configure
+             I removed my now redundant definition of SUN_TPUTS from
+             CFLAGS. I also added "-I/usr/include" to CFLAGS under
+             Solaris to prevent gcc from seeing conflicting versions
+             of system header files in /usr/local/include.
+
+06/12/2001 Markus Gyger (logged here by mcs)
+           Lots of files.
+             Lots of corrections to misspellings and typos in the
+             comments.
+           getline.c
+             Markus reverted a supposed fix that I added a day or two
+             ago. I had incorrectly thought that in Solaris 8, Sun had
+             finally brought their declaration of the callback
+             function of tputs() into line with other systems, but it
+             turned out that gcc was pulling in a GNU version of
+             term.h from /usr/local/include, and this was what
+             confused me.
+
+05/12/2001 mcs@astro.caltech.edu
+           Makefile.in
+             I added @CFLAGS@ to the CFLAGS assignment, so that
+             if CFLAGS is set as an environment variable when
+             configure is run, the corresponding make variable
+             includes its values in the output makefile.
+
+05/12/2001 mcs@astro.caltech.edu
+           getline.c libtecla.h libtecla.map man3/gl_get_line.3
+           man3/gl_last_signal.3
+             I added a function that programs can use to find out
+             which signal caused gl_get_line() to return EINTR.
+
+05/12/2001 mcs@astro.caltech.edu
+           getline.c
+             When the newline action was triggered by a printable
+             character, it failed to display that character. It now
+             does. Also, extra control codes that I had added, to
+             clear to the end of the display after the carriage return,
+             but before displaying the prompt, were confusing expect
+             scripts, so I have removed them. This step is now done
+             instead in gl_redisplay() after displaying the full input
+             line.
+
+05/12/2001 mcs@astro.caltech.edu
+           getline.c man3/gl_get_line.3
+             A user convinced me that continuing to invoke meta
+             keybindings for meta characters that are printable is a
+             bad idea, as is allowing users to ask to have setlocale()
+             called behind the application's back. I have thus changed
+             this. The setlocale configuration option has gone, and
+             gl_get_line() is now completely 8-bit clean, by default.
+             This means that if a meta character is printable, it is
+             treated as a literal character, rather than a potential
+             M-c binding.  Meta bindings can still be invoked via
+             their Esc-c equivalents, and indeed most terminal
+             emulators either output such escape pairs by default when
+             the meta character is pressed, or can be configured to do
+             so. I have documented how to configure xterm to do this,
+             in the man page.
+
+03/12/2001 mcs@astro.caltech.edu
+           getline.c man3/gl_get_line.3
+             gl_get_line() by default now prints any 8-bit printable
+             characters that don't match keybindings. Previously
+             characters > 127 were only printed if preceded by the
+             literal-next action.  Alternatively, by placing the
+             command literal_if_printable in the tecla configuration
+             file, all printable characters are treated as literal
+             characters, even if they are bound to action functions.
+
+             For international users of programs written by
+             programmers that weren't aware of the need to call
+             setlocale() to support alternate character sets, the
+             configuration file can now also contain the single-word
+             command "setlocale", which tells gl_get_line() to remedy
+             this.
+
+27/11/2001 mcs@astro.caltech.edu
+           demo.c demo2.c enhance man3/gl_get_line.3
+             All demos and programs now call setlocale(LC_CTYPE,"").
+             This makes them support character sets of different
+             locales, where specified with the LC_CTYPE, LC_ALL, or
+             LANG environment variables. I also added this to the demo
+             in the man page, and documented its effect.
+
+27/11/2001 mcs@astro.caltech.edu
+           getline.c
+             When displaying unsigned characters with values over
+             127 literally, previously it was assumed that they would
+             all be displayable. Now isprint() is consulted, and if it
+             says that a character isn't printable, the character code
+             is displayed in octal like \307. In non-C locales, some
+             characters with values > 127 are displayable, and
+             isprint() tells gl_get_line() which are and which aren't.
+
+27/11/2001 mcs@astro.caltech.edu
+           getline.c pathutil.c history.c enhance.c demo2.c
+             All arguments of the ctype.h character class functions
+             are now cast to (int)(unsigned char). Previously they
+             were cast to (int), which doesn't correctly conform to
+             the requirements of the C standard, and could cause
+             problems for characters with values > 127 on systems
+             with signed char's.
+
+26/11/2001 mcs@astro.caltech.edu
+           man3/enhance.3 man3/libtecla.3
+             I started writing a man page for the enhance program.
+
+26/11/2001 mcs@astro.caltech.edu
+           Makefile.in Makefile.rules INSTALL
+             It is now possible to specify whether the demos and other
+             programs are to be built, by overriding the default
+             values of the DEMOS, PROGRAMS and PROGRAMS_R variables.
+             I have also documented the BINDIR variable and the
+             install_bin makefile target.
+
+22/11/2001 mcs@astro.caltech.edu
+           getline.c libtecla.h libtecla.map man3/gl_get_line.3
+           man3/gl_ignore_signal.3 man3/gl_trap_signal.3
+             Signal handling has now been modified to be customizable.
+             Signals that are trapped by default can be removed from
+             the list of trapped signals, and signals that aren't
+             currently trapped, can be added to the list. Applications
+             can also specify the signal and terminal environments in
+             which an application's signal handler is invoked, and
+             what gl_get_line() does after the signal handler returns.
+
+13/11/2001 mcs@astro.caltech.edu
+           getline.c man3/gl_get_line.3
+             Added half-bright, reverse-video and blinking text to the
+             available prompt formatting options.
+           getline.c
+             Removed ^O from the default VT100 sgr0 capability
+             string.  Apparently it can cause problems with some
+             terminal emulators, and we don't need it, since it turns
+             off the alternative character set mode, which we don't
+             use.
+           getline.c
+             gl_tigetstr() and gl_tgetstr() didn't guard against the
+             error returns of tigetstr() and tgetstr() respectively.
+             They now do.
+
+11/11/2001 mcs@astro.caltech.edu
+           getline.c libtecla.h libtecla.map man3/gl_get_line.3
+           man3/gl_prompt_style.3
+             Although the default remains to display the prompt string
+             literally, the new gl_prompt_style() function can be used
+             to enable text attribute formatting directives in prompt
+             strings, such as underlining, bold font, and highlighting
+             directives.
+
+09/11/2001 mcs@astro.caltech.edu
+           enhance.c Makefile.rules configure.in configure
+             I added a new program to the distribution that allows one
+             to run most third party programs with the tecla library
+             providing command-line editing.
+
+08/11/2001 mcs@astro.caltech.edu
+           libtecla.h getline.c man3/gl_get_line.3 history.c history.h
+             I added a max_lines argument to gl_show_history() and
+             _glh_show_history(). This can optionally be used to
+             set a limit on the number of history lines displayed.
+           libtecla.h getline.c man3/gl_get_line.3
+             I added a new function called gl_replace_prompt(). This
+             can be used by gl_get_line() callback functions to
+             request that a new prompt be use when they return.
+
+06/11/2001 mcs@astro.caltech.edu
+           getline.c man3/gl_get_line.3
+             I implemented, bound and documented the list-history
+             action, used for listing historical lines of the current
+             history group.
+           getline.c man3/gl_get_line.3 man3/gl_echo_mode.3
+             I wrote functions to specify and query whether subsequent
+             lines will be visible as they are being typed.
+
+28/10/2001 mcs@astro.caltech.edu
+           getline.c man3/gl_get_line.3
+             For those cases where a terminal provides its own
+             high-level terminal editing facilities, you can now
+             specify an edit-mode argument of 'none'. This disables
+             all tecla key bindings, and by using canonical terminal
+             input mode instead of raw input mode, editing is left up
+             to the terminal driver.
+
+21/10/2001 mcs@astro.caltech.edu
+           libtecla.h getline.c history.c history.h
+           man3/gl_get_line.3 man3/gl_history_info.3
+             I added the new gl_state_of_history(),
+             gl_range_of_history() and gl_size_of_history()
+             functions for querying information about the
+             history list.
+           history.c
+             While testing the new gl_size_of_history()
+             function, I noticed that when the history buffer
+             wrapped, any location nodes of old lines between
+             the most recent line and the end of the buffer
+             weren't being removed. This could result in bogus
+             entries appearing at the start of the history list.
+             Now fixed.
+
+20/10/2001 mcs@astro.caltech.edu
+
+           libtecla.h getline.c history.c history.h
+           man3/gl_get_line.3 man3/gl_lookup_history.3
+             I added a function called gl_lookup_history(), that
+             the application can use to lookup lines in the history
+             list.
+           libtecla.h getline.c history.c history.h man3/gl_get_line.3
+             gl_show_history() now takes a format string argument
+             to control how the line is displayed, and with what
+             information. It also now provides the option of either
+             displaying all history lines or just those of the
+             current history group.
+           getline.c man3/gl_get_line.3
+             gl_get_line() only archives lines in the history buffer
+             if the newline action was invoked by a newline or
+             carriage return character.
+
+16/10/2001 mcs@astro.caltech.edu
+
+           history.c history.h getline.c libtecla.h libtecla.map
+           man3/gl_get_line.3 man3/gl_resize_history.3
+           man3/gl_limit_history.3 man3/gl_clear_history.3
+           man3/gl_toggle_history.3
+	     I added a number of miscellaneous history configuration
+	     functions. You can now resize or delete the history
+	     buffer, limit the number of lines that are allowed in the
+	     buffer, clear either all history or just the history of
+	     the current history group, and temporarily enable and
+	     disable the history mechanism.
+
+13/10/2001 mcs@astro.caltech.edu
+
+           getline.c
+             tputs_fp is now only declared if using termcap or
+             terminfo.
+           getline.c libtecla.map man3/gl_get_line.3
+           man3/gl_terminal_size.3
+             I added a public gl_terminal_size() function for
+             updating and querying the current size of the terminal.
+           update_version configure.in libtecla.h
+             A user noted that on systems where the configure script
+             couldn't be used, it was inconvenient to have the version
+             number macros set by the configure script, so they are
+             now specified in libtecla.h. To reduce the likelihood
+             that the various files where the version number now
+             appears might get out of sync, I have written the
+             update_version script, which changes the version number
+             in all of these files to a given value.
+
+01/10/2001 mcs@astro.caltech.edu
+
+           getline.c history.c history.h man3/gl_get_line.3
+             I added a max_lines argument to gl_save_history(), to
+             allow people to optionally place a ceiling on the number
+             of history lines saved. Specifying this as -1 sets the
+             ceiling to infinity.
+
+01/10/2001 mcs@astro.caltech.edu
+
+           configure.in configure
+             Under digital unix, getline wouldn't compile with
+             _POSIX_C_SOURCE set, due to type definitions needed by
+             select being excluded by this flag. Defining the
+             _OSF_SOURCE macro as well on this system, resolved this.
+
+30/09/2001 mcs@astro.caltech.edu
+
+           getline.c libtecla.h history.c history.h man3/gl_get_line.3
+           man3/gl_group_history.3
+             I implemented history streams. History streams
+             effectively allow multiple history lists to be stored in
+             a single history buffer. Lines in the buffer are tagged
+             with the current stream identification number, and
+             lookups only consider lines that are marked with the
+             current stream identifier.
+           getline.c libtecla.h history.c history.h man3/gl_get_line.3
+           man3/gl_show_history.3
+             The new gl_show_history function displays the current
+             history to a given stdio output stream.
+
+29/09/2001 mcs@astro.caltech.edu
+
+           getline.c
+             Previously new_GetLine() installed a persistent signal
+             handler to be sure to catch the SIGWINCH (terminal size
+             change) signal between calls to gl_get_line(). This had
+             the drawback that if multiple GetLine objects were
+             created, only the first GetLine object used after the
+             signal was received, would see the signal and adapt to
+             the new terminal size. Instead of this, a signal handler
+             for sigwinch is only installed while gl_get_line() is
+             running, and just after installing this handler,
+             gl_get_line() checks for terminal size changes that
+             might have occurred while the signal handler wasn't
+             installed.
+           getline.c
+             Dynamically allocated copies of capability strings looked
+             up in the terminfo or termcap databases are now made, so
+             that calls to setupterm() etc for one GetLine object
+             don't get trashed when another GetLine object calls
+             setupterm() etc. It is now safe to allocate and use
+             multiple GetLine objects, albeit only within a single
+             thread.
+           
+28/09/2001 mcs@astro.caltech.edu
+
+           version.c Makefile.rules
+             I added a function for querying the version number of
+             the library.
+
+26/09/2001 mcs@astro.caltech.edu
+
+           getline.c man3/gl_get_line.3
+             I added the new gl_watch_fd() function, which allows
+             applications to register callback functions to be invoked
+             when activity is seen on arbitrary file descriptors while
+             gl_get_line() is awaiting keyboard input from the user.
+
+           keytab.c
+             If a request is received to delete a non-existent
+             binding, which happens to be an ambiguous prefix of other
+             bindings no complaint is now generated about it being
+             ambiguous.
+
+23/09/2001 mcs@astro.caltech.edu
+
+           getline.c history.c history.h man3/gl_get_line.3
+           libtecla.map demo.c
+             I added new public functions for saving and restoring the
+             contents of the history list. The demo program now uses
+             these functions to load and save history in ~/.demo_history.
+
+23/09/2001 mcs@astro.caltech.edu
+
+           getline.c
+             On trying the demo for the first time on a KDE konsole
+             terminal, I discovered that the default M-O binding
+             to repeat history was hiding the arrow keys, which are
+             M-OA etc. I have removed this binding. The M-o (ie the
+             lower case version of this), is still bound.
+
+18/09/2001 mcs@astro.caltech.edu
+
+           getline.c man3/gl_get_line.3 libtecla.map
+             Automatic reading of ~/.teclarc is now postponed until
+             the first call to gl_get_line(), to give the application
+             the chance to specify alternative configuration sources
+             with the new function gl_configure_getline(). The latter
+             function allows configuration to be done with a string, a
+             specified application-specific file, and/or a specified
+             user-specific file. I also added a read-init-files action
+             function, for re-reading the configuration files, if any.
+             This is by default bound to ^X^R. This is all documented
+             in gl_get_line.3.
+
+08/09/2001 mcs@astro.caltech.edu
+
+           getline.c man3/gl_get_line.3
+             It is now possible to bind actions to key-sequences
+             that start with printable characters. Previously
+             keysequences were required to start with meta or control
+             characters. This is documented in gl_get_line.3.
+
+           getline.c man3/gl_get_line.3
+             A customized completion function can now arrange for
+             gl_get_line() to return the current input line whenever a
+             successful completion has been made. This is signalled by
+             setting the last character of the optional continuation
+             suffix to a newline character. This is documented in
+             gl_get_line.3.
+
+05/07/2001 Bug reported by Mike MacFaden, fixed by mcs
+
+           configure.in
+             There was a bug in the configure script that only
+             revealed itself on systems without termcap but not
+             terminfo (eg. NetBSD). I traced the bug back to a lack of
+             sufficient quoting of multi-line m4 macro arguments in
+             configure.in, and have now fixed this and recreated the
+             configure script.
+
+05/07/2001 Bug reported and patched by Mike MacFaden (patch modified
+           by mcs to match original intentions).
+
+           getline.c
+             getline.c wouldn't compile when termcap was selected as
+             the terminal information database. setupterm() was being
+             passed a non-existent variable, in place of the term[]
+             argument of gl_control_strings(). Also if
+             gl_change_terminal() is called with term==NULL, "ansi"
+             is now substituted.
+
+02/07/2001 Version 1.3.3 released.
+
+27/06/2001 mcs@astro.caltech.edu
+
+           getline.c expand.c cplmatch.c
+             Added checks to fprintf() statements that write to the
+             terminal.
+           getline.c
+             Move the cursor to the end of the line before suspending,
+             so that the cursor doesn't get left in the middle of the
+             input line.
+           Makefile.in
+             On systems that don't support shared libraries, the
+             distclean target of make deleted libtecla.h. This has
+             now been fixed.
+           getline.c
+             gl_change_terminal() was being called by gl_change_editor(),
+             with the unwanted side effect that raw terminal modes were
+             stored as those to be restored later, if called by an
+             action function. gl_change_terminal() was being called in
+             this case to re-establish terminal-specific key bindings,
+             so I have just split this part of the function out into
+             a separate function for both gl_change_editor() and
+             gl_change_terminal() to call.
+
+12/06/2001 mcs@astro.caltech.edu
+
+           getline.c
+             Signal handling has been improved. Many more signals are
+             now trapped, and instead of using a simple flag set by a
+             signal handler, race conditions are avoided by blocking
+             signals during most of the gl_get_line() code, and
+             unblocking them via calls to sigsetjmp(), just before
+             attempting to read each new character from the user.
+             The matching use of siglongjmp() in the signal
+             handlers ensures that signals are reblocked correctly
+             before they are handled. In most cases, signals cause
+             gl_get_line() to restore the terminal modes and signal
+             handlers of the calling application, then resend the
+             signal to the application. In the case of SIGINT, SIGHUP,
+             SIGPIPE, and SIGQUIT, if the process still exists after
+             the signals are resent, gl_get_line() immediately returns
+             with appropriate values assigned to errno. If SIGTSTP,
+             SIGTTIN or SIGTTOU signals are received, the process is
+             suspended. If any other signal is received, and the
+             process continues to exist after the signal is resent to
+             the calling application, line input is resumed after the
+             terminal is put back into raw mode, the gl_get_line()
+             signal handling is restored, and the input line redrawn.
+           man/gl_get_line(3)
+             I added a SIGNAL HANDLING section to the gl_get_line()
+             man page, describing the new signal handling features.
+
+21/05/2001 Version 1.3.2 released.
+
+21/05/2001 mcs@astro.caltech.edu
+
+           getline.c
+             When vi-replace-char was used to replace the character at
+             the end of the line, it left the cursor one character to
+             its right instead of on top of it. Now rememdied.
+           getline.c
+             When undoing, to properly emulate vi, the cursor is now
+             left at the leftmost of the saved and current cursor
+             positions.
+           getline.c man3/gl_get_line.3
+             Implemented find-parenthesis (%), delete-to-paren (M-d%),
+             vi-change-to-paren (M-c%), copy-to-paren (M-y%).
+           cplfile.c pcache.c
+             In three places I was comparing the last argument of
+             strncmp() to zero instead of the return value of
+             strncmp().
+
+20/05/2001 mcs@astro.caltech.edu
+
+           getline.c man3/gl_get_line.3
+             Implemented and documented the vi-repeat-change action,
+             bound to the period key. This repeats the last action
+             that modified the input line.
+
+19/05/2001 mcs@astro.caltech.edu
+
+           man3/gl_get_line.3
+             I documented the new action functions and bindings
+             provided by Tim Eliseo, plus the ring-bell action and
+             the new "nobeep" configuration option.
+           getline.c
+             I modified gl_change_editor() to remove and reinstate the
+             terminal settings as well as the default bindings, since
+             these have editor-specific differences. I also modified
+             it to not abort if a key-sequence can't be bound for some
+             reason. This allows the new vi-mode and emacs-mode
+             bindings to be used safely.
+           getline.c
+             When the line was re-displayed on receipt of a SIGWINCH
+             signal, the result wasn't visible until the next
+             character was typed, since a call to fflush() was needed.
+             gl_redisplay_line() now calls gl_flush_output() to remedy
+             this.
+
+17/05/2001 mcs@astro.catlech.edu
+
+           getline.c
+             Under Linux, calling fflush(gl->output_fd) hangs if
+             terminal output has been suspended with ^S. With the
+             tecla library taking responsability for reading the stop
+             and start characters this was a problem, because once
+             hung in fflush(), the keyboard input loop wasn't entered,
+             so the user couldn't type the start character to resume
+             output.  To remedy this, I now have the terminal process
+             these characters, rather than the library.
+
+12/05/2001 mcs@astro.caltech.edu
+
+           getline.c
+             The literal-next action is now implemented as a single
+             function which reads the next character itself.
+             Previously it just set a flag which effected the
+             interpretation of the next character read by the input
+             loop.
+           getline.c
+             Added a ring-bell action function. This is currently
+             unbound to any key by default, but it is used internally,
+             and can be used by users that want to disable any of the
+             default key-bindings.
+
+12/05/2001 Tim Eliseo    (logged here by mcs)
+
+           getline.c
+             Don't reset gl->number until after calling an action
+             function. By looking at whether gl->number is <0 or
+             not, action functions can then tell whether the count
+             that they were passed was explicitly specified by the
+             user, as opposed to being defaulted to 1.
+           getline.c
+             In vi, the position at which input mode is entered
+             acts as a barrier to backward motion for the few
+             backward moving actions that are enabled in input mode.
+             Tim added this barrier to getline.
+           getline.c
+             In gl_get_line() after reading an input line, or
+             having the read aborted by a signal, the sig_atomic_t
+             gl_pending_signal was being compared to zero instead
+             of -1 to see if no signals had been received.
+             gl_get_line() will thus have been calling raise(-1),
+             which luckily didn't seem to do anything. Tim also
+             arranged for errno to be set to EINTR when a signal
+             aborts gl_get_line().
+           getline.c
+             The test in gl_add_char_to_line() for detecting
+             when overwriting a character with a wider character,
+             had a < where it needed a >. Overwriting with a wider
+             character thus overwrote trailing characters. Tim also
+             removed a redundant copy of the character into the
+             line buffer.
+           getline.c
+             gl_cursor_left() and gl->cursor_right() were executing
+             a lot of redundant code, when the existing call to the
+             recently added gl_place_cursor() function, does all that
+             is necessary.
+           getline.c
+             Remove redundant code from backward_kill_line() by
+             re-implimenting in terms of gl_place_cursor() and
+             gl_delete_chars().
+           getline.c
+             gl_forward_delete_char() now records characters in cut
+             buffer when in vi command mode.
+           getline.c
+             In vi mode gl_backward_delete_char() now only deletes
+             up to the point at which input mode was entered. Also
+             gl_delete_chars() restores from the undo buffer when
+             deleting in vi insert mode.
+           getline.c
+             Added action functions, vi-delete-goto-column,
+             vi-change-to-bol, vi-change-line, emacs-mode, vi-mode,
+             vi-forward-change-find, vi-backward-change-find,
+             vi-forward-change-to, vi-backward-change-to,
+             vi-change-goto-col, forward-delete-find, backward-delete-find,
+             forward-delete-to, backward-delete-to,
+             delete-refind, delete-invert-refind, forward-copy-find,
+             backward-copy-find, forward-copy-to, backward-copy-to
+             copy-goto-column, copy-rest-of-line, copy-to-bol, copy-line,
+             history-re-search-forward, history-re-search-backward.
+
+06/05/2001 Version 1.3.1 released.
+
+03/05/2001 mcs@astro.caltech.edu
+
+           configure.in
+             Old versions of GNU ld don't accept version scripts.
+             Under Linux I thus added a test to try out ld with
+             the --version-script argument to see if it works.
+             If not, version scripts aren't used.
+           configure.in
+             My test for versions of Solaris earlier than 7
+             failed when confronted by a three figure version
+             number (2.5.1). Fixed.
+
+30/04/2001 mcs@astro.caltech.edu
+
+           getline.c
+             In vi mode, history-search-backward and
+             history-search-forward weren't doing anything when
+             invoked at the start of an empty line, whereas
+             they should have acted like up-history and down-history.
+           Makefile.in Makefile.rules
+             When shared libraries are being created, the build
+             procedure now arranges for any alternate library
+             links to be created as well, before linking the
+             demos. Without this the demos always linked to the
+             static libraries (which was perfectly ok, but wasn't a
+             good example).
+           Makefile.in Makefile.rules
+             On systems on which shared libraries were being created,
+             if there were no alternate list of names, make would
+             abort due to a Bourne shell 'for' statement that didn't
+             have any arguments. Currently there are no systems who's
+             shared library configurations would trigger this
+             problem.
+           Makefile.rules
+             The demos now relink to take account of changes to the
+             library.
+           configure.in configure
+             When determining whether the reentrant version of the
+             library should be compiled by default, the configure
+             script now attempts to compile a dummy program that
+             includes all of the appropriate system headers and
+             defines _POSIX_C_SOURCE. This should now be a robust test
+             on systems which use C macros to alias these function
+             names to other internal functions.
+           configure.in
+             Under Solaris 2.6 and earlier, the curses library is in
+             /usr/ccs/lib. Gcc wasn't finding this. In addition to
+             remedying this, I had to remove "-z text" from
+             LINK_SHARED under Solaris to get it to successfully
+             compile the shared library against the static curses
+             library.
+           configure.in
+             Under Linux the -soname directive was being used
+             incorrectly, citing the fully qualified name of the
+             library instead of its major version alias. This will
+             unfortunately mean that binaries linked with the 1.2.3
+             and 1.2.4 versions of the shared library won't use
+             later versions of the library unless relinked.
+
+30/04/2001 mcs@astro.caltech.edu
+
+           getline.c
+             In gl_get_input_line(), don't redundantly copy the
+             start_line if start_line == gl->line.
+
+30/04/2001 Version 1.3.0 released.
+
+28/04/2001 mcs@astro.caltech.edu
+
+           configure.in
+             I removed the --no-undefined directive from the Linux
+             LINK_SHARED command. After recent patches to our RedHat
+             7.0 systems ld started reporting some internal symbols of
+             libc as being undefined.  Using nm on libc indicated that
+             the offending symbols are indeed defined, albeit as
+             "common" symbols, so there appears to be a bug in
+             RedHat's ld. Removing this flag allows the tecla shared
+             library to compile, and programs appear to function fine.
+           man3/gl_get_line.3
+             The default key-sequence used to invoke the
+             read-from-file action was incorrectly cited as ^Xi
+             instead of ^X^F.
+
+26/04/2001 mcs@astro.caltech.edu
+
+           getline.c man3/gl_get_line.3
+             A new vi-style editing mode was added. This involved
+             adding many new action functions, adding support for
+             specifying editing modes in users' ~/.teclarc files,
+             writing a higher level cursor motion function to support
+             the different line-end bounds required in vi command
+             mode, and a few small changes to support the fact that vi
+             has two modes, input mode and command mode with different
+             bindings.
+
+             When vi editing mode is enabled, any binding that starts
+             with an escape or a meta character, is interpreted as a
+             command-mode binding, and switches the library to vi
+             command mode if not already in that mode. Once in command
+             mode the first character of all keysequences entered
+             until input mode is re-enabled, are quietly coerced to
+             meta characters before being looked up in the key-binding
+             table. So, for example, in the key-binding table, the
+             standard vi command-mode 'w' key, which moves the cursor
+             one word to the right, is represented by M-w. This
+             emulates vi's dual sets of bindings in a natural way
+             without needing large changes to the library, or new
+             binding syntaxes. Since cursor keys normally emit
+             keysequences which start with escape, it also does
+             something sensible when a cursor key is pressed during
+             input mode (unlike true vi, which gets upset).
+
+             I also added a ^Xg binding for the new list-glob action
+             to both the emacs and vi key-binding tables. This lists
+             the files that match the wild-card expression that
+             precedes it on the command line.
+
+             The function that reads in ~/.teclarc used to tell
+             new_GetLine() to abort if it encountered anything that it
+             didn't understand in this file. It now just reports an
+             error and continues onto the next line.
+           Makefile.in:
+             When passing LIBS=$(LIBS) to recursive invokations of
+             make, quotes weren't included around the $(LIBS) part.
+             This would cause problems if LIBS ever contained more
+             than one word (with the supplied configure script this
+             doesn't happen currently). I added these quotes.
+           expand.c man3/ef_expand_file.3:
+             I wrote a new public function called ef_list_expansions(),
+             to list the matching filenames returned by
+             ef_expand_file().
+
+             I also fixed the example in the man page, which cited
+             exp->file instead of exp->files, and changed the
+             dangerous name 'exp' with 'expn'.
+           keytab.c:
+             Key-binding tables start with 100 elements, and are
+             supposedly incremented in size by 100 elements whenever
+             the a table runs out of space. The realloc arguments to
+             do this were wrong. This would have caused problems if
+             anybody added a lot of personal bindings in their
+             ~/.teclarc file. I only noticed it because the number of
+             key bindings needed by the new vi mode exceeded this
+             number.
+           libtecla.map
+             ef_expand_file() is now reported as having been added in
+             the upcoming 1.3.0 release.
+
+25/03/2001 Markus Gyger  (logged here by mcs)
+
+           Makefile.in:
+             Make symbolic links to alternative shared library names
+             relative instead of absolute.
+           Makefile.rules:
+             The HP-UX libtecla.map.opt file should be made in the
+             compilation directory, to allow the source code directory
+             to be on a readonly filesystem.
+           cplmatch.c demo2.c history.c pcache.c
+             To allow the library to be compiled with a C++ compiler,
+             without generating warnings, a few casts were added where
+             void* return values were being assigned directly to
+             none void* pointer variables.
+
+25/03/2001 mcs@astro.caltech.edu
+
+           libtecla.map:
+             Added comment header to explain the purpose of the file.
+             Also added cpl_init_FileArgs to the list of exported
+             symbols. This symbol is deprecated, and no longer
+             documented, but for backwards compatibility, it should
+             still be exported.
+           configure:
+             I had forgotten to run autoconf before releasing version
+             1.2.4, so I have just belatedly done so.  This enables
+             Markus' changes to "configure.in" documented previously,
+             (see 17/03/2001).
+
+20/03/2001 John Levon   (logged here by mcs)
+
+           libtecla.h
+             A couple of the function prototypes in libtecla.h have
+             (FILE *) argument declarations, which means that stdio.h
+             needs to be included. The header file should be self
+             contained, so libtecla.h now includes stdio.h.
+
+18/03/2001 Version 1.2.4 released.
+
+           README html/index.html configure.in
+             Incremented minor version from 3 to 4.
+
+18/03/2001 mcs@astro.caltech.edu
+
+           getline.c
+             The fix for the end-of-line problem that I released a
+             couple of weeks ago, only worked for the first line,
+             because I was handling this case when the cursor position
+             was equal to the last column, rather than when the cursor
+             position modulo ncolumn was zero.
+           Makefile.in Makefile.rules
+             The demos are now made by default, their rules now being
+             int Makefile.rules instead of Makefile.in.
+           INSTALL
+             I documented how to compile the library in a different
+             directory than the distribution directory.
+             I also documented features designed to facilitate
+             configuring and building the library as part of another
+             package.
+
+17/03/2001 Markus Gyger (logged here by mcs)
+
+           getline.c
+             Until now cursor motions were done one at a time. Markus
+             has added code to make use the of the terminfo capability
+             that moves the cursor by more than one position at a
+             time. This greatly improves performance when editing near
+             the start of long lines.
+           getline.c
+             To further improve performance, Markus switched from
+             writing one character at a time to the terminal, using
+             the write() system call, to using C buffered output
+             streams. The output buffer is only flushed when
+             necessary.
+           Makefile.rules Makefile.in configure.in
+             Added support for compiling for different architectures
+             in different directories. Simply create another directory
+             and run the configure script located in the original
+             directory.
+           Makefile.in configure.in libtecla.map
+             Under Solaris, Linux and HP-UX, symbols that are to be
+             exported by tecla shared libraries are explicitly specified
+             via symbol map files. Only publicly documented functions
+             are thus visible to applications.
+           configure.in
+             When linking shared libraries under Solaris SPARC,
+             registers that are reserved for applications are marked
+             as off limits to the library, using -xregs=no%appl when
+             compiling with Sun cc, or -mno-app-regs when compiling
+             with gcc. Also removed -z redlocsym for Solaris, which
+             caused problems under some releases of ld.
+           homedir.c  (after minor changes by mcs)
+             Under ksh, ~+ expands to the current value of the ksh
+             PWD environment variable, which contains the path of
+             the current working directory, including any symbolic
+             links that were traversed to get there. The special
+             username "+" is now treated equally by tecla, except
+             that it substitutes the return value of getcwd() if PWD
+             either isn't set, or if it points at a different
+             directory than that reported by getcwd().
+
+08/03/2001 Version 1.2.3 released.
+
+08/03/2001 mcs@astro.caltech.edu
+
+           getline.c
+             On compiling the library under HP-UX for the first time
+             I encountered and fixed a couple of bugs:
+
+             1. On all systems except Solaris, the callback function
+                required by tputs() takes an int argument for the
+                character that is to be printed. Under Solaris it
+                takes a char argument. The callback function was
+                passing this argument, regardless of type, to write(),
+                which wrote the first byte of the argument.  This was
+                fine under Solaris and under little-endian systems,
+                because the first byte contained the character to be
+                written, but on big-endian systems, it always wrote
+                the zero byte at the other end of the word. As a
+                result, no control characters were being written to
+                the terminal.
+             2. While attempting to start a newline after the user hit
+                enter, the library was outputting the control sequence
+                for moving the cursor down, instead of the newline
+                character. On many systems the control sequence for
+                moving the cursor down happends to be a newline
+                character, but under HP-UX it isn't. The result was
+                that no new line was being started under HP-UX.
+
+04/03/2001 mcs@astro.caltech.edu
+
+           configure.in Makefile.in Makefile.stub configure config.guess
+           config.sub Makefile.rules install-sh PORTING README INSTALL
+             Configuration and compilation of the library is now
+             performed with the help of an autoconf configure
+             script. In addition to relieving the user of the need to
+             edit the Makefile, this also allows automatic compilation
+             of the reentrant version of the library on platforms that
+             can handle it, along with the creation of shared
+             libraries where configured. On systems that aren't known
+             to the configure script, just the static tecla library is
+             compiled. This is currently the case on all systems
+             except Linux, Solaris and HP-UX. In the hope that
+             installers will provide specific conigurations for other
+             systems, the configure.in script is heavily commented,
+             and instructions on how to use are included in a new
+             PORTING file.
+
+24/02/2001 Version 1.2b released.
+
+22/02/2001 mcs@astro.caltech.edu
+
+           getline.c
+             It turns out that most terminals, but not all, on writing
+             a character in the rightmost column, don't wrap the
+             cursor onto the next line until the next character is
+             output. This library wasn't aware of this and thus if one
+             tried to reposition the cursor from the last column,
+             gl_get_line() thought that it was moving relative to a
+             point on the next line, and thus moved the cursor up a
+             line. The fix was to write one extra character when in
+             the last column to force the cursor onto the next line,
+             then backup the cursor to the start of the new line.
+           getline.c
+             On terminal initialization, the dynamic LINES and COLUMNS
+             environment variables were ignored unless
+             terminfo/termcap didn't return sensible dimensions. In
+             practice, when present they should override the static
+             versions in the terminfo/termcap databases. This is the
+             new behavior. In reality this probably won't have caused
+             many problems, because a SIGWINCH signal which informs of
+             terminal size changes is sent when the terminal is
+             opened, so the dimensions established during
+             initialization quickly get updated on most systems.
+
+18/02/2001 Version 1.2a released.
+
+18/02/2001 mcs@astro.caltech.edu
+
+           getline.c
+             Three months ago I moved the point at which termios.h
+             was included in getline.c. Unfortunately, I didn't notice
+             that this moved it to after the test for TIOCGWINSZ being
+             defined. This resulted in SIGWINCH signals not being
+             trapped for, and thus terminal size changes went
+             unnoticed. I have now moved the test to after the 
+             inclusion of termios.h.
+
+12/02/2001 Markus Gyger     (described here by mcs)
+
+           man3/pca_lookup_file.3 man3/gl_get_line.3
+           man3/ef_expand_file.3 man3/cpl_complete_word.3
+             In the 1.2 release of the library, all functions in the
+             library were given man pages. Most of these simply
+             include one of the above 4 man pages, which describe the
+             functions while describing the modules that they are in.
+             Markus added all of these function names to the lists in
+             the "NAME" headers of the respective man pages.
+             Previously only the primary function of each module was
+             named there.
+
+11/02/2001 mcs@astro.caltech.edu
+
+           getline.c
+             On entering a line that wrapped over two or more
+             terminal, if the user pressed enter when the cursor
+             wasn't on the last of the wrapped lines, the text of the
+             wrapped lines that followed it got mixed up with the next
+             line written by the application, or the next input
+             line. Somehow this slipped through the cracks and wasn't
+             noticed until now. Anyway, it is fixed now.
+
+09/02/2001 Version 1.2 released.
+
+04/02/2001 mcs@astro.caltech.edu
+
+           pcache.c libtecla.h
+             With all filesystems local, demo2 was very fast to start
+             up, but on a Sun system with one of the target
+             directories being on a remote nfs mounted filesystem, the
+             startup time was many seconds. This was due to the
+             executable selection callback being applied to all files
+             in the path at startup. To avoid this, all files are now
+             included in the cache, and the application specified
+             file-selection callback is only called on files as they
+             are matched. Whether the callback rejected or accepted
+             them is then cached so that the next time an already
+             checked file is looked at, the callback doesn't have to
+             be called. As a result, startup is now fast on all
+             systems, and since usually there are only a few matching
+             file completions at a time, the delay during completion
+             is also usually small. The only exception is if the user
+             tries to complete an empty string, at which point all
+             files have to be checked. Having done this once, however,
+             doing it again is fast.
+           man3/pca_lookup_file.3
+             I added a man page documenting the new PathCache module.
+           man3/<many-new-files>.3
+             I have added man pages for all of the functions in each
+             of the modules. These 1-line pages use the .so directive
+             to redirect nroff to the man page of the parent module.
+           man Makefile update_html
+             I renamed man to man3 to make it easier to test man page
+             rediction, and updated Makefile and update_html
+             accordingly. I also instructed update_html to ignore
+             1-line man pages when making html equivalents of the man
+             pages.
+           cplmatch.c
+             In cpl_list_completions() the size_t return value of
+             strlen() was being used as the length argument of a "%*s"
+             printf directive. This ought to be an int, so the return
+             value of strlen() is now cast to int. This would have
+             caused problems on architectures where the size of a
+             size_t is not equal to the size of an int.
+
+02/02/2001 mcs@astro.caltech.edu
+
+           getline.c
+             Under UNIX, certain terminal bindings are set using the
+             stty command. This, for example, specifies which control
+             key generates a user-interrupt (usually ^C or ^Y). What I
+             hadn't realized was that ASCII NUL is used as the way to
+             specify that one of these bindings is unset. I have now
+             modified the code to skip unset bindings, leaving the
+             corresponding action bound to the built-in default, or a
+             user provided binding.
+
+28/01/2001 mcs@astro.caltech.edu
+
+           pcache.c libtecla.h
+             A new module was added which supports searching for files
+             in any colon separated list of directories, such as the
+             unix execution PATH environment variable. Files in these
+             directories, after being individually okayed for
+             inclusion via an application provided callback, are
+             cached in a PathCache object. You can then look up the
+             full pathname of a given filename, or you can use the
+             provided completion callback to list possible completions
+             in the path-list. The contents of relative directories,
+             such as ".", obviously can't be cached, so these
+             directories are read on the fly during lookups and
+             completions. The obvious application of this facility is
+             to provide Tab-completion of commands, and thus a
+             callback to place executable files in the cache, is
+             provided.
+           demo2.c
+             This new program demonstrates the new PathCache
+             module. It reads and processes lines of input until the
+             word 'exit' is entered, or C-d is pressed. The default
+             tab-completion callback is replaced with one which at the
+             start of a line, looks up completions of commands in the
+             user's execution path, and when invoked in other parts of
+             the line, reverts to normal filename completion. Whenever
+             a new line is entered, it extracts the first word on the
+             line, looks it up in the user's execution path to see if
+             it corresponds to a known command file, and if so,
+             displays the full pathname of the file, along with the
+             remaining arguments.
+           cplfile.c
+             I added an optional pair of callback function/data
+             members to the new cpl_file_completions() configuration
+             structure. Where provided, this callback is asked
+             on a file-by-file basis, which files should be included
+             in the list of file completions. For example, a callback
+             is provided for listing only completions of executable
+             files.
+           cplmatch.c
+             When listing completions, the length of the type suffix
+             of each completion wasn't being taken into account
+             correctly when computing the column widths. Thus the
+             listing appeared ragged sometimes. This is now fixed.
+           pathutil.c
+             I added a function for prepending a string to a path,
+             and another for testing whether a pathname referred to
+             an executable file.
+
+28/01/2001 mcs@astro.caltech.edu
+
+           libtecla.h cplmatch.c man/cpl_complete_word.3
+             The use of a publically defined structure to configure
+             the cpl_file_completions() callback was flawed, so a new
+             approach has been designed, and the old method, albeit
+             still supported, is no longer documented in the man
+             pages. The definition of the CplFileArgs structure in
+             libtecla.h is now accompanied by comments warning people
+             not to modify it, since modifications could break
+             applications linked to shared versions of the tecla
+             library. The new method involves an opaque CplFileConf
+             object, instances of which are returned by a provided
+             constructor function, configured with provided accessor
+             functions, and when no longer needed, deleted with a
+             provided destructor function. This is documented in the
+             cpl_complete_word man page. The cpl_file_completions()
+             callback distinguishes what type of configuration
+             structure it has been sent by virtue of a code placed at
+             the beginning of the CplFileConf argument by its
+             constructor.
+
+04/01/2001 mcs@astro.caltech.edu (Release of version 1.1j)
+
+           getline.c
+             I added upper-case bindings for the default meta-letter
+             keysequences such as M-b. They thus continue to work
+             when the user has caps-lock on.
+           Makefile
+             I re-implemented the "install" target in terms of new
+             install_lib, install_inc and install_man targets. When
+             distributing the library with other packages, these new
+             targets allows for finer grained control of the
+             installation process.
+
+30/12/2000 mcs@astro.caltech.edu
+
+           getline.c man/gl_get_line.3
+             I realized that the recall-history action that I
+             implemented wasn't what Markus had asked me for. What he
+             actually wanted was for down-history to continue going
+             forwards through a previous history recall session if no
+             history recall session had been started while entering
+             the current line. I have thus removed the recall-history
+             action and modified the down-history action function
+             accordingly.
+
+24/12/2000 mcs@astro.caltech.edu
+
+           getline.c
+             I modified gl_get_line() to allow the previously returned
+             line to be passed in the start_line argument.
+           getline.c man/gl_get_line.3
+             I added a recall-history action function, bound to M^P.
+             This recalls the last recalled history line, regardless
+             of whether it was from the current or previous line.
+
+13/12/2000 mcs@astro.caltech.edu (Release of version 1.1i)
+
+           getline.c history.h history.c man/gl_get_line.3
+             I implemented the equivalent of the ksh Operate action. I
+             have named the tecla equivalent "repeat-history". This
+             causes the line that is to be edited to returned, and
+             arranges for the next most recent history line to be
+             preloaded on the next call to gl_get_line(). Repeated
+             invocations of this action thus result in successive
+             history lines being repeated - hence the
+             name. Implementing the ksh Operate action was suggested
+             by Markus Gyger. In ksh it is bound to ^O, but since ^O
+             is traditionally bound by the default terminal settings,
+             to stop-output, I have bound the tecla equivalent to M-o.
+
+01/12/2000 mcs@astro.caltech.edu (Release of version 1.1h)
+
+           getline.c keytab.c keytab.h man/gl_get_line.3
+             I added a digit-argument action, to allow repeat
+             counts for actions to be entered. As in both tcsh
+             and readline, this is bound by default to each of
+             M-0, M-1 through to M-9, the number being appended
+             to the current repeat count. Once one of these has been
+             pressed, the subsequent digits of the repeat count can be
+             typed with or without the meta key pressed. It is also
+             possible to bind digit-argument to other keys, with or
+             without a numeric final keystroke. See man page for
+             details.
+
+           getline.c man/gl_get_line.3
+             Markus noted that my choice of M-< for the default
+             binding of read-from-file, could be confusing, since
+             readline binds this to beginning-of-history. I have
+             thus rebound it to ^X^F (ie. like find-file in emacs).
+
+           getline.c history.c history.h man/gl_get_line.3
+             I have now implemented equivalents of the readline
+             beginning-of-history and end-of-history actions.
+             These are bound to M-< and M-> respectively.
+
+           history.c history.h
+             I Moved the definition of the GlHistory type, and
+             its subordinate types from history.h to history.c.
+             There is no good reason for any other module to
+             have access to the innards of this structure.
+
+27/11/2000 mcs@astro.caltech.edu (Release of version 1.1g)
+
+           getline.c man/gl_get_line.3
+             I added a "read-from-file" action function and bound it
+             by default to M-<. This causes gl_get_line() to
+             temporarily return input from the file who's name
+             precedes the cursor.
+             
+26/11/2000 mcs@astro.caltech.edu
+
+           getline.c keytab.c keytab.h man/gl_get_line.3
+             I have reworked some of the keybinding code again.
+
+             Now, within key binding strings, in addition to the
+             previously existing notation, you can now use M-a to
+             denote meta-a, and C-a to denote control-a. For example,
+             a key binding which triggers when the user presses the
+             meta key, the control key and the letter [
+             simultaneously, can now be denoted by M-C-[, or M-^[ or
+             \EC-[ or \E^[.
+
+             I also updated the man page to use M- instead of \E in
+             the list of default bindings, since this looks cleaner.
+
+           getline.c man/gl_get_line.3
+             I added a copy-region-as-kill action function and
+             gave it a default binding to M-w.
+
+22/11/2000 mcs@astro.caltech.edu
+
+           *.c
+             Markus Gyger sent me a copy of a previous version of
+             the library, with const qualifiers added in appropriate
+             places. I have done the same for the latest version.
+             Among other things, this gets rid of the warnings
+             that are generated if one tells the compiler to
+             const qualify literal strings.
+
+           getline.c getline.h glconf.c
+             I have moved the contents of glconf.c and the declaration
+             of the GetLine structure into getline.c. This is cleaner,
+             since now only functions in getline.c can mess with the
+             innards of GetLine objects. It also clears up some problems
+             with system header inclusion order under Solaris, and also
+             the possibility that this might result in inconsistent
+             system macro definitions, which in turn could cause different
+             declarations of the structure to be seen in different files.
+
+           hash.c
+             I wrote a wrapper function to go around strcmp(), such that
+             when hash.c is compiled with a C++ compiler, the pointer
+             to the wrapper function is a C++ function pointer.
+             This makes it compatible with comparison function pointer
+             recorded in the hash table.
+
+           cplmatch.c getline.c libtecla.h
+             Markus noted that the Sun C++ compiler wasn't able to
+             match up the declaration of cpl_complete_word() in
+             libtecla.h, where it is surrounded by a extern "C" {}
+             wrapper, with the definition of this function in
+             cplmatch.c. My suspicion is that the compiler looks not
+             only at the function name, but also at the function
+             arguments to see if two functions match, and that the
+             match_fn() argument, being a fully blown function pointer
+             declaration, got interpetted as that of a C function in
+             one case, and a C++ function in the other, thus
+             preventing a match.
+
+             To fix this I now define a CplMatchFn typedef in libtecla.h,
+             and use this to declare the match_fn callback.
+
+20/11/2000 (Changes suggested by Markus Gyger to support C++ compilers):
+           expand.c
+             Renamed a variable called "explicit" to "xplicit", to
+             avoid conflicts when compiling with C++ compilers.
+           *.c
+             Added explicit casts when converting from (void *) to
+             other pointer types. This isn't needed in C but it is
+             in C++.
+           getline.c
+             tputs() has a strange declaration under Solaris. I was
+             enabling this declaration when the SPARC feature-test
+             macro was set. Markus changed the test to hinge on the
+             __sun and __SVR4 macros.
+           direader.c glconf.c stringrp.c
+             I had omitted to include string.h in these two files.
+
+           Markus also suggested some other changes, which are still
+           under discussion. With the just above changes however, the
+           library compiles without complaint using g++.
+
+19/11/2000 mcs@astro.caltech.edu
+           getline.h getline.c keytab.c keytab.h glconf.c
+           man/gl_get_line.3
+             I added support for backslash escapes (include \e
+             for the keyboard escape key) and literal binary
+             characters to the characters allowed within key sequences
+             of key bindings.
+
+           getline.h getline.c keytab.c keytab.h glconf.c
+           man/gl_get_line.3
+             I introduced symbolic names for the arrow keys, and
+             modified the library to use the cursor key sequences
+             reported by terminfo/termcap in addition to the default
+             ANSI ones. Anything bound to the symbolically named arrow
+             keys also gets bound to the default and terminfo/termcap
+             cursor key sequences. Note that under Solaris
+             terminfo/termcap report the properties of hardware X
+             terminals when TERM is xterm instead of the terminal
+             emulator properties, and the cursor keys on these two
+             systems generate different key sequences. This is an
+             example of why extra default sequences are needed.
+
+           getline.h getline.c keytab.c
+             For some reason I was using \e to represent the escape
+             character. This is supported by gcc, which thus doesn't
+             emit a warning except with the -pedantic flag, but isn't
+             part of standard C. I now use a macro to define escape
+             as \033 in getline.h, and this is now used wherever the
+             escape character is needed.
+
+17/11/2000 mcs@astro.caltech.edu (Release of version 1.1d)
+
+           getline.c, man/gl_get_line(3), html/gl_get_line.html
+             In tcsh ^D is bound to a function which does different
+             things depending on where the cursor is within the input
+             line. I have implemented its equivalent in the tecla
+             library. When invoked at the end of the line this action
+             function displays possible completions. When invoked on
+             an empty line it causes gl_get_line() to return NULL,
+             thus signalling end of input. When invoked within a line
+             it invokes forward-delete-char, as before. The new action
+             function is called del-char-or-list-or-eof.
+
+           getline.c, man/gl_get_line(3), html/gl_get_line.html
+             I found that the complete-word and expand-file actions
+             had underscores in their names instead of hyphens. This
+             made them different from all other action functions, so I
+             have changed the underscores to hyphens.
+
+           homedir.c
+             On SCO UnixWare while getpwuid_r() is available, the
+             associated _SC_GETPW_R_SIZE_MAX macro used by sysconf()
+             to find out how big to make the buffer to pass to this
+             function to cater for any password entry, doesn't
+             exist. I also hadn't catered for the case where sysconf()
+             reports that this limit is indeterminate. I have thus
+             change the code to substitute a default limit of 1024 if
+             either the above macro isn't defined or if sysconf() says
+             that the associated limit is indeterminate.
+           
+17/11/2000 mcs@astro.caltech.edu (Release of version 1.1c)
+
+           getline.c, getline.h, history.c, history.h
+             I have modified the way that the history recall functions
+             operate, to make them better emulate the behavior of
+             tcsh. Previously the history search bindings always
+             searched for the prefix that preceded the cursor, then
+             left the cursor at the same point in the line, so that a
+             following search would search using the same prefix. This
+             isn't how tcsh operates. On finding a matching line, tcsh
+             puts the cursor at the end of the line, but arranges for
+             the followup search to continue with the same prefix,
+             unless the user does any cursor motion or character
+             insertion operations in between, in which case it changes
+             the search prefix to the new set of characters that are
+             before the cursor. There are other complications as well,
+             which I have attempted to emulate. As far as I can
+             tell, the tecla history recall facilities now fully
+             emulate those of tcsh.
+
+16/11/2000 mcs@astro.caltech.edu (Release of version 1.1b)
+
+           demo.c:
+             One can now quit from the demo by typing exit.
+
+           keytab.c:
+             The first entry of the table was getting deleted
+             by _kt_clear_bindings() regardless of the source
+             of the binding. This deleted the up-arrow binding.
+             Symptoms noted by gazelle@yin.interaccess.com.
+
+           getline.h:
+             Depending on which system include files were include
+             before the inclusion of getline.h, SIGWINCH and
+             TIOCGWINSZ might or might not be defined. This resulted
+             in different definitions of the GetLine object in
+             different files, and thus some very strange bugs! I have
+             now added #includes for the necessary system header files
+             in getline.h itself. The symptom was that on creating a
+             ~/.teclarc file, the demo program complained of a NULL
+             argument to kt_set_keybinding() for the first line of the
+             file.
+
+15/11/2000 mcs@astro.caltech.edu (Release of version 1.1a)
+
+           demo.c:
+             I had neglected to check the return value of
+             new_GetLine() in the demo program. Oops.
+
+           getline.c libtecla.h:
+             I wrote gl_change_terminal(). This allows one to change to
+             a different terminal or I/O stream, by specifying the
+             stdio streams to use for input and output, along with the
+             type of terminal that they are connected to.
+
+           getline.c libtecla.h:
+             Renamed GetLine::isterm to GetLine::is_term. Standard
+             C reserves names that start with "is" followed by
+             alphanumeric characters, so this avoids potential
+             clashes in the future.
+
+           keytab.c keytab.h
+             Each key-sequence can now have different binding
+             functions from different sources, with the user provided
+             binding having the highest precedence, followed by the
+             default binding, followed by any terminal specific
+             binding. This allows gl_change_terminal() to redefine the
+             terminal-specific bindings each time that
+             gl_change_terminal() is called, without overwriting the
+             user specified or default bindings. In the future, it will
+             also allow for reconfiguration of user specified
+             bindings after the call to new_GetLine(). Ie. deleting a
+             user specified binding should reinstate any default or
+             terminal specific binding.
+
+           man/cpl_complete_word.3 html/cpl_complete_word.html
+           man/ef_expand_file.3    html/ef_expand_file.html
+           man/gl_get_line.3       html/gl_get_line.html
+             I added sections on thread safety to the man pages of the
+             individual modules.
+
+           man/gl_get_line.3       html/gl_get_line.html
+             I documented the new gl_change_terminal() function.
+
+           man/gl_get_line.3       html/gl_get_line.html
+             In the description of the ~/.teclarc configuration file,
+             I had omitted the 'bind' command word in the example
+             entry. I have now remedied this.
+
diff --git a/libtecla-1.6.1/html/cpl_complete_word.html b/libtecla-1.6.1/html/cpl_complete_word.html new file mode 100644 index 0000000..0c976c0 --- /dev/null +++ b/libtecla-1.6.1/html/cpl_complete_word.html @@ -0,0 +1,382 @@ + +Manual Page + + +
+cpl_complete_word              cpl_complete_word
+
+
+
+

NAME

+       cpl_complete_word,         cfc_file_start,         cfc_literal_escapes,
+       cfc_set_check_fn,       cpl_add_completion,       cpl_file_completions,
+       cpl_last_error,        cpl_list_completions,        cpl_recall_matches,
+       cpl_record_error, del_CplFileConf, del_WordCompletion, new_CplFileConf,
+       new_WordCompletion - lookup possible completions for a word
+
+

SYNOPSIS

+       #include <stdio.h>
+       #include <libtecla.h>
+
+       WordCompletion *new_WordCompletion(void);
+
+       WordCompletion *del_WordCompletion(WordCompletion *cpl);
+
+
+       #define CPL_MATCH_FN(fn) int (fn)(WordCompletion *cpl, \
+                                         void *data, \
+                                         const char *line, \
+                                         int word_end)
+       typedef CPL_MATCH_FN(CplMatchFn);
+
+       CPL_MATCH_FN(cpl_file_completions);
+
+
+       CplMatches *cpl_complete_word(WordCompletion *cpl,
+                                     const char *line,
+                                     int word_end, void *data,
+                                     CplMatchFn *match_fn);
+
+       CplMatches *cpl_recall_matches(WordCompletion *cpl);
+
+       int cpl_list_completions(CplMatches *result, FILE *fp,
+                                int term_width);
+
+       int cpl_add_completion(WordCompletion *cpl,
+                              const char *line, int word_start,
+                              int word_end, const char *suffix,
+                              const char *type_suffix,
+                              const char *cont_suffix);
+
+       void cpl_record_error(WordCompletion *cpl,
+                             const char *errmsg);
+
+       const char *cpl_last_error(WordCompletion *cpl);
+
+
+       #define CPL_CHECK_FN(fn) int (fn)(void *data, \
+                                         const char *pathname)
+
+       typedef CPL_CHECK_FN(CplCheckFn);
+
+       CPL_CHECK_FN(cpl_check_exe);
+
+       CplFileConf *new_CplFileConf(void);
+
+       CplFileConf *del_CplFileConf(CplFileConf *cfc);
+
+       void cfc_literal_escapes(CplFileConf *cfc, int literal);
+
+       void cfc_file_start(CplFileConf *cfc, int start_index);
+
+       void cfc_set_check_fn(CplFileConf *cfc, CplCheckFn *chk_fn,
+                             void *chk_data);
+
+
+
+

DESCRIPTION

+       The  cpl_complete_word() function is part of the tecla library (see the
+       libtecla man page). It  is  usually  called  behind  the
+       scenes  by  gl_get_line,  but  can  also be called sepa-
+       rately.
+
+       Given an input line containing an incomplete word to be  completed,  it
+       calls  a  user-provided callback function (or the provided file-comple-
+       tion callback function) to look up all possible completion suffixes for
+       that  word.  The  callback function is expected to look backward in the
+       line, starting from the specified cursor position, to find the start of
+       the  word  to be completed, then to look up all possible completions of
+       that word and record them, one at a  time  by  calling  cpl_add_comple-
+       tion().
+
+
+       Descriptions of the functions of this module are as follows:
+
+         WordCompletion *new_WordCompletion(void)
+
+       This  function  creates  the  resources used by the cpl_complete_word()
+       function. In particular, it maintains the memory that is used to return
+       the results of calling cpl_complete_word().
+
+         WordCompletion *del_WordCompletion(WordCompletion *cpl)
+
+       This  function  deletes  the resources that were returned by a previous
+       call to new_WordCompletion(). It always returns  NULL  (ie.  a  deleted
+       object). It does nothing if the cpl argument is NULL.
+
+       The  callback  functions  which  lookup  possible completions should be
+       defined with the following macro (which is defined in libtecla.h).
+
+         #define CPL_MATCH_FN(fn) int (fn)(WordCompletion *cpl, \
+                                           void *data, \
+                                           const char *line, \
+                                           int word_end)
+
+       Functions of this type are called by cpl_complete_word(),  and  all  of
+       the  arguments of the callback are those that were passed to said func-
+       tion. In particular, the line argument contains the input line contain-
+       ing  the word to be completed, and word_end is the index of the charac-
+       ter that follows the last character of the incomplete word within  this
+       string.  The  callback  is expected to look backwards from word_end for
+       the start of the incomplete word. What constitutes the start of a  word
+       clearly  depends on the application, so it makes sense for the callback
+       to take on this responsibility. For example, the builtin filename  com-
+       pletion  function  looks backwards until it hits an unescaped space, or
+       the start of the line.  Having found the start of the word,  the  call-
+       back  should  then  lookup  all  possible completions of this word, and
+       record each completion via separate calls to  cpl_add_completion().  If
+       the  callback  needs access to an application-specific symbol table, it
+       can pass it and any other data that it needs, via  the  data  argument.
+       This removes any need for globals.
+
+       The callback function should return 0 if no errors occur. On failure it
+       should return 1, and register a terse description of the error by call-
+       ing cpl_record_error().
+
+         void cpl_record_error(WordCompletion *cpl,
+                               const char *errmsg);
+
+       The last error message recorded by calling cpl_record_error(), can sub-
+       sequently be queried by calling cpl_last_error(), as described later.
+
+         int cpl_add_completion(WordCompletion *cpl,
+                                const char *line, int word_start,
+                                int word_end, const char *suffix,
+                                const char *type_suffix,
+                                const char *cont_suffix);
+
+       The cpl_add_completion() function is called zero or more times  by  the
+       completion  callback function to record each possible completion in the
+       specified WordCompletion object.  These  completions  are  subsequently
+       returned by cpl_complete_word(), as described later. The cpl, line, and
+       word_end arguments should be those that were  passed  to  the  callback
+       function.  The word_start argument should be the index within the input
+       line string of the start of the word  that  is  being  completed.  This
+       should  equal  word_end if a zero-length string is being completed. The
+       suffix argument is the string that would have to  be  appended  to  the
+       incomplete  word  to  complete  it.  If this needs any quoting (eg. the
+       addition of backslashes before special charaters) to  be  valid  within
+       the displayed input line, this should be included. A copy of the suffix
+       string is allocated internally, so there is no need  to  maintain  your
+       copy of the string after cpl_add_completion() returns.
+
+       Note  that  in  the  array  of  possible completions which the cpl_com-
+       plete_word() function returns, the suffix recorded  by  cpl_add_comple-
+       tion()  is listed along with the concatentation of this suffix with the
+       word that lies between word_start and word_end in the input line.
+
+       The type_suffix argument specifies an optional string to be appended to
+       the  completion  if it is displayed as part of a list of completions by
+       cpl_list_completions(). The intention is that this indicate to the user
+       the  type of each completion. For example, the file completion function
+       places a directory separator after completions that are directories, to
+       indicate  their  nature to the user. Similary, if the completion were a
+       function, you could indicate this to the user by setting type_suffix to
+       "()". Note that the type_suffix string isn't copied, so if the argument
+       isn't a literal string between speech marks, be sure  that  the  string
+       remains  valid  for  at  least  as  long  as  the  results  of cpl_com-
+       plete_word() are needed.
+
+       The cont_suffix is a continuation suffix to  append  to  the  completed
+       word  in  the  input line if this is the only completion. This is some-
+       thing that isn't part of the completion itself, but that gives the user
+       an  indication  about how they might continue to extend the token.  For
+       example, the file-completion callback function adds a directory separa-
+       tor  if the completed word is a directory. If the completed word were a
+       function name, you could similarly aid the user  by  arranging  for  an
+       open parenthesis to be appended.
+
+         CplMatches *cpl_complete_word(WordCompletion *cpl,
+                                       const char *line,
+                                       int word_end, void *data,
+                                       CplMatchFn *match_fn);
+
+       The  cpl_complete_word()  is  normally  called  behind  the  scenes  by
+       gl_get_line, but can also be called  separately  if  you
+       separately  allocate  a WordCompletion object. It performs word comple-
+       tion, as described at the beginning of this section. Its first argument
+       is  a resource object previously returned by new_WordCompletion().  The
+       line argument is the input line string, containing the word to be  com-
+       pleted.  The  word_end  argument contains the index of the character in
+       the input line, that just follows the last character of the word to  be
+       completed.  When  called  by  gl_get_line(), this is the character over
+       which the user pressed TAB.  The  match_fn  argument  is  the  function
+       pointer of the callback function which will lookup possible completions
+       of the word, as described above, and the data argument provides  a  way
+       for the application to pass arbitrary data to the callback function.
+
+       If  no errors occur, the cpl_complete_word() function returns a pointer
+       to a CplMatches container, as defined below. This  container  is  allo-
+       cated as part of the cpl object that was passed to cpl_complete_word(),
+       and will thus change on each call which uses the same cpl argument.
+
+         typedef struct {
+           char *completion;        /* A matching completion */
+                                    /*  string */
+           char *suffix;            /* The part of the */
+                                    /*  completion string which */
+                                    /*  would have to be */
+                                    /*  appended to complete the */
+                                    /*  original word. */
+           const char *type_suffix; /* A suffix to be added when */
+                                    /*  listing completions, to */
+                                    /*  indicate the type of the */
+                                    /*  completion. */
+         } CplMatch;
+
+         typedef struct {
+           char *suffix;            /* The common initial part */
+                                    /*  of all of the completion */
+                                    /*  suffixes. */
+           const char *cont_suffix; /* Optional continuation */
+                                    /*  string to be appended to */
+                                    /*  the sole completion when */
+                                    /*  nmatch==1. */
+           CplMatch *matches;       /* The array of possible */
+                                    /*  completion strings, */
+                                    /*  sorted into lexical */
+                                    /*  order. */
+           int nmatch;              /* The number of elements in */
+                                    /*  the above matches[] */
+                                    /*  array. */
+         } CplMatches;
+
+       If an error occurs during completion, cpl_complete_word() returns NULL.
+       A   description   of   the   error  can  be  acquired  by  calling  the
+       cpl_last_error() function.
+
+         const char *cpl_last_error(WordCompletion *cpl);
+
+       The cpl_last_error() function returns a terse description of the  error
+       which  occurred on the last call to cpl_complete_word() or cpl_add_com-
+       pletion().
+
+         CplMatches *cpl_recall_matches(WordCompletion *cpl);
+
+       As a convenience, the  return  value  of  the  last  call  to  cpl_com-
+       plete_word()   can   be   recalled   at   a   later   time  by  calling
+       cpl_recall_matches(). If cpl_complete_word()  returned  NULL,  so  will
+       cpl_recall_matches().
+
+         int cpl_list_completions(CplMatches *result, FILE *fp,
+                                  int terminal_width);
+
+       When the cpl_complete_word() function returns multiple possible comple-
+       tions, the cpl_list_completions() function can be called upon  to  list
+       them,  suitably arranged across the available width of the terminal. It
+       arranges for the displayed columns of completions to all have the  same
+       width,  set  by the longest completion. It also appends the type_suffix
+       strings that were recorded with each completion, thus indicating  their
+       types to the user.
+
+
+

THE BUILT-IN FILENAME-COMPLETION CALLBACK

+       By  default the gl_get_line function, passes the follow-
+       ing completion callback function to cpl_complete_word(). This  function
+       can  also  be  used  separately,  either  by  sending  it  to  cpl_com-
+       plete_word(), or by calling it directly from your own completion  call-
+       back function.
+
+         CPL_MATCH_FN(cpl_file_completions);
+
+       Certain aspects of the behavior of this callback can be changed via its
+       data argument. If you are happy with its default behavior you can  pass
+       NULL  in  this argument. Otherwise it should be a pointer to a CplFile-
+       Conf object, previously allocated by calling new_CplFileConf().
+
+         CplFileConf *new_CplFileConf(void);
+
+       CplFileConf  objects  encapsulate  the  configuration   parameters   of
+       cpl_file_completions().  These parameters, which start out with default
+       values, can be changed by  calling  the  accessor  functions  described
+       below.
+
+       By default, the cpl_file_completions() callback function searches back-
+       wards for the start of the filename being completed,  looking  for  the
+       first  un-escaped  space or the start of the input line. If you wish to
+       specify a different location, call cfc_file_start() with the  index  at
+       which the filename starts in the input line. Passing start_index=-1 re-
+       enables the default behavior.
+
+         void cfc_file_start(CplFileConf *cfc, int start_index);
+
+       By default, when cpl_file_completions() looks  at  a  filename  in  the
+       input  line,  each  lone  backslash in the input line is interpreted as
+       being a special character which removes any special significance of the
+       character  which  follows  it, such as a space which should be taken as
+       part of the filename rather than delimiting the start of the  filename.
+       These  backslashes  are thus ignored while looking for completions, and
+       subsequently added before spaces, tabs and literal backslashes  in  the
+       list  of  completions.  To have unescaped backslashes treated as normal
+       characters, call cfc_literal_escapes() with a  non-zero  value  in  its
+       literal argument.
+
+         void cfc_literal_escapes(CplFileConf *cfc, int literal);
+
+       By  default, cpl_file_completions() reports all files who's names start
+       with the prefix that is being completed. If you only  want  a  selected
+       subset  of  these  files to be reported in the list of completions, you
+       can arrange this by providing a callback function which takes the  full
+       pathname  of  a file, and returns 0 if the file should be ignored, or 1
+       if the file should be included in the list of completions. To  register
+       such    a    function   for   use   by   cpl_file_completions(),   call
+       cfc_set_check_fn(), and pass it a pointer  to  the  function,  together
+       with  a pointer to any data that you would like passed to this callback
+       whenever it is called. Your callback can make its  decisions  based  on
+       any property of the file, such as the filename itself, whether the file
+       is readable, writable or executable, or even based  on  what  the  file
+       contains.
+
+         #define CPL_CHECK_FN(fn) int (fn)(void *data, \
+                                           const char *pathname)
+         typedef CPL_CHECK_FN(CplCheckFn);
+
+         void cfc_set_check_fn(CplFileConf *cfc,
+                               CplCheckFn *chk_fn, void *chk_data);
+
+       The  cpl_check_exe() function is a provided callback of the above type,
+       for use with cpl_file_completions(). It returns non-zero if  the  file-
+       name  that  it is given represents a normal file that the user has exe-
+       cute permission to. You could use this to  have  cpl_file_completions()
+       only list completions of executable files.
+
+       When  you have finished with a CplFileConf variable, you can pass it to
+       the del_CplFileConf() destructor function to reclaim its memory.
+
+         CplFileConf *del_CplFileConf(CplFileConf *cfc);
+
+
+
+

THREAD SAFETY

+       In multi-threaded programs, you should use the libtecla_r.a version  of
+       the library. This uses POSIX reentrant functions where available (hence
+       the _r suffix), and disables features that rely on non-reentrant system
+       functions.  In  the  case  of this module, the only disabled feature is
+       username completion  in  ~username/  expressions,  in  cpl_file_comple-
+       tions().
+
+       Using  the  libtecla_r.a  version of the library, it is safe to use the
+       facilities of this module  in  multiple  threads,  provided  that  each
+       thread  uses  a  separately  allocated  WordCompletion object. In other
+       words, if two threads want to do word completion, they should each call
+       new_WordCompletion() to allocate their own completion objects.
+
+
+

FILES

+       libtecla.a    -    The tecla library
+       libtecla.h    -    The tecla header file.
+
+
+

SEE ALSO

+       libtecla, gl_get_line, ef_expand_file,
+       pca_lookup_file
+
+
+

AUTHOR

+       Martin Shepherd  (mcs@astro.caltech.edu)
+
+
+
+                                              cpl_complete_word
+
+ diff --git a/libtecla-1.6.1/html/ef_expand_file.html b/libtecla-1.6.1/html/ef_expand_file.html new file mode 100644 index 0000000..c780cf6 --- /dev/null +++ b/libtecla-1.6.1/html/ef_expand_file.html @@ -0,0 +1,213 @@ + +Manual Page + + +
+ef_expand_file                    ef_expand_file
+
+
+
+

NAME

+       ef_expand_file,   del_ExpandFile,   ef_last_error,  ef_list_expansions,
+       new_ExpandFile - expand filenames containing ~user/$envvar and wildcard
+       expressions
+
+

SYNOPSIS

+       #include <libtecla.h>
+
+       ExpandFile *new_ExpandFile(void);
+
+       ExpandFile *del_ExpandFile(ExpandFile *ef);
+
+       FileExpansion *ef_expand_file(ExpandFile *ef,
+                                     const char *path,
+                                     int pathlen);
+
+       int ef_list_expansions(FileExpansion *result, FILE *fp,
+                              int term_width);
+
+       const char *ef_last_error(ExpandFile *ef);
+
+
+

DESCRIPTION

+       The  ef_expand_file()  function  is  part of the tecla library (see the
+       libtecla man page). It  expands  a  specified  filename,
+       converting  ~user/  and  ~/ expressions at the start of the filename to
+       the corresponding home directories, replacing $envvar with the value of
+       the  corresponding  environment  variable,  and  then, if there are any
+       wildcards, matching these against existing  filenames.  Backslashes  in
+       the  input filename are interpreted as escaping any special meanings of
+       the characters that follow them.  Only backslahes that  are  themselves
+       preceded by backslashes are preserved in the expanded filename.
+
+       In  the  presence  of  wildcards,  the  returned list of filenames only
+       includes the names of existing files which match the wildcards.  Other-
+       wise,  the  original  filename is returned after expansion of tilde and
+       dollar expressions, and the result  is  not  checked  against  existing
+       files. This mimics the file-globbing behavior of the unix tcsh shell.
+
+       The supported wildcards and their meanings are:
+         *        -  Match any sequence of zero or more characters.
+         ?        -  Match any single character.
+         [chars]  -  Match any single character that appears in
+                     'chars'.  If 'chars' contains an expression of
+                     the form a-b, then any character between a and
+                     b, including a and b, matches. The '-'
+                     character looses its special meaning as a
+                     range specifier when it appears at the start
+                     of the sequence of characters. The ']'
+                     character also looses its significance as the
+                     terminator of the range expression if it
+                     appears immediately after the opening '[', at
+                     which point it is treated one of the
+                     characters of the range. If you want both '-'
+                     and ']' to be part of the range, the '-'
+                     should come first and the ']' second.
+
+         [^chars] -  The same as [chars] except that it matches any
+                     single character that doesn't appear in
+                     'chars'.
+
+       Note that wildcards never match the initial dot in filenames that start
+       with '.'. The initial '.' must be explicitly specified in the filename.
+       This  again  mimics  the globbing behavior of most unix shells, and its
+       rational is based in the fact that in unix, files with names that start
+       with  '.'  are usually hidden configuration files, which are not listed
+       by default by the ls command.
+
+       The following is a complete example of how to use  the  file  expansion
+       function.
+
+         #include <stdio.h>
+         #include <libtecla.h>
+
+         int main(int argc, char *argv[])
+         {
+           ExpandFile *ef;      /* The expansion resource object */
+           char *filename;      /* The filename being expanded */
+           FileExpansion *expn; /* The results of the expansion */
+           int i;
+
+           ef = new_ExpandFile();
+           if(!ef)
+             return 1;
+
+           for(arg = *(argv++); arg; arg = *(argv++)) {
+             if((expn = ef_expand_file(ef, arg, -1)) == NULL) {
+               fprintf(stderr, "Error expanding %s (%s).\n", arg,
+                                ef_last_error(ef));
+             } else {
+               printf("%s matches the following files:\n", arg);
+               for(i=0; i<expn->nfile; i++)
+                 printf(" %s\n", expn->files[i]);
+             }
+           }
+
+           ef = del_ExpandFile(ef);
+           return 0;
+         }
+
+       Descriptions of the functions used above are as follows:
+
+         ExpandFile *new_ExpandFile(void)
+
+       This  function creates the resources used by the ef_expand_file() func-
+       tion. In particular, it maintains the memory that is used to record the
+       array  of matching filenames that is returned by ef_expand_file(). This
+       array is expanded as needed, so there is no built in limit to the  num-
+       ber of files that can be matched.
+
+         ExpandFile *del_ExpandFile(ExpandFile *ef)
+
+       This  function  deletes  the resources that were returned by a previous
+       call to new_ExpandFile(). It always returns NULL (ie a deleted object).
+       It does nothing if the ef argument is NULL.
+
+       A container of the following type is returned by ef_expand_file().
+
+         typedef struct {
+           int exists;   /* True if the files in files[] exist */
+           int nfile;    /* The number of files in files[] */
+           char **files; /* An array of 'nfile' filenames. */
+         } FileExpansion;
+
+         FileExpansion *ef_expand_file(ExpandFile *ef,
+                                       const char *path,
+                                       int pathlen)
+
+       The  ef_expand_file()  function  performs  filename expansion, as docu-
+       mented at the start of this section. Its first argument is  a  resource
+       object  returned  by  new_ExpandFile().  A  pointer to the start of the
+       filename to be matched is passed via the path argument. This must be  a
+       normal  NUL  terminated  string, but unless a length of -1 is passed in
+       pathlen, only the first pathlen characters will be used in the filename
+       expansion.   If  the length is specified as -1, the whole of the string
+       will be expanded.
+
+       The function returns a pointer to a container who's  contents  are  the
+       results  of  the expansion. If there were no wildcards in the filename,
+       the nfile member will be 1, and the exists member should be queried  if
+       it  is  important to know if the expanded file currently exists or not.
+       If there were wildcards, then the contained files[] array will  contain
+       the names of the nfile existing files that matched the wildcarded file-
+       name, and the exists member will  have  the  value  1.  Note  that  the
+       returned container belongs to the specified ef object, and its contents
+       will change on each call, so if you need to retain the results of  more
+       than  one  call  to  ef_expand_file(), you should either make a private
+       copy  of  the  returned  results,  or  create  multiple  file-expansion
+       resource objects via multiple calls to new_ExpandFile().
+
+       On  error,  NULL  is  returned,  and an explanation of the error can be
+       determined by calling ef_last_error(ef).
+
+         const char *ef_last_error(ExpandFile *ef)
+
+       This function returns  the  message  which  describes  the  error  that
+       occurred  on  the last call to ef_expand_file(), for the given (Expand-
+       File *ef) resource object.
+
+         int ef_list_expansions(FileExpansion *result, FILE *fp,
+                                int terminal_width);
+
+       The ef_list_expansions() function provides a convenient way to list the
+       filename expansions returned by ef_expand_file(). Like the unix ls com-
+       mand, it arranges the filenames into equal width columns,  each  column
+       having  the  width  of  the largest file. The number of columns used is
+       thus determined by the length of the longest filename, and  the  speci-
+       fied  terminal  width.  Beware  that filenames that are longer than the
+       specified terminal width are printed without being truncated, so output
+       longer than the specified terminal width can occur. The list is written
+       to the stdio stream specified by the fp argument.
+
+
+

THREAD SAFETY

+       In multi-threaded programs, you should use the libtecla_r.a version  of
+       the library. This uses POSIX reentrant functions where available (hence
+       the _r suffix), and disables features that rely on non-reentrant system
+       functions. Currently there are no features disabled in this module.
+
+       Using  the  libtecla_r.a  version of the library, it is safe to use the
+       facilities of this module  in  multiple  threads,  provided  that  each
+       thread  uses  a separately allocated ExpandFile object. In other words,
+       if two threads want  to  do  file  expansion,  they  should  each  call
+       new_ExpandFile() to allocate their own file-expansion objects.
+
+
+

FILES

+       libtecla.a    -    The tecla library
+       libtecla.h    -    The tecla header file.
+
+
+

SEE ALSO

+       libtecla, gl_get_line, cpl_complete_word,
+       pca_lookup_file
+
+
+

AUTHOR

+       Martin Shepherd  (mcs@astro.caltech.edu)
+
+
+
+                                                 ef_expand_file
+
+ diff --git a/libtecla-1.6.1/html/enhance.html b/libtecla-1.6.1/html/enhance.html new file mode 100644 index 0000000..62b4e2f --- /dev/null +++ b/libtecla-1.6.1/html/enhance.html @@ -0,0 +1,75 @@ + +Manual Page + + +
+enhance                                  enhance
+
+
+
+

NAME

+       enhance  - A program that adds command-line editing to third party pro-
+       grams.
+
+

SYNOPSIS

+       enhance command [ argument ... ]
+
+
+

DESCRIPTION

+       The enhance program provides enhanced command-line  editing  facilities
+       to  users  of  third  party applications, to which one doesn't have any
+       source code. It does this by  placing  a  pseudo-terminal  between  the
+       application and the real terminal. It uses the tecla command-line edit-
+       ing library to read input from the real terminal,  then  forwards  each
+       just  completed  input line to the application via the pseudo-terminal.
+       All output from the application is forwarded back unchanged to the real
+       terminal.
+
+       Whenever  the application stops generating output for more than a tenth
+       of a second, the enhance program treats the  latest  incomplete  output
+       line  as the prompt, and redisplays any incompleted input line that the
+       user has typed after it. Note that the small delay, which is  impercep-
+       tible  to  the  user, isn't necessary for correct operation of the pro-
+       gram. It is just an optimization, designed to stop the input line  from
+       being redisplayed so often that it slows down output.
+
+       Note  that  the  user-level command-line editing facilities provided by
+       the Tecla library are documented in the tecla man page
+
+
+

DEFICIENCIES

+       The one major problem that hasn't been solved yet, is how to deal  with
+       applications  that  change  whether typed input is echo'd by their con-
+       trolling terminal. For example, programs that ask for a password,  such
+       as  ftp  and telnet, temporarily tell their controlling terminal not to
+       echo what the user types. Since this request goes  to  the  application
+       side  of the psuedo terminal, the enhance program has no way of knowing
+       that this has happened, and continues to echo typed input to  its  con-
+       trolling terminal, while the user types their password.
+
+       Furthermore, before executing the host application, the enhance program
+       initially sets the pseudo terminal to noecho mode, so  that  everything
+       that  it sends to the program doesn't get redundantly echoed. If a pro-
+       gram that switches to noecho mode explicitly  restores  echoing  after-
+       wards, rather than restoring the terminal modes that were previously in
+       force, then subsequently, every time that you enter a new input line, a
+       duplicate copy will be displayed on the next line.
+
+
+

FILES

+       libtecla.a    -   The tecla library.
+       ~/.teclarc    -   The tecla personal customization file.
+
+
+

SEE ALSO

+       tecla, libtecla
+
+
+

AUTHOR

+       Martin Shepherd  (mcs@astro.caltech.edu)
+
+
+
+                                                        enhance
+
+ diff --git a/libtecla-1.6.1/html/gl_get_line.html b/libtecla-1.6.1/html/gl_get_line.html new file mode 100644 index 0000000..3df39d6 --- /dev/null +++ b/libtecla-1.6.1/html/gl_get_line.html @@ -0,0 +1,1996 @@ + +Manual Page + + +
+gl_get_line                          gl_get_line
+
+
+
+

NAME

+       gl_get_line,    new_GetLine,    del_GetLine,   gl_customize_completion,
+       gl_change_terminal, gl_configure_getline, gl_load_history, gl_save_his-
+       tory,   gl_group_history,   gl_show_history,  gl_watch_fd,  gl_inactiv-
+       ity_timeout,  gl_terminal_size,  gl_set_term_size,   gl_resize_history,
+       gl_limit_history,  gl_clear_history,  gl_toggle_history, gl_lookup_his-
+       tory,  gl_state_of_history,  gl_range_of_history,   gl_size_of_history,
+       gl_echo_mode,   gl_replace_prompt,  gl_prompt_style,  gl_ignore_signal,
+       gl_trap_signal, gl_last_signal, gl_completion_action,  gl_display_text,
+       gl_return_status,  gl_error_message, gl_catch_blocked, gl_list_signals,
+       gl_bind_keyseq, gl_erase_terminal, gl_automatic_history, gl_append_his-
+       tory,  gl_query_char, gl_read_char - allow the user to compose an input
+       line
+
+

SYNOPSIS

+       #include <stdio.h>
+       #include <libtecla.h>
+
+       GetLine *new_GetLine(size_t linelen, size_t histlen);
+
+       GetLine *del_GetLine(GetLine *gl);
+
+       char *gl_get_line(GetLine *gl, const char *prompt,
+                         const char *start_line, int start_pos);
+
+       int gl_query_char(GetLine *gl, const char *prompt,
+                         char defchar);
+
+       int gl_read_char(GetLine *gl);
+
+       int gl_customize_completion(GetLine *gl, void *data,
+                                   CplMatchFn *match_fn);
+
+       int gl_change_terminal(GetLine *gl, FILE *input_fp,
+                              FILE *output_fp, const char *term);
+
+       int gl_configure_getline(GetLine *gl,
+                                const char *app_string,
+                                const char *app_file,
+                                const char *user_file);
+
+       int gl_bind_keyseq(GetLine *gl, GlKeyOrigin origin,
+                          const char *keyseq, const char *action);
+
+       int gl_save_history(GetLine *gl, const char *filename,
+                           const char *comment, int max_lines);
+
+       int gl_load_history(GetLine *gl, const char *filename,
+                           const char *comment);
+
+       int gl_watch_fd(GetLine *gl, int fd, GlFdEvent event,
+                       GlFdEventFn *callback, void *data);
+
+       int gl_inactivity_timeout(GetLine *gl, GlTimeoutFn *callback,
+                          void *data, unsigned long sec,
+                          unsigned long nsec);
+
+       int gl_group_history(GetLine *gl, unsigned stream);
+
+       int gl_show_history(GetLine *gl, FILE *fp,
+                           const char *fmt, int all_groups,
+                           int max_lines);
+
+       int gl_resize_history(GetLine *gl, size_t bufsize);
+
+       void gl_limit_history(GetLine *gl, int max_lines);
+
+       void gl_clear_history(GetLine *gl, int all_groups);
+
+       void gl_toggle_history(GetLine *gl, int enable);
+
+       GlTerminalSize gl_terminal_size(GetLine *gl,
+                                       int def_ncolumn,
+                                       int def_nline);
+
+       int gl_set_term_size(GetLine *gl, int ncolumn, int nline);
+
+       int gl_lookup_history(GetLine *gl, unsigned long id,
+                             GlHistoryLine *hline);
+
+       void gl_state_of_history(GetLine *gl,
+                                GlHistoryState *state);
+
+       void gl_range_of_history(GetLine *gl,
+                                GlHistoryRange *range);
+
+       void gl_size_of_history(GetLine *gl, GlHistorySize *size);
+
+       void gl_echo_mode(GetLine *gl, int enable);
+
+       void gl_replace_prompt(GetLine *gl, const char *prompt);
+
+       void gl_prompt_style(GetLine *gl, GlPromptStyle style);
+
+       int gl_ignore_signal(GetLine *gl, int signo);
+
+       int gl_trap_signal(GetLine *gl, int signo, unsigned flags,
+                          GlAfterSignal after, int errno_value);
+
+       int gl_last_signal(GetLine *gl);
+
+       int gl_completion_action(GetLine *gl,
+                                void *data, CplMatchFn *match_fn,
+                                int list_only, const char *name,
+                                const char *keyseq);
+
+       int gl_register_action(GetLine *gl, void *data,
+                              GlActionFn *fn, const char *name,
+                              const char *keyseq);
+
+       int gl_display_text(GetLine *gl, int indentation,
+                           const char *prefix,
+                           const char *suffix, int fill_char,
+                           int def_width, int start,
+                           const char *string);
+
+       GlReturnStatus gl_return_status(GetLine *gl);
+
+       const char *gl_error_message(GetLine *gl, char *buff,
+                                    size_t n);
+
+       void gl_catch_blocked(GetLine *gl);
+
+       int gl_list_signals(GetLine *gl, sigset_t *set);
+
+       int gl_append_history(GetLine *gl, const char *line);
+
+       int gl_automatic_history(GetLine *gl, int enable);
+
+
+
+

DESCRIPTION

+       The gl_get_line() function is part of the tecla library (see the libte-
+       cla(@LIBR_MANEXT@) man page). If the user is typing at a terminal, each
+       call prompts them for an line of input, then provides interactive edit-
+       ing facilities, similar to those of the unix tcsh shell. In addition to
+       simple command-line editing, it supports recall of  previously  entered
+       command  lines,  TAB  completion  of  file names, and in-line wild-card
+       expansion of filenames. Documentation of both the  user-level  command-
+       line  editing features and all user configuration options, can be found
+       in the tecla man page. This  man  page  concerns  itself
+       with  documentation for programmers interested in using this library in
+       their application.
+
+
+

AN EXAMPLE

+       The following shows a complete example of how to use the  gl_get_line()
+       function to get input from the user:
+
+         #include <stdio.h>
+         #include <locale.h>
+         #include <libtecla.h>
+
+         int main(int argc, char *argv[])
+         {
+           char *line;    /* The line that the user typed */
+           GetLine *gl;   /* The gl_get_line() resource object */
+
+           setlocale(LC_CTYPE, ""); /* Adopt the user's choice */
+                                    /* of character set. */
+
+           gl = new_GetLine(1024, 2048);
+           if(!gl)
+             return 1;
+
+           while((line=gl_get_line(gl, "$ ", NULL, -1)) != NULL &&
+                  strcmp(line, "exit\n") != 0)
+             printf("You typed: %s\n", line);
+
+           gl = del_GetLine(gl);
+           return 0;
+         }
+
+       In  the  example, first the resources needed by the gl_get_line() func-
+       tion are created by calling new_GetLine(). This  allocates  the  memory
+       used  in  subsequent calls to the gl_get_line() function, including the
+       history buffer for recording previously entered lines. Then one or more
+       lines are read from the user, until either an error occurs, or the user
+       types exit. Then finally the resources that were allocated by  new_Get-
+       Line(),  are  returned to the system by calling del_GetLine(). Note the
+       use of the NULL return value of del_GetLine() to make gl NULL. This  is
+       a safety precaution. If the program subsequently attempts to pass gl to
+       gl_get_line(), said  function  will  complain,  and  return  an  error,
+       instead of attempting to use the deleted resource object.
+
+
+
+

THE FUNCTIONS USED IN THE EXAMPLE

+       The descriptions of the functions used in the example are as follows:
+
+         GetLine *new_GetLine(size_t linelen, size_t histlen)
+
+       This  function creates the resources used by the gl_get_line() function
+       and returns an opaque pointer to the object that  contains  them.   The
+       maximum  length of an input line is specified via the linelen argument,
+       and the number of bytes to allocate for storing history lines is set by
+       the histlen argument. History lines are stored back-to-back in a single
+       buffer of this size. Note that this means that the  number  of  history
+       lines  that  can be stored at any given time, depends on the lengths of
+       the individual lines.  If you want to place an upper limit on the  num-
+       ber  of  lines  that can be stored, see the gl_limit_history() function
+       described later. If you don't want history at all, specify  histlen  as
+       zero, and no history buffer will be allocated.
+
+       On error, a message is printed to stderr and NULL is returned.
+
+         GetLine *del_GetLine(GetLine *gl)
+
+       This  function  deletes  the resources that were returned by a previous
+       call to new_GetLine(). It always returns NULL (ie a deleted object). It
+       does nothing if the gl argument is NULL.
+
+         char *gl_get_line(GetLine *gl, const char *prompt,
+                          const char *start_line, int start_pos);
+
+       The  gl_get_line()  function  can be called any number of times to read
+       input from the user. The gl argument must have been previously returned
+       by  a call to new_GetLine(). The prompt argument should be a normal NUL
+       terminated string, specifying the prompt to present the user  with.  By
+       default  prompts  are  displayed  literally,  but  if  enabled with the
+       gl_prompt_style() function (see later), prompts can contain  directives
+       to  do underlining, switch to and from bold fonts, or turn highlighting
+       on and off.
+
+       If you want to specify the initial contents of the line, for  the  user
+       to  edit,  pass the desired string via the start_line argument. You can
+       then specify which character of this line the cursor is initially posi-
+       tioned  over,  using  the  start_pos argument. This should be -1 if you
+       want the cursor to follow the last character of the start line. If  you
+       don't want to preload the line in this manner, send start_line as NULL,
+       and set start_pos to -1. Note that the line  pointer  returned  by  one
+       call  to  gl_get_line()  can  be  passed  back  to  the  next  call  to
+       gl_get_line() via the start_line. This allows the application  to  take
+       the  last entered line, and if it contains an error, to then present it
+       back to the user for re-editing, with the cursor  initially  positioned
+       where the error was encountered.
+
+       The gl_get_line() function returns a pointer to the line entered by the
+       user, or NULL on error or at the end of the input. The returned pointer
+       is  part  of  the  specified gl resource object, and thus should not be
+       free'd by the caller, or assumed to be unchanging from one call to  the
+       next.  When  reading  from a user at a terminal, there will always be a
+       newline character at the end of the returned line.  When standard input
+       is being taken from a pipe or a file, there will similarly be a newline
+       unless the input line was too long to store in the internal buffer.  In
+       the latter case you should call gl_get_line() again to read the rest of
+       the line. Note  that  this  behavior  makes  gl_get_line()  similar  to
+       fgets().    In   fact   when   stdin   isn't   connected  to  a  termi-
+       nal,gl_get_line() just calls fgets().
+
+
+

THE RETURN STATUS OF GL_GET_LINE

+       As described above, the gl_get_line() function has two possible  return
+       values;  a pointer to the completed input line, or NULL. Extra informa-
+       tion about what caused gl_get_line() to return  is  available  both  by
+       inspecting errno, and by calling the gl_return_status() function.
+
+
+         GlReturnStatus gl_return_status(GetLine *gl);
+
+
+       The  following  are  the  possible enumerated values that this function
+       returns.
+
+
+         GLR_NEWLINE     -  The last call to gl_get_line()
+                            successfully returned a completed
+                            input line.
+
+         GLR_BLOCKED     -  gl_get_line() was in non-blocking
+                            server mode, and returned early to
+                            avoid blocking the process while
+                            waiting for terminal I/O. The
+                            gl_pending_io() function can be
+                            used to see what type of I/O
+                            gl_get_line() was waiting for.
+                            (see the gl_io_mode man page
+                            for details).
+
+         GLR_SIGNAL      -  A signal was caught by
+                            gl_get_line() that had an
+                            after-signal disposition of
+                            GLS_ABORT (See gl_trap_signal()).
+
+         GLR_TIMEOUT     -  The inactivity timer expired while
+                            gl_get_line() was waiting for
+                            input, and the timeout callback
+                            function returned GLTO_ABORT.
+                            See gl_inactivity_timeout() for
+                            information about timeouts.
+
+         GLR_FDABORT     -  An application I/O callack returned
+                            GLFD_ABORT (see gl_watch_fd()).
+
+         GLR_EOF         -  End of file reached. This can happen
+                            when input is coming from a file or a
+                            pipe, instead of the terminal. It also
+                            occurs if the user invokes the
+                            list-or-eof or del-char-or-list-or-eof
+                            actions at the start of a new line.
+
+         GLR_ERROR       -  An unexpected error caused
+                            gl_get_line() to abort (consult
+                            errno and/or
+                            gl_error_message() for details.
+
+
+       When gl_return_status() returns GLR_ERROR, and the value of errno isn't
+       sufficient to explain what happened, you can use the gl_error_message()
+       function to request a description of the last error that occurred.
+
+
+         const char *gl_error_message(GetLine *gl, char *buff,
+                                      size_t n);
+
+
+       The return value is a pointer to the message that occurred. If the buff
+       argument  is  NULL, this will be a pointer to a buffer within gl, who's
+       value will probably change on the next call to any function  associated
+       with gl_get_line(). Otherwise, if a non-NULL buff argument is provided,
+       the error message, including a '\0' terminator, will be written  within
+       the  first  n  elements  of this buffer, and the return value will be a
+       pointer to the first element of this buffer. If the message  won't  fit
+       in the provided buffer, it will be truncated to fit.
+
+
+

OPTIONAL PROMPT FORMATTING

+       Whereas by default the prompt string that you specify is displayed lit-
+       erally, without any special interpretation of the characters within it,
+       the  gl_prompt_style()  function can be used to enable optional format-
+       ting directives within the prompt.
+
+         void gl_prompt_style(GetLine *gl, GlPromptStyle style);
+
+       The style argument, which specifies the formatting style, can take  any
+       of the following values:
+
+         GL_FORMAT_PROMPT   -  In this style, the formatting
+                               directives described below, when
+                               included in prompt strings, are
+                               interpreted as follows:
+
+                                 %B  -  Display subsequent
+                                        characters with a bold
+                                        font.
+                                 %b  -  Stop displaying characters
+                                        with the bold font.
+                                 %F  -  Make subsequent characters
+                                        flash.
+                                 %f  -  Turn off flashing
+                                        characters.
+                                 %U  -  Underline subsequent
+                                        characters.
+                                 %u  -  Stop underlining
+                                        characters.
+                                 %P  -  Switch to a pale (half
+                                        brightness) font.
+                                 %p  -  Stop using the pale font.
+                                 %S  -  Highlight subsequent
+                                        characters (also known as
+                                        standout mode).
+                                 %s  -  Stop highlighting
+                                        characters.
+                                 %V  -  Turn on reverse video.
+                                 %v  -  Turn off reverse video.
+                                 %%  -  Display a single %
+                                        character.
+
+                               For example, in this mode, a prompt
+                               string like "%UOK%u$ " would
+                               display the prompt "OK$ ",
+                               but with the OK part
+                               underlined.
+
+                               Note that although a pair of
+                               characters that starts with a %
+                               character, but doesn't match any of
+                               the above directives is displayed
+                               literally, if a new directive is
+                               subsequently introduced which does
+                               match, the displayed prompt will
+                               change, so it is better to always
+                               use %% to display a literal %.
+
+                               Also note that not all terminals
+                               support all of these text
+                               attributes, and that some substitute
+                               a different attribute for missing
+                               ones.
+
+         GL_LITERAL_PROMPT  -  In this style, the prompt string is
+                               printed literally. This is the
+                               default style.
+
+
+

ALTERNATE CONFIGURATION SOURCES

+       As mentioned above, by default users have the option of configuring the
+       behavior of gl_get_line() via a configuration file called  .teclarc  in
+       their  home directories. The fact that all applications share this same
+       configuration file is both an advantage and a  disadvantage.   In  most
+       cases it is an advantage, since it encourages uniformity, and frees the
+       user from having to configure each  application  separately.   In  some
+       applications, however, this single means of configuration is a problem.
+       This is particularly  true  of  embedded  software,  where  there's  no
+       filesystem  to read a configuration file from, and also in applications
+       where a radically different choice of keybindings is needed to  emulate
+       a  legacy  keyboard  interface.  To cater for such cases, the following
+       function allows the application to control where configuration informa-
+       tion is read from.
+
+
+         int gl_configure_getline(GetLine *gl,
+                                  const char *app_string,
+                                  const char *app_file,
+                                  const char *user_file);
+
+
+       It allows the configuration commands that would normally be read from a
+       user's ~/.teclarc file, to be read from any or none of,  a  string,  an
+       application specific configuration file, and/or a user-specific config-
+       uration file. If this function is  called  before  the  first  call  to
+       gl_get_line(),  the default behavior of reading ~/.teclarc on the first
+       call to  gl_get_line()  is  disabled,  so  all  configuration  must  be
+       achieved  using the configuration sources specified with this function.
+
+       If app_string != NULL, then it is interpreted as  a  string  containing
+       one  or  more  configuration commands, separated from each other in the
+       string by embedded newline characters. If app_file != NULL then  it  is
+       interpreted  as the full pathname of an application-specific configura-
+       tion file. If user_file != NULL then it  is  interpreted  as  the  full
+       pathname of a user-specific configuration file, such as ~/.teclarc. For
+       example, in the following call,
+
+
+         gl_configure_getline(gl, "edit-mode vi \n nobeep",
+                                  "/usr/share/myapp/teclarc",
+                                  "~/.teclarc");
+
+
+       the app_string argument causes the calling application to start  in  vi
+       edit-mode,  instead of the default emacs mode, and turns off the use of
+       the terminal bell by the library. It then attempts to read  system-wide
+       configuration     commands     from    an    optional    file    called
+       /usr/share/myapp/teclarc, then finally reads  user-specific  configura-
+       tion  commands from an optional .teclarc file in the user's home direc-
+       tory. Note that the arguments are listed in ascending order  of  prior-
+       ity,  with  the  contents  of app_string being potentially overriden by
+       commands in app_file, and commands in app_file potentially being  over-
+       riden by commands in user_file.
+
+       You  can  call this function as many times as needed, the results being
+       cumulative, but note that copies of any  filenames  specified  via  the
+       app_file and user_file arguments are recorded internally for subsequent
+       use by the read-init-files key-binding function, so if you plan to call
+       this  function multiple times, be sure that the last call specifies the
+       filenames that you want re-read when the user requests that the config-
+       uration files be re-read.
+
+       Individual  key  sequences  can  also  be  bound  and unbound using the
+       gl_bind_keyseq() function.
+
+
+         int gl_bind_keyseq(GetLine *gl, GlKeyOrigin origin,
+                            const char *keyseq,
+                            const char *action);
+
+
+       The origin argument specifies the priority of the binding, according to
+       who  it  is being established for, and must be one of the following two
+       values.
+
+         GL_USER_KEY   -   The user requested this key-binding.
+         GL_APP_KEY    -   This is a default binding set by the
+                           application.
+
+       When both user and application bindings for a given  key-sequence  have
+       been  specified,  the  user binding takes precedence. The application's
+       binding is subsequently reinstated  if  the  user's  binding  is  later
+       unbound  via  either  another to this function, or a call to gl_config-
+       ure_getline().
+
+       The keyseq argument specifies the key-sequence to be bound or  unbound,
+       and is expressed in the same way as in a ~/.teclarc configuration file.
+       The action argument must either be a string containing the name of  the
+       action  to bind the key-sequence to, or it must be NULL or "" to unbind
+       the key-sequence.
+
+
+

CUSTOMIZED WORD COMPLETION

+       If in your application, you would like to have TAB completion  complete
+       other  things  in  addition to or instead of filenames, you can arrange
+       this by registering an alternate completion callback  function,  via  a
+       call to the gl_customize_completion() function.
+
+         int gl_customize_completion(GetLine *gl, void *data,
+                                     CplMatchFn *match_fn);
+
+       The  data  argument  provides  a way for your application to pass arbi-
+       trary, application-specific information to the callback function.  This
+       is  passed  to  the callback every time that it is called. It might for
+       example, point to the symbol table from which possible completions  are
+       to  be sought. The match_fn argument specifies the callback function to
+       be called. The CplMatchFn function type is defined in libtecla.h, as is
+       a  CPL_MATCH_FN() macro that you can use to declare and prototype call-
+       back functions. The declaration and responsibilities of callback  func-
+       tions  are  described  in depth in the cpl_complete_word
+       man page.
+
+       In brief, the callback function is responsible for looking backwards in
+       the  input  line, back from the point at which the user pressed TAB, to
+       find the start of the word being completed. It then must lookup  possi-
+       ble  completions  of this word, and record them one by one in the Word-
+       Completion object that is passed to it as an argument, by  calling  the
+       cpl_add_completion()  function. If the callback function wishes to pro-
+       vide filename completion in addition to its own  specific  completions,
+       it  has  the  option of itself calling the builtin file-name completion
+       callback.    This    also,    is    documented    in    the    cpl_com-
+       plete_word(@FUNC_MANEXT@) man page.
+
+       Note  that  if you would like gl_get_line() to return the current input
+       line when a successful completion is been made, you  can  arrange  this
+       when you call cpl_add_completion(), by making the last character of the
+       continuation suffix a newline character. If you do this, the input line
+       will   be   updated  to  display  the  completion,  together  with  any
+       contiuation suffix up to the newline character, then gl_get_line() will
+       return this input line.
+
+
+       If, for some reason, your callback function needs to write something to
+       the terminal, it must call gl_normal_io() before doing  so.  This  will
+       start  a  new line after the input line that is currently being edited,
+       reinstate normal terminal I/O, and tell gl_get_line()  that  the  input
+       line will need to be redrawn when the callback returns.
+
+
+

ADDING COMPLETION ACTIONS

+       In  the  previous  section the ability to customize the behavior of the
+       only default completion action, complete-word, was described.  In  this
+       section  the  ability  to  install additional action functions, so that
+       different types of word completion  can  be  bound  to  different  key-
+       sequences,  is  described.  This  is  achieved  by using the gl_comple-
+       tion_action() function.
+
+
+         int gl_completion_action(GetLine *gl,
+                                  void *data, CplMatchFn *match_fn,
+                                  int list_only, const char *name,
+                                  const char *keyseq);
+
+
+       The data and match_fn  arguments  are  as  described  in  the  cpl_com-
+       plete_word  man  page, and specify the callback function that should be
+       invoked to  identify  possible  completions.   The  list_only  argument
+       determines  whether  the action that is being defined should attempt to
+       complete the word as far as possible in the input line before  display-
+       ing  any  possible  ambiguous  completions, or whether it should simply
+       display the list of possible completions  without  touching  the  input
+       line. The former option is selected by specifying a value of 0, and the
+       latter by specifying a value of 1. The name argument specifies the name
+       by  which  configuration  files and future invokations of this function
+       should refer to the action. This must either be the name of an existing
+       completion  action  to  be  changed,  or be a new unused name for a new
+       action. Finally, the keyseq argument specifies the default key-sequence
+       to  bind  the  action  to.  If this is NULL, no new keysequence will be
+       bound to the action.
+
+       Beware that in order for the user to be able to change the key-sequence
+       that  is  bound  to actions that are installed in this manner, when you
+       call gl_completion_action() to install a given  action  for  the  first
+       time,  you  should  do this between calling new_GetLine() and the first
+       call to gl_get_line().  Otherwise, when the user's  configuration  file
+       is  read on the first call to gl_get_line(), the name of the your addi-
+       tional action won't be known, and any reference to it in the configura-
+       tion file will generate an error.
+
+       As  discussed for gl_customize_completion(), if your callback function,
+       for some reason, needs to write anything to the terminal, it must  call
+       gl_normal_io() before doing so.
+
+
+

DEFINING CUSTOM ACTIONS

+       Although  the built-in key-binding actions are sufficient for the needs
+       of most applications, occasionally a specialized application  may  need
+       to  define  one  or  more custom actions, bound to application-specific
+       key-sequences. For example, a sales application would benefit from hav-
+       ing  a key-sequence that displayed the part name that corresponded to a
+       part number preceding the cursor. Such a feature is clearly beyond  the
+       scope  of the built-in action functions. So for such special cases, the
+       gl_register_action() function is provided.
+
+
+         int gl_register_action(GetLine *gl, void *data,
+                       GlActionFn *fn, const char *name,
+                       const char *keyseq);
+
+
+       This function lets the application register an external  function,  fn,
+       that  will  thereafter  be  called  whenever  either the specified key-
+       sequence, keyseq, is entered by the user, or the user enters any  other
+       key-sequence  that  the user subsequently binds to the specified action
+       name, name, in their configuration file. The data  argument  can  be  a
+       pointer  to  anything that the application wishes to have passed to the
+       action function, fn, whenever that function is invoked.
+
+       The action function, fn, should be declared using the following  macro,
+       which is defined in libtecla.h.
+
+
+         #define GL_ACTION_FN(fn) GlAfterAction (fn)(GetLine *gl, \
+                     void *data, int count, size_t curpos, \
+                     const char *line)
+
+
+       The  gl  and  data  arguments  are those that were previously passed to
+       gl_register_action() when the action function was registered. The count
+       argument  is a numeric argument which the user has the option of enter-
+       ing using the digit-argument action, before invoking the action. If the
+       user doesn't enter a number, then the count argument is set to 1. Nomi-
+       nally this argument is interpreted as a repeat count, meaning that  the
+       action  should  be  repeated  that many times. In practice however, for
+       some actions a repeat count makes little sense. In such cases,  actions
+       can  either  simply  ignore  the count argument, or use its value for a
+       different purpose.
+
+       A copy of the current input line is passed in the read-only line  argu-
+       ment.  The  current  cursor position within this string is given by the
+       index contained in the curpos argument. Note that  direct  manipulation
+       of  the  input  line  and the cursor position is not permitted. This is
+       because the rules dicated by various modes,  such  as  vi  mode  versus
+       emacs  mode,  no-echo mode, and insert mode versus overstrike mode etc,
+       make it too complex for an application writer  to  write  a  conforming
+       editing action, as well as constrain future changes to the internals of
+       gl_get_line(). A potential solution to this dilema would  be  to  allow
+       the  action  function  to  edit  the  line  using  the existing editing
+       actions. This is currently under consideration.
+
+       If the action function wishes to write text to  the  terminal,  without
+       this  getting  mixed  up  with the displayed text of the input line, or
+       read from the terminal without having to handle raw terminal I/O,  then
+       before  doing  either  of these operations, it must temporarily suspend
+       line editing by calling  the  gl_normal_io()  function.  This  function
+       flushes  any  pending  output  to the terminal, moves the cursor to the
+       start of the line that follows the last  terminal  line  of  the  input
+       line,  then  restores  the terminal to a state that is suitable for use
+       with the C stdio facilities. The latter includes such things as restor-
+       ing the normal mapping of \n to \r\n, and, when in server mode, restor-
+       ing the normal blocking form of terminal I/O. Having called this  func-
+       tion, the action function can read from and write to the terminal with-
+       out the fear of creating a mess.  It isn't  necessary  for  the  action
+       function to restore the original editing environment before it returns.
+       This is done automatically by gl_get_line() after the  action  function
+       returns.  The following is a simple example of an action function which
+       writes the sentence "Hello world" on a new terminal line after the line
+       being  edited. When this function returns, the input line is redrawn on
+       the line that follows the "Hello world" line, and line editing resumes.
+
+
+         static GL_ACTION_FN(say_hello_fn)
+         {
+           if(gl_normal_io(gl))   /* Temporarily suspend editing */
+             return GLA_ABORT;
+           printf("Hello world\n");
+           return GLA_CONTINUE;
+         }
+
+
+       Action  functions  must  return  one  of  the following values, to tell
+       gl_get_line() how to procede.
+
+
+         GLA_ABORT     -   Cause gl_get_line() to return NULL.
+         GLA_RETURN    -   Cause gl_get_line() to return the
+                           completed input line.
+         GLA_CONTINUE  -   Resume command-line editing.
+
+
+       Note that the name argument of gl_register_action() specifies the  name
+       by  which  a  user can refer to the action in their configuration file.
+       This allows them to re-bind the action to an alternate key-seqeunce. In
+       order  for  this  to work, it is necessary to call gl_register_action()
+       between calling new_GetLine() and the first call to gl_get_line().
+
+
+

HISTORY FILES

+       To save the contents of the history buffer before quitting your  appli-
+       cation,  and subsequently restore them when you next start the applica-
+       tion, the following functions are provided.
+
+
+        int gl_save_history(GetLine *gl, const char *filename,
+                            const char *comment, int max_lines);
+        int gl_load_history(GetLine *gl, const char *filename,
+                            const char *comment);
+
+
+       The filename argument specifies the name to give the history file  when
+       saving, or the name of an existing history file, when loading. This may
+       contain home-directory and environment variable  expressions,  such  as
+       "~/.myapp_history" or "$HOME/.myapp_history".
+
+       Along  with each history line, extra information about it, such as when
+       it was entered by the user, and what its nesting level is, is  recorded
+       as  a comment preceding the line in the history file. Writing this as a
+       comment allows the history file to double as a command  file,  just  in
+       case  you  wish  to replay a whole session using it. Since comment pre-
+       fixes differ in different languages, the comment argument  is  provided
+       for  specifying  the  comment  prefix. For example, if your application
+       were a unix shell, such as the bourne  shell,  you  would  specify  "#"
+       here.  Whatever  you choose for the comment character, you must specify
+       the same prefix to gl_load_history() that  you  used  when  you  called
+       gl_save_history() to write the history file.
+
+       The  max_lines  must be either -1 to specify that all lines in the his-
+       tory list be saved, or a positive number specifying a  ceiling  on  how
+       many of the most recent lines should be saved.
+
+       Both  fuctions return non-zero on error, after writing an error message
+       to stderr. Note that gl_load_history() does not consider the  non-exis-
+       tence of a file to be an error.
+
+
+

MULTIPLE HISTORY LISTS

+       If your application uses a single GetLine object for entering many dif-
+       ferent types of input lines, you may wish gl_get_line() to  distinguish
+       the different types of lines in the history list, and only recall lines
+       that match the current type  of  line.  To  support  this  requirement,
+       gl_get_line()  marks  lines  being recorded in the history list with an
+       integer identifier chosen by the application.  Initially  this  identi-
+       fier  is  set to 0 by new_GetLine(), but it can be changed subsequently
+       by calling gl_group_history().
+
+
+         int gl_group_history(GetLine *gl, unsigned id);
+
+
+       The integer identifier id can be any number chosen by the  application,
+       but  note  that  gl_save_history()  and  gl_load_history() preserve the
+       association between identifiers and historical input lines between pro-
+       gram  invokations,  so you should choose fixed identifiers for the dif-
+       ferent types of input line used by your application.
+
+       Whenever gl_get_line() appends a new input line to  the  history  list,
+       the  current  history  identifier  is  recorded with it, and when it is
+       asked to recall a historical input line, it only recalls lines that are
+       marked with the current identifier.
+
+
+

DISPLAYING HISTORY

+       The history list can be displayed by calling gl_show_history().
+
+
+         int gl_show_history(GetLine *gl, FILE *fp,
+                             const char *fmt,
+                             int all_groups,
+                             int max_lines);
+
+
+       This  displays  the  current  contents of the history list to the stdio
+       output stream fp. If the max_lines argument is greater than or equal to
+       zero,  then  no  more than this number of the most recent lines will be
+       displayed. If the all_groups argument is non-zero, lines from all  his-
+       tory  groups  are  displayed.  Otherwise  just  those  of the currently
+       selected history group are displayed. The format string argument,  fmt,
+       determines  how the line is displayed. This can contain arbitrary char-
+       acters which are written verbatim, interleaved with any of the  follow-
+       ing format directives:
+
+         %D  -  The date on which the line was originally
+                entered, formatted like 2001-11-20.
+         %T  -  The time of day when the line was entered,
+                formatted like 23:59:59.
+         %N  -  The sequential entry number of the line in
+                the history buffer.
+         %G  -  The number of the history group which the
+                line belongs to.
+         %%  -  A literal % character.
+         %H  -  The history line itself.
+
+       Thus a format string like "%D %T  %H0 would output something like:
+
+         2001-11-20 10:23:34  Hello world
+
+       Note  the  inclusion  of  an  explicit  newline character in the format
+       string.
+
+
+

LOOKING UP HISTORY

+       The gl_lookup_history() function allows the calling application to look
+       up lines in the history list.
+
+
+         typedef struct {
+           const char *line;    /* The requested historical */
+                                /*  line. */
+           unsigned group;      /* The history group to which */
+                                /*  the line belongs. */
+           time_t timestamp;    /* The date and time at which */
+                                /*  the line was originally */
+                                /*  entered. */
+         } GlHistoryLine;
+
+         int gl_lookup_history(GetLine *gl, unsigned long id,
+                               GlHistoryLine *hline);
+
+
+       The  id  argument indicates which line to look up, where the first line
+       that was entered in the history list after new_GetLine() was called, is
+       denoted  by  0, and subsequently entered lines are denoted with succes-
+       sively higher numbers. Note that the range of lines currently preserved
+       in the history list can be queried by calling the gl_range_of_history()
+       function, described later. If the requested  line  is  in  the  history
+       list,  the  details of the line are recorded in the variable pointed to
+       by the hline argument, and 1 is returned. Otherwise 0 is returned,  and
+       the variable pointed to by hline is left unchanged.
+
+       Beware  that  the string returned in hline->line is part of the history
+       buffer, so it must not be modified by the caller, and will be  recycled
+       on  the next call to any function that takes gl as its argument. There-
+       fore you should make a private copy of this string if you need to  keep
+       it around.
+
+
+

MANUAL HISTORY ARCHIVAL

+       By default, whenever a line is entered by the user, it is automatically
+       appended to the history list, just  before  gl_get_line()  returns  the
+       line  to  the  caller.  This is convenient for the majority of applica-
+       tions, but there are also applications that need finer grained  control
+       over  what gets added to the history list. In such cases, the automatic
+       addition of entered lines to the history list  can  be  turned  off  by
+       calling the gl_automatic_history() function.
+
+
+         int gl_automatic_history(GetLine *gl, int enable);
+
+
+       If  this  function  is  called  with  its  enable  argument  set  to 0,
+       gl_get_line() won't automatically archive subsequently  entered  lines.
+       Automatic  archiving  can be reenabled at a later time, by calling this
+       function again, with its enable argument set  to  1.   While  automatic
+       history  archiving  is  disabled,  the  calling application can use the
+       gl_append_history() to append lines to the history list as needed.
+
+
+         int gl_append_history(GetLine *gl, const char *line);
+
+
+       The line argument specifies the line to be added to the  history  list.
+       This  must  be  a normal ' ' terminated string. If this string contains
+       any newline characters, the line that gets archived in the history list
+       will  be  terminated by the first of these. Otherwise it will be termi-
+       nated by the ' ' terminator.  If the line is longer  than  the  maximum
+       input  line  length,  that was specified when new_GetLine() was called,
+       when the line  is  recalled,  it  will  get  truncated  to  the  actual
+       gl_get_line() line length.
+
+       If successful, gl_append_history() returns 0. Otherwise it returns non-
+       zero, and sets errno to one of the following values.
+
+
+          EINVAL  -  One of the arguments passed to
+                     gl_append_history() was NULL.
+          ENOMEM  -  The specified line was longer than the allocated
+                     size of the history buffer (as specified when
+                     new_GetLine() was called), so it couldn't be
+                     archived.
+
+
+       A textual description of the error can optionally be obtained by  call-
+       ing gl_error_message(). Note that after such an error, the history list
+       remains in a valid state to receive new history lines, so there is lit-
+       tle harm in simply ignoring the return status of gl_append_history().
+
+
+

MISCELLANEOUS HISTORY CONFIGURATION

+       If  you  wish  to change the size of the history buffer that was origi-
+       nally specified in the call to new_GetLine(), you can do  so  with  the
+       gl_resize_history() function.
+
+
+         int gl_resize_history(GetLine *gl, size_t histlen);
+
+
+       The  histlen argument specifies the new size in bytes, and if you spec-
+       ify this as 0, the buffer will be deleted.
+
+       As mentioned in the discussion of new_GetLine(), the  number  of  lines
+       that can be stored in the history buffer, depends on the lengths of the
+       individual lines. For example, a 1000 byte buffer could  equally  store
+       10  lines  of average length 100 bytes, or 2 lines of average length 50
+       bytes. Although the buffer is never expanded when new lines are  added,
+       a  list  of  pointers  into the buffer does get expanded when needed to
+       accomodate the number of lines currently stored in the buffer. To place
+       an upper limit on the number of lines in the buffer, and thus a ceiling
+       on  the  amount  of  memory  used  in  this  list,  you  can  call  the
+       gl_limit_history() function.
+
+
+         void gl_limit_history(GetLine *gl, int max_lines);
+
+
+       The  max_lines  should  either be a positive number >= 0, specifying an
+       upper limit on the number of lines in the buffer, or be  -1  to  cancel
+       any  previously  specified  limit.  When a limit is in effect, only the
+       max_lines most recently appended lines are kept in  the  buffer.  Older
+       lines are discarded.
+
+       To  discard  lines  from the history buffer, use the gl_clear_history()
+       function.
+
+         void gl_clear_history(GetLine *gl, int all_groups);
+
+       The all_groups argument tells the function whether to delete  just  the
+       lines  associated  with  the  current  history group (see gl_group_his-
+       tory()), or all historical lines in the buffer.
+
+       The gl_toggle_history() function allows you to toggle  history  on  and
+       off without losing the current contents of the history list.
+
+
+         void gl_toggle_history(GetLine *gl, int enable);
+
+
+       Setting  the  enable argument to 0 turns off the history mechanism, and
+       setting it to 1 turns it back on. When history is turned  off,  no  new
+       lines  will  be added to the history list, and history lookup key-bind-
+       ings will act as though there is nothing in the history buffer.
+
+
+

QUERYING HISTORY INFORMATION

+       The configured state of the  history  list  can  be  queried  with  the
+       gl_history_state() function.
+
+
+         typedef struct {
+           int enabled;     /* True if history is enabled */
+           unsigned group;  /* The current history group */
+           int max_lines;   /* The current upper limit on the */
+                            /*  number of lines in the history */
+                            /*  list, or -1 if unlimited. */
+         } GlHistoryState;
+
+         void gl_state_of_history(GetLine *gl,
+                                  GlHistoryState *state);
+
+       On  return,  the status information is recorded in the variable pointed
+       to by the state argument.
+
+       The gl_range_of_history() function returns  the  number  and  range  of
+       lines in the history list.
+
+
+       typedef struct {
+         unsigned long oldest;  /* The sequential entry number */
+                                /*  of the oldest line in the */
+                                /*  history list. */
+         unsigned long newest;  /* The sequential entry number */
+                                /*  of the newest line in the */
+                                /*  history list. */
+         int nlines;            /* The number of lines in the */
+                                /*  history list. */
+       } GlHistoryRange;
+
+       void gl_range_of_history(GetLine *gl, GlHistoryRange *range);
+
+       The  return values are recorded in the variable pointed to by the range
+       argument. If the nlines member of this structure is greater than  zero,
+       then  the  oldest  and  newest members report the range of lines in the
+       list, and newest=oldest+nlines-1.  Otherwise they are both zero.
+
+       The gl_size_of_history() function returns the total size of the history
+       buffer and the amount of the buffer that is currently occupied.
+
+         typedef struct {
+           size_t size;      /* The size of the history buffer */
+                             /*  (bytes). */
+           size_t used;      /* The number of bytes of the */
+                             /*  history buffer that are */
+                             /*  currently occupied. */
+         } GlHistorySize;
+
+         void gl_size_of_history(GetLine *gl, GlHistorySize *size);
+
+       On  return, the size information is recorded in the variable pointed to
+       by the size argument.
+
+
+

CHANGING TERMINALS

+       The new_GetLine() constructor function assumes that input is to be read
+       from stdin, and output written to stdout. The following function allows
+       you to switch to different input and output streams.
+
+         int gl_change_terminal(GetLine *gl, FILE *input_fp,
+                                FILE *output_fp, const char *term);
+
+       The gl argument is the object that was returned by new_GetLine().   The
+       input_fp  argument  specifies  the  stream  to read from, and output_fp
+       specifies the stream to be written to. Only if both of these refer to a
+       terminal,  will  interactive  terminal  input  be  enabled.   Otherwise
+       gl_get_line() will simply call fgets() to read command input.  If  both
+       streams refer to a terminal, then they must refer to the same terminal,
+       and the type of this terminal must be specified via the term  argument.
+       The value of the term argument is looked up in the terminal information
+       database (terminfo or termcap), in order  to  determine  which  special
+       control  sequences  are needed to control various aspects of the termi-
+       nal.  new_GetLine()  for  example,   passes   the   return   value   of
+       getenv("TERM")  in  this argument. Note that if one or both of input_fp
+       and output_fp don't refer to a terminal, then it is legal to pass  NULL
+       instead of a terminal type.
+
+       Note that if you want to pass file descriptors to gl_change_terminal(),
+       you can do this by creating  stdio  stream  wrappers  using  the  POSIX
+       fdopen() function.
+
+
+

EXTERNAL EVENT HANDLING

+       By  default, gl_get_line() doesn't return until either a complete input
+       line has been entered by the user, or an error occurs. In programs that
+       need  to  watch for I/O from other sources than the terminal, there are
+       two options.
+
+
+         1. Use the functions described in the
+            gl_io_mode man page to switch
+            gl_get_line() into non-blocking server mode. In this mode,
+            gl_get_line() becomes a non-blocking, incremental
+            line-editing function that can safely be called from
+            an external event loop. Although this is a very
+            versatile method, it involves taking on some
+            responsibilities that are normally performed behind
+            the scenes by gl_get_line().
+
+         2. While gl_get_line() is waiting for keyboard
+            input from the user, you can ask it to also watch for
+            activity on arbitrary file descriptors, such as
+            network sockets, pipes etc, and have it call functions
+            of your choosing when activity is seen. This works on
+            any system that has the select() system call,
+            which is most, if not all flavors of unix.
+
+
+       Registering a file descriptor to be watched by  gl_get_line()  involves
+       calling the gl_watch_fd() function.
+
+
+         int gl_watch_fd(GetLine *gl, int fd, GlFdEvent event,
+                         GlFdEventFn *callback, void *data);
+
+
+       If  this returns non-zero, then it means that either your arguments are
+       invalid, or that this facility isn't supported on the host system.
+
+       The fd argument is the file descriptor to be watched. The  event  argu-
+       ment  specifies  what  type of activity is of interest, chosen from the
+       following enumerated values:
+
+
+         GLFD_READ   -  Watch for the arrival of data to be read.
+         GLFD_WRITE  -  Watch for the ability to write to the file
+                        descriptor without blocking.
+         GLFD_URGENT -  Watch for the arrival of urgent
+                        out-of-band data on the file descriptor.
+
+
+       The callback argument  is  the  function  to  call  when  the  selected
+       activity  is seen. It should be defined with the following macro, which
+       is defined in libtecla.h.
+
+
+         #define GL_FD_EVENT_FN(fn) GlFdStatus (fn)(GetLine *gl, \
+                                             void *data, int fd, \
+                                             GlFdEvent event)
+
+       The data argument of the gl_watch_fd() function is passed to the  call-
+       back  function  for  its  own  use, and can point to anything you like,
+       including NULL. The file descriptor and the  event  argument  are  also
+       passed  to  the callback function, and this potentially allows the same
+       callback function to be registered to  more  than  one  type  of  event
+       and/or  more than one file descriptor. The return value of the callback
+       function should be one of the following values.
+
+
+         GLFD_ABORT    -  Tell gl_get_line() to abort. When this
+                          happens, gl_get_line() returns
+                          NULL, and a following call to
+                          gl_return_status() will return
+                          GLR_FDABORT. Note that if the
+                          application needs errno always to
+                          have a meaningful value when
+                          gl_get_line() returns NULL,
+                          the callback function should set
+                          errno appropriately.
+         GLFD_REFRESH  -  Redraw the input line then continue
+                          waiting for input. Return this if
+                          your callback wrote to the terminal.
+         GLFD_CONTINUE -  Continue to wait for input, without
+                          redrawing the line.
+
+       Note that before calling the callback, gl_get_line() blocks  most  sig-
+       nals,  and  leaves its own signal handlers installed, so if you need to
+       catch a particular signal you will need  to  both  temporarily  install
+       your  own  signal  handler, and unblock the signal. Be sure to re-block
+       the signal (if it was originally blocked) and  reinstate  the  original
+       signal handler, if any, before returning.
+
+
+
+       If  the  callback  function  needs to read or write to the terminal, it
+       should ideally first call gl_normal_io(gl) to temporarily suspend  line
+       editing.  This  will  restore  the terminal to canonical, blocking-I/O,
+       mode, and move the cursor to the start of a new terminal  line.  Later,
+       when  the  callback  returns,  gl_get_line()  will  notice that gl_nor-
+       mal_io() was called, redisplay the input line and resume editing.  Note
+       that in this case the return values, GLFD_REFRESH and GLFD_CONTINUE are
+       equivalent.
+
+
+
+       To support cases where the callback function calls a third-party  func-
+       tion  which  occasionally and u0prisicre-enabledesbeforee themicallback
+       automatic conversion of "0 to "
+       function  is called. If the callack knows that the third-party function
+       wrote to the terminal, it should then return  the  GLFD_REFRESH  return
+       value, to tell gl_get_line() to redisplay the input line.
+
+
+
+       To  remove  a  callback  function  that you previously registered for a
+       given file descriptor and event, simply  call  gl_watch_fd()  with  the
+       same  file descriptor and event arguments, but with a callback argument
+       of 0. The data argument is ignored in this case.
+
+
+

SETTING AN INACTIVITY TIMEOUT

+       On systems with the select() system call,  the  gl_inactivity_timeout()
+       function can be used to set or cancel an inactivity timeout. Inactivity
+       in this case refers both to keyboard input, and  to  I/O  on  any  file
+       descriptors  registered by prior and subsequent calls to gl_watch_fd().
+       On oddball systems that don't have select(), this call has no effect.
+
+
+         int gl_inactivity_timeout(GetLine *gl, GlTimeoutFn *callback,
+                            void *data, unsigned long sec,
+                            unsigned long nsec);
+
+
+       The timeout is specified in the form of an integral number  of  seconds
+       and  an  integral number of nanoseconds, via the sec and nsec arguments
+       respectively. Subsequently, whenever no activity is seen for this  time
+       period, the function specified via the callback argument is called. The
+       data argument of gl_inactivity_timeout() is  passed  verbatim  to  this
+       callback  function whenever it is invoked, and can thus be used to pass
+       arbitrary application-specific information to the callback. The follow-
+       ing  macro is provided in libtecla.h for applications to use to declare
+       and prototype timeout callback functions.
+
+
+         #define GL_TIMEOUT_FN(fn) \
+                      GlAfterTimeout (fn)(GetLine *gl, void *data)
+
+
+       On returning, the application's callback is expected to return  one  of
+       the  following  enumerators  to tell gl_get_line() how to procede after
+       the timeout has been handled by the callback.
+
+
+         GLTO_ABORT    -  Tell gl_get_line() to abort. When
+                          this happens, gl_get_line() will
+                          return NULL, and a following call
+                          to gl_return_status() will return
+                          GLR_TIMEOUT. Note that if the
+                          application needs errno always to
+                          have a meaningful value when
+                          gl_get_line() returns NULL,
+                          the callback function should set
+                          errno appropriately.
+         GLTO_REFRESH  -  Redraw the input line, then continue
+                          waiting for input. You should return
+                          this value if your callback wrote to the
+                          terminal without having first called
+                          gl_normal_io(gl).
+         GLTO_CONTINUE -  In normal blocking-I/O mode, continue to
+                          wait for input, without redrawing the
+                          user's input line.
+                          In non-blocking server I/O mode (see
+                          gl_io_mode), cause gl_get_line()
+                          to act as though I/O blocked. This means
+                          that gl_get_line() will immediately
+                          return NULL, and a following call
+                          to gl_return_status() will return
+                          GLR_BLOCKED.
+
+
+       Note that before calling the callback, gl_get_line() blocks  most  sig-
+       nals,  and  leaves its own signal handlers installed, so if you need to
+       catch a particular signal you will need  to  both  temporarily  install
+       your  own  signal  handler, and unblock the signal. Be sure to re-block
+       the signal (if it was originally blocked) and  reinstate  the  original
+       signal handler, if any, before returning.
+
+
+
+       If  the  callback  function  needs to read or write to the terminal, it
+       should ideally first call gl_normal_io(gl) to temporarily suspend  line
+       editing.  This  will  restore  the terminal to canonical, blocking-I/O,
+       mode, and move the cursor to the start of a new terminal  line.  Later,
+       when  the  callback  returns,  gl_get_line()  will  notice that gl_nor-
+       mal_io() was called, redisplay the input line and resume editing.  Note
+       that in this case the return values, GLTO_REFRESH and GLTO_CONTINUE are
+       equivalent.
+
+
+
+       To support cases where the callback function calls a third-party  func-
+       tion  which  occasionally and u0prisicre-enabledesbeforee themicallback
+       automatic conversion of "0 to "
+       function  is called. If the callack knows that the third-party function
+       wrote to the terminal, it should then return  the  GLTO_REFRESH  return
+       value, to tell gl_get_line() to redisplay the input line.
+
+
+
+       Note  that  although the timeout argument includes a nano-second compo-
+       nent, few computer clocks presently have  resolutions  that  are  finer
+       than  a few milliseconds, so asking for less than a few milliseconds is
+       equivalent to requesting zero seconds on a  lot  of  systems.  If  this
+       would  be  a  problem,  you  should  base your timeout selection on the
+       actual   resolution   of   the   host    clock    (eg.    by    calling
+       sysconf(_SC_CLK_TCK)).
+
+
+
+       To  turn off timeouts, simply call gl_inactivity_timeout() with a call-
+       back argument of 0. The data argument is ignored in this case.
+
+
+

SIGNAL HANDLING DEFAULTS

+       By default, the gl_get_line() function intercepts a number of  signals.
+       This  is particularly important for signals which would by default ter-
+       minate the process, since the terminal needs to be restored to a usable
+       state  before  this  happens.  In  this  section,  the signals that are
+       trapped  by  default,  and  how  gl_get_line()  responds  to  them,  is
+       described.  Changing  these defaults is the topic of the following sec-
+       tion.
+
+       When the following subset of signals are  caught,  gl_get_line()  first
+       restores  the  terminal  settings  and signal handling to how they were
+       before gl_get_line() was called, resends the signal, to allow the call-
+       ing  application's  signal  handlers  to handle it, then if the process
+       still exists, gl_get_line() returns NULL and sets  errno  as  specified
+       below.
+
+
+        SIGINT  -  This signal is generated both by the keyboard
+                   interrupt key (usually ^C), and the keyboard
+                   break key.
+
+                   errno=EINTR
+
+        SIGHUP  -  This signal is generated when the controlling
+                   terminal exits.
+
+                   errno=ENOTTY
+
+        SIGPIPE -  This signal is generated when a program attempts
+                   to write to a pipe who's remote end isn't being
+                   read by any process. This can happen for example
+                   if you have called gl_change_terminal() to
+                   redirect output to a pipe hidden under a pseudo
+                   terminal.
+
+                   errno=EPIPE
+
+        SIGQUIT -  This signal is generated by the keyboard quit
+                   key (usually ^\).
+
+                   errno=EINTR
+
+        SIGABRT -  This signal is generated by the standard C,
+                   abort() function. By default it both
+                   terminates the process and generates a core
+                   dump.
+
+                   errno=EINTR
+
+        SIGTERM -  This is the default signal that the UN*X
+                   kill command sends to processes.
+
+                   errno=EINTR
+
+       Note  that in the case of all of the above signals, POSIX mandates that
+       by default the process is terminated, with the addition of a core  dump
+       in  the  case  of  the  SIGQUIT  signal. In other words, if the calling
+       application doesn't override the default handler by supplying  its  own
+       signal  handler, receipt of the corresponding signal will terminate the
+       application before gl_get_line() returns.
+
+       If gl_get_line() aborts with errno set to EINTR, you can find out  what
+       signal caused it to abort, by calling the following function.
+
+         int gl_last_signal(const GetLine *gl);
+
+       This  returns the numeric code (eg. SIGINT) of the last signal that was
+       received during the most recent call to gl_get_line(), or -1 if no sig-
+       nals were received.
+
+       On  systems  that support it, when a SIGWINCH (window change) signal is
+       received, gl_get_line() queries the terminal to find out its new  size,
+       redraws the current input line to accomodate the new size, then returns
+       to waiting for keyboard input from the user. Unlike other signals, this
+       signal isn't resent to the application.
+
+       Finally, the following signals cause gl_get_line() to first restore the
+       terminal  and  signal  environment  to  that  which  prevailed   before
+       gl_get_line() was called, then resend the signal to the application. If
+       the process still exists after the  signal  has  been  delivered,  then
+       gl_get_line() then re-establishes its own signal handlers, switches the
+       terminal back to raw mode, redisplays the input line, and goes back  to
+       awaiting terminal input from the user.
+
+        SIGCONT    -  This signal is generated when a suspended
+                      process is resumed.
+
+        SIGPOLL    -  On SVR4 systems, this signal notifies the
+                      process of an asynchronous I/O event. Note
+                      that under 4.3+BSD, SIGIO and SIGPOLL are
+                      the same. On other systems, SIGIO is ignored
+                      by default, so gl_get_line() doesn't
+                      trap it by default.
+
+        SIGPWR     -  This signal is generated when a power failure
+                      occurs (presumably when the system is on a
+                      UPS).
+
+        SIGALRM    -  This signal is generated when a timer
+                      expires.
+
+        SIGUSR1    -  An application specific signal.
+
+        SIGUSR2    -  Another application specific signal.
+
+        SIGVTALRM  -  This signal is generated when a virtual
+                      timer expires (see man setitimer(2)).
+
+        SIGXCPU    -  This signal is generated when a process
+                      exceeds its soft CPU time limit.
+
+        SIGXFSZ    -  This signal is generated when a process
+                      exceeds its soft file-size limit.
+
+        SIGTSTP    -  This signal is generated by the terminal
+                      suspend key, which is usually ^Z, or the
+                      delayed terminal suspend key, which is
+                      usually ^Y.
+
+        SIGTTIN    -  This signal is generated if the program
+                      attempts to read from the terminal while the
+                      program is running in the background.
+
+        SIGTTOU    -  This signal is generated if the program
+                      attempts to write to the terminal while the
+                      program is running in the background.
+
+
+       Obviously not all of the above signals are supported on all systems, so
+       code to support them is conditionally compiled into the tecla  library.
+
+       Note  that  if SIGKILL or SIGPOLL, which by definition can't be caught,
+       or any of the hardware generated exception signals,  such  as  SIGSEGV,
+       SIGBUS  and  SIGFPE, are received and unhandled while gl_get_line() has
+       the terminal in raw mode, the program will be  terminated  without  the
+       terminal  having been restored to a usable state. In practice, job-con-
+       trol shells usually reset the terminal settings when a  process  relin-
+       quishes  the controlling terminal, so this is only a problem with older
+       shells.
+
+
+

CUSTOMIZED SIGNAL HANDLING

+       The previous section listed the signals  that  gl_get_line()  traps  by
+       default,  and described how it responds to them. This section describes
+       how to both add and remove signals from the list  of  trapped  signals,
+       and  how to specify how gl_get_line() should respond to a given signal.
+
+       If you don't need gl_get_line() to do anything in response to a  signal
+       that  it  normally  traps, you can tell to gl_get_line() to ignore that
+       signal by calling gl_ignore_signal().
+
+         int gl_ignore_signal(GetLine *gl, int signo);
+
+       The signo argument is the number of the signal (eg.  SIGINT)  that  you
+       want  to  have  ignored. If the specified signal isn't currently one of
+       those being trapped, this function does nothing.
+
+       The gl_trap_signal() function allows you to either add a new signal  to
+       the  list that gl_get_line() traps, or modify how it responds to a sig-
+       nal that it already traps.
+
+         int gl_trap_signal(GetLine *gl, int signo, unsigned flags,
+                            GlAfterSignal after, int errno_value);
+
+       The signo argument is the number of the signal that you  wish  to  have
+       trapped. The flags argument is a set of flags which determine the envi-
+       ronment in which the application's signal handler is invoked, the after
+       argument  tells gl_get_line() what to do after the application's signal
+       handler returns, and errno_value tells gl_get_line() what to set  errno
+       to if told to abort.
+
+       The  flags  argument  is  a bitwise OR of zero or more of the following
+       enumerators:
+
+         GLS_RESTORE_SIG  -  Restore the caller's signal
+                             environment while handling the
+                             signal.
+
+         GLS_RESTORE_TTY  -  Restore the caller's terminal settings
+                             while handling the signal.
+
+         GLS_RESTORE_LINE -  Move the cursor to the start of the
+                             line following the input line before
+                             invoking the application's signal
+                             handler.
+
+         GLS_REDRAW_LINE  -  Redraw the input line when the
+                             application's signal handler returns.
+
+         GLS_UNBLOCK_SIG  -  Normally, if the calling program has
+                             a signal blocked (man sigprocmask),
+                             gl_get_line() does not trap that
+                             signal. This flag tells gl_get_line()
+                             to trap the signal and unblock it for
+                             the duration of the call to
+                             gl_get_line().
+
+         GLS_DONT_FORWARD -  If this flag is included, the signal
+                             will not be forwarded to the signal
+                             handler of the calling program.
+
+       Two commonly useful flag combinations are also enumerated as follows:
+
+         GLS_RESTORE_ENV   = GLS_RESTORE_SIG | GLS_RESTORE_TTY |
+                             GLS_REDRAW_LINE
+
+         GLS_SUSPEND_INPUT = GLS_RESTORE_ENV | GLS_RESTORE_LINE
+
+
+       If your signal handler, or the default system signal handler  for  this
+       signal, if you haven't overridden it, never either writes to the termi-
+       nal, nor suspends or terminates  the  calling  program,  then  you  can
+       safely set the flags argument to 0.
+
+       If your signal handler always writes to the terminal, reads from it, or
+       suspends or terminates the program, you should specify the flags  argu-
+       ment as GL_SUSPEND_INPUT, so that:
+
+       1. The cursor doesn't get left in the middle of the input
+          line.
+       2. So that the user can type in input and have it echoed.
+       3. So that you don't need to end each output line with
+          \r\n, instead of just \n.
+
+       The  GL_RESTORE_ENV combination is the same as GL_SUSPEND_INPUT, except
+       that it doesn't move the cursor, and if  your  signal  handler  doesn't
+       read  or write anything to the terminal, the user won't see any visible
+       indication that a signal was caught. This can be useful if you  have  a
+       signal  handler  that  only  occasionally writes to the terminal, where
+       using GL_SUSPEND_LINE would cause the input line  to  be  unnecessarily
+       duplicated  when nothing had been written to the terminal.  Such a sig-
+       nal handler, when it does write to the  terminal,  should  be  sure  to
+       start a new line at the start of its first write, by writing a new line
+       before returning. If the signal arrives while the user  is  entering  a
+       line  that only occupies a signal terminal line, or if the cursor is on
+       the last terminal line of a longer input line, this will have the  same
+       effect  as  GL_SUSPEND_INPUT. Otherwise it will start writing on a line
+       that already contains part of the displayed input line.   This  doesn't
+       do any harm, but it looks a bit ugly, which is why the GL_SUSPEND_INPUT
+       combination is better if you know that you are always going to be writ-
+       ting to the terminal.
+
+       The  after argument, which determines what gl_get_line() does after the
+       application's signal handler returns (if it returns), can take any  one
+       of the following values:
+
+         GLS_RETURN   - Return the completed input line, just as
+                        though the user had pressed the return
+                        key.
+
+         GLS_ABORT    - Cause gl_get_line() to abort. When
+                        this happens, gl_get_line() returns
+                        NULL, and a following call to
+                        gl_return_status() will return
+                        GLR_SIGNAL. Note that if the
+                        application needs errno always to
+                        have a meaningful value when
+                        gl_get_line() returns NULL,
+                        the callback function should set
+                        errno appropriately.
+         GLS_CONTINUE - Resume command line editing.
+
+       The  errno_value argument is intended to be combined with the GLS_ABORT
+       option, telling gl_get_line() what to set the standard  errno  variable
+       to  before returning NULL to the calling program. It can also, however,
+       be used with the GL_RETURN option, in case you wish to have  a  way  to
+       distinguish  between  an  input  line that was entered using the return
+       key, and one that was entered by the receipt of a signal.
+
+
+

RELIABLE SIGNAL HANDLING

+       Signal handling is suprisingly hard to do reliably without race  condi-
+       tions.  In gl_get_line() a lot of care has been taken to allow applica-
+       tions to perform reliable signal handling  around  gl_get_line().  This
+       section explains how to make use of this.
+
+       As  an  example of the problems that can arise if the application isn't
+       written correctly, imagine that one's application has a  SIGINT  signal
+       handler that sets a global flag. Now suppose that the application tests
+       this flag just before invoking gl_get_line(). If a SIGINT  signal  hap-
+       pens  to  be received in the small window of time between the statement
+       that tests the value  of  this  flag,  and  the  statement  that  calls
+       gl_get_line(), then gl_get_line() will not see the signal, and will not
+       be interrupted. As a result, the application won't be able  to  respond
+       to  the  signal  until  the  user gets around to finishing entering the
+       input line and gl_get_line() returns.  Depending  on  the  application,
+       this  might  or might not be a disaster, but at the very least it would
+       puzzle the user.
+
+       The way to avoid such problems is to do the following.
+
+       1. If needed, use the gl_trap_signal() function to
+          configure gl_get_line() to abort when important
+          signals are caught.
+
+       2. Configure gl_get_line() such that if any of the
+          signals that it catches are blocked when
+          gl_get_line() is called, they will be unblocked
+          automatically during times when gl_get_line() is
+          waiting for I/O. This can be done either
+          on a per signal basis, by calling the
+          gl_trap_signal() function, and specifying the
+          GLS_UNBLOCK attribute of the signal, or globally by
+          calling the gl_catch_blocked() function.
+
+
+            void gl_catch_blocked(GetLine *gl);
+
+
+          This function simply adds the GLS_UNBLOCK attribute
+          to all of the signals that it is currently configured to
+          trap.
+
+       3. Just before calling gl_get_line(), block delivery
+          of all of the signals that gl_get_line() is
+          configured to trap. This can be done using the POSIX
+          sigprocmask() function in conjunction with the
+          gl_list_signals() function.
+
+
+             int gl_list_signals(GetLine *gl, sigset_t *set);
+
+
+          This function returns the set of signals that it is
+          currently configured to catch in the set argument,
+          which is in the form required by sigprocmask().
+
+       4. In the example, one would now test the global flag that
+          the signal handler sets, knowing that there is now no
+          danger of this flag being set again until
+          gl_get_line() unblocks its signals while performing
+          I/O.
+
+       5. Eventually gl_get_line() returns, either because
+          a signal was caught, an error occurred, or the user
+          finished entering their input line.
+
+       6. Now one would check the global signal flag again, and if
+          it is set, respond to it, and zero the flag.
+
+       7. Use sigprocmask() to unblock the signals that were
+          blocked in step 3.
+
+       The same technique can be used around certain POSIX signal-aware  func-
+       tions,  such  as  sigsetjmp()  and sigsuspend(), and in particular, the
+       former of these two functions can be  used  in  conjunction  with  sig-
+       longjmp() to implement race-condition free signal handling around other
+       long-running system calls. The way to do this, is  explained  next,  by
+       showing how gl_get_line() manages to reliably trap signals around calls
+       to functions like read() and select() without race conditions.
+
+       The first thing that gl_get_line() does, whenever it is called,  is  to
+       use  the  POSIX  sigprocmask() function to block the delivery of all of
+       the signals that it is currently configured to catch. This is redundant
+       if  the  application  has already blocked them, but it does no harm. It
+       undoes this step just before returning.
+
+       Whenever gl_get_line() needs to call read() or  select()  to  wait  for
+       input  from  the  user,  it first calls the POSIX sigsetjmp() function,
+       being sure to specify a non-zero value for its savesigs argument.   The
+       reason for the latter argument will become clear shortly.
+
+       If sigsetjmp() returns zero, gl_get_line() then does the following.
+
+
+       a. It uses the POSIX sigaction() function to register
+          a temporary signal handler to all of the signals that it
+          is configured to catch. This signal handler does two
+          things.
+
+          1. It records the number of the signal that was received
+             in a file-scope variable.
+
+          2. It then calls the POSIX siglongjmp()
+             function using the buffer that was passed to
+             sigsetjmp() for its first argument, and
+             a non-zero value for its second argument.
+
+          When this signal handler is registered, the sa_mask
+          member of the struct sigaction act argument of the
+          call to sigaction() is configured to contain all of
+          the signals that gl_get_line() is catching. This
+          ensures that only one signal will be caught at once by
+          our signal handler, which in turn ensures that multiple
+          instances of our signal handler don't tread on each
+          other's toes.
+
+       b. Now that the signal handler has been set up,
+          gl_get_line() unblocks all of the signals that it
+          is configured to catch.
+
+       c. It then calls the read() or select() system
+          calls to wait for keyboard input.
+
+       d. If this system call returns (ie. no signal is received),
+          gl_get_line() blocks delivery of the signals of
+          interest again.
+
+       e. It then reinstates the signal handlers that were
+          displaced by the one that was just installed.
+
+
+       Alternatively,  if sigsetjmp() returns non-zero, this means that one of
+       the signals being trapped was caught while the above steps were execut-
+       ing. When this happens, gl_get_line() does the following.
+
+       First,  note  that  when  a  call to siglongjmp() causes sigsetjmp() to
+       return, provided that the savesigs argument  of  sigsetjmp()  was  non-
+       zero, as specified above, the signal process mask is restored to how it
+       was when sigsetjmp() was  called.  This  is  the  important  difference
+       between  sigsetjmp()  and  the  older  problematic setjmp(), and is the
+       essential ingredient that makes it possible to  avoid  signal  handling
+       race  conditions.   Because  of  this we are guaranteed that all of the
+       signals that we blocked before calling sigsetjmp() are blocked again as
+       soon  as any signal is caught. The following statements, which are then
+       executed, are thus guaranteed to be executed without any  further  sig-
+       nals being caught.
+
+       1. If so instructed by the gl_get_line() configuration
+          attributes of the signal that was caught,
+          gl_get_line() restores the terminal attributes to
+          the state that they had when gl_get_line() was
+          called. This is particularly important for signals that
+          suspend or terminate the process, since otherwise the
+          terminal would be left in an unusable state.
+
+       2. It then reinstates the application's signal handlers.
+
+       3. Then it uses the C standard-library raise()
+          function to re-send the application the signal that
+          was caught.
+
+       3. Next it unblocks delivery of the signal that we just
+          sent. This results in the signal that was just sent
+          via raise(), being caught by the application's
+          original signal handler, which can now handle it as it
+          sees fit.
+
+       4. If the signal handler returns (ie. it doesn't terminate
+          the process), gl_get_line() blocks delivery of the
+          above signal again.
+
+       5. It then undoes any actions performed in the first of the
+          above steps, and redisplays the line, if the signal
+          configuration calls for this.
+
+       6. gl_get_line() then either resumes trying to
+          read a character, or aborts, depending on the
+          configuration of the signal that was caught.
+
+       What  the above steps do in essence is to take asynchronously delivered
+       signals and handle them synchronously, one at a time, at a point in the
+       code where gl_get_line() has complete control over its environment.
+
+
+

THE TERMINAL SIZE

+       On  most  systems  the combination of the TIOCGWINSZ ioctl and the SIG-
+       WINCH signal is used to maintain an accurate idea of the terminal size.
+       The  terminal  size  is  newly queried every time that gl_get_line() is
+       called and whenever a SIGWINCH signal is received.
+
+       On the few systems where this mechanism  isn't  available,  at  startup
+       new_GetLine()  first  looks for the LINES and COLUMNS environment vari-
+       ables.  If these aren't found, or they contain unusable values, then if
+       a  terminal information database like terminfo or termcap is available,
+       the default size of the terminal is looked up in this database. If this
+       too fails to provide the terminal size, a default size of 80 columns by
+       24 lines is used.
+
+       Even on systems that do support ioctl(TIOCGWINSZ), if the  terminal  is
+       on the other end of a serial line, the terminal driver generally has no
+       way of detecting when a resize occurs or of querying what  the  current
+       size  is.  In  such  cases  no SIGWINCH is sent to the process, and the
+       dimensions returned by ioctl(TIOCGWINSZ) aren't correct. The  only  way
+       to  handle  such  instances is to provide a way for the user to enter a
+       command that tells the remote system what the new size is. This command
+       would  then  call the gl_set_term_size() function to tell gl_get_line()
+       about the change in size.
+
+
+         int gl_set_term_size(GetLine *gl, int ncolumn, int nline);
+
+
+       The ncolumn and nline arguments are used to specify the new  dimensions
+       of  the  terminal, and must not be less than 1. On systems that do sup-
+       port ioctl(TIOCGWINSZ), this function first calls ioctl(TIOCSWINSZ)  to
+       tell  the  terminal  driver  about  the change in size. In non-blocking
+       server-I/O mode, if a line is currently being input, the input line  is
+       then redrawn to accomodate the changed size. Finally the new values are
+       recorded in gl for future use by gl_get_line().
+
+       The gl_terminal_size() function allows you to query the current size of
+       the  terminal,  and  install an alternate fallback size for cases where
+       the size isn't available.  Beware  that  the  terminal  size  won't  be
+       available  if  reading from a pipe or a file, so the default values can
+       be important even on systems that do support ways of  finding  out  the
+       terminal size.
+
+         typedef struct {
+           int nline;        /* The terminal has nline lines */
+           int ncolumn;      /* The terminal has ncolumn columns */
+         } GlTerminalSize;
+
+         GlTerminalSize gl_terminal_size(GetLine *gl,
+                                         int def_ncolumn,
+                                         int def_nline);
+
+       This  function  first  updates gl_get_line()'s fallback terminal dimen-
+       sions, then records its findings in the return value.
+
+       The def_ncolumn and def_nline specify the default  number  of  terminal
+       columns  and  lines to use if the terminal size can't be determined via
+       ioctl(TIOCGWINSZ) or environment variables.
+
+
+

HIDING WHAT YOU TYPE

+       When entering sensitive information, such as passwords, it is best  not
+       to  have  the  text that you are entering echoed on the terminal.  Fur-
+       thermore, such text should not be recorded in the history  list,  since
+       somebody  finding  your  terminal  unattended  could then recall it, or
+       somebody snooping through your directories could see it in your history
+       file. With this in mind, the gl_echo_mode() function allows you to tog-
+       gle on and off the display and archival of  any  text  that  is  subse-
+       quently entered in calls to gl_get_line().
+
+
+         int gl_echo_mode(GetLine *gl, int enable);
+
+
+       The enable argument specifies whether entered text should be visible or
+       not. If it is 0, then subsequently entered lines will not be visible on
+       the terminal, and will not be recorded in the history list. If it is 1,
+       then subsequent input lines will be displayed as they are entered,  and
+       provided  that  history  hasn't  been  turned off via a call to gl_tog-
+       gle_history(), then they will also be archived  in  the  history  list.
+       Finally,  if  the  enable argument is -1, then the echoing mode is left
+       unchanged, which allows you to non-destructively query the current set-
+       ting  via the return value. In all cases, the return value of the func-
+       tion is 0 if echoing was disabled before the function was called, and 1
+       if it was enabled.
+
+       When  echoing  is  turned  off,  note that although tab completion will
+       invisibly complete your prefix as far as  possible,  ambiguous  comple-
+       tions will not be displayed.
+
+
+

SINGLE CHARACTER QUERIES

+       Using  gl_get_line() to query the user for a single character reply, is
+       inconvenient for the user, since they must hit the enter or return  key
+       before  the  character that they typed is returned to the program. Thus
+       the gl_query_char() function has been  provided  for  single  character
+       queries like this.
+
+
+         int gl_query_char(GetLine *gl, const char *prompt,
+                           char defchar);
+
+
+       This function displays the specified prompt at the start of a new line,
+       and waits for the user to type a character. When the user types a char-
+       acter, gl_query_char() displays it to the right of the prompt, starts a
+       newline, then returns the character to the calling program. The  return
+       value  of the function is the character that was typed. If the read had
+       to be aborted for some reason, EOF is returned instead. In  the  latter
+       case, the application can call the previously documented gl_return_sta-
+       tus(), to find out what went wrong. This could, for example, have  been
+       the  reception of a signal, or the optional inactivity timer going off.
+
+       If the user simply hits enter, the value of  the  defchar  argument  is
+       substituted.  This  means  that  when  the  user hits either newline or
+       return, the character specified in  defchar,  is  displayed  after  the
+       prompt,  as  though the user had typed it, as well as being returned to
+       the calling application. If such a replacement is not important, simply
+       pass '0 as the value of defchar.
+
+       If  the  entered character is an unprintable character, it is displayed
+       symbolically. For example, control-A is displayed as ^A, and characters
+       beyond 127 are displayed in octal, preceded by a backslash.
+
+       As with gl_get_line(), echoing of the entered character can be disabled
+       using the gl_echo_mode() function.
+
+       If the calling process is suspended while waiting for the user to  type
+       their  response,  the  cursor is moved to the line following the prompt
+       line, then when the process resumes, the  prompt  is  redisplayed,  and
+       gl_query_char() resumes waiting for the user to type a character.
+
+       Note that in non-blocking server mode, (see gl_io_mode),
+       if an incomplete input line is  in  the  process  of  being  read  when
+       gl_query_char()  is  called,  the  partial input line is discarded, and
+       erased from the terminal, before the new prompt is displayed. The  next
+       call to gl_get_line() will thus start editing a new line.
+
+
+

READING RAW CHARACTERS

+       Whereas  the  gl_query_char()  function  visibly prompts the user for a
+       character, and displays what they typed,  the  gl_read_char()  function
+       reads a signal character from the user, without writing anything to the
+       terminal, or perturbing any incompletely entered input line. This means
+       that it can be called not only from between calls to gl_get_line(), but
+       also from callback functions that the application has registered to  be
+       called by gl_get_line().
+
+
+         int gl_read_char(GetLine *gl);
+
+
+       On  success,  the  return value of gl_read_char() is the character that
+       was read. On failure, EOF is returned, and the gl_return_status() func-
+       tion  can  be called to find out what went wrong. Possibilities include
+       the optional inactivity timer going off, the receipt of a  signal  that
+       is configured to abort gl_get_line(), or terminal I/O blocking, when in
+       non-blocking server-I/O mode.
+
+       Beware that certain keyboard keys, such as function  keys,  and  cursor
+       keys,  usually generate at least 3 characters each, so a single call to
+       gl_read_char() won't be enough to identify such keystrokes.
+
+
+

CLEARING THE TERMINAL

+       The calling program can clear the terminal by  calling  gl_erase_termi-
+       nal(). In non-blocking server-I/O mode, this function also arranges for
+       the current input line to be redrawn from scratch when gl_get_line() is
+       next called.
+
+
+         int gl_erase_terminal(GetLine *gl);
+
+
+
+

DISPLAYING TEXT DYNAMICALLY

+       Between calls to gl_get_line(), the gl_display_text() function provides
+       a convenient way to display  paragraphs  of  text,  left-justified  and
+       split  over  one or more terminal lines according to the constraints of
+       the current width of the terminal. Examples of the use of this function
+       may  be  found in the demo programs, where it is used to display intro-
+       ductions. In those examples the advanced use of optional prefixes, suf-
+       fixes  and  filled  lines  to draw a box around the text is also illus-
+       trated.
+
+
+         int gl_display_text(GetLine *gl, int indentation,
+                             const char *prefix,
+                             const char *suffix, int fill_char,
+                             int def_width, int start,
+                             const char *string);
+
+       If gl isn't currently connected to a terminal, for example if the  out-
+       put of a program that uses gl_get_line() is being piped to another pro-
+       gram or redirected to a file, then the value of the def_width parameter
+       is used as the terminal width.
+
+       The  indentation  argument specifies the number of characters to use to
+       indent each line of ouput. The fill_char argument specifies the charac-
+       ter that will be used to perform this indentation.
+
+       The  prefix argument can either be NULL, or be a string to place at the
+       beginning of each new line (after  any  indentation).   Similarly,  the
+       suffix  argument can either be NULL, or be a string to place at the end
+       of each line. The suffix is placed flush against the right edge of  the
+       terminal,  and  any space between its first character and the last word
+       on that line is filled with the character specified via  the  fill_char
+       argument.  Normally the fill-character is a space.
+
+       The  start  argument  tells  gl_display_text() how many characters have
+       already been written to the current terminal line, and  thus  tells  it
+       the  starting  column  index  of the cursor.  Since the return value of
+       gl_display_text() is the ending column index of the cursor, by  passing
+       the  return value of one call to the start argument of the next call, a
+       paragraph that is broken between more than one string can  be  composed
+       by  calling  gl_display_text() for each successive portion of the para-
+       graph. Note that literal newline characters are necessary at the end of
+       each paragraph to force a new line to be started.
+
+       On error, gl_display_text() returns -1.
+
+
+

CALLBACK FUNCTION FACILITIES

+       Unless  otherwise  stated,  callback  functions, such as tab completion
+       callbacks and event callbacks should not call  any  functions  in  this
+       module.  The following functions, however, are designed specifically to
+       be used by callback functions.
+
+       Calling  the  gl_replace_prompt()  function  from  a   callback   tells
+       gl_get_line()  to display a different prompt when the callback returns.
+       Except in non-blocking server mode, it has no effect  if  used  between
+       calls   to   gl_get_line().   In  non-blocking  server  mode  (see  the
+       gl_io_mode man page, when  used  between  two  calls  to
+       gl_get_line()  that  are  operating on the same input line, the current
+       input line will be re-drawn with the new prompt on the  following  call
+       to gl_get_line().
+
+
+         void gl_replace_prompt(GetLine *gl, const char *prompt);
+
+
+
+

INTERNATIONAL CHARACTER SETS

+       Since  libtecla version 1.4.0, gl_get_line() has been 8-bit clean. This
+       means that all 8-bit characters that are printable in the  user's  cur-
+       rent  locale  are  now  displayed verbatim and included in the returned
+       input line.  Assuming that the calling  program  correctly  contains  a
+       call like the following,
+
+         setlocale(LC_CTYPE, "");
+
+       then  the  current locale is determined by the first of the environment
+       variables LC_CTYPE, LC_ALL, and LANG, that is found to contain a  valid
+       locale  name.  If  none  of these variables are defined, or the program
+       neglects to call setlocale, then the default C locale is used, which is
+       US  7-bit  ASCII.  On  most  unix-like platforms, you can get a list of
+       valid locales by typing the command:
+
+         locale -a
+
+       at the shell prompt. Further documentation on how the user can make use
+       of  this  to  enter  international  characters  can  be  found  in  the
+       tecla man page.
+
+
+

THREAD SAFETY

+       In a multi-threaded program, you should use the libtecla_r.a version of
+       the  library.  This  uses reentrant versions of system functions, where
+       available. Unfortunately neither terminfo nor termcap were designed  to
+       be reentrant, so you can't safely use the functions of the getline mod-
+       ule in multiple threads (you can use the  separate  file-expansion  and
+       word-completion  modules in multiple threads, see the corresponding man
+       pages for details). However due to the use of POSIX reentrant functions
+       for looking up home directories etc, it is safe to use this module from
+       a single thread of a multi-threaded program, provided that  your  other
+       threads don't use any termcap or terminfo functions.
+
+
+

FILES

+       libtecla.a      -    The tecla library
+       libtecla.h      -    The tecla header file.
+       ~/.teclarc      -    The personal tecla customization file.
+
+
+

SEE ALSO

+       libtecla, gl_io_mode, tecla, ef_expand_file,
+       cpl_complete_word, pca_lookup_file
+
+
+

AUTHOR

+       Martin Shepherd  (mcs@astro.caltech.edu)
+
+
+
+                                                    gl_get_line
+
+ diff --git a/libtecla-1.6.1/html/gl_io_mode.html b/libtecla-1.6.1/html/gl_io_mode.html new file mode 100644 index 0000000..98c15a7 --- /dev/null +++ b/libtecla-1.6.1/html/gl_io_mode.html @@ -0,0 +1,509 @@ + +Manual Page + + +
+gl_io_mode                            gl_io_mode
+
+
+
+

NAME

+        gl_io_mode, gl_raw_io, gl_normal_io, gl_tty_signals, gl_abandon_line,
+        gl_handle_signal,  gl_pending_io  -  How  to use gl_get_line() from an
+       external event loop.
+
+

SYNOPSIS

+       #include <libtecla.h>
+
+       int gl_io_mode(GetLine *gl, GlIOMode mode);
+
+       int gl_raw_io(GetLine *gl);
+
+       int gl_normal_io(GetLine *gl);
+
+       int gl_tty_signals(void (*term_handler)(int),
+                          void (*susp_handler)(int),
+                          void (*cont_handler)(int),
+                          void (*size_handler)(int));
+
+       void gl_abandon_line(GetLine *gl);
+
+       void gl_handle_signal(int signo, GetLine *gl, int ngl);
+
+       GlPendingIO gl_pending_io(GetLine *gl);
+
+
+
+

DESCRIPTION

+       The gl_get_line() function,  which  is  documented  separately  in  the
+       gl_get_line  man page, supports two different I/O modes.
+       These are selected by calling the gl_io_mode() function.
+
+
+         int gl_io_mode(GetLine *gl, GlIOMode mode);
+
+
+       The mode argument of this function specifies the new I/O mode, and must
+       be one of the following.
+
+
+         GL_NORMAL_MODE   -  Select the normal blocking-I/O mode.
+                             In this mode gl_get_line()
+                             doesn't return until either an error
+                             occurs of the user finishes entering a
+                             new line. This mode is the focus of
+                             the gl_get_line man page.
+
+         GL_SERVER_MODE   -  Select non-blocking server I/O mode.
+                             In this mode, since non-blocking
+                             terminal I/O is used, the entry of
+                             each new input line typically requires
+                             many calls to gl_get_line() from
+                             an external I/O-driven event loop.
+                             This mode is the focus of this man
+                             page.
+
+
+       Newly created GetLine objects start in normal I/O mode, so to switch to
+       non-blocking server mode requires an initial call to gl_io_mode().
+
+
+

SERVER I/O MODE

+       In non-blocking server I/O mode, the application is required to have an
+       event  loop  which  calls  gl_get_line()  whenever  the  terminal  file
+       descriptor can do the type I/O that gl_get_line() is  waiting  for.  To
+       determine  which type of I/O gl_get_line() is waiting for, the applica-
+       tion calls the gl_pending_io() function.
+
+
+         GlPendingIO gl_pending_io(GetLine *gl);
+
+
+       The return value of this function is one of the following  two  enumer-
+       ated values.
+
+
+         GLP_READ    -  gl_get_line() is waiting to write a
+                        character to the terminal.
+
+         GLP_WRITE   -  gl_get_line() is waiting to read a
+                        character from the keyboad.
+
+
+       If  the application is using either the select() or poll() system calls
+       to watch for I/O on a group of file descriptors, then  it  should  call
+       the gl_pending_io() function before each call to these functions to see
+       which direction of I/O it should tell them to watch for, and  configure
+       their  arguments  accordingly. In the case of the select() system call,
+       this means using the FD_SET() macro to add the terminal file descriptor
+       either to the set of file descriptors to be watched for readability, or
+       the set to be watched for writability.
+
+       As in normal I/O mode, the return value of gl_get_line()  is  either  a
+       pointer  to a completed input line, or NULL. However, whereas in normal
+       I/O mode a NULL return value always means that an  error  occurred,  in
+       non-blocking  server  mode,  NULL  is  also returned when gl_get_line()
+       can't read or write to the terminal  without  blocking.  Thus  in  non-
+       blocking  server  mode,  in order to determine when a NULL return value
+       signifies that an error occurred or not, it is necessary  to  call  the
+       gl_return_status()  function.  If  this function returns the enumerated
+       value, GLR_BLOCKED, as documented in the gl_get_line man
+       page,  this  means  that gl_get_line() is waiting for I/O, and no error
+       has occurred.
+
+       When gl_get_line() returns NULL and gl_return_status()  indicates  that
+       this  is  due  to  blocked  terminal  I/O,  the application should call
+       gl_get_line() again when the type of I/O  reported  by  gl_pending_io()
+       becomes  possible.  The  prompt,  start_line and start_pos arguments of
+       gl_get_line() will be ignored on these calls.  If you  need  to  change
+       the  prompt  of  the  line that is currently being edited, then you can
+       call   the   gl_replace_prompt()   function    (documented    in    the
+       gl_get_line man page) between calls to gl_get_line().
+
+
+

GIVING UP THE TERMINAL

+       A  complication  that  is unique to non-blocking server mode is that it
+       requires that the terminal  be  left  in  raw  mode  between  calls  to
+       gl_get_line().  If  this  weren't  the  case,  the  external event loop
+       wouldn't be able to detect individual key-presses, and the  basic  line
+       editing implemented by the terminal driver would clash with the editing
+       provided by gl_get_line(). What this means is that any  time  that  the
+       terminal  needs  to  be used for other things than entering a new input
+       line with gl_get_line(), it needs to be restored to a usable state.  In
+       particular, whenever the process is suspended or terminated, the termi-
+       nal must be returned to a  normal  state.  If  this  isn't  done,  then
+       depending  on  the characteristics of the shell that was used to invoke
+       the program, the user may end up with a hung terminal. To this end, the
+       gl_normal_io()  function is provided for switching the terminal back to
+       the state that it was in when raw mode was last established.
+
+
+         int gl_normal_io(GetLine *gl);
+
+
+       What this function does is first flush any pending output to the termi-
+       nal,  then move the cursor to the start of the terminal line which fol-
+       lows the end of the incompletely entered input line. At this  point  it
+       is  safe  to  suspend  or terminate the process, and it is safe for the
+       application to read and write to the terminal. To resume entry  of  the
+       input line, the application should call the gl_raw_io() function.
+
+
+         int gl_raw_io(GetLine *gl);
+
+
+       This  function  starts  a  new line, redisplays the partially completed
+       input line (if any), restores the cursor position within this  line  to
+       where it was when gl_normal_io() was called, then switches back to raw,
+       non-blocking terminal mode ready to continue entry of  the  input  line
+       when gl_get_line() is next called.
+
+       Note that in non-blocking server mode, if gl_get_line() is called after
+       a call to gl_normal_io(), without an intervening call  to  gl_raw_io(),
+       gl_get_line()  will  call  gl_raw_mode()  itself, and the terminal will
+       remain in this mode when gl_get_line() returns.
+
+
+

SIGNAL HANDLING

+       In the previous section it was pointed out that in non-blocking  server
+       mode,  the  terminal must be restored to a sane state whenever a signal
+       is received that either suspends or terminates the process.  In  normal
+       I/O  mode,  this  is done for you by gl_get_line(), but in non-blocking
+       server mode, since the terminal is left in raw mode  between  calls  to
+       gl_get_line(),  this signal handling has to be done by the application.
+       Since there are many signals that can suspend or terminate  a  process,
+       as  well  as other signals that are important to gl_get_line(), such as
+       the SIGWINCH signal, which tells it when the terminal size has changed,
+       the  gl_tty_signals()  function  is provided for installing signal han-
+       dlers for all pertinent signals.
+
+
+         int gl_tty_signals(void (*term_handler)(int),
+                            void (*susp_handler)(int),
+                            void (*cont_handler)(int),
+                            void (*size_handler)(int));
+
+
+       What this does is use  gl_get_line()'s  internal  list  of  signals  to
+       assign specified signal handlers to groups of signals. The arguments of
+       this function are as follows.
+
+
+         term_handler  -  This is the signal handler that is to be
+                          used to trap signals that by default
+                          terminate any process that receives
+                          them (eg. SIGINT or SIGTERM).
+
+         susp_handler  -  This is the signal handler that is to be
+                          used to trap signals that by default
+                          suspend any process that receives them,
+                          (eg. SIGTSTP or SIGTTOU).
+
+         cont_handler  -  This is the signal handler that is to be
+                          used to trap signals that are usually
+                          sent when a process resumes after being
+                          suspended (usually SIGCONT). Beware that there is
+                          nothing to stop a user from sending one of these
+                          signals at other times.
+
+         size_handler  -  This signal handler is used to trap
+                          signals that are sent to processes when
+                          their controlling terminals are resized
+                          by the user (eg. SIGWINCH).
+
+
+       These arguments can all be the same, if so desired, and you can specify
+       SIG_IGN  (ignore  this  signal)  or  SIG_DFL  (use  the system-provided
+       default signal handler) instead of a function where pertinent. In  par-
+       ticular, it is rarely useful to trap SIGCONT, so the cont_handler argu-
+       ment will usually be SIG_DFL or SIG_IGN.
+
+       The gl_tty_signals() function uses the POSIX  sigaction()  function  to
+       install  these  signal  handlers,  and it is careful to use the sa_mask
+       member of each sigaction structure to ensure that  only  one  of  these
+       signals  is  ever  delivered  at  a time. This guards against different
+       instances of these signal handlers from simultaneously trying to  write
+       to common global data, such as a shared sigsetjmp() buffer or a signal-
+       received flag.
+
+       The signal handlers that are installed by this  function,  should  call
+       the gl_handle_signal().
+
+
+         void gl_handle_signal(int signo, GetLine *gl, int ngl);
+
+
+       The  signo  argument tells this function which signal it is being asked
+       to respond to, and the gl argument should be a  pointer  to  the  first
+       element  of  an  array of ngl GetLine objects. If your application only
+       has one of these objects, just pass its pointer as the gl argument  and
+       specify ngl as 1.
+
+       Depending  on the signal that is being handled, this function does dif-
+       ferent things.
+
+
+   Terminal resize signals (SIGWINCH)
+       If the signal indicates that the terminal was resized, then it arranges
+       for the next call to gl_get_line() to ask the terminal for its new size
+       and redraw the input line accordingly. In order that  gl_get_line()  be
+       called as soon as possible to do this, gl_handle_signal() also arranges
+       that the next call to gl_pending_io() will return  GLP_WRITE.  Thus  if
+       the  application waits for I/O in select() or poll(), then the applica-
+       tion needs to ensure that these functions will be reliably aborted when
+       a  signal is caught and handled by the application. More on this below.
+
+
+

Process termination signals.

+       If the signal that was caught is one of those that  by  default  termi-
+       nates  any  process  that receives it, then gl_handle_signal() does the
+       following steps.
+
+       1. First it blocks the delivery of all signals that can be
+          blocked (ie. SIGKILL and SIGSTOP can't be blocked)
+
+       2. Next it calls gl_normal_io() for each of the ngl
+          GetLine objects. Note that this does nothing to any of the
+          GetLine objects that aren't currently in raw mode.
+
+       3. Next it sets the signal handler of the signal to its default,
+          process-termination disposition.
+
+       4. Next it re-sends the process the signal that was caught.
+
+       5. Finally it unblocks delivery of this signal, which
+          results in the process being terminated.
+
+
+

Process suspension signals.

+       If the default disposition of the signal is to suspend the process, the
+       same steps are executed as for process termination signals, except that
+       when the process is later resumed,  gl_handle_signal()  continues,  and
+       does the following steps.
+
+       6. It re-blocks delivery of the signal.
+
+       7. It reinstates the signal handler of the signal to the one
+          that was displaced when its default disposition was substituted.
+
+       8. For any of the GetLine objects that were in raw mode when
+          gl_handle_signal() was called, gl_handle_signal() then
+          calls gl_raw_io(), to resume entry of the input lines on
+          those terminals.
+
+       9. Finally, it restores the signal process mask to how it
+          was when gl_handle_signal() was called.
+
+       Note  that  the  process  is suspended or terminated using the original
+       signal that was caught, rather than using the uncatchable  SIGSTOP  and
+       SIGKILL signals. This is important, because when a process is suspended
+       or terminated, the parent of the process may wish  to  use  the  status
+       value returned by the wait() system call to figure out which signal was
+       responsible. In particular, most shells use this information to print a
+       corresponding  message to the terminal. Users would be rightly confused
+       if when their process received a SIGPIPE signal, the program  responded
+       by  sending itself a SIGKILL signal, and the shell then printed out the
+       provocative statement, "Killed!".
+
+
+

INTERRUPTING THE EVENT LOOP

+       If a signal is caught and handled when the application's event loop  is
+       waiting  in  select()  or  poll(), these functions will be aborted with
+       errno set to EINTR. When  this  happens  the  event  loop  should  call
+       gl_pending_io(),  before  calling  select()  or poll() again. It should
+       then arrange for select() or poll() to wait for the type  of  I/O  that
+       this reports. This is necessary, because any signal handler which calls
+       gl_handle_signal(),  will  frequently  change  the  type  of  I/O  that
+       gl_get_line() is waiting for.
+
+       Unfortunately, if a signal arrives between the statements which config-
+       ure the arguments of select() or poll() and the calls  to  these  func-
+       tions,  then the signal will not be seen by these functions, which will
+       then not be aborted. If these functions are waiting for keyboard  input
+       from  the  user  when  the  signal  is received, and the signal handler
+       arranges to redraw the input line to accomodate a  terminal  resize  or
+       the resumption of the process, then this redisplay will be end up being
+       delayed until the user hits the next key. Apart from puzzling the user,
+       this  clearly  isn't  a serious problem. However there is a way, albeit
+       complicated, to completely avoid this  race  condition.  The  following
+       steps illustrate this.
+
+       1. Block all of the signals that gl_get_line() catches,
+          by passing the signal set returned by gl_list_signals() to
+          sigprocmask().
+
+       2. Call gl_pending_io() and set up the arguments of
+          select() or poll() accordingly.
+
+       3. Call sigsetjmp() with a non-zero savesigs argument.
+
+       4. Initially this sigsetjmp() statement will return zero,
+          indicating that control isn't resuming there after a matching
+          call to siglongjmp().
+
+       5. Replace all of the handlers of the signals that gl_get_line()
+          is configured to catch, with a signal handler that first records
+          the number of the signal that was caught, in a file-scope variable,
+          then calls siglongjmp() with a non-zero value argument, to
+          return execution to the above sigsetjmp()
+          statement.  Registering these signal handlers can conveniently be
+          done using the gl_tty_signals() function.
+
+       6. Set the file-scope variable that the above signal handler uses to
+          record any signal that is caught to -1, so that we can check
+          whether a signal was caught by seeing if it contains a valid signal
+          number.
+
+       7. Now unblock the signals that were blocked in step 1. Any signal
+          that was received by the process in between step 1 and now will
+          now be delivered, and trigger our signal handler, as will any
+          signal that is received until we block these signals again.
+
+       8. Now call select() or poll().
+
+       9. When select() returns, again block the signals that were
+          unblocked in step 7.
+
+       If a signal is arrived any time during the above steps, our signal han-
+       dler will be triggered and cause control to return to  the  sigsetjmp()
+       statement,  where this time, sigsetjmp() will return non-zero, indicat-
+       ing that a signal was caught. When this  happens  we  simply  skip  the
+       above  block of statements, and continue with the following statements,
+       which are executed regardless of whether or not  a  signal  is  caught.
+       Note  that when sigsetjmp() returns, regardless of why it returned, the
+       process signal mask is returned to how  it  was  when  sigsetjmp()  was
+       called.  Thus  the following statements are always executed with all of
+       our signals blocked.
+
+       9. Reinstate the signal handlers that were displaced in step 5.
+
+       10. Check wether a signal was caught, by checking the file-scope
+           variable that the signal handler records signal numbers in.
+
+       11. If a signal was caught, send this signal to the application
+           again, and unblock just this signal, so that it invokes the
+           signal handler which we just reinstated in step 10.
+
+       12. Unblock all of the signals that were blocked in step 7.
+
+       Since this is complicated, note that demo3.c includes a working example
+       of  how to do this. The method used there however, is more general than
+       the above. What it provides is a wrapper function around select() which
+       encompasses   steps   3  to  11.  In  this  wrapper,  rather  than  use
+       gl_list_signals()  to  figure  out  the  signals  to  block,  and   and
+       gl_tty_signals() to assign and revert signal handlers, one of its argu-
+       ments is a sigset_t which specifies which signals to block  and  assign
+       signal  handlers to. This function thus doesn't depend on gl_get_line()
+       and can thus be used in other situations where race-condition-free sig-
+       nal handling is required.
+
+
+

SIGNALS CAUGHT BY GL_GET_LINE

+       Since  the  application  is  expected to handle signals in non-blocking
+       server mode, gl_get_line() doesn't attempt to duplicate this when it is
+       being  called.  If one of the signals that it is configured to catch is
+       sent  to  the  application  while  gl_get_line()   is   being   called,
+       gl_get_line() reinstates the caller's signal handlers, then just before
+       returning, re-sends the signal to the process to let the  application's
+       signal  handler handle it. If the process isn't terminated by this sig-
+       nal, gl_get_line() returns NULL, and a following call to gl_return_sta-
+       tus() returns the enumerated value GLR_SIGNAL.
+
+
+

ABORTING LINE INPUT

+       Often,  rather  than  letting  it  terminate  the process, applications
+       respond to the SIGINT user-interrupt signal  by  aborting  the  current
+       input  line.  The  way to do this in non-blocking server-I/O mode is to
+       not call gl_handle_signal() when this signal is caught, but instead  to
+       call the gl_abandon_line().
+
+
+         void gl_abandon_line(GetLine *gl);
+
+
+       This function arranges that when gl_get_line() is next called, it first
+       flushes any pending output to the terminal, then discardes the  current
+       input  line,  outputs a new prompt on the next line, and finally starts
+       accepting input of a new input line from the user.
+
+
+

SIGNAL SAFE FUNCTIONS

+       Provided that certain rules are followed, the following  functions  can
+       have  been  written  to  be safely callable from signal handlers. Other
+       functions in this library should not be called from signal handlers.
+
+
+         gl_normal_io()
+         gl_raw_io()
+         gl_handle_signal()
+         gl_abandon_line()
+
+
+       In order for this to be true, all signal handlers that call these func-
+       tions  must  be  registered in such a way that only one instance of any
+       one of them can be running at one time. The way to do this  is  to  use
+       the  POSIX  sigaction()  function  to register all signal handlers, and
+       when doing this, use the sa_mask member of the corresponding  sigaction
+       structure,  to  indicate  that all of the signals who's handlers invoke
+       the above functions, should be blocked when the current signal is being
+       handled.  This prevents two signal handlers from operating on a GetLine
+       object at the same time.
+
+       To prevent signal  handlers  from  accessing  a  GetLine  object  while
+       gl_get_line()  or  any of its associated public functions are operating
+       on it, all public functions associated  with  gl_get_line(),  including
+       gl_get_line()  itself,  temporarily  block the delivery of signals when
+       they are accessing GetLine objects. Beware that the only  signals  that
+       they  block  are the signals that gl_get_line() is currently configured
+       to catch, so be sure that if you call any of the above  functions  from
+       signal  handlers,  that the signals that these handlers are assigned to
+       are configured to be caught by gl_get_line() (see gl_trap_signal()).
+
+
+

USING TIMEOUTS TO POLL

+       If instead of using select() or poll() to wait for I/O,  your  applica-
+       tion  just needs to get out of gl_get_line() periodically to briefly do
+       something else before returning to accept input from the user, this can
+       be  done  in  non-blocking server mode by using the gl_inactivity_time-
+       out() function (see  gl_get_line),  to  specify  that  a
+       callback  function that returns GLTO_CONTINUE should be called whenever
+       gl_get_line() has been waiting for I/O for more than a specified amount
+       of time.
+
+       When  this callback is triggered, gl_get_line() will return NULL, and a
+       following call to gl_return_status() will return GLR_BLOCKED.
+
+       Beware that gl_get_line() won't return until the user  hasn't  typed  a
+       key  for  the  specified  interval, so if the interval is long, and the
+       user keeps typing, gl_get_line() may not return for a while.  In  other
+       words  there is no guarantee that it will return in the time specified.
+
+
+

THE SERVER DEMO PROGRAM

+       The demo3 program that is distributed  with  the  library,  provides  a
+       working  example  of  how to use non-blocking server I/O mode in a real
+       program. As far  as  the  user  is  concerned,  this  program  operates
+       identically to the main demo program (called demo), except that whereas
+       the main demo program uses the normal blocking I/O  mode,  demo3  using
+       non-blocking  I/O  and  an  external event loop. The source code can be
+       found in demo3.c, and the comments therein explain the various steps.
+
+
+

FILES

+       libtecla.a      -    The tecla library
+       libtecla.h      -    The tecla header file.
+
+
+

SEE ALSO

+       libtecla, gl_get_line, tecla, ef_expand_file,
+       cpl_complete_word, pca_lookup_file
+
+
+

AUTHOR

+       Martin Shepherd  (mcs@astro.caltech.edu)
+
+
+
+                                                     gl_io_mode
+
+ diff --git a/libtecla-1.6.1/html/index.html b/libtecla-1.6.1/html/index.html new file mode 100644 index 0000000..29889da --- /dev/null +++ b/libtecla-1.6.1/html/index.html @@ -0,0 +1,122 @@ +The tecla command-line editing library. + +

The Tecla command-line editing library.

+ +The tecla library provides UNIX and LINUX programs with interactive +command line editing facilities, similar to those of the UNIX tcsh +shell. In addition to simple command-line editing, it supports recall +of previously entered command lines, TAB completion of file names or +other tokens, and in-line wild-card expansion of filenames. The +internal functions which perform file-name completion and wild-card +expansion are also available externally for optional use by programs. +

+In addition, the library includes a path-searching module. This +allows an application to provide completion and lookup of files +located in UNIX style paths. Although not built into the line editor +by default, it can easily be called from custom tab-completion +callback functions. This was originally conceived for completing the +names of executables and providing a way to look up their locations in +the user's PATH environment variable, but it can easily be asked to +look up and complete other types of files in any list of directories. + +

+Note that special care has been taken to allow the use of this library +in threaded programs. The option to enable this is discussed in the +Makefile, and specific discussions of thread safety are presented in +the included man pages. +

+The current version is version 1.6.1. This may be obtained from: +

+ http://www.astro.caltech.edu/~mcs/tecla/libtecla-1.6.1.tar.gz +

+ +For the sake of automated scripts, the following URL always points to +the latest version. Note that the version number can be found in the +README file. + +

+ http://www.astro.caltech.edu/~mcs/tecla/libtecla.tar.gz +

+ +The library is distributed under a permissive non-copyleft +free software license (the X11 license with +the name of the copyright holder changed). This is compatible with, +but not as restrictive as the GNU GPL. + +

Release notes

+ +The list of major changes that accompany each new release can be found +here. + +

Modifications

+ +The gory details of changes in the latest and previous versions of the +library can be found here. + +

Library documentation

+ +The following are html versions of the libtecla man pages: + +
    +
  • tecla - Documentation for users of programs which use gl_get_line(). +
  • libtecla - A programmers introduction to the tecla library. +
  • gl_get_line - The interactive line-input function. +
  • gl_io_mode - Using gl_get_line() in a non-blocking fashion. +
  • cpl_complete_word - The word (eg. filename) completion function. +
  • ef_expand_file - The filename expansion function. +
  • pca_lookup_file - A directory-list based filename lookup and completion module. +
  • enhance - A program that adds command-line editing to third party programs. +
+ +

Portability

+ +In principle, the standard version of the library should compile +without any problems on any UNIX or UNIX like system. So far it has +been reported to work on the following systems: + +
+  Sun Solaris 2.5,2.6,7,8,9 with any of gcc, Sun C, or g++.
+  Mandrake Linux 7.1 etc.., gcc
+  Red Hat Linux 7 etc.., gcc
+  Fedora Core 1, gcc
+  Suse Linux 6.4, gcc
+  IBM AIX 4.3.3, gcc
+  HP-UX 10.20, HP-UX 11, gcc, c89
+  FreeBSD, gcc
+  Alpha OSF1, cc, gcc
+  Mac OS X
+  Cygwin (running under Windows)
+  QNX
+  NetBSD 1.6, 386, gcc
+  SGI IRIX 6.5
+
+ +There haven't been many reports concerning the POSIX reentrant +version, so the absence of any of the above from the following list of +systems on which the reentrant version is known to work, shouldn't be +taken as an indication that the reentrant version doesn't work. + +
+  Sun Solaris 2.5,2.6,7,8,9 with any of gcc, Sun C, or g++.
+  Mandrake Linux, gcc
+  RedHat Linux, gcc
+  Fedora Core, gcc
+  SuSE Linux, gcc
+  HP-UX 11, gcc
+  IBM AIX 4.3.3, gcc
+  Alpha OSF1, cc
+  SGI IRIX 6.5
+
+ +The only system that is known to have issues with the reentrant +version of the library is SCO UnixWare 7.1.1. The problem is in the +system provided signal.h, which breaks when POSIX_C_SOURCE is +defined. It has been reported that this can be "fixed" by editing +signal.h. + +

+If you compile the library on a system that isn't mentioned above, +please send E-mail to mcs@astro.caltech.edu. +


+Martin Shepherd (31-Oct-2004) + diff --git a/libtecla-1.6.1/html/libtecla.html b/libtecla-1.6.1/html/libtecla.html new file mode 100644 index 0000000..2b85d7a --- /dev/null +++ b/libtecla-1.6.1/html/libtecla.html @@ -0,0 +1,138 @@ + +Manual Page + + +
+libtecla                                libtecla
+
+
+
+

NAME

+       libtecla - An interactive command-line input library.
+
+

SYNOPSIS

+       @CC@ ... -ltecla -lcurses
+
+
+

DESCRIPTION

+       The tecla library provides programs with interactive command line edit-
+       ing facilities, similar to those of the unix tcsh shell. In addition to
+       simple  command-line  editing, it supports recall of previously entered
+       command lines, TAB completion of file names or other  tokens,  and  in-
+       line  wild-card  expansion  of  filenames. The internal functions which
+       perform file-name completion and wild-card expansion are also available
+       externally for optional use by the calling program.
+
+       The  various  parts  of the library are documented in the following man
+       pages:
+
+         tecla              -  Use level documentation of the
+                               command-line editing facilities
+                               provided by gl_get_line().
+         gl_get_line        -  The interactive line-input module.
+         gl_io_mode         -  How to use gl_get_line() in an
+                               incremental, non-blocking fashion.
+         cpl_complete_word  -  The word completion module.
+         ef_expand_file     -  The filename expansion module.
+         pca_lookup_file    -  A directory-list based filename
+                               lookup and completion module.
+
+       In addition there is one  optional  application  distributed  with  the
+       library:
+
+         enhance            -  Add command-line editing to third
+                                  party applications.
+
+
+

THREAD SAFETY

+       If  the  library  is compiled with -D_POSIX_C_SOURCE=199506L, reentrant
+       versions of as many functions as possible are used. This includes using
+       getpwuid_r() and getpwnam_r() instead of getpwuid() and getpwnam() when
+       looking up the home directories of specific users in the password  file
+       (for  ~user/ expansion), and readdir_r() instead of readdir() for read-
+       ing directory entries when doing  filename  completion.  The  reentrant
+       version of the library is usually called libtecla_r.a instead of libte-
+       cla.a, so if only the latter is available, it probably isn't  the  cor-
+       rect version to link with threaded programs.
+
+       Reentrant  functions  for  iterating  through  the password file aren't
+       available, so when the library is compiled to be reentrant, TAB comple-
+       tion  of  incomplete  usernames  in ~username/ expressions is disabled.
+       This doesn't disable expansion of complete ~username expressions, which
+       can  be  done  reentrantly, or expansion of the parts of filenames that
+       follow them, so this doesn't remove much functionality.
+
+       The terminfo functions setupterm(), tigetstr(), tigetnum() and  tputs()
+       also aren't reentrant, but very few programs will want to interact with
+       multiple terminals, so this shouldn't prevent this library  from  being
+       used in threaded programs.
+
+
+

LIBRARY VERSION NUMBER

+       The  version  number  of the library can be queried using the following
+       function.
+
+        void libtecla_version(int *major, int *minor, int *micro);
+
+
+       On return, this function records the three components of  the  libtecla
+       version  number  in  *major,  *minor, *micro. The formal meaning of the
+       three components is as follows.
+
+
+        major - Incrementing this number implies that a change has
+                been made to the library's public interface, which
+                makes it binary incompatible  with programs that
+                were linked with previous shared versions of the
+                tecla library.
+
+        minor - This number is incremented by one whenever
+                additional functionality, such as new functions or
+                modules, are added to the library.
+
+        micro - This is incremented whenever modifications to the
+                library are made which make no changes to the
+                public interface, but which fix bugs and/or improve
+                the behind-the-scenes implementation.
+
+
+
+

TRIVIA

+       In Spanish, a "tecla" is the key of a keyboard. Since this library cen-
+       ters  on  keyboard  input,  and  given that I wrote much of the library
+       while working in Chile, this seemed like a suitable name.
+
+
+

FILES

+       libtecla.a    -   The tecla library.
+       libtecla.h    -   The tecla header file.
+       ~/.teclarc    -   The tecla personal customization file.
+
+
+

SEE ALSO

+       gl_get_line, tecla, gl_io_mode, ef_expand_file,
+       cpl_complete_word, pca_lookup_file, enhance
+
+
+

AUTHOR

+       Martin Shepherd  (mcs@astro.caltech.edu)
+
+
+

ACKNOWLEDGMENTS

+       Markus Gyger  - Lots of assistance, including help with
+                       shared libraries, configuration information,
+                       particularly for Solaris; modifications to
+                       support C++ compilers, improvements for ksh
+                       users, faster cursor motion, output
+                       buffering, and changes to make gl_get_line()
+                       8-bit clean.
+       Mike MacFaden - Suggestions, feedback and testing that led
+                       to many of the major new functions that were
+                       added in version 1.4.0.
+       Tim Eliseo    - Many vi-mode bindings and fixes.
+
+
+
+                                                       libtecla
+
+ diff --git a/libtecla-1.6.1/html/pca_lookup_file.html b/libtecla-1.6.1/html/pca_lookup_file.html new file mode 100644 index 0000000..d5e1e7b --- /dev/null +++ b/libtecla-1.6.1/html/pca_lookup_file.html @@ -0,0 +1,312 @@ + +Manual Page + + +
+pca_lookup_file                  pca_lookup_file
+
+
+
+

NAME

+       pca_lookup_file,    del_PathCache,    del_PcaPathConf,   new_PathCache,
+       new_PcaPathConf, pca_last_error,  pca_path_completions,  pca_scan_path,
+       pca_set_check_fn,  ppc_file_start,  ppc_literal_escapes - lookup a file
+       in a list of directories
+
+

SYNOPSIS

+       #include <libtecla.h>
+
+       PathCache *new_PathCache(void);
+
+       PathCache *del_PathCache(PathCache *pc);
+
+       int pca_scan_path(PathCache *pc, const char *path);
+
+       void pca_set_check_fn(PathCache *pc, CplCheckFn *check_fn,
+                             void *data);
+
+       char *pca_lookup_file(PathCache *pc, const char *name,
+                             int name_len, int literal);
+
+       const char *pca_last_error(PathCache *pc);
+
+       CPL_MATCH_FN(pca_path_completions);
+
+
+
+

DESCRIPTION

+       The PathCache object is part of  the  tecla  library  (see  the  libte-
+       cla(@LIBR_MANEXT@) man page).
+
+       PathCache objects allow an application to search for files in any colon
+       separated list of directories, such as the unix execution PATH environ-
+       ment  variable. Files in absolute directories are cached in a PathCache
+       object, whereas relative directories are scanned  as  needed.  Using  a
+       PathCache  object,  you can look up the full pathname of a simple file-
+       name, or you can obtain a list of the possible completions of  a  given
+       filename  prefix.  By  default all files in the list of directories are
+       targets for lookup and completion, but a versatile  mechanism  is  pro-
+       vided  for only selecting specific types of files. The obvious applica-
+       tion of this facility is to provide Tab-completion and lookup  of  exe-
+       cutable  commands  in  the  unix  PATH,  so  an optional callback which
+       rejects all but executable files, is provided.
+
+
+

AN EXAMPLE

+       Under UNIX, the following example program looks  up  and  displays  the
+       full pathnames of each of the command names on the command line.
+
+         #include <stdio.h>
+         #include <stdlib.h>
+         #include <libtecla.h>
+
+         int main(int argc, char *argv[])
+         {
+           int i;
+         /*
+          * Create a cache for executable files.
+          */
+           PathCache *pc = new_PathCache();
+           if(!pc)
+             exit(1);
+         /*
+          * Scan the user's PATH for executables.
+          */
+           if(pca_scan_path(pc, getenv("PATH"))) {
+             fprintf(stderr, "%s\n", pca_last_error(pc));
+             exit(1);
+           }
+         /*
+          * Arrange to only report executable files.
+          */
+          pca_set_check_fn(pc, cpl_check_exe, NULL);
+         /*
+          * Lookup and display the full pathname of each of the
+          * commands listed on the command line.
+          */
+           for(i=1; i<argc; i++) {
+             char *cmd = pca_lookup_file(pc, argv[i], -1, 0);
+             printf("The full pathname of '%s' is %s\n", argv[i],
+                    cmd ? cmd : "unknown");
+           }
+           pc = del_PathCache(pc);  /* Clean up */
+           return 0;
+         }
+
+       The following is an example of what this does on my laptop under linux:
+
+         $ ./example less more blob
+         The full pathname of 'less' is /usr/bin/less
+         The full pathname of 'more' is /bin/more
+         The full pathname of 'blob' is unknown
+         $
+
+
+

FUNCTION DESCRIPTIONS

+       In order to use the facilities of this module, you must first  allocate
+       a PathCache object by calling the new_PathCache() constructor function.
+
+         PathCache *new_PathCache(void)
+
+       This function creates the resources needed to cache and lookup files in
+       a list of directories. It returns NULL on error.
+
+
+

POPULATING THE CACHE

+       Once you have created a cache, it needs to be populated with files.  To
+       do this, call the pca_scan_path() function.
+
+         int pca_scan_path(PathCache *pc, const char *path);
+
+       Whenever this function is called, it discards the current  contents  of
+       the  cache,  then  scans  the list of directories specified in its path
+       argument for files. The path argument must be  a  string  containing  a
+       colon-separated       list       of      directories,      such      as
+       "/usr/bin:/home/mcs/bin:.". This can include directories  specified  by
+       absolute pathnames such as "/usr/bin", as well as sub-directories spec-
+       ified by relative pathnames such as "." or "bin". Files in the absolute
+       directories  are  immediately cached in the specified PathCache object,
+       whereas sub-directories, whose identities obviously change whenever the
+       current  working  directory is changed, are marked to be scanned on the
+       fly whenever a file is looked up.
+
+       On success this function return  0.  On  error  it  returns  1,  and  a
+       description of the error can be obtained by calling pca_last_error(pc).
+
+
+

LOOKING UP FILES

+       Once the cache has been populated with files, you can look up the  full
+       pathname   of   a   file,   simply   by   specifying  its  filename  to
+       pca_lookup_file().
+
+         char *pca_lookup_file(PathCache *pc, const char *name,
+                               int name_len, int literal);
+
+       To make it possible to pass this function a filename which is  actually
+       part  of  a longer string, the name_len argument can be used to specify
+       the length of the filename at the start of the name[] argument. If  you
+       pass  -1  for  this length, the length of the string will be determined
+       with strlen(). If the name[]  string  might  contain  backslashes  that
+       escape  the  special  meanings  of spaces and tabs within the filename,
+       give the literal argument,  the  value  0.  Otherwise,  if  backslashes
+       should  be  treated  as  normal characters, pass 1 for the value of the
+       literal argument.
+
+
+

FILENAME COMPLETION

+       Looking up the potential completions of a filename-prefix in the  file-
+       name  cache, is achieved by passing the provided pca_path_completions()
+       callback function to the cpl_complete_word() function (see the cpl_com-
+       plete_word(@FUNC_MANEXT@) man page).
+
+         CPL_MATCH_FN(pca_path_completions);
+
+       This  callback  requires that its data argument be a pointer to a PcaP-
+       athConf object. Configuration objects of this  type  are  allocated  by
+       calling new_PcaPathConf().
+
+         PcaPathConf *new_PcaPathConf(PathCache *pc);
+
+       This  function returns an object initialized with default configuration
+       parameters, which determine  how  the  cpl_path_completions()  callback
+       function  behaves. The functions which allow you to individually change
+       these parameters are discussed below.
+
+       By default, the pca_path_completions() callback function searches back-
+       wards  for  the  start of the filename being completed, looking for the
+       first un-escaped space or the start of the input line. If you  wish  to
+       specify  a  different location, call ppc_file_start() with the index at
+       which the filename starts in the input line. Passing start_index=-1 re-
+       enables the default behavior.
+
+         void ppc_file_start(PcaPathConf *ppc, int start_index);
+
+       By  default,  when  pca_path_completions()  looks  at a filename in the
+       input line, each lone backslash in the input  line  is  interpreted  as
+       being a special character which removes any special significance of the
+       character which follows it, such as a space which should  be  taken  as
+       part  of the filename rather than delimiting the start of the filename.
+       These backslashes are thus ignored while looking for  completions,  and
+       subsequently  added  before spaces, tabs and literal backslashes in the
+       list of completions. To have unescaped backslashes  treated  as  normal
+       characters,  call  ppc_literal_escapes()  with  a non-zero value in its
+       literal argument.
+
+         void ppc_literal_escapes(PcaPathConf *ppc, int literal);
+
+       When you have finished with a PcaPathConf variable, you can pass it  to
+       the del_PcaPathConf() destructor function to reclaim its memory.
+
+         PcaPathConf *del_PcaPathConf(PcaPathConf *ppc);
+
+
+
+

BEING SELECTIVE

+       If  you  are  only  interested  in certain types or files, such as, for
+       example, executable files, or files whose names  end  in  a  particular
+       suffix, you can arrange for the file completion and lookup functions to
+       be selective in the filenames that they return.  This is done by regis-
+       tering  a  callback  function  with  your PathCache object. Thereafter,
+       whenever a filename is found which  either  matches  a  filename  being
+       looked  up, or matches a prefix which is being completed, your callback
+       function will be called with the full pathname of the  file,  plus  any
+       application-specific data that you provide, and if the callback returns
+       1 the filename will be reported as a match, and if  it  returns  0,  it
+       will  be  ignored.   Suitable  callback  functions and their prototypes
+       should be declared with the following macro. The CplCheckFn typedef  is
+       also provided in case you wish to declare pointers to such functions.
+
+         #define CPL_CHECK_FN(fn) int (fn)(void *data, \
+                                           const char *pathname)
+         typedef CPL_CHECK_FN(CplCheckFn);
+
+       Registering    one    of   these   functions   involves   calling   the
+       pca_set_check_fn() function. In  addition  to  the  callback  function,
+       passed  via  the  check_fn argument, you can pass a pointer to anything
+       via the data argument. This pointer will be passed on to your  callback
+       function,  via  its  own  data argument, whenever it is called, so this
+       provides a way to pass appplication specific data to your callback.
+
+         void pca_set_check_fn(PathCache *pc, CplCheckFn *check_fn,
+                               void *data);
+
+       Note that these callbacks are passed the full pathname of each matching
+       file,  so the decision about whether a file is of interest can be based
+       on any property of the file, not just its filename. As an example,  the
+       provided cpl_check_exe() callback function looks at the executable per-
+       missions of the file and the permissions of its parent directories, and
+       only  returns  1  if  the user has execute permission to the file. This
+       callback function can thus be used to lookup or complete command  names
+       found  in  the  directories listed in the user's PATH environment vari-
+       able. The example program given earlier in this  man  page  provides  a
+       demonstration of this.
+
+       Beware  that  if somebody tries to complete an empty string, your call-
+       back will get called once for every file in the cache, which could num-
+       ber  in  the  thousands. If your callback does anything time consuming,
+       this could result in an unacceptable delay for the user,  so  callbacks
+       should be kept short.
+
+       To  improve performance, whenever one of these callbacks is called, the
+       choice that it makes is cached, and the  next  time  the  corresponding
+       file  is  looked  up, instead of calling the callback again, the cached
+       record of whether it was accepted or rejected is used. Thus if somebody
+       tries  to  complete  an  empty  string, and hits tab a second time when
+       nothing appears to happen, there will only be one long delay, since the
+       second  pass  will operate entirely from the cached dispositions of the
+       files. These cached dipositions are discarded whenever  pca_scan_path()
+       is called, and whenever pca_set_check_fn() is called with changed call-
+       back function or data arguments.
+
+
+

ERROR HANDLING

+       If pca_scan_path() reports that an error occurred by returning  1,  you
+       can   obtain   a   terse   description   of   the   error   by  calling
+       pca_last_error(pc). This returns an internal string containing an error
+       message.
+
+         const char *pca_last_error(PathCache *pc);
+
+
+
+

CLEANING UP

+       Once  you  have  finished using a PathCache object, you can reclaim its
+       resources by passing it to  the  del_PathCache()  destructor  function.
+       This  takes a pointer to one of these objects, and always returns NULL.
+
+         PathCache *del_PathCache(PathCache *pc);
+
+
+

THREAD SAFETY

+       In multi-threaded programs, you should use the libtecla_r.a version  of
+       the library. This uses POSIX reentrant functions where available (hence
+       the _r suffix), and disables features that rely on non-reentrant system
+       functions.  In  the  case  of this module, the only disabled feature is
+       username completion  in  ~username/  expressions,  in  cpl_path_comple-
+       tions().
+
+       Using  the  libtecla_r.a  version of the library, it is safe to use the
+       facilities of this module  in  multiple  threads,  provided  that  each
+       thread uses a separately allocated PathCache object. In other words, if
+       two threads want to do path searching, they should each call  new_Path-
+       Cache() to allocate their own caches.
+
+
+

FILES

+       libtecla.a    -    The tecla library
+       libtecla.h    -    The tecla header file.
+
+
+

SEE ALSO

+       libtecla, gl_get_line, ef_expand_file,
+       cpl_complete_word
+
+
+

AUTHOR

+       Martin Shepherd  (mcs@astro.caltech.edu)
+
+
+
+                                                pca_lookup_file
+
+ diff --git a/libtecla-1.6.1/html/release.html b/libtecla-1.6.1/html/release.html new file mode 100644 index 0000000..b048f4f --- /dev/null +++ b/libtecla-1.6.1/html/release.html @@ -0,0 +1,590 @@ +The tecla library release notes +
+This file lists major changes which accompany each new release.
+
+Version 1.6.1:
+
+  This is primarily a minor bug-fix release.
+
+  One added feature is the ability to call gl_normal_io() from
+  callbacks registered by gl_watch_fd() and
+  gl_inactivity_timeout(). This allows these callbacks to cleanly
+  suspend line editing before either reading from the terminal, or
+  writing to the terminal; and then subsequently causes the input line
+  to be automatically redisplayed, and line-editing to be resumed by
+  gl_get_line(), as soon as the callback returns.
+
+  Another minor change is that if the terminal type specified in the
+  TERM environment variable is set to "dumb", gl_get_line() now treats
+  the terminal as though it were a non-interactive stream, rather than
+  treating it as a VT100-compatible terminal. This means that it
+  doesn't either prompt for input, or perform any command-line
+  editing, even when it really is interacting with a terminal. This is
+  aimed at the rare situation where a third-pary program that connects
+  to libtecla through an embedded pseudo-terminal, needs to be forced
+  to behave as though it weren't talking to a terminal, in order that
+  it be useable in non-interactive scripts.
+
+  Note that in the previous release, the optional configuration
+  function, gl_tty_signals(), was incorrectly swapping the suspend and
+  terminal signal handlers before installing them.
+
+  A configuration problem that prevented select() from being used
+  under MacOS X, has been fixed.
+
+  Although not documented in the man page, it was meant to be possible
+  to take the input line that one call to gl_get_line() returned, and
+  ask the next call to gl_get_line() to present it back to the user
+  for re-editing, simply by passing the pointer returned by one call
+  to gl_get_line() as the start_line argument of the next call to
+  gl_get_line(). This feature unfortunately stopped working in 1.6.0,
+  so this release restores it, and officially documents it in the man
+  page documentation of gl_get_line().
+
+  In the previous version of the library, calling gl_terminal_size()
+  on a system without SIGWINCH support, would crash the
+  application. This has been fixed.
+
+  Libtecla now apparently compiles cleanly under IRIX.
+
+Version 1.6.0:
+
+  This release is primarily a bug-fix release. However there are also
+  four new functions, so the minor version number has been
+  incremented to reflect this.
+
+  Two of the new functions are gl_automatic_history() and
+  gl_append_history(). The former of these functions allows the
+  application to tell gl_get_line() not to automatically archive
+  entered lines in the history list. The second of these functions
+  allows the application to explicitly append a line to the history
+  list. Thus together, these two functions allow the calling
+  application to take over control of what is placed in the history
+  list.
+
+  The third new function is gl_query_char(), which prompts the user
+  for a single character reply, which the user can then type without
+  having to hit return to enter it. Unless echoing is disabled, the
+  character that is entered is then displayed after the prompt,
+  and a newline is started.
+
+  Finally, the 4th new function is gl_read_char(), which also reads
+  a single character from the user, but doesn't prompt the user, write
+  anything to the terminal, or disturb any partially entered input
+  line. It is thus safe to call this function not only from between
+  calls to gl_get_line(), but also from application callback
+  functions, even if gl_normal_io() hasn't been called.
+
+  When using the history-search-backwards or history-search-forwards
+  actions, if the search prefix that the user typed, contains any of
+  the *,? or [ globbing characters, it is now treated as a glob
+  pattern to be matched against historical lines, instead of a simple
+  prefix.
+
+  I have added a --without-file-system option to the configure
+  script. This is intended for use in embedded systems that either
+  don't have filesystems, or where the file-system code in libtecla is
+  seen as unwanted bloat. See the INSTALL document for details.
+
+  Similarly, I also added a --without-file-actions option to the
+  configure script. This allows the application author/installer to
+  prevent users of gl_get_line() from accessing the filesystem with
+  the builtin actions of gl_get_line(). It does this by removing a
+  number of action functions, such as expand-filename, and list-glob,
+  and by changing the default behavior of other actions, such as
+  complete-word and list-or-eof, to show no completions.
+
+  Now to the bugs that have been fixed. Version 1.5.0 had a lot of big
+  internal changes, so there are a number of bugs that needed to be
+  fixed.  There was a bug which caused a crash if gl_load_history()
+  was called multiple times. There was another bug which caused a
+  prompt not to be displayed on the next line after switching from
+  reading input from a file to reading from the terminal. Also, in
+  tecla configuration files, backslash escaped characters within
+  key-binding key-sequences weren't being escaped. Thus ^\\ got
+  interpretted as a control-\ followed by a \ character instead of as
+  a control-\. There was a bug in the history recall mechanism which
+  caused the search prefix to be forgotten in certain complicated
+  usage scenarios. There was a minor memory leak in the
+  gl_configure_getline() function. Finally, if gl_get_line() was
+  aborted by a signal, or any other abnormal event, the value of errno
+  which originally indicated what had happened, got zeroed by the
+  code that restored the terminal to a usable state. Thus the
+  application couldn't figure out what had caused the error, apart
+  from by looking at gl_return_status(). All of these bugs have been
+  fixed.
+
+  In the Makefile, there were a number of places where install-sh was
+  invoked without a path prefix. This has now been remedied.
+
+  A fully functional workaround for a bug in Solaris' terminal I/O
+  code has also been implemented. This bug, which only manifested
+  itself in libtecla's uncommonly used non-blocking server I/O mode,
+  caused characters entered while in normal I/O mode, between calls to
+  gl_get_line() to be invisible to the next call to gl_get_line(),
+  until the user typed at least one more key after raw terminal mode
+  was restored.
+
+  The Gnu autoconf config.guess and config.sub scripts have been
+  updated to their latest versions. Apparently the old versions that I
+  was previously using were too old to know about certain BSD ports.
+  
+Version 1.5.0:
+
+  This release includes several major new features for those using
+  gl_get_line(), shared library support in Darwin, better cross
+  compilation support, and various minor bug fixes.
+
+  The biggest new feature is the option of a non-blocking I/O mode, in
+  which gl_get_line() can safely be called from an application's
+  external event-loop to incrementally read input lines from the user.
+  This feature is documented in the gl_io_mode(3) man page.
+
+  In addition, there is now support for the definition of additional
+  word-completion action functions, which can then be bound to
+  different keys. See the documentation of the gl_completion_action()
+  function in the gl_get_line(3) man page.
+
+  Externally defined action functions can also be defined, although
+  presently they don't have write access to the input line, so they
+  are restricted to operations that display information text to the
+  terminal, or modify the environment of the calling application in
+  some way. See the documentation of the gl_register_action() function
+  in the gl_get_line(3) man page.
+
+  Some of the non-blocking I/O support functions can also be used for
+  improved signal handling in the normal blocking mode. In particular,
+  the gl_list_signals() and gl_catch_blocked() functions make it
+  easier to write reliable signal handling around gl_get_line(). The
+  new "RELIABLE SIGNAL HANDLING" section of the gl_get_line(3) man
+  page is intended as an introduction to this subject.
+
+  Programs can now clear the terminal between calls to gl_get_line(),
+  by calling the new gl_erase_terminal() function.
+
+  The gl_display_text() function, now used in the demos to display
+  introductory banners, is provided for formatting text according to
+  the width of the terminal.
+
+  It is now possible to install inactivity timeout callbacks in
+  gl_get_line(), using the new gl_inactivity_timeout() function.
+
+  The new gl_set_term_size() function allows the application to
+  explicitly set the terminal size, for cases, such as when one is
+  using a terminal at the end of a serial lineq, where the terminal
+  driver doesn't send the process a SIGWINCH when the terminal size
+  changes.
+
+  The new gl_bind_keyseq() function provides a convenient
+  alternative to gl_configure_getline(), for binding or unbinding
+  one key-sequence at a time.
+
+  gl_get_line()s signal handling, file-descriptor event-handling,
+  inactivity-timeout handling and server-mode non-blocking I/O
+  features now not only work when input is coming from a terminal, but
+  now also work when input is coming from non-interactive streams,
+  such as files and pipes.
+
+  The history implementation has been re-written to make it more
+  efficient and easier to modify. The biggest user-level change is
+  that when recalling history lines using a search prefix, the same
+  line is no longer returned more than once in a row.  Previously this
+  duplicate elimination only worked when one was recalling a line
+  without specifying a search prefix, and this was naively performed
+  by preventing neighboring duplicates from existing in the history
+  list, rather than by skipping duplicates at search time.
+
+  In previous versions of the library, when gl_get_line() and its
+  associated public functions detected invalid arguments, or couldn't
+  allocate memory, etc, error messages were written to stderr. This
+  isn't appropriate for library functions, so instead of writing such
+  messages to stderr, these messages are now recorded in buffers
+  within the affected GetLine object. The latest error message can
+  then subsequently be queried by calling gl_error_message(). The use
+  of errno has also been expanded, and a new function called
+  gl_return_status() has been provided to expand on the cause of the
+  last return from gl_get_line().
+
+  User level usage and configuration information has now been split
+  out of the gl_get_line(3) man page into a separate tecla(7) man
+  page. The enhance(3) man page has also been renamed to enhance(1).
+
+  When expanding "~/", gl_get_line() now checks for, and returns the
+  value of the HOME environment variable, if it exists, in preference
+  to looking up the directory of the current user in the password
+  file.
+
+  When the terminal was resized to a narrower width, previous versions
+  of gl_get_line() would redraw the line higher up the terminal. This
+  bug has been fixed. A bug in history recall has also been fixed, in
+  which an error message was being generated if one attempted to
+  recall a line while the cursor was at the end of the longest
+  possible input line. A more serious bug, in which callbacks
+  registered by gl_watch_fd() weren't being called for write-events,
+  has also been fixed. Finally, a few minor fixes have been made to
+  improve support under QNX and Mac OS X.
+
+  Beware that in this release, much of the underlying code has
+  undergone some radical re-work, so although backwards compatibility
+  of all documented features has been preserved, there may be some
+  lingering bugs that could break existing programs.  So, if you plan
+  to use this version in production code, please test it as far as
+  possible within your application before releasing it to your
+  clients, and as always, please report any unexpected behavior.
+
+Version 1.4.1:
+
+  This is a maintenance release. It includes minor changes to support
+  Mac OS X (Darwin), the QNX real-time operating system, and Cygwin
+  under Windows. It also fixes an oversight that was preventing the
+  tab key from inserting tab characters when users unbound the
+  complete-word action from it.
+
+Version 1.4.0:
+
+  The contents of the history list can now be saved and restored with
+  the new gl_save_history() and gl_load_history() functions.
+
+  Event handlers can now be registered to watch for and respond to I/O
+  on arbitrary file descriptors while gl_get_line() is waiting for
+  terminal input from the user. See the gl_get_line(3) man page
+  for details on gl_watch_fd().
+
+  As an optional alternative to getting configuration information only
+  from ~/.teclarc, the new gl_configure_getline() function allows
+  configuration commands to be taken from any of, a string, a
+  specified application-specific file, and/or a specified
+  user-specific file. See the gl_get_line(3) man page for details.
+
+  The version number of the library can now be queried using the
+  libtecla_version() function. See the libtecla(3) man page.
+
+  The new gl_group_history() function allows applications to group
+  different types of input line in the history buffer, and arrange for
+  only members of the appropriate group to be recalled on a given call
+  to gl_get_line(). See the gl_get_line(3) man page.
+
+  The new gl_show_history() function displays the current history list
+  to a given stdio output stream. See the gl_get_line(3) man page.
+
+  new_GetLine() now allows you to specify a history buffer size of
+  zero, thus requesting that no history buffer be allocated. You can
+  subsequently resize or delete the history buffer at any time, by
+  calling gl_resize_history(), limit the number of lines that are
+  allowed in the buffer by calling gl_limit_history(), clear either
+  all history lines from the history list, or just the history lines
+  that are associated with the current history group, by calling
+  gl_clear_history, and toggle the history mechanism on and off by
+  calling gl_toggle_history().
+
+  The new gl_terminal_size() function can be used to query the
+  current terminal size. It can also be used to supply a default
+  terminal size on systems where no mechanism is available for
+  looking up the size.
+
+  The contents and configuration of the history list can now be
+  obtained by the calling application, by calling the new
+  gl_lookup_history(), gl_state_of_history(), gl_range_of_history()
+  and gl_size_of_history() functions. See the gl_get_line(3) man page.
+
+  Echoing of the input line as it is typed, can now be turned on and
+  off via the new gl_echo_mode() function. While echoing is disabled,
+  newly entered input lines are omitted from the history list.  See
+  the gl_get_line(3) man page.
+
+  While the default remains to display the prompt string literally,
+  the new gl_prompt_style() function can be used to enable text
+  attribute formatting directives in prompt strings, such as
+  underlining, bold font, and highlighting directives.
+
+  Signal handling in gl_get_line() is now customizable. The default
+  signal handling behavior remains essentially the same, except that
+  the SIGTSTP, SIGTTIN and SIGTTOU are now forwarded to the
+  corresponding signal handler of the calling program, instead of
+  causing a SIGSTOP to be sent to the application.  It is now possible
+  to remove signals from the list that are trapped by gl_get_line(),
+  as well as add new signals to this list. The signal and terminal
+  environments in which the signal handler of the calling program is
+  invoked, and what gl_get_line() does after the signal handler
+  returns, is now customizable on a per signal basis. You can now also
+  query the last signal that was caught by gl_get_line(). This is
+  useful when gl_get_line() aborts with errno=EINTR, and you need to
+  know which signal caused it to abort.
+
+  Key-sequences bound to action functions can now start with printable
+  characters. Previously only keysequences starting with control or
+  meta characters were permitted.
+
+  gl_get_line() is now 8-bit clean. If the calling program has
+  correctly called setlocale(LC_CTYPE,""), then the user can select an
+  alternate locale by setting the standard LC_CTYPE, LC_ALL, or LANG
+  environment variables, and international characters can then be
+  entered directly, either by using a non-US keyboard, or by using a
+  compose key on a standard US keyboard. Note that in locales in which
+  meta characters become printable, meta characters no longer match
+  M-c bindings, which then have to be entered using their escape-c
+  equivalents.  Fortunately most modern terminal emulators either
+  output the escape-c version by default when the meta key is used, or
+  can be configured to do so (see the gl_get_line(3) man page), so in
+  most cases you can continue to use the meta key.
+
+  Completion callback functions can now tell gl_get_line() to return
+  the input line immediately after a successful tab completion, simply
+  by setting the last character of the optional continuation suffix to
+  a newline character (ie. in the call to cpl_add_completion()).
+
+  It is now safe to create and use multiple GetLine objects, albeit
+  still only from a single thread. In conjunction with the new
+  gl_configure_getline() function, this optionally allows multiple
+  GetLine objects with different bindings to be used to implement
+  different input modes.
+
+  The edit-mode configuration command now accepts the argument,
+  none. This tells gl_get_line() to revert to using just the native
+  line editing facilities provided by the terminal driver. This could
+  be used if the termcap or terminfo entry of the host terminal were
+  badly corrupted.
+
+  Application callback functions invoked by gl_get_line() can now
+  change the displayed prompt using the gl_replace_prompt() function.
+
+  Their is now an optional program distributed with the library. This
+  is a beta release of a program which adds tecla command-line editing
+  to virtually any third party application without the application
+  needing to be linked to the library. See the enhance(3) man page for
+  further details. Although built and installed by default, the
+  INSTALL document explains how to prevent this.
+
+  The INSTALL document now explains how you can stop the demo programs
+  from being built and installed.
+
+  NetBSD/termcap fixes. Mike MacFaden reported two problems that he
+  saw when compiling libtecla under NetBSD. Both cases were related to
+  the use of termcap.  Most systems use terminfo, so this problem has
+  gone unnoticed until now, and won't have affected the grand majority
+  of users.  The configure script had a bug which prevented the check
+  for CPP working properly, and getline.c wouldn't compile due to an
+  undeclared variable when USE_TERMCAP was defined. Both problems have
+  now been fixed. Note that if you successfully compiled version
+  1.3.3, this problem didn't affect you.
+
+  An unfortunate and undocumented binding of the key-sequence M-O was
+  shadowing the arrow-key bindings on systems that use ^[OA etc. I
+  have removed this binding (the documented lower case M-o binding
+  remains bound). Under the KDE konsole terminal this was causing the
+  arrow keys to do something other than expected.
+
+  There was a bug in the history list code which could result in
+  strange entries appearing at the start of the history list once
+  enough history lines had been added to the list to cause the
+  circular history buffer to wrap. This is now fixed.
+ 
+Version 1.3.3:
+
+  Signal handling has been re-written, and documentation of its
+  behaviour has been added to the gl_get_line(3) man page. In addition
+  to eliminating race conditions, and appropriately setting errno for
+  those signals that abort gl_get_line(), many more signals are now
+  intercepted, making it less likely that the terminal will be left in
+  raw mode by a signal that isn't trapped by gl_get_line().
+
+  A bug was also fixed that was leaving the terminal in raw mode if
+  the editing mode was changed interactively between vi and emacs.
+  This was only noticeable when running programs from old shells that
+  don't reset terminal modes.
+
+Version 1.3.2:
+
+  Tim Eliseo contributed a number of improvements to vi mode,
+  including a fuller set of vi key-bindings, implementation of the vi
+  constraint that the cursor can't backup past the point at which
+  input mode was entered, and restoration of overwritten characters
+  when backspacing in overwrite mode. There are also now new bindings
+  to allow users to toggle between vi and emacs modes interactively.
+  The terminal bell is now used in some circumstances, such as when an
+  unrecognized key sequence is entered. This can be turned off by the
+  new nobeep option in the tecla configuration file.
+
+  Unrelated to the above, a problem under Linux which prevented ^Q
+  from being used to resume terminal output after the user had pressed
+  ^S, has been fixed.
+
+Version 1.3.1:
+
+  In vi mode a bug was preventing the history-search-backward and
+  history-search-forward actions from doing anything when invoked on
+  empty lines. On empty lines they now act like up-history and
+  down-history respectively, as in emacs mode.
+
+  When creating shared libraries under Linux, the -soname directive
+  was being used incorrectly. The result is that Linux binaries linked
+  with the 1.2.3, 1.2.4 and 1.3.0 versions of the tecla shared
+  libraries, will refuse to see other versions of the shared library
+  until relinked with version 1.3.1 or higher.
+
+  The configure script can now handle the fact that under Solaris-2.6
+  and earlier, the only curses library is a static one that hides in
+  /usr/ccs/lib. Under Linux it now also caters for old versions of GNU
+  ld which don't accept version scripts.
+
+  The demos are now linked against the shared version of the library
+  if possible. Previously they were always linked with the static
+  version.
+
+Version 1.3.0:
+
+  The major change in this release is the addition of an optional vi
+  command-line editing mode in gl_get_line(), along with lots of new
+  action functions to support its bindings. To enable this, first
+  create a ~/.teclarc file if you don't already have one, then add the
+  following line to it.
+
+   edit-mode vi
+
+  The default vi bindings, which are designed to mimic those of the vi
+  editor as closely as possible, are described in the gl_get_line(3)
+  man page.
+
+  A new convenience function called ef_list_expansions() has been
+  added for listing filename expansions. See the ef_list_expansions(3)
+  man page for details. This is used in a new list-glob binding, bound
+  to ^Xg in emacs mode, and ^G in vi input mode.
+
+  A bug has been fixed in the key-binding table expansion code. This
+  bug would have caused problems to anybody who defined more than
+  about 18 personalized key-bindings in their ~/.teclarc file.
+
+Version 1.2.4:
+
+  Buffered I/O is now used for writing to terminals, and where
+  supported, cursor motion is done with move-n-positions terminfo
+  capabilities instead of doing lots of move-1-position requests. This
+  greatly improves how the library feels over slow links.
+
+  You can now optionally compile different architectures in different
+  directories, without having to make multiple copies of the
+  distribution. This is documented in the INSTALL file.
+
+  The ksh ~+ directive is now supported.
+
+  Thanks to Markus Gyger for the above improvements.
+
+  Documentation has been added to the INSTALL file describing features
+  designed to facilitate configuration and installation of the library
+  as part of larger packages. These features are intended to remove
+  the need to modify the tecla distribution's configuration and build
+  procedures when embedding the libtecla distribution in other package
+  distributions.
+
+  A previous fix to stop the cursor from warping when the last
+  character of the input line was in the last column of the terminal,
+  was only being used for the first terminal line of the input line.
+  It is now used for all subsequent lines as well, as originally
+  intended.
+  
+Version 1.2.3:
+
+  The installation procedure has been better automated with the
+  addition of an autoconf configure script. This means that installers
+  can now compile and install the library by typing:
+
+    ./configure
+    make
+    make install
+
+  On all systems this makes at least the normal static version of the
+  tecla library. It also makes the reentrant version if reentrant
+  POSIX functions are detected.  Under Solaris, Linux and HP-UX the
+  configuration script arranges for shared libraries to be compiled in
+  addition to the static libraries.  It is hoped that installers will
+  return information about how to compile shared libraries on other
+  systems, for inclusion in future releases, and to this end, a new
+  PORTING guide has been provided.
+
+  The versioning number scheme has been changed. This release would
+  have been 1.2c, but instead will be refered to as 1.2.3. The
+  versioning scheme, based on conventions used by Sun Microsystems, is
+  described in configure.in.
+
+  The library was also tested under HP-UX, and this revealed two
+  serious bugs, both of which have now been fixed.
+  
+  The first bug prevented the library from writing control codes to
+  terminals on big-endian machines, with the exception of those
+  running under Solaris. This was due to an int variable being used
+  where a char was needed.
+
+  The second bug had the symptom that on systems that don't use the
+  newline character as the control code for moving the cursor down a
+  line, a newline wasn't started when the user hit enter.
+
+Version 1.2b:
+
+  Two more minor bug fixes:
+
+  Many terminals don't wrap the cursor to the next line when a
+  character is written to the rightmost terminal column. Instead, they
+  delay starting a new line until one more character is written, at
+  which point they move the cursor two positions.  gl_get_line()
+  wasn't aware of this, so cursor repositionings just after writing
+  the last character of a column, caused it to erroneously go up a
+  line. This has now been remedied, using a method that should work
+  regardless of whether a terminal exhibits this behavior or not.
+
+  Some systems dynamically record the current terminal dimensions in
+  environment variables called LINES and COLUMNS. On such systems,
+  during the initial terminal setup, these values should override the
+  static values read from the terminal information databases, and now
+  do.  Previously they were only used if the dimensions returned by
+  terminfo/termcap looked bogus.
+
+Version 1.2a:
+
+  This minor release fixes the following two bugs:
+
+  The initial terminal size and subsequent changes thereto, weren't
+  being noticed by gl_get_line(). This was because the test for the
+  existence of TIOCWINSZ was erroneously placed before the inclusion
+  of termios.h. One of the results was that on input lines that
+  spanned more than one terminal line, the cursor occasionally jumped
+  unexpectedly to the previous terminal line.
+
+  On entering a line that wrapped over multiple terminal lines,
+  gl_get_line() simply output a carriage-return line-feed at the point
+  at which the user pressed return. Thus if one typed in such a line,
+  then moved back onto one of the earlier terminal lines before
+  hitting return, the cursor was left on a line containing part of the
+  line that had just been entered. This didn't do any harm, but it
+  looked a mess.
+
+Version 1.2:
+
+  A new facility for looking up and completing filenames in UNIX-style
+  paths has now been added (eg. you can search for, or complete
+  commands using the UNIX PATH environment variable). See the
+  pca_lookup_file(3) man page.
+
+  The already existing filename completion callback can now be made
+  selective in what types of files it lists. See the
+  cpl_complete_word(3) man page.
+
+  Due to its potential to break applications when changed, the use of
+  the publically defined CplFileArgs structure to configure the
+  cpl_file_completions() callback is now deprecated.  The definition
+  of this structure has been frozen, and its documentation has been
+  removed from the man pages.  It will remain supported, but if you
+  have used it, you are recommended to switch to the new method, which
+  involves a new opaque configuration object, allocated via a provided
+  constructor function, configured via accessor functions, and
+  eventually deleted with a provided destructor function. The
+  cpl_file_completions() callback distinguishes which structure type
+  it has been sent by virtue of a code placed at the start of the new
+  structure by the constructor.  It is assumed that no existing
+  applications set the boolean 'escaped' member of the CplFileArgs
+  structure to 4568.  The new method is documented in the
+  cpl_complete_word(3) man page.
+
+Version 1.1j
+
+  This was the initial public release on freshmeat.org.
+
diff --git a/libtecla-1.6.1/html/tecla.html b/libtecla-1.6.1/html/tecla.html new file mode 100644 index 0000000..6f270c1 --- /dev/null +++ b/libtecla-1.6.1/html/tecla.html @@ -0,0 +1,1120 @@ + +Manual Page + + +
+tecla                                      tecla
+
+
+
+

NAME

+       tecla, teclarc - The user interface provided by the Tecla library.
+
+

DESCRIPTION

+       This  man  page  describes  the  command-line editing features that are
+       available to users of programs that read keyboard input via  the  Tecla
+       library.  Users  of  the  tcsh shell will find the default key-bindings
+       very familiar. Users of the bash shell will also find it  quite  famil-
+       iar,  but with a few minor differences, most notably in how forward and
+       backward searches through the list  of  historical  commands  are  per-
+       formed.  There  are  two  major editing modes, one with emacs-like key-
+       bindings and another with vi-like key-bindings. By default  emacs  mode
+       is  enabled,  but  vi mode can alternatively be selected via the user's
+       configuration file. This file can also be used to change  the  bindings
+       of individual keys to suit the user's preferences. By default, tab com-
+       pletion is provided. If the application  hasn't  reconfigured  this  to
+       complete  other  types  of symbols, then tab completion completes file-
+       names.
+
+
+

KEY SEQUENCE NOTATION

+       In the rest of this man page,  and  also  in  all  Tecla  configuration
+       files, key-sequences are expressed as follows.
+
+
+       ^A  or  C-a
+           This is a control-A, entered by pressing the control key at
+           the same time as the A key.
+
+       \E    or   M-
+           In key-sequences, both of these notations can be entered
+           either by pressing the escape key, then the following key, or by
+           pressing the Meta key at the same time as the following key. Thus
+           the key sequence M-p can be typed in two ways, by pressing
+           the escape key, followed by pressing p, or by pressing the
+           Meta key at the same time as p.
+
+       up
+           This refers to the up-arrow key.
+
+       down
+           This refers to the down-arrow key.
+
+       left
+           This refers to the left-arrow key.
+
+       right
+           This refers to the right-arrow key.
+
+       a
+           This is just a normal A key.
+
+
+
+

THE TECLA CONFIGURATION FILE

+       By  default, Tecla looks for a file called .teclarc in your home direc-
+       tory (ie. ~/.teclarc).  If it finds this file, it reads it,  interpret-
+       ing each line as defining a new key binding or an editing configuration
+       option. Since the emacs keybindings are installed by  default,  if  you
+       want to use the non-default vi editing mode, the most important item to
+       go in this file is the following line:
+
+         edit-mode vi
+
+       This will re-configure the default bindings for vi-mode.  The  complete
+       set of arguments that this command accepts are:
+
+         vi     -  Install key-bindings like those of the vi
+                   editor.
+         emacs  -  Install key-bindings like those of the emacs
+                   editor. This is the default.
+         none   -  Use just the native line editing facilities
+                   provided by the terminal driver.
+
+       To  prevent the terminal bell from being rung, such as when an unrecog-
+       nized control-sequence is typed, place the following line in  the  con-
+       figuration file:
+
+         nobeep
+
+       An  example of a key binding line in the configuration file is the fol-
+       lowing.
+
+         bind M-[2~ insert-mode
+
+       On many keyboards, the above key sequence is generated when one presses
+       the  insert  key,  so  with this keybinding, one can toggle between the
+       emacs-mode insert and overwrite modes by hitting  one  key.  One  could
+       also  do  it by typing out the above sequence of characters one by one.
+       As explained above, the M- part of this sequence can be typed either by
+       pressing  the  escape  key before the following key, or by pressing the
+       Meta key at the same time as the following key. Thus if you had set the
+       above  key binding, and the insert key on your keyboard didn't generate
+       the above key sequence, you could still type it in either of  the  fol-
+       lowing 2 ways.
+
+         1. Hit the escape key momentarily, then press '[', then '2', then
+            finally '~'.
+
+         2. Press the meta key at the same time as pressing the '[' key,
+            then press '2', then '~'.
+
+       If  you  set a keybinding for a key-sequence that is already bound to a
+       function, the new binding overrides the old one. If in the new  binding
+       you  omit the name of the new function to bind to the key-sequence, the
+       original binding becomes undefined.
+
+       Starting with versions of libtecla later than 1.3.3 it is now  possible
+       to  bind keysequences that begin with a printable character. Previously
+       key-sequences were required to start with a control or meta  character.
+
+       Note  that  the special keywords "up", "down", "left" and "right" refer
+       to the arrow keys, and are thus not treated as  keysequences.  So,  for
+       example, to rebind the up and down arrow keys to use the history search
+       mechanism instead of the simple history recall method, you could  place
+       the following in your configuration file:
+
+         bind up history-search-backwards
+         bind down history-search-backwards
+
+       To unbind an existing binding, you can do this with the bind command by
+       omitting to name any action to rebind the key sequence to.   For  exam-
+       ple,  by  not  specifying  an  action  function,  the following command
+       unbinds the default beginning-of-line action from the ^A key sequence:
+
+         bind ^A
+
+       If you create a ~/.teclarc configuration file, but it appears  to  have
+       no effect on the program, check the documentation of the program to see
+       if the author chose a different name for this file.
+
+
+

FILENAME AND TILDE COMPLETION

+       With the default key bindings, pressing the TAB key (aka.  ^I)  results
+       in  Tecla  attempting to complete the incomplete filename that precedes
+       the cursor. Tecla searches backwards from the cursor, looking  for  the
+       start  of  the  filename,  stopping  when it hits either a space or the
+       start of the line. If more than one file has the specified prefix, then
+       Tecla  completes  the  filename  up to the point at which the ambiguous
+       matches start to differ, then lists the possible matches.
+
+       In addition to literally written filenames, Tecla  can  complete  files
+       that  start  with  ~/  and  ~user/ expressions and that contain $envvar
+       expressions. In particular, if you hit TAB within an incomplete  ~user,
+       expression,  Tecla  will  attempt to complete the username, listing any
+       ambiguous matches.
+
+       The completion binding is implemented using the  cpl_word_completions()
+       function,  which is also available separately to users of this library.
+       See the cpl_word_completions(@LIBR_MANEXT@) man page for more  details.
+
+
+

FILENAME EXPANSION

+       With  the default key bindings, pressing ^X* causes Tecla to expand the
+       filename that precedes the cursor, replacing ~/ and ~user/  expressions
+       with  the corresponding home directories, and replacing $envvar expres-
+       sions with the value of the specified  environment  variable,  then  if
+       there  are any wildcards, replacing the so far expanded filename with a
+       space-separated list of the files which match the wild cards.
+
+       The expansion binding is implemented using the  ef_expand_file()  func-
+       tion.  See the ef_expand_file man page for more details.
+
+
+

RECALLING PREVIOUSLY TYPED LINES

+       Every time that a new line is entered by the user, it is appended to  a
+       list  of  historical input lines maintained within the GetLine resource
+       object. You can traverse up and down this list using the  up  and  down
+       arrow  keys.  Alternatively,  you  can  do the same with the ^P, and ^N
+       keys, and in vi command mode you can alternatively  use  the  k  and  j
+       characters.  Thus  pressing  up-arrow  once, replaces the current input
+       line  with  the  previously  entered  line.  Pressing  up-arrow  again,
+       replaces  this  with  the line that was entered before it, etc.. Having
+       gone back one or more lines into the history list, one  can  return  to
+       newer  lines  by  pressing down-arrow one or more times. If you do this
+       sufficient times, you will return to the original line  that  you  were
+       entering when you first hit up-arrow.
+
+       Note  that  in  vi mode, all of the history recall functions switch the
+       library into command mode.
+
+       In emacs mode the M-p and M-n keys work just like the ^P and  ^N  keys,
+       except  that  they  skip all but those historical lines which share the
+       prefix that precedes the cursor. In vi command mode the  upper  case  K
+       and  J  characters  do the same thing, except that the string that they
+       search for includes the character under the cursor as well as what pre-
+       cedes it.
+
+       Thus for example, suppose that you were in emacs mode, and you had just
+       entered the following list of commands in the order shown:
+
+         ls ~/tecla/
+         cd ~/tecla
+         ls -l getline.c
+         emacs ~/tecla/getline.c
+
+       If you next typed:
+
+         ls
+
+       and then hit M-p, then rather than returning the previously typed emacs
+       line, which doesn't start with "ls", Tecla would recall the "ls -l get-
+       line.c" line. Pressing M-p again would recall the "ls ~/tecla/" line.
+
+       Note that if the string that you are searching for, contains any of the
+       special  characters, *, ?, or '[', then it is interpretted as a pattern
+       to be matched. Thus, cotinuing with the above example, after typing  in
+       the list of commands shown, if you then typed:
+
+         *tecla*
+
+       and  hit M-p, then the "emacs ~/tecla/getline.c" line would be recalled
+       first, since it contains the word tecla somewhere in  the  line,  Simi-
+       larly, hitting M-p again, would recall the "ls ~/tecla/" line, and hit-
+       ting it once more would recall the "ls ~/tecla/" line. The pattern syn-
+       tax  is  the  same  as  that  described  for filename expansion, in the
+       ef_expand_file(@LIBR_MANEXT@ man page.
+
+
+

HISTORY FILES

+       Authors of programs that use the Tecla library have the option of  sav-
+       ing historical command-lines in a file before exiting, and subsequently
+       reading them back in from this file when the program is  next  started.
+       There  is no standard name for this file, since it makes sense for each
+       application to use its own history file, so that commands from  differ-
+       ent applications don't get mixed up.
+
+
+

INTERNATIONAL CHARACTER SETS

+       Since  libtecla  version  1.4.0, Tecla has been 8-bit clean. This means
+       that all 8-bit characters that are  printable  in  the  user's  current
+       locale  are  now  displayed verbatim and included in the returned input
+       line.  Assuming that the calling program correctly contains a call like
+       the following,
+
+         setlocale(LC_CTYPE, "");
+
+       then  the  current locale is determined by the first of the environment
+       variables LC_CTYPE, LC_ALL, and LANG, that is found to contain a  valid
+       locale  name.  If  none  of these variables are defined, or the program
+       neglects to call setlocale, then the default C locale is used, which is
+       US  7-bit  ASCII.  On  most  unix-like platforms, you can get a list of
+       valid locales by typing the command:
+
+         locale -a
+
+       at the shell prompt.
+
+
+   Meta keys and locales
+       Beware that in most locales other than the default C locale, meta char-
+       acters  become  printable,  and  they  are then no longer considered to
+       match M-c style key bindings. This allows international  characters  to
+       be  entered  with  the compose key without unexpectedly triggering meta
+       key bindings. You can still invoke meta bindings, since there are actu-
+       ally  two  ways  to  do  this.  For example the binding M-c can also be
+       invoked by pressing the escape key momentarily,  then  pressing  the  c
+       key,  and  this  will  work regardless of locale. Moreover, many modern
+       terminal emulators, such as gnome's gnome-terminal's and KDE's  konsole
+       terminals,  already  generate  escape  pairs like this when you use the
+       meta key, rather than a real meta character, and other  emulators  usu-
+       ally  have  a  way to request this behavior, so you can continue to use
+       the meta key on most systems.
+
+       For example, although xterm terminal emulators generate real 8-bit meta
+       characters by default when you use the meta key, they can be configured
+       to output the equivalent escape pair by setting their  EightBitInput  X
+       resource  to  False.  You can either do this by placing a line like the
+       following in your ~/.Xdefaults file,
+
+         XTerm*EightBitInput: False
+
+       or by starting an xterm with an -xrm '*EightBitInput:  False'  command-
+       line  argument. In recent versions of xterm you can toggle this feature
+       on and off with the "Meta Sends Escape" option in the menu that is dis-
+       played  when you press the left mouse button and the control key within
+       an xterm window. In CDE, dtterms can be similarly coerced  to  generate
+       escape pairs in place of meta characters, by setting the Dtterm*KshMode
+       resource to True.
+
+
+   Entering international characters
+       If you don't have a keyboard that generates all  of  the  international
+       characters  that  you  need,  there  is usually a compose key that will
+       allow you to enter special characters, or a  way  to  create  one.  For
+       example, under X windows on unix-like systems, if your keyboard doesn't
+       have a compose key, you can designate a redundant  key  to  serve  this
+       purpose  with  the  xmodmap  command. For example, on many PC keyboards
+       there is a microsoft-windows key,  which  is  otherwise  useless  under
+       Linux. On my laptop the xev program reports that pressing this key gen-
+       erates keycode 115, so to turn this key into a compose key,  I  do  the
+       following:
+
+         xmodmap -e 'keycode 115 = Multi_key'
+
+       I  can  then  enter an i with a umlaut over it by typing this key, fol-
+       lowed by ", followed by i.
+
+
+

THE AVAILABLE KEY BINDING FUNCTIONS

+       The following is a list of the editing functions provided by the  Tecla
+       library.  The  names  in the leftmost column of the list can be used in
+       configuration files to specify which function a given key  or  combina-
+       tion of keys should invoke. They are also used in the next two sections
+       to list the default key-bindings in emacs and vi modes.
+
+         user-interrupt           -  Send a SIGINT signal to the
+                                     parent process.
+         abort                    -  Send a SIGABRT signal to the
+                                     parent process.
+         suspend                  -  Suspend the parent process.
+         stop-output              -  Pause terminal output.
+         start-output             -  Resume paused terminal output.
+         literal-next             -  Arrange for the next character
+                                     to be treated as a normal
+                                     character. This allows control
+                                     characters to be entered.
+         cursor-right             -  Move the cursor one character
+                                     right.
+         cursor-left              -  Move the cursor one character
+                                     left.
+         insert-mode              -  Toggle between insert mode and
+                                     overwrite mode.
+         beginning-of-line        -  Move the cursor to the
+                                     beginning of the line.
+         end-of-line              -  Move the cursor to the end of
+                                     the line.
+         delete-line              -  Delete the contents of the
+                                     current line.
+         kill-line                -  Delete everything that follows
+                                     the cursor.
+         backward-kill-line       -  Delete all characters between
+                                     the cursor and the start of the
+                                     line.
+         forward-word             -  Move to the end of the word
+                                     which follows the cursor.
+         forward-to-word          -  Move the cursor to the start of
+                                     the word that follows the
+                                     cursor.
+         backward-word            -  Move to the start of the word
+                                     which precedes the cursor.
+         goto-column              -  Move the cursor to the
+                                     1-relative column in the line
+                                     specified by any preceding
+                                     digit-argument sequences (see
+                                     ENTERING REPEAT COUNTS below).
+         find-parenthesis         -  If the cursor is currently
+                                     over a parenthesis character,
+                                     move it to the matching
+                                     parenthesis character. If not
+                                     over a parenthesis character
+                                     move right to the next close
+                                     parenthesis.
+         forward-delete-char      -  Delete the character under the
+                                     cursor.
+         backward-delete-char     -  Delete the character which
+                                     precedes the cursor.
+         list-or-eof              -  This is intended for binding
+                                     to ^D. When invoked when the
+                                     cursor is within the line it
+                                     displays all possible
+                                     completions then redisplays
+                                     the line unchanged. When
+                                     invoked on an empty line, it
+                                     signals end-of-input (EOF) to
+                                     the caller of gl_get_line().
+         del-char-or-list-or-eof  -  This is intended for binding
+                                     to ^D. When invoked when the
+                                     cursor is within the line it
+                                     invokes forward-delete-char.
+                                     When invoked at the end of the
+                                     line it displays all possible
+                                     completions then redisplays
+                                     the line unchanged. When
+                                     invoked on an empty line, it
+                                     signals end-of-input (EOF) to
+                                     the caller of gl_get_line().
+         forward-delete-word      -  Delete the word which follows
+                                     the cursor.
+         backward-delete-word     -  Delete the word which precedes
+                                     the cursor.
+         upcase-word              -  Convert all of the characters
+                                     of the word which follows the
+                                     cursor, to upper case.
+         downcase-word            -  Convert all of the characters
+                                     of the word which follows the
+                                     cursor, to lower case.
+         capitalize-word          -  Capitalize the word which
+                                     follows the cursor.
+         change-case              -  If the next character is upper
+                                     case, toggle it to lower case
+                                     and vice versa.
+         redisplay                -  Redisplay the line.
+         clear-screen             -  Clear the terminal, then
+                                     redisplay the current line.
+         transpose-chars          -  Swap the character under the
+                                     cursor with the character just
+                                     before the cursor.
+         set-mark                 -  Set a mark at the position of
+                                     the cursor.
+         exchange-point-and-mark  -  Move the cursor to the last
+                                     mark that was set, and move
+                                     the mark to where the cursor
+                                     used to be.
+         kill-region              -  Delete the characters that lie
+                                     between the last mark that was
+                                     set, and the cursor.
+         copy-region-as-kill      -  Copy the text between the mark
+                                     and the cursor to the cut
+                                     buffer, without deleting the
+                                     original text.
+         yank                     -  Insert the text that was last
+                                     deleted, just before the
+                                     current position of the cursor.
+         append-yank              -  Paste the current contents of
+                                     the cut buffer, after the
+                                     cursor.
+         up-history               -  Recall the next oldest line
+                                     that was entered. Note that
+                                     in vi mode you are left in
+                                     command mode.
+         down-history             -  Recall the next most recent
+                                     line that was entered. If no
+                                     history recall session is
+                                     currently active, the next
+                                     line from a previous recall
+                                     session is recalled. Note that
+                                     in vi mode you are left in
+                                     command mode.
+         history-search-backward  -  Recall the next oldest line
+                                     who's prefix matches the string
+                                     which currently precedes the
+                                     cursor (in vi command-mode the
+                                     character under the cursor is
+                                     also included in the search
+                                     string).  Note that in vi mode
+                                     you are left in command mode.
+         history-search-forward   -  Recall the next newest line
+                                     who's prefix matches the string
+                                     which currently precedes the
+                                     cursor (in vi command-mode the
+                                     character under the cursor is
+                                     also included in the search
+                                     string).  Note that in vi mode
+                                     you are left in command mode.
+         history-re-search-backward -Recall the next oldest line
+                                     who's prefix matches that
+                                     established by the last
+                                     invocation of either
+                                     history-search-forward or
+                                     history-search-backward.
+         history-re-search-forward - Recall the next newest line
+                                     who's prefix matches that
+                                     established by the last
+                                     invocation of either
+                                     history-search-forward or
+                                     history-search-backward.
+         complete-word            -  Attempt to complete the
+                                     incomplete word which
+                                     precedes the cursor. Unless
+                                     the host program has customized
+                                     word completion, filename
+                                     completion is attempted. In vi
+                                     commmand mode the character
+                                     under the cursor is also
+                                     included in the word being
+                                     completed, and you are left in
+                                     vi insert mode.
+         expand-filename          -  Within the command line, expand
+                                     wild cards, tilde expressions
+                                     and dollar expressions in the
+                                     filename which immediately
+                                     precedes the cursor. In vi
+                                     commmand mode the character
+                                     under the cursor is also
+                                     included in the filename being
+                                     expanded, and you are left in
+                                     vi insert mode.
+         list-glob                -  List any filenames which match
+                                     the wild-card, tilde and dollar
+                                     expressions in the filename
+                                     which immediately precedes the
+                                     cursor, then redraw the input
+                                     line unchanged.
+         list-history             -  Display the contents of the
+                                     history list for the current
+                                     history group. If a repeat
+                                     count of > 1 is specified,
+                                     only that many of the most
+                                     recent lines are displayed.
+                                     See the "ENTERING REPEAT
+                                     COUNTS" section.
+         read-from-file           -  Temporarily switch to reading
+                                     input from the file who's
+                                     name precedes the cursor.
+         read-init-files          -  Re-read teclarc configuration
+                                     files.
+         beginning-of-history     -  Move to the oldest line in the
+                                     history list. Note that in vi
+                                     mode you are left in command
+                                     mode.
+         end-of-history           -  Move to the newest line in the
+                                     history list (ie. the current
+                                     line). Note that in vi mode
+                                     this leaves you in command
+                                     mode.
+         digit-argument           -  Enter a repeat count for the
+                                     next key-binding function.
+                                     For details, see the ENTERING
+                                     REPEAT COUNTS section.
+         newline                  -  Terminate and return the
+                                     current contents of the
+                                     line, after appending a
+                                     newline character. The newline
+                                     character is normally '\n',
+                                     but will be the first
+                                     character of the key-sequence
+                                     that invoked the newline
+                                     action, if this happens to be
+                                     a printable character. If the
+                                     action was invoked by the
+                                     '\n' newline character or the
+                                     '\r' carriage return
+                                     character, the line is
+                                     appended to the history
+                                     buffer.
+         repeat-history           -  Return the line that is being
+                                     edited, then arrange for the
+                                     next most recent entry in the
+                                     history buffer to be recalled
+                                     when Tecla is next called.
+                                     Repeatedly invoking this
+                                     action causes successive
+                                     historical input lines to be
+                                     re-executed. Note that this
+                                     action is equivalent to the
+                                     'Operate' action in ksh.
+         ring-bell                -  Ring the terminal bell, unless
+                                     the bell has been silenced via
+                                     the nobeep configuration
+                                     option (see the THE TECLA
+                                     CONFIGURATION FILE section).
+         forward-copy-char        -  Copy the next character into
+                                     the cut buffer (NB. use repeat
+                                     counts to copy more than one).
+         backward-copy-char       -  Copy the previous character
+                                     into the cut buffer.
+         forward-copy-word        -  Copy the next word into the cut
+                                     buffer.
+         backward-copy-word       -  Copy the previous word into the
+                                     cut buffer.
+         forward-find-char        -  Move the cursor to the next
+                                     occurrence of the next
+                                     character that you type.
+         backward-find-char       -  Move the cursor to the last
+                                     occurrence of the next
+                                     character that you type.
+         forward-to-char          -  Move the cursor to the
+                                     character just before the next
+                                     occurrence of the next
+                                     character that the user types.
+         backward-to-char         -  Move the cursor to the
+                                     character just after the last
+                                     occurrence before the cursor
+                                     of the next character that the
+                                     user types.
+         repeat-find-char         -  Repeat the last
+                                     backward-find-char,
+                                     forward-find-char,
+                                     backward-to-char or
+                                     forward-to-char.
+         invert-refind-char       -  Repeat the last
+                                     backward-find-char,
+                                     forward-find-char,
+                                     backward-to-char, or
+                                     forward-to-char in the
+                                     opposite direction.
+         delete-to-column         -  Delete the characters from the
+                                     cursor up to the column that
+                                     is specified by the repeat
+                                     count.
+         delete-to-parenthesis    -  Delete the characters from the
+                                     cursor up to and including
+                                     the matching parenthesis, or
+                                     next close parenthesis.
+         forward-delete-find      -  Delete the characters from the
+                                     cursor up to and including the
+                                     following occurence of the
+                                     next character typed.
+         backward-delete-find     -  Delete the characters from the
+                                     cursor up to and including the
+                                     preceding occurence of the
+                                     next character typed.
+         forward-delete-to        -  Delete the characters from the
+                                     cursor up to, but not
+                                     including, the following
+                                     occurence of the next
+                                     character typed.
+         backward-delete-to       -  Delete the characters from the
+                                     cursor up to, but not
+                                     including, the preceding
+                                     occurence of the next
+                                     character typed.
+         delete-refind            -  Repeat the last *-delete-find
+                                     or *-delete-to action.
+         delete-invert-refind     -  Repeat the last *-delete-find
+                                     or *-delete-to action, in the
+                                     opposite direction.
+         copy-to-column           -  Copy the characters from the
+                                     cursor up to the column that
+                                     is specified by the repeat
+                                     count, into the cut buffer.
+         copy-to-parenthesis      -  Copy the characters from the
+                                     cursor up to and including
+                                     the matching parenthesis, or
+                                     next close parenthesis, into
+                                     the cut buffer.
+         forward-copy-find        -  Copy the characters from the
+                                     cursor up to and including the
+                                     following occurence of the
+                                     next character typed, into the
+                                     cut buffer.
+         backward-copy-find       -  Copy the characters from the
+                                     cursor up to and including the
+                                     preceding occurence of the
+                                     next character typed, into the
+                                     cut buffer.
+         forward-copy-to          -  Copy the characters from the
+                                     cursor up to, but not
+                                     including, the following
+                                     occurence of the next
+                                     character typed, into the cut
+                                     buffer.
+         backward-copy-to         -  Copy the characters from the
+                                     cursor up to, but not
+                                     including, the preceding
+                                     occurence of the next
+                                     character typed, into the cut
+                                     buffer.
+         copy-refind              -  Repeat the last *-copy-find
+                                     or *-copy-to action.
+         copy-invert-refind       -  Repeat the last *-copy-find
+                                     or *-copy-to action, in the
+                                     opposite direction.
+         vi-mode                  -  Switch to vi mode from emacs
+                                     mode.
+         emacs-mode               -  Switch to emacs mode from vi
+                                     mode.
+         vi-insert                -  From vi command mode, switch to
+                                     insert mode.
+         vi-overwrite             -  From vi command mode, switch to
+                                     overwrite mode.
+         vi-insert-at-bol         -  From vi command mode, move the
+                                     cursor to the start of the line
+                                     and switch to insert mode.
+         vi-append-at-eol         -  From vi command mode, move the
+                                     cursor to the end of the line
+                                     and switch to append mode.
+         vi-append                -  From vi command mode, move the
+                                     cursor one position right, and
+                                     switch to insert mode.
+         vi-replace-char          -  From vi command mode, replace
+                                     the character under the cursor
+                                     with the the next character
+                                     entered.
+         vi-forward-change-char   -  From vi command mode, delete
+                                     the next character then enter
+                                     insert mode.
+         vi-backward-change-char  -  From vi command mode, delete
+                                     the preceding character then
+                                     enter insert mode.
+         vi-forward-change-word   -  From vi command mode, delete
+                                     the next word then enter
+                                     insert mode.
+         vi-backward-change-word  -  From vi command mode, delete
+                                     the preceding word then
+                                     enter insert mode.
+         vi-change-rest-of-line   -  From vi command mode, delete
+                                     from the cursor to the end of
+                                     the line, then enter insert
+                                     mode.
+         vi-change-line           -  From vi command mode, delete
+                                     the current line, then enter
+                                     insert mode.
+         vi-change-to-bol         -  From vi command mode, delete
+                                     all characters between the
+                                     cursor and the beginning of
+                                     the line, then enter insert
+                                     mode.
+         vi-change-to-column      -  From vi command mode, delete
+                                     the characters from the cursor
+                                     up to the column that is
+                                     specified by the repeat count,
+                                     then enter insert mode.
+         vi-change-to-parenthesis -  Delete the characters from the
+                                     cursor up to and including
+                                     the matching parenthesis, or
+                                     next close parenthesis, then
+                                     enter vi insert mode.
+         vi-forward-change-find   -  From vi command mode, delete
+                                     the characters from the
+                                     cursor up to and including the
+                                     following occurence of the
+                                     next character typed, then
+                                     enter insert mode.
+         vi-backward-change-find  -  From vi command mode, delete
+                                     the characters from the
+                                     cursor up to and including the
+                                     preceding occurence of the
+                                     next character typed, then
+                                     enter insert mode.
+         vi-forward-change-to     -  From vi command mode, delete
+                                     the characters from the
+                                     cursor up to, but not
+                                     including, the following
+                                     occurence of the next
+                                     character typed, then enter
+                                     insert mode.
+         vi-backward-change-to    -  From vi command mode, delete
+                                     the characters from the
+                                     cursor up to, but not
+                                     including, the preceding
+                                     occurence of the next
+                                     character typed, then enter
+                                     insert mode.
+         vi-change-refind         -  Repeat the last
+                                     vi-*-change-find or
+                                     vi-*-change-to action.
+         vi-change-invert-refind  -  Repeat the last
+                                     vi-*-change-find or
+                                     vi-*-change-to action, in the
+                                     opposite direction.
+         vi-undo                  -  In vi mode, undo the last
+                                     editing operation.
+         vi-repeat-change         -  In vi command mode, repeat the
+                                     last command that modified the
+                                     line.
+
+
+

DEFAULT KEY BINDINGS IN EMACS MODE

+       The following default key bindings, which can be overriden by the Tecla
+       configuration  file,  are designed to mimic most of the bindings of the
+       unix tcsh shell, when it is in emacs editing mode.
+
+       This is the default editing mode of the Tecla library.
+
+       Under UNIX the terminal driver sets a number of special keys  for  cer-
+       tain  functions. The tecla library attempts to use the same keybindings
+       to maintain consistency. The key sequences shown for  the  following  6
+       bindings  are  thus just examples of what they will probably be set to.
+       If you have used the stty  command  to  change  these  keys,  then  the
+       default bindings should match.
+
+         ^C     ->   user-interrupt
+         ^\     ->   abort
+         ^Z     ->   suspend
+         ^Q     ->   start-output
+         ^S     ->   stop-output
+         ^V     ->   literal-next
+
+       The  cursor  keys are refered to by name, as follows. This is necessary
+       because different types of terminals generate different  key  sequences
+       when their cursor keys are pressed.
+
+         right  ->   cursor-right
+         left   ->   cursor-left
+         up     ->   up-history
+         down   ->   down-history
+
+       The remaining bindings don't depend on the terminal setttings.
+
+         ^F     ->   cursor-right
+         ^B     ->   cursor-left
+         M-i    ->   insert-mode
+         ^A     ->   beginning-of-line
+         ^E     ->   end-of-line
+         ^U     ->   delete-line
+         ^K     ->   kill-line
+         M-f    ->   forward-word
+         M-b    ->   backward-word
+         ^D     ->   del-char-or-list-or-eof
+         ^H     ->   backward-delete-char
+         ^?     ->   backward-delete-char
+         M-d    ->   forward-delete-word
+         M-^H   ->   backward-delete-word
+         M-^?   ->   backward-delete-word
+         M-u    ->   upcase-word
+         M-l    ->   downcase-word
+         M-c    ->   capitalize-word
+         ^R     ->   redisplay
+         ^L     ->   clear-screen
+         ^T     ->   transpose-chars
+         ^@     ->   set-mark
+         ^X^X   ->   exchange-point-and-mark
+         ^W     ->   kill-region
+         M-w    ->   copy-region-as-kill
+         ^Y     ->   yank
+         ^P     ->   up-history
+         ^N     ->   down-history
+         M-p    ->   history-search-backward
+         M-n    ->   history-search-forward
+         ^I     ->   complete-word
+         ^X*    ->   expand-filename
+         ^X^F   ->   read-from-file
+         ^X^R   ->   read-init-files
+         ^Xg    ->   list-glob
+         ^Xh    ->   list-history
+         M-<    ->   beginning-of-history
+         M->    ->   end-of-history
+         \n     ->   newline
+         \r     ->   newline
+         M-o    ->   repeat-history
+         M-^V   ->   vi-mode
+
+         M-0, M-1, ... M-9  ->  digit-argument  (see below)
+
+       Note  that  ^I is what the TAB key generates, and that ^@ can be gener-
+       ated not only by pressing the control key and the @ key simultaneously,
+       but  also  by  pressing  the  control key and the space bar at the same
+       time.
+
+
+

DEFAULT KEY BINDINGS IN VI MODE

+       The following default key bindings are designed to mimic the  vi  style
+       of  editing  as  closely  as possible. This means that very few editing
+       functions are provided in the initial  character  input  mode,  editing
+       functions  instead  being  provided  by the vi command mode. Vi command
+       mode is entered whenever the escape character is pressed, or whenever a
+       key-sequence  that starts with a meta character is entered. In addition
+       to mimicing vi, libtecla provides bindings for  tab  completion,  wild-
+       card expansion of file names, and historical line recall.
+
+       To  learn  how  to tell the Tecla library to use vi mode instead of the
+       default emacs editing mode, see the earlier section entitled THE  TECLA
+       CONFIGURATION FILE.
+
+       Under  UNIX  the terminal driver sets a number of special keys for cer-
+       tain functions. The Tecla library attempts to use the same  keybindings
+       to maintain consistency, binding them both in input mode and in command
+       mode. The key sequences shown for the following  6  bindings  are  thus
+       just  examples  of  what they will probably be set to. If you have used
+       the stty command to change these keys, then the default bindings should
+       match.
+
+         ^C     ->   user-interrupt
+         ^\     ->   abort
+         ^Z     ->   suspend
+         ^Q     ->   start-output
+         ^S     ->   stop-output
+         ^V     ->   literal-next
+         M-^C   ->   user-interrupt
+         M-^\   ->   abort
+         M-^Z   ->   suspend
+         M-^Q   ->   start-output
+         M-^S   ->   stop-output
+
+       Note  that above, most of the bindings are defined twice, once as a raw
+       control code like ^C and then a second time as a  meta  character  like
+       M-^C.  The  former is the binding for vi input mode, whereas the latter
+       is the binding for vi command mode.  Once  in  command  mode  all  key-
+       sequences  that the user types that they don't explicitly start with an
+       escape or a meta key, have their first key secretly converted to a meta
+       character  before  the key sequence is looked up in the key binding ta-
+       ble. Thus, once in command mode, when you type the letter i, for  exam-
+       ple, the Tecla library actually looks up the binding for M-i.
+
+       The  cursor  keys are refered to by name, as follows. This is necessary
+       because different types of terminals generate different  key  sequences
+       when their cursor keys are pressed.
+
+         right  ->   cursor-right
+         left   ->   cursor-left
+         up     ->   up-history
+         down   ->   down-history
+
+       The  cursor  keys  normally  generate  a keysequence that start with an
+       escape character, so beware that using the arrow keys will put you into
+       command mode (if you aren't already in command mode).
+
+       The  following  are  the terminal-independent key bindings for vi input
+       mode.
+
+         ^D     ->   list-or-eof
+         ^G     ->   list-glob
+         ^H     ->   backward-delete-char
+         ^I     ->   complete-word
+         \r     ->   newline
+         \n     ->   newline
+         ^L     ->   clear-screen
+         ^N     ->   down-history
+         ^P     ->   up-history
+         ^R     ->   redisplay
+         ^U     ->   backward-kill-line
+         ^W     ->   backward-delete-word
+         ^X*    ->   expand-filename
+         ^X^F   ->   read-from-file
+         ^X^R   ->   read-init-files
+         ^?     ->   backward-delete-char
+
+       The following are the key bindings that are defined in vi command mode,
+       this  being  specified  by  them all starting with a meta character. As
+       mentioned above, once in command mode the  initial  meta  character  is
+       optional.  For example, you might enter command mode by typing Esc, and
+       then press h twice to move the cursor two positions to the left. Both h
+       characters  get  quietly  converted to M-h before being compared to the
+       key-binding table, the first one because Escape followed by a character
+       is  always  converted  to the equivalent meta character, and the second
+       because command mode was already active.
+
+         M-\     ->   cursor-right     (Meta-space)
+         M-$     ->   end-of-line
+         M-*     ->   expand-filename
+         M-+     ->   down-history
+         M--     ->   up-history
+         M-<     ->   beginning-of-history
+         M->     ->   end-of-history
+         M-^     ->   beginning-of-line
+         M-;     ->   repeat-find-char
+         M-,     ->   invert-refind-char
+         M-|     ->   goto-column
+         M-~     ->   change-case
+         M-.     ->   vi-repeat-change
+         M-%     ->   find-parenthesis
+         M-a     ->   vi-append
+         M-A     ->   vi-append-at-eol
+         M-b     ->   backward-word
+         M-B     ->   backward-word
+         M-C     ->   vi-change-rest-of-line
+         M-cb    ->   vi-backward-change-word
+         M-cB    ->   vi-backward-change-word
+         M-cc    ->   vi-change-line
+         M-ce    ->   vi-forward-change-word
+         M-cE    ->   vi-forward-change-word
+         M-cw    ->   vi-forward-change-word
+         M-cW    ->   vi-forward-change-word
+         M-cF    ->   vi-backward-change-find
+         M-cf    ->   vi-forward-change-find
+         M-cT    ->   vi-backward-change-to
+         M-ct    ->   vi-forward-change-to
+         M-c;    ->   vi-change-refind
+         M-c,    ->   vi-change-invert-refind
+         M-ch    ->   vi-backward-change-char
+         M-c^H   ->   vi-backward-change-char
+         M-c^?   ->   vi-backward-change-char
+         M-cl    ->   vi-forward-change-char
+         M-c\    ->   vi-forward-change-char  (Meta-c-space)
+         M-c^    ->   vi-change-to-bol
+         M-c0    ->   vi-change-to-bol
+         M-c$    ->   vi-change-rest-of-line
+         M-c|    ->   vi-change-to-column
+         M-c%    ->   vi-change-to-parenthesis
+         M-dh    ->   backward-delete-char
+         M-d^H   ->   backward-delete-char
+         M-d^?   ->   backward-delete-char
+         M-dl    ->   forward-delete-char
+         M-d     ->   forward-delete-char    (Meta-d-space)
+         M-dd    ->   delete-line
+         M-db    ->   backward-delete-word
+         M-dB    ->   backward-delete-word
+         M-de    ->   forward-delete-word
+         M-dE    ->   forward-delete-word
+         M-dw    ->   forward-delete-word
+         M-dW    ->   forward-delete-word
+         M-dF    ->   backward-delete-find
+         M-df    ->   forward-delete-find
+         M-dT    ->   backward-delete-to
+         M-dt    ->   forward-delete-to
+         M-d;    ->   delete-refind
+         M-d,    ->   delete-invert-refind
+         M-d^    ->   backward-kill-line
+         M-d0    ->   backward-kill-line
+         M-d$    ->   kill-line
+         M-D     ->   kill-line
+         M-d|    ->   delete-to-column
+         M-d%    ->   delete-to-parenthesis
+         M-e     ->   forward-word
+         M-E     ->   forward-word
+         M-f     ->   forward-find-char
+         M-F     ->   backward-find-char
+         M--     ->   up-history
+         M-h     ->   cursor-left
+         M-H     ->   beginning-of-history
+         M-i     ->   vi-insert
+         M-I     ->   vi-insert-at-bol
+         M-j     ->   down-history
+         M-J     ->   history-search-forward
+         M-k     ->   up-history
+         M-K     ->   history-search-backward
+         M-l     ->   cursor-right
+         M-L     ->   end-of-history
+         M-n     ->   history-re-search-forward
+         M-N     ->   history-re-search-backward
+         M-p     ->   append-yank
+         M-P     ->   yank
+         M-r     ->   vi-replace-char
+         M-R     ->   vi-overwrite
+         M-s     ->   vi-forward-change-char
+         M-S     ->   vi-change-line
+         M-t     ->   forward-to-char
+         M-T     ->   backward-to-char
+         M-u     ->   vi-undo
+         M-w     ->   forward-to-word
+         M-W     ->   forward-to-word
+         M-x     ->   forward-delete-char
+         M-X     ->   backward-delete-char
+         M-yh    ->   backward-copy-char
+         M-y^H   ->   backward-copy-char
+         M-y^?   ->   backward-copy-char
+         M-yl    ->   forward-copy-char
+         M-y\    ->   forward-copy-char  (Meta-y-space)
+         M-ye    ->   forward-copy-word
+         M-yE    ->   forward-copy-word
+         M-yw    ->   forward-copy-word
+         M-yW    ->   forward-copy-word
+         M-yb    ->   backward-copy-word
+         M-yB    ->   backward-copy-word
+         M-yf    ->   forward-copy-find
+         M-yF    ->   backward-copy-find
+         M-yt    ->   forward-copy-to
+         M-yT    ->   backward-copy-to
+         M-y;    ->   copy-refind
+         M-y,    ->   copy-invert-refind
+         M-y^    ->   copy-to-bol
+         M-y0    ->   copy-to-bol
+         M-y$    ->   copy-rest-of-line
+         M-yy    ->   copy-line
+         M-Y     ->   copy-line
+         M-y|    ->   copy-to-column
+         M-y%    ->   copy-to-parenthesis
+         M-^E    ->   emacs-mode
+         M-^H    ->   cursor-left
+         M-^?    ->   cursor-left
+         M-^L    ->   clear-screen
+         M-^N    ->   down-history
+         M-^P    ->   up-history
+         M-^R    ->   redisplay
+         M-^D    ->   list-or-eof
+         M-^I    ->   complete-word
+         M-\r    ->   newline
+         M-\n    ->   newline
+         M-^X^R  ->   read-init-files
+         M-^Xh   ->   list-history
+
+         M-0, M-1, ... M-9  ->  digit-argument  (see below)
+
+       Note that ^I is what the TAB key generates.
+
+
+

ENTERING REPEAT COUNTS

+       Many of  the  key  binding  functions  described  previously,  take  an
+       optional count, typed in before the target keysequence.  This is inter-
+       preted as a repeat count by most bindings. A notable exception  is  the
+       goto-column binding, which interprets the count as a column number.
+
+       By default you can specify this count argument by pressing the meta key
+       while typing in the numeric count. This relies  on  the  digit-argument
+       action  being bound to Meta-0, Meta-1 etc.  Once any one of these bind-
+       ings has been activated, you can optionally take your  finger  off  the
+       meta  key  to type in the rest of the number, since every numeric digit
+       thereafter is treated as part of the number, unless it is  preceded  by
+       the  literal-next binding. As soon as a non-digit, or literal digit key
+       is pressed the repeat count is terminated and either  causes  the  just
+       typed  character to be added to the line that many times, or causes the
+       next key-binding function to be given that argument.
+
+       For example, in emacs mode, typing:
+
+         M-12a
+
+       causes the letter 'a' to be added to the line 12 times, whereas
+
+         M-4M-c
+
+       Capitalizes the next 4 words.
+
+       In vi command mode the Meta modifier  is  automatically  added  to  all
+       characters  typed  in,  so  to  enter  a count in vi command-mode, just
+       involves typing in the number, just as it does in the vi editor itself.
+       So for example, in vi command mode, typing:
+
+         4w2x
+
+       moves  the cursor four words to the right, then deletes two characters.
+
+       You can also bind digit-argument to other key sequences. If  these  end
+       in  a  numeric  digit,  that  digit gets appended to the current repeat
+       count. If it doesn't end in a numeric digit,  a  new  repeat  count  is
+       started  with  a  value  of zero, and can be completed by typing in the
+       number, after letting go of the key which triggered the  digit-argument
+       action.
+
+
+

FILES

+       libtecla.a      -    The Tecla library
+       libtecla.h      -    The Tecla header file.
+       ~/.teclarc      -    The personal Tecla customization file.
+
+
+

SEE ALSO

+       libtecla, gl_get_line, gl_io_mode, ef_expand_file,
+       cpl_complete_word, pca_lookup_file
+
+
+

AUTHOR

+       Martin Shepherd  (mcs@astro.caltech.edu)
+
+
+
+                                                          tecla
+
+ diff --git a/libtecla-1.6.1/install-sh b/libtecla-1.6.1/install-sh new file mode 100755 index 0000000..e9de238 --- /dev/null +++ b/libtecla-1.6.1/install-sh @@ -0,0 +1,251 @@ +#!/bin/sh +# +# install - install a program, script, or datafile +# This comes from X11R5 (mit/util/scripts/install.sh). +# +# Copyright 1991 by the Massachusetts Institute of Technology +# +# Permission to use, copy, modify, distribute, and sell this software and its +# documentation for any purpose is hereby granted without fee, provided that +# the above copyright notice appear in all copies and that both that +# copyright notice and this permission notice appear in supporting +# documentation, and that the name of M.I.T. not be used in advertising or +# publicity pertaining to distribution of the software without specific, +# written prior permission. M.I.T. makes no representations about the +# suitability of this software for any purpose. It is provided "as is" +# without express or implied warranty. +# +# Calling this script install-sh is preferred over install.sh, to prevent +# `make' implicit rules from creating a file called install from it +# when there is no Makefile. +# +# This script is compatible with the BSD install script, but was written +# from scratch. It can only install one file at a time, a restriction +# shared with many OS's install programs. + + +# set DOITPROG to echo to test this script + +# Don't use :- since 4.3BSD and earlier shells don't like it. +doit="${DOITPROG-}" + + +# put in absolute paths if you don't have them in your path; or use env. vars. + +mvprog="${MVPROG-mv}" +cpprog="${CPPROG-cp}" +chmodprog="${CHMODPROG-chmod}" +chownprog="${CHOWNPROG-chown}" +chgrpprog="${CHGRPPROG-chgrp}" +stripprog="${STRIPPROG-strip}" +rmprog="${RMPROG-rm}" +mkdirprog="${MKDIRPROG-mkdir}" + +transformbasename="" +transform_arg="" +instcmd="$mvprog" +chmodcmd="$chmodprog 0755" +chowncmd="" +chgrpcmd="" +stripcmd="" +rmcmd="$rmprog -f" +mvcmd="$mvprog" +src="" +dst="" +dir_arg="" + +while [ x"$1" != x ]; do + case $1 in + -c) instcmd="$cpprog" + shift + continue;; + + -d) dir_arg=true + shift + continue;; + + -m) chmodcmd="$chmodprog $2" + shift + shift + continue;; + + -o) chowncmd="$chownprog $2" + shift + shift + continue;; + + -g) chgrpcmd="$chgrpprog $2" + shift + shift + continue;; + + -s) stripcmd="$stripprog" + shift + continue;; + + -t=*) transformarg=`echo $1 | sed 's/-t=//'` + shift + continue;; + + -b=*) transformbasename=`echo $1 | sed 's/-b=//'` + shift + continue;; + + *) if [ x"$src" = x ] + then + src=$1 + else + # this colon is to work around a 386BSD /bin/sh bug + : + dst=$1 + fi + shift + continue;; + esac +done + +if [ x"$src" = x ] +then + echo "install: no input file specified" + exit 1 +else + true +fi + +if [ x"$dir_arg" != x ]; then + dst=$src + src="" + + if [ -d $dst ]; then + instcmd=: + chmodcmd="" + else + instcmd=mkdir + fi +else + +# Waiting for this to be detected by the "$instcmd $src $dsttmp" command +# might cause directories to be created, which would be especially bad +# if $src (and thus $dsttmp) contains '*'. + + if [ -f $src -o -d $src ] + then + true + else + echo "install: $src does not exist" + exit 1 + fi + + if [ x"$dst" = x ] + then + echo "install: no destination specified" + exit 1 + else + true + fi + +# If destination is a directory, append the input filename; if your system +# does not like double slashes in filenames, you may need to add some logic + + if [ -d $dst ] + then + dst="$dst"/`basename $src` + else + true + fi +fi + +## this sed command emulates the dirname command +dstdir=`echo $dst | sed -e 's,[^/]*$,,;s,/$,,;s,^$,.,'` + +# Make sure that the destination directory exists. +# this part is taken from Noah Friedman's mkinstalldirs script + +# Skip lots of stat calls in the usual case. +if [ ! -d "$dstdir" ]; then +defaultIFS=' +' +IFS="${IFS-${defaultIFS}}" + +oIFS="${IFS}" +# Some sh's can't handle IFS=/ for some reason. +IFS='%' +set - `echo ${dstdir} | sed -e 's@/@%@g' -e 's@^%@/@'` +IFS="${oIFS}" + +pathcomp='' + +while [ $# -ne 0 ] ; do + pathcomp="${pathcomp}${1}" + shift + + if [ ! -d "${pathcomp}" ] ; + then + $mkdirprog "${pathcomp}" + else + true + fi + + pathcomp="${pathcomp}/" +done +fi + +if [ x"$dir_arg" != x ] +then + $doit $instcmd $dst && + + if [ x"$chowncmd" != x ]; then $doit $chowncmd $dst; else true ; fi && + if [ x"$chgrpcmd" != x ]; then $doit $chgrpcmd $dst; else true ; fi && + if [ x"$stripcmd" != x ]; then $doit $stripcmd $dst; else true ; fi && + if [ x"$chmodcmd" != x ]; then $doit $chmodcmd $dst; else true ; fi +else + +# If we're going to rename the final executable, determine the name now. + + if [ x"$transformarg" = x ] + then + dstfile=`basename $dst` + else + dstfile=`basename $dst $transformbasename | + sed $transformarg`$transformbasename + fi + +# don't allow the sed command to completely eliminate the filename + + if [ x"$dstfile" = x ] + then + dstfile=`basename $dst` + else + true + fi + +# Make a temp file name in the proper directory. + + dsttmp=$dstdir/#inst.$$# + +# Move or copy the file name to the temp name + + $doit $instcmd $src $dsttmp && + + trap "rm -f ${dsttmp}" 0 && + +# and set any options; do chmod last to preserve setuid bits + +# If any of these fail, we abort the whole thing. If we want to +# ignore errors from any of these, just make sure not to ignore +# errors from the above "$doit $instcmd $src $dsttmp" command. + + if [ x"$chowncmd" != x ]; then $doit $chowncmd $dsttmp; else true;fi && + if [ x"$chgrpcmd" != x ]; then $doit $chgrpcmd $dsttmp; else true;fi && + if [ x"$stripcmd" != x ]; then $doit $stripcmd $dsttmp; else true;fi && + if [ x"$chmodcmd" != x ]; then $doit $chmodcmd $dsttmp; else true;fi && + +# Now rename the file to the real destination. + + $doit $rmcmd -f $dstdir/$dstfile && + $doit $mvcmd $dsttmp $dstdir/$dstfile + +fi && + + +exit 0 diff --git a/libtecla-1.6.1/ioutil.c b/libtecla-1.6.1/ioutil.c new file mode 100644 index 0000000..daf02c9 --- /dev/null +++ b/libtecla-1.6.1/ioutil.c @@ -0,0 +1,330 @@ +/* + * Copyright (c) 2000, 2001, 2002, 2003, 2004 by Martin C. Shepherd. + * + * All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, and/or sell copies of the Software, and to permit persons + * to whom the Software is furnished to do so, provided that the above + * copyright notice(s) and this permission notice appear in all copies of + * the Software and that both the above copyright notice(s) and this + * permission notice appear in supporting documentation. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT + * OF THIRD PARTY RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR + * HOLDERS INCLUDED IN THIS NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL + * INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING + * FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, + * NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION + * WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + * Except as contained in this notice, the name of a copyright holder + * shall not be used in advertising or otherwise to promote the sale, use + * or other dealings in this Software without prior written authorization + * of the copyright holder. + */ + +#include +#include +#include +#include +#include + +#include "ioutil.h" + +static int _io_pad_line(GlWriteFn *write_fn, void *data, int c, int n); + +/*....................................................................... + * Display a left-justified string over multiple terminal lines, + * taking account of the specified width of the terminal. Optional + * indentation and an option prefix string can be specified to be + * displayed at the start of each new terminal line used, and if + * needed, a single paragraph can be broken across multiple calls. + * Note that literal newlines in the input string can be used to force + * a newline at any point, and that in order to allow individual + * paragraphs to be written using multiple calls to this function, + * unless an explicit newline character is specified at the end of the + * string, a newline will not be started at the end of the last word + * in the string. Note that when a new line is started between two + * words that are separated by spaces, those spaces are not output, + * whereas when a new line is started because a newline character was + * found in the string, only the spaces before the newline character + * are discarded. + * + * Input: + * write_fn GlWriteFn * The callback function to use to write the + * output. + * data void * A pointer to arbitrary data to be passed to + * write_fn() whenever it is called. + * fp FILE * The stdio stream to write to. + * indentation int The number of fill characters to use to + * indent the start of each new terminal line. + * prefix const char * An optional prefix string to write after the + * indentation margin at the start of each new + * terminal line. You can specify NULL if no + * prefix is required. + * suffix const char * An optional suffix string to draw at the end + * of the terminal line. The line will be padded + * where necessary to ensure that the suffix ends + * in the last column of the terminal line. If + * no suffix is desired, specify NULL. + * fill_char int The padding character to use when indenting + * and filling up to the suffix. + * term_width int The width of the terminal being written to. + * start int The number of characters already written to + * the start of the current terminal line. This + * is primarily used to allow individual + * paragraphs to be written over multiple calls + * to this function, but can also be used to + * allow you to start the first line of a + * paragraph with a different prefix or + * indentation than those specified above. + * string const char * The string to be written. + * Output: + * return int On error -1 is returned. Otherwise the + * return value is the terminal column index at + * which the cursor was left after writing the + * final word in the string. Successful return + * values can thus be passed verbatim to the + * 'start' arguments of subsequent calls to + * _io_display_text() to allow the printing of a + * paragraph to be broken across multiple calls + * to _io_display_text(). + */ +int _io_display_text(GlWriteFn *write_fn, void *data, int indentation, + const char *prefix, const char *suffix, int fill_char, + int term_width, int start, const char *string) +{ + int ndone; /* The number of characters written from string[] */ + int nnew; /* The number of characters to be displayed next */ + int was_space; /* True if the previous character was a space or tab */ + int last = start; /* The column number of the last character written */ + int prefix_len; /* The length of the optional line prefix string */ + int suffix_len; /* The length of the optional line prefix string */ + int margin_width; /* The total number of columns used by the indentation */ + /* margin and the prefix string. */ + int i; +/* + * Check the arguments? + */ + if(!string || !write_fn) { + errno = EINVAL; + return -1; + }; +/* + * Enforce sensible values on the arguments. + */ + if(term_width < 0) + term_width = 0; + if(indentation > term_width) + indentation = term_width; + else if(indentation < 0) + indentation = 0; + if(start > term_width) + start = term_width; + else if(start < 0) + start = 0; +/* + * Get the length of the prefix string. + */ + prefix_len = prefix ? strlen(prefix) : 0; +/* + * Get the length of the suffix string. + */ + suffix_len = suffix ? strlen(suffix) : 0; +/* + * How many characters are devoted to indenting and prefixing each line? + */ + margin_width = indentation + prefix_len; +/* + * Write as many terminal lines as are needed to display the whole string. + */ + for(ndone=0; string[ndone]; start=0) { + last = start; +/* + * Write spaces from the current position in the terminal line to the + * width of the requested indentation margin. + */ + if(indentation > 0 && last < indentation) { + if(_io_pad_line(write_fn, data, fill_char, indentation - last)) + return -1; + last = indentation; + }; +/* + * If a prefix string has been specified, display it unless we have + * passed where it should end in the terminal output line. + */ + if(prefix_len > 0 && last < margin_width) { + int pstart = last - indentation; + int plen = prefix_len - pstart; + if(write_fn(data, prefix+pstart, plen) != plen) + return -1; + last = margin_width; + }; +/* + * Locate the end of the last complete word in the string before + * (term_width - start) characters have been seen. To handle the case + * where a single word is wider than the available space after the + * indentation and prefix margins, always make sure that at least one + * word is printed after the margin, regardless of whether it won't + * fit on the line. The two exceptions to this rule are if an embedded + * newline is found in the string or the end of the string is reached + * before any word has been seen. + */ + nnew = 0; + was_space = 0; + for(i=ndone; string[i] && (last+i-ndone < term_width - suffix_len || + (nnew==0 && last==margin_width)); i++) { + if(string[i] == '\n') { + if(!was_space) + nnew = i-ndone; + break; + } else if(isspace((int) string[i])) { + if(!was_space) { + nnew = i-ndone+1; + was_space = 1; + }; + } else { + was_space = 0; + }; + }; +/* + * Does the end of the string delimit the last word that will fit on the + * output line? + */ + if(nnew==0 && string[i] == '\0') + nnew = i-ndone; +/* + * Write the new line. + */ + if(write_fn(data, string+ndone, nnew) != nnew) + return -1; + ndone += nnew; + last += nnew; +/* + * Start a newline unless we have reached the end of the input string. + * In the latter case, in order to give the caller the chance to + * concatenate multiple calls to _io_display_text(), omit the newline, + * leaving it up to the caller to write this. + */ + if(string[ndone] != '\0') { +/* + * If a suffix has been provided, pad out the end of the line with spaces + * such that the suffix will end in the right-most terminal column. + */ + if(suffix_len > 0) { + int npad = term_width - suffix_len - last; + if(npad > 0 && _io_pad_line(write_fn, data, fill_char, npad)) + return -1; + last += npad; + if(write_fn(data, suffix, suffix_len) != suffix_len) + return -1; + last += suffix_len; + }; +/* + * Start a new line. + */ + if(write_fn(data, "\n", 1) != 1) + return -1; +/* + * Skip any spaces and tabs that follow the last word that was written. + */ + while(string[ndone] && isspace((int)string[ndone]) && + string[ndone] != '\n') + ndone++; +/* + * If the terminating character was a literal newline character, + * skip it in the input string, since we just wrote it. + */ + if(string[ndone] == '\n') + ndone++; + last = 0; + }; + }; +/* + * Return the column number of the last character printed. + */ + return last; +} + +/*....................................................................... + * Write a given number of spaces to the specified stdio output string. + * + * Input: + * write_fn GlWriteFn * The callback function to use to write the + * output. + * data void * A pointer to arbitrary data to be passed to + * write_fn() whenever it is called. + * c int The padding character. + * n int The number of spaces to be written. + * Output: + * return int 0 - OK. + * 1 - Error. + */ +static int _io_pad_line(GlWriteFn *write_fn, void *data, int c, int n) +{ + enum {FILL_SIZE=20}; + char fill[FILL_SIZE+1]; +/* + * Fill the buffer with the specified padding character. + */ + memset(fill, c, FILL_SIZE); + fill[FILL_SIZE] = '\0'; +/* + * Write the spaces using the above literal string of spaces as + * many times as needed to output the requested number of spaces. + */ + while(n > 0) { + int nnew = n <= FILL_SIZE ? n : FILL_SIZE; + if(write_fn(data, fill, nnew) != nnew) + return 1; + n -= nnew; + }; + return 0; +} + +/*....................................................................... + * The following is an output callback function which uses fwrite() + * to write to the stdio stream specified via its callback data argument. + * + * Input: + * data void * The stdio stream to write to, specified via a + * (FILE *) pointer cast to (void *). + * s const char * The string to be written. + * n int The length of the prefix of s[] to attempt to + * write. + * Output: + * return int The number of characters written from s[]. This + * should normally be a number in the range 0 to n. + * To signal that an I/O error occurred, return -1. + */ +GL_WRITE_FN(_io_write_stdio) +{ + int ndone; /* The total number of characters written */ + int nnew; /* The number of characters written in the latest write */ +/* + * The callback data is the stdio stream to write to. + */ + FILE *fp = (FILE *) data; +/* + * Because of signals we may need to do more than one write to output + * the whole string. + */ + for(ndone=0; ndone +#include +#include +#include +#include + +#include "keytab.h" +#include "strngmem.h" +#include "getline.h" +#include "errmsg.h" +#include "hash.h" + +/* + * When allocating or reallocating the key-binding table, how + * many entries should be added? + */ +#define KT_TABLE_INC 100 + +/* + * Define the size of the hash table that is used to associate action + * names with action functions. This should be a prime number. + */ +#define KT_HASH_SIZE 113 + +/* + * Define a binary-symbol-table object. + */ +struct KeyTab { + ErrMsg *err; /* Information about the last error */ + int size; /* The allocated dimension of table[] */ + int nkey; /* The current number of members in the table */ + KeySym *table; /* The table of lexically sorted key sequences */ + HashTable *actions; /* The hash table of actions */ + StringMem *smem; /* Memory for allocating strings */ +}; + +static int _kt_extend_table(KeyTab *kt); +static int _kt_parse_keybinding_string(const char *keyseq, + char *binary, int *nc); +static int _kt_compare_strings(const char *s1, int n1, const char *s2, int n2); +static void _kt_assign_action(KeySym *sym, KtBinder binder, KtKeyFn *keyfn, + void *data); +static char _kt_backslash_escape(const char *string, const char **endp); +static int _kt_is_emacs_meta(const char *string); +static int _kt_is_emacs_ctrl(const char *string); +static KtKeyMatch _kt_locate_keybinding(KeyTab *kt, const char *binary_keyseq, + int nc, int *first, int *last); + +/*....................................................................... + * Create a new key-binding symbol table. + * + * Output: + * return KeyTab * The new object, or NULL on error. + */ +KeyTab *_new_KeyTab(void) +{ + KeyTab *kt; /* The object to be returned */ +/* + * Allocate the container. + */ + kt = (KeyTab *) malloc(sizeof(KeyTab)); + if(!kt) { + errno = ENOMEM; + return NULL; + }; +/* + * Before attempting any operation that might fail, initialize the + * container at least up to the point at which it can safely be passed + * to del_KeyTab(). + */ + kt->err = NULL; + kt->size = KT_TABLE_INC; + kt->nkey = 0; + kt->table = NULL; + kt->actions = NULL; + kt->smem = NULL; +/* + * Allocate a place to record error messages. + */ + kt->err = _new_ErrMsg(); + if(!kt->err) + return _del_KeyTab(kt); +/* + * Allocate the table. + */ + kt->table = (KeySym *) malloc(sizeof(kt->table[0]) * kt->size); + if(!kt->table) { + errno = ENOMEM; + return _del_KeyTab(kt); + }; +/* + * Allocate a hash table of actions. + */ + kt->actions = _new_HashTable(NULL, KT_HASH_SIZE, IGNORE_CASE, NULL, 0); + if(!kt->actions) + return _del_KeyTab(kt); +/* + * Allocate a string allocation object. This allows allocation of + * small strings without fragmenting the heap. + */ + kt->smem = _new_StringMem(KT_TABLE_INC); + if(!kt->smem) + return _del_KeyTab(kt); + return kt; +} + +/*....................................................................... + * Delete a KeyTab object. + * + * Input: + * kt KeyTab * The object to be deleted. + * Output: + * return KeyTab * The deleted object (always NULL). + */ +KeyTab *_del_KeyTab(KeyTab *kt) +{ + if(kt) { + if(kt->table) + free(kt->table); + kt->actions = _del_HashTable(kt->actions); + kt->smem = _del_StringMem(kt->smem, 1); + kt->err = _del_ErrMsg(kt->err); + free(kt); + }; + return NULL; +} + +/*....................................................................... + * Increase the size of the table to accomodate more keys. + * + * Input: + * kt KeyTab * The table to be extended. + * Output: + * return int 0 - OK. + * 1 - Error. + */ +static int _kt_extend_table(KeyTab *kt) +{ +/* + * Attempt to increase the size of the table. + */ + KeySym *newtab = (KeySym *) realloc(kt->table, sizeof(kt->table[0]) * + (kt->size + KT_TABLE_INC)); +/* + * Failed? + */ + if(!newtab) { + _err_record_msg(kt->err, "Can't extend keybinding table", END_ERR_MSG); + errno = ENOMEM; + return 1; + }; +/* + * Install the resized table. + */ + kt->table = newtab; + kt->size += KT_TABLE_INC; + return 0; +} + +/*....................................................................... + * Add, update or remove a keybinding to the table. + * + * Input: + * kt KeyTab * The table to add the binding to. + * binder KtBinder The source of the binding. + * keyseq const char * The key-sequence to bind. + * action char * The action to associate with the key sequence, or + * NULL to remove the action associated with the + * key sequence. + * Output: + * return int 0 - OK. + * 1 - Error. + */ +int _kt_set_keybinding(KeyTab *kt, KtBinder binder, const char *keyseq, + const char *action) +{ + KtKeyFn *keyfn; /* The action function */ + void *data; /* The callback data of the action function */ +/* + * Check arguments. + */ + if(kt==NULL || !keyseq) { + errno = EINVAL; + if(kt) + _err_record_msg(kt->err, "NULL argument(s)", END_ERR_MSG); + return 1; + }; +/* + * Lookup the function that implements the specified action. + */ + if(!action) { + keyfn = 0; + data = NULL; + } else { + Symbol *sym = _find_HashSymbol(kt->actions, action); + if(!sym) { + _err_record_msg(kt->err, "Unknown key-binding action: ", action, + END_ERR_MSG); + errno = EINVAL; + return 1; + }; + keyfn = (KtKeyFn *) sym->fn; + data = sym->data; + }; +/* + * Record the action in the table. + */ + return _kt_set_keyfn(kt, binder, keyseq, keyfn, data); +} + +/*....................................................................... + * Add, update or remove a keybinding to the table, specifying an action + * function directly. + * + * Input: + * kt KeyTab * The table to add the binding to. + * binder KtBinder The source of the binding. + * keyseq char * The key-sequence to bind. + * keyfn KtKeyFn * The action function, or NULL to remove any existing + * action function. + * data void * A pointer to anonymous data to be passed to keyfn + * whenever it is called. + * Output: + * return int 0 - OK. + * 1 - Error. + */ +int _kt_set_keyfn(KeyTab *kt, KtBinder binder, const char *keyseq, + KtKeyFn *keyfn, void *data) +{ + const char *kptr; /* A pointer into keyseq[] */ + char *binary; /* The binary version of keyseq[] */ + int nc; /* The number of characters in binary[] */ + int first,last; /* The first and last entries in the table which */ + /* minimally match. */ + int size; /* The size to allocate for the binary string */ + int i; +/* + * Check arguments. + */ + if(kt==NULL || !keyseq) { + errno = EINVAL; + if(kt) + _err_record_msg(kt->err, "NULL argument(s)", END_ERR_MSG); + return 1; + }; +/* + * Work out a pessimistic estimate of how much space will be needed + * for the binary copy of the string, noting that binary meta characters + * embedded in the input string get split into two characters. + */ + for(size=0,kptr = keyseq; *kptr; kptr++) + size += IS_META_CHAR(*kptr) ? 2 : 1; +/* + * Allocate a string that has the length of keyseq[]. + */ + binary = _new_StringMemString(kt->smem, size + 1); + if(!binary) { + errno = ENOMEM; + _err_record_msg(kt->err, "Insufficient memory to record key sequence", + END_ERR_MSG); + return 1; + }; +/* + * Convert control and octal character specifications to binary characters. + */ + if(_kt_parse_keybinding_string(keyseq, binary, &nc)) { + binary = _del_StringMemString(kt->smem, binary); + return 1; + }; +/* + * Lookup the position in the table at which to insert the binding. + */ + switch(_kt_locate_keybinding(kt, binary, nc, &first, &last)) { +/* + * If an exact match for the key-sequence is already in the table, + * simply replace its binding function (or delete the entry if + * the new binding is 0). + */ + case KT_EXACT_MATCH: + if(keyfn) { + _kt_assign_action(kt->table + first, binder, keyfn, data); + } else { + _del_StringMemString(kt->smem, kt->table[first].keyseq); + memmove(kt->table + first, kt->table + first + 1, + (kt->nkey - first - 1) * sizeof(kt->table[0])); + kt->nkey--; + }; + binary = _del_StringMemString(kt->smem, binary); + break; +/* + * If an ambiguous match has been found and we are installing a + * callback, then our new key-sequence would hide all of the ambiguous + * matches, so we shouldn't allow it. + */ + case KT_AMBIG_MATCH: + if(keyfn) { + _err_record_msg(kt->err, "Can't bind \"", keyseq, + "\", because it is a prefix of another binding", + END_ERR_MSG); + binary = _del_StringMemString(kt->smem, binary); + errno = EPERM; + return 1; + }; + break; +/* + * If the entry doesn't exist, create it. + */ + case KT_NO_MATCH: +/* + * Add a new binding? + */ + if(keyfn) { + KeySym *sym; +/* + * We will need a new entry, extend the table if needed. + */ + if(kt->nkey + 1 > kt->size) { + if(_kt_extend_table(kt)) { + binary = _del_StringMemString(kt->smem, binary); + return 1; + }; + }; +/* + * Make space to insert the new key-sequence before 'last'. + */ + if(last < kt->nkey) { + memmove(kt->table + last + 1, kt->table + last, + (kt->nkey - last) * sizeof(kt->table[0])); + }; +/* + * Insert the new binding in the vacated position. + */ + sym = kt->table + last; + sym->keyseq = binary; + sym->nc = nc; + for(i=0; iactions + i; + action->fn = 0; + action->data = NULL; + }; + sym->binder = -1; + _kt_assign_action(sym, binder, keyfn, data); + kt->nkey++; + }; + break; + case KT_BAD_MATCH: + binary = _del_StringMemString(kt->smem, binary); + return 1; + break; + }; + return 0; +} + +/*....................................................................... + * Perform a min-match lookup of a key-binding. + * + * Input: + * kt KeyTab * The keybinding table to lookup in. + * binary_keyseq char * The binary key-sequence to lookup. + * nc int the number of characters in keyseq[]. + * Input/Output: + * first,last int * If there is an ambiguous or exact match, the indexes + * of the first and last symbols that minimally match + * will be assigned to *first and *last respectively. + * If there is no match, then first and last will + * bracket the location where the symbol should be + * inserted. + * Output: + * return KtKeyMatch One of the following enumerators: + * KT_EXACT_MATCH - An exact match was found. + * KT_AMBIG_MATCH - An ambiguous match was found. + * KT_NO_MATCH - No match was found. + * KT_BAD_MATCH - An error occurred while searching. + */ +static KtKeyMatch _kt_locate_keybinding(KeyTab *kt, const char *binary_keyseq, + int nc, int *first, int *last) +{ + int mid; /* The index at which to bisect the table */ + int bot; /* The lowest index of the table not searched yet */ + int top; /* The highest index of the table not searched yet */ + int test; /* The return value of strcmp() */ +/* + * Perform a binary search for the key-sequence. + */ + bot = 0; + top = kt->nkey - 1; + while(top >= bot) { + mid = (top + bot)/2; + test = _kt_compare_strings(kt->table[mid].keyseq, kt->table[mid].nc, + binary_keyseq, nc); + if(test > 0) + top = mid - 1; + else if(test < 0) + bot = mid + 1; + else { + *first = *last = mid; + return KT_EXACT_MATCH; + }; + }; +/* + * An exact match wasn't found, but top is the index just below the + * index where a match would be found, and bot is the index just above + * where the match ought to be found. + */ + *first = top; + *last = bot; +/* + * See if any ambiguous matches exist, and if so make *first and *last + * refer to the first and last matches. + */ + if(*last < kt->nkey && kt->table[*last].nc > nc && + _kt_compare_strings(kt->table[*last].keyseq, nc, binary_keyseq, nc)==0) { + *first = *last; + while(*last+1 < kt->nkey && kt->table[*last+1].nc > nc && + _kt_compare_strings(kt->table[*last+1].keyseq, nc, binary_keyseq, nc)==0) + (*last)++; + return KT_AMBIG_MATCH; + }; +/* + * No match. + */ + return KT_NO_MATCH; +} + +/*....................................................................... + * Lookup the sub-array of key-bindings who's key-sequences minimally + * match a given key-sequence. + * + * Input: + * kt KeyTab * The keybinding table to lookup in. + * binary_keyseq char * The binary key-sequence to lookup. + * nc int the number of characters in keyseq[]. + * Input/Output: + * matches KeySym ** The array of minimally matching symbols + * can be found in (*matches)[0..nmatch-1], unless + * no match was found, in which case *matches will + * be set to NULL. + * nmatch int The number of ambiguously matching symbols. This + * will be 0 if there is no match, 1 for an exact + * match, and a number greater than 1 for an ambiguous + * match. + * Output: + * return KtKeyMatch One of the following enumerators: + * KT_EXACT_MATCH - An exact match was found. + * KT_AMBIG_MATCH - An ambiguous match was found. + * KT_NO_MATCH - No match was found. + * KT_BAD_MATCH - An error occurred while searching. + */ +KtKeyMatch _kt_lookup_keybinding(KeyTab *kt, const char *binary_keyseq, + int nc, KeySym **matches, int *nmatch) +{ + KtKeyMatch status; /* The return status */ + int first,last; /* The indexes of the first and last matching entry */ + /* in the symbol table. */ +/* + * Check the arguments. + */ + if(!kt || !binary_keyseq || !matches || !nmatch || nc < 0) { + errno = EINVAL; + if(kt) + _err_record_msg(kt->err, "NULL argument(s)", END_ERR_MSG); + return KT_BAD_MATCH; + }; +/* + * Lookup the indexes of the binding-table entries that bracket the + * target key-sequence. + */ + status = _kt_locate_keybinding(kt, binary_keyseq, nc, &first, &last); +/* + * Translate the indexes into the corresponding subarray of matching + * table entries. + */ + switch(status) { + case KT_EXACT_MATCH: + case KT_AMBIG_MATCH: + *matches = kt->table + first; + *nmatch = last - first + 1; + break; + default: + *matches = NULL; + *nmatch = 0; + break; + }; + return status; +} + +/*....................................................................... + * Convert a keybinding string into a uniq binary representation. + * + * Control characters can be given directly in their binary form, + * expressed as either ^ or C-, followed by the character, expressed in + * octal, like \129 or via C-style backslash escapes, with the addition + * of '\E' to denote the escape key. Similarly, meta characters can be + * given directly in binary or expressed as M- followed by the character. + * Meta characters are recorded as two characters in the binary output + * string, the first being the escape key, and the second being the key + * that was modified by the meta key. This means that binding to + * \EA or ^[A or M-A are all equivalent. + * + * Input: + * keyseq char * The key sequence being added. + * Input/Output: + * binary char * The binary version of the key sequence will be + * assigned to binary[], which must have at least + * as many characters as keyseq[] plus the number + * of embedded binary meta characters. + * nc int * The number of characters assigned to binary[] + * will be recorded in *nc. + * Output: + * return int 0 - OK. + * 1 - Error. + */ +static int _kt_parse_keybinding_string(const char *keyseq, char *binary, + int *nc) +{ + const char *iptr = keyseq; /* Pointer into keyseq[] */ + char *optr = binary; /* Pointer into binary[] */ + char c; /* An intermediate character */ +/* + * Parse the input characters until they are exhausted or the + * output string becomes full. + */ + while(*iptr) { +/* + * Check for special characters. + */ + switch(*iptr) { + case '^': /* A control character specification */ +/* + * Convert the caret expression into the corresponding control + * character unless no character follows the caret, in which case + * record a literal caret. + */ + if(iptr[1]) { +/* + * Get the next, possibly escaped, character. + */ + if(iptr[1] == '\\') { + c = _kt_backslash_escape(iptr+2, &iptr); + } else { + c = iptr[1]; + iptr += 2; + }; +/* + * Convert the character to a control character. + */ + *optr++ = MAKE_CTRL(c); + } else { + *optr++ = *iptr++; + }; + break; +/* + * A backslash-escaped character? + */ + case '\\': +/* + * Convert the escape sequence to a binary character. + */ + *optr++ = _kt_backslash_escape(iptr+1, &iptr); + break; +/* + * Possibly an emacs-style meta character? + */ + case 'M': + if(_kt_is_emacs_meta(iptr)) { + *optr++ = GL_ESC_CHAR; + iptr += 2; + } else { + *optr++ = *iptr++; + }; + break; +/* + * Possibly an emacs-style control character specification? + */ + case 'C': + if(_kt_is_emacs_ctrl(iptr)) { + *optr++ = MAKE_CTRL(iptr[2]); + iptr += 3; + } else { + *optr++ = *iptr++; + }; + break; + default: + +/* + * Convert embedded meta characters into an escape character followed + * by the meta-unmodified character. + */ + if(IS_META_CHAR(*iptr)) { + *optr++ = GL_ESC_CHAR; + *optr++ = META_TO_CHAR(*iptr); + iptr++; +/* + * To allow keysequences that start with printable characters to + * be distinguished from the cursor-key keywords, prepend a backslash + * to the former. This same operation is performed in gl_interpret_char() + * before looking up a keysequence that starts with a printable character. + */ + } else if(iptr==keyseq && !IS_CTRL_CHAR(*iptr) && + strcmp(keyseq, "up") != 0 && strcmp(keyseq, "down") != 0 && + strcmp(keyseq, "left") != 0 && strcmp(keyseq, "right") != 0) { + *optr++ = '\\'; + *optr++ = *iptr++; + } else { + *optr++ = *iptr++; + }; + }; + }; +/* + * How many characters were placed in the output array? + */ + *nc = optr - binary; + return 0; +} + +/*....................................................................... + * Add, remove or modify an action. + * + * Input: + * kt KeyTab * The key-binding table. + * action char * The name of the action. + * fn KtKeyFn * The function that implements the action, or NULL + * to remove an existing action. + * data void * A pointer to arbitrary callback data to pass to the + * action function whenever it is called. + * Output: + * return int 0 - OK. + * 1 - Error. + */ +int _kt_set_action(KeyTab *kt, const char *action, KtKeyFn *fn, void *data) +{ + Symbol *sym; /* The symbol table entry of the action */ +/* + * Check the arguments. + */ + if(!kt || !action) { + errno = EINVAL; + if(kt) + _err_record_msg(kt->err, "NULL argument(s)", END_ERR_MSG); + return 1; + }; +/* + * If no function was provided, delete an existing action. + */ + if(!fn) { + sym = _del_HashSymbol(kt->actions, action); + return 0; + }; +/* + * If the action already exists, replace its action function. + */ + sym = _find_HashSymbol(kt->actions, action); + if(sym) { + sym->fn = (void (*)(void))fn; + sym->data = data; + return 0; + }; +/* + * Add a new action. + */ + if(!_new_HashSymbol(kt->actions, action, 0, (void (*)(void))fn, data, 0)) { + _err_record_msg(kt->err, "Insufficient memory to record key-binding action", + END_ERR_MSG); + return 1; + }; + return 0; +} + +/*....................................................................... + * Compare two strings of specified length which may contain embedded + * ascii NUL's. + * + * Input: + * s1 char * The first of the strings to be compared. + * n1 int The length of the string in s1. + * s2 char * The second of the strings to be compared. + * n2 int The length of the string in s2. + * Output: + * return int < 0 if(s1 < s2) + * 0 if(s1 == s2) + * > 0 if(s1 > s2) + */ +static int _kt_compare_strings(const char *s1, int n1, const char *s2, int n2) +{ + int i; +/* + * Find the first character where the two strings differ. + */ + for(i=0; i= KTB_NBIND) + return; +/* + * Record the action according to its source. + */ + action = sym->actions + binder; + action->fn = keyfn; + action->data = data; +/* + * Find the highest priority binding source that has supplied an + * action. Note that the actions[] array is ordered in order of + * descreasing priority, so the first entry that contains a function + * is the one to use. + */ + for(i=0; iactions[i].fn; i++) + ; +/* + * Record the index of this action for use during lookups. + */ + sym->binder = i < KTB_NBIND ? i : -1; + return; +} + +/*....................................................................... + * Remove all key bindings that came from a specified source. + * + * Input: + * kt KeyTab * The table of key bindings. + * binder KtBinder The source of the bindings to be cleared. + */ +void _kt_clear_bindings(KeyTab *kt, KtBinder binder) +{ + int oldkey; /* The index of a key in the original binding table */ + int newkey; /* The index of a key in the updated binding table */ +/* + * If there is no table, then no bindings exist to be deleted. + */ + if(!kt) + return; +/* + * Clear bindings of the given source. + */ + for(oldkey=0; oldkeynkey; oldkey++) + _kt_assign_action(kt->table + oldkey, binder, 0, NULL); +/* + * Delete entries that now don't have a binding from any source. + */ + newkey = 0; + for(oldkey=0; oldkeynkey; oldkey++) { + KeySym *sym = kt->table + oldkey; + if(sym->binder < 0) { + _del_StringMemString(kt->smem, sym->keyseq); + } else { + if(oldkey != newkey) + kt->table[newkey] = *sym; + newkey++; + }; + }; +/* + * Record the number of keys that were kept. + */ + kt->nkey = newkey; + return; +} + +/*....................................................................... + * Translate a backslash escape sequence to a binary character. + * + * Input: + * string const char * The characters that follow the backslash. + * Input/Output: + * endp const char ** If endp!=NULL, on return *endp will be made to + * point to the character in string[] which follows + * the escape sequence. + * Output: + * return char The binary character. + */ +static char _kt_backslash_escape(const char *string, const char **endp) +{ + char c; /* The output character */ +/* + * Is the backslash followed by one or more octal digits? + */ + switch(*string) { + case '0': case '1': case '2': case '3': + case '4': case '5': case '6': case '7': + c = strtol(string, (char **)&string, 8); + break; + case 'a': + c = '\a'; + string++; + break; + case 'b': + c = '\b'; + string++; + break; + case 'e': case 'E': /* Escape */ + c = GL_ESC_CHAR; + string++; + break; + case 'f': + c = '\f'; + string++; + break; + case 'n': + c = '\n'; + string++; + break; + case 'r': + c = '\r'; + string++; + break; + case 't': + c = '\t'; + string++; + break; + case 'v': + c = '\v'; + string++; + break; + case '\0': + c = '\\'; + break; + default: + c = *string++; + break; + }; +/* + * Report the character which follows the escape sequence. + */ + if(endp) + *endp = string; + return c; +} + +/*....................................................................... + * Return non-zero if the next two characters are M- and a third character + * follows. Otherwise return 0. + * + * Input: + * string const char * The sub-string to scan. + * Output: + * return int 1 - The next two characters are M- and these + * are followed by at least one character. + * 0 - The next two characters aren't M- or no + * character follows a M- pair. + */ +static int _kt_is_emacs_meta(const char *string) +{ + return *string++ == 'M' && *string++ == '-' && *string; +} + +/*....................................................................... + * Return non-zero if the next two characters are C- and a third character + * follows. Otherwise return 0. + * + * Input: + * string const char * The sub-string to scan. + * Output: + * return int 1 - The next two characters are C- and these + * are followed by at least one character. + * 0 - The next two characters aren't C- or no + * character follows a C- pair. + */ +static int _kt_is_emacs_ctrl(const char *string) +{ + return *string++ == 'C' && *string++ == '-' && *string; +} + +/*....................................................................... + * Merge an array of bindings with existing bindings. + * + * Input: + * kt KeyTab * The table of key bindings. + * binder KtBinder The source of the bindings. + * bindings const KtKeyBinding * The array of bindings. + * n int The number of bindings in bindings[]. + * Output: + * return int 0 - OK. + * 1 - Error. + */ +int _kt_add_bindings(KeyTab *kt, KtBinder binder, const KtKeyBinding *bindings, + unsigned n) +{ + int i; +/* + * Check the arguments. + */ + if(!kt || !bindings) { + errno = EINVAL; + if(kt) + _err_record_msg(kt->err, "NULL argument(s)", END_ERR_MSG); + return 1; + }; +/* + * Install the array of bindings. + */ + for(i=0; ierr, "NULL argument(s)", END_ERR_MSG); + return 1; + }; +/* + * Lookup the symbol table entry of the action. + */ + sym = _find_HashSymbol(kt->actions, action); + if(!sym) + return 1; +/* + * Return the function and ccallback data associated with the action. + */ + if(fn) + *fn = (KtKeyFn *) sym->fn; + if(data) + *data = sym->data; + return 0; +} + +/*....................................................................... + * Return extra information (ie. in addition to that provided by errno) + * about the last error to occur in any of the public functions of this + * module. + * + * Input: + * kt KeyTab * The table of key bindings. + * Output: + * return const char * A pointer to the internal buffer in which + * the error message is temporarily stored. + */ +const char *_kt_last_error(KeyTab *kt) +{ + return kt ? _err_get_msg(kt->err) : "NULL KeyTab argument"; +} diff --git a/libtecla-1.6.1/keytab.h b/libtecla-1.6.1/keytab.h new file mode 100644 index 0000000..b275c98 --- /dev/null +++ b/libtecla-1.6.1/keytab.h @@ -0,0 +1,157 @@ +#ifndef keytab_h +#define keytab_h + +/* + * Copyright (c) 2000, 2001, 2002, 2003, 2004 by Martin C. Shepherd. + * + * All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, and/or sell copies of the Software, and to permit persons + * to whom the Software is furnished to do so, provided that the above + * copyright notice(s) and this permission notice appear in all copies of + * the Software and that both the above copyright notice(s) and this + * permission notice appear in supporting documentation. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT + * OF THIRD PARTY RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR + * HOLDERS INCLUDED IN THIS NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL + * INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING + * FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, + * NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION + * WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + * Except as contained in this notice, the name of a copyright holder + * shall not be used in advertising or otherwise to promote the sale, use + * or other dealings in this Software without prior written authorization + * of the copyright holder. + */ + +#include "libtecla.h" + +/*-----------------------------------------------------------------------* + * This module defines a binary-search symbol table of key-bindings. * + *-----------------------------------------------------------------------*/ + +/* + * All key-binding functions are defined as follows. + * + * Input: + * gl GetLine * The resource object of this library. + * count int A positive repeat count specified by the user, + * or 1. Action functions should ignore this if + * repeating the action multiple times isn't + * appropriate. + * data void * A pointer to action-specific data, + * cast to (void *). + * Output: + * return int 0 - OK. + * 1 - Error. + */ +#define KT_KEY_FN(fn) int (fn)(GetLine *gl, int count, void *data) + +typedef KT_KEY_FN(KtKeyFn); + +/* + * Allow the association of arbitrary callback data with each action + * function. + */ +typedef struct { + KtKeyFn *fn; /* The acion function */ + void *data; /* A pointer to arbitrary data to be passed to */ + /* fn() whenever it is called. */ +} KtAction; + +/* + * Enumerate the possible sources of key-bindings in order of decreasing + * priority. + */ +typedef enum { + KTB_USER, /* This is a binding being set by the user */ + KTB_NORM, /* This is the default binding set by the library */ + KTB_TERM, /* This is a binding taken from the terminal settings */ +/* The following entry must always be last */ + KTB_NBIND /* The number of binding sources listed above */ +} KtBinder; + +/* + * Define an entry of a key-binding binary symbol table. + */ +typedef struct { + char *keyseq; /* The key sequence that triggers the macro */ + int nc; /* The number of characters in keyseq[] */ + KtAction actions[KTB_NBIND]; /* Bindings from different sources */ + int binder; /* The index of the highest priority element */ + /* of actions[] that has been assigned an */ + /* action function, or -1 if none have. */ +} KeySym; + +/* + * Provide an opaque type alias to the symbol table container. + */ +typedef struct KeyTab KeyTab; + +/* + * Create a new symbol table. + */ +KeyTab *_new_KeyTab(void); + +/* + * Delete the symbol table. + */ +KeyTab *_del_KeyTab(KeyTab *kt); + +int _kt_set_keybinding(KeyTab *kt, KtBinder binder, + const char *keyseq, const char *action); +int _kt_set_keyfn(KeyTab *kt, KtBinder binder, const char *keyseq, + KtKeyFn *fn, void *data); + +int _kt_set_action(KeyTab *kt, const char *action, KtKeyFn *fn, void *data); + +/* + * Lookup the function that implements a given action. + */ +int _kt_lookup_action(KeyTab *kt, const char *action, + KtKeyFn **fn, void **data); + +typedef enum { + KT_EXACT_MATCH, /* An exact match was found */ + KT_AMBIG_MATCH, /* An ambiguous match was found */ + KT_NO_MATCH, /* No match was found */ + KT_BAD_MATCH /* An error occurred while searching */ +} KtKeyMatch; + +KtKeyMatch _kt_lookup_keybinding(KeyTab *kt, const char *binary_keyseq, + int nc, KeySym **matches, int *nmatch); + +/* + * Remove all key bindings that came from a specified source. + */ +void _kt_clear_bindings(KeyTab *kt, KtBinder binder); + +/* + * When installing an array of keybings each binding is defined by + * an element of the following type: + */ +typedef struct { + const char *keyseq; /* The sequence of keys that trigger this binding */ + const char *action; /* The name of the action function that is triggered */ +} KtKeyBinding; + +/* + * Merge an array of bindings with existing bindings. + */ +int _kt_add_bindings(KeyTab *kt, KtBinder binder, const KtKeyBinding *bindings, + unsigned n); + +/* + * Get information about the last error in this module. + */ +const char *_kt_last_error(KeyTab *kt); + +#endif diff --git a/libtecla-1.6.1/libtecla.h b/libtecla-1.6.1/libtecla.h new file mode 100644 index 0000000..fbbf220 --- /dev/null +++ b/libtecla-1.6.1/libtecla.h @@ -0,0 +1,1834 @@ +#ifndef libtecla_h +#define libtecla_h + +/* + * Copyright (c) 2000, 2001, 2002, 2003, 2004 by Martin C. Shepherd. + * + * All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, and/or sell copies of the Software, and to permit persons + * to whom the Software is furnished to do so, provided that the above + * copyright notice(s) and this permission notice appear in all copies of + * the Software and that both the above copyright notice(s) and this + * permission notice appear in supporting documentation. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT + * OF THIRD PARTY RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR + * HOLDERS INCLUDED IN THIS NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL + * INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING + * FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, + * NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION + * WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + * Except as contained in this notice, the name of a copyright holder + * shall not be used in advertising or otherwise to promote the sale, use + * or other dealings in this Software without prior written authorization + * of the copyright holder. + */ + +#ifdef __cplusplus +extern "C" { +#endif + +#include /* FILE * */ +#include /* size_t */ +#include /* time_t */ +#include /* struct sigaction */ + +/* + * The following are the three components of the libtecla version number. + * Note that it is better to use the libtecla_version() function than these + * macros since the macros only tell you which version of the library your + * code was compiled against, whereas the libtecla_version() function + * tells you which version of the shared tecla library your program is + * actually linked to. + */ +#define TECLA_MAJOR_VER 1 +#define TECLA_MINOR_VER 6 +#define TECLA_MICRO_VER 1 + +/*....................................................................... + * Query the version number of the tecla library. + * + * Input: + * major int * The major version number of the library + * will be assigned to *major. This number is + * only incremented when a change to the library is + * made that breaks binary (shared library) and/or + * compilation backwards compatibility. + * minor int * The minor version number of the library + * will be assigned to *minor. This number is + * incremented whenever new functions are added to + * the public API. + * micro int * The micro version number of the library will be + * assigned to *micro. This number is incremented + * whenever internal changes are made that don't + * change the public API, such as bug fixes and + * performance enhancements. + */ +void libtecla_version(int *major, int *minor, int *micro); + +/*----------------------------------------------------------------------- + * The getline module provides interactive command-line input, recall + * and editing by users at terminals. See the gl_getline(3) man page for + * more details. + *-----------------------------------------------------------------------*/ + +/* + * Provide an opaque handle for the resource object that is defined in + * getline.h. + */ +typedef struct GetLine GetLine; + +/* + * The following two functions are used to create and delete the + * resource objects that are used by the gl_getline() function. + */ +GetLine *new_GetLine(size_t linelen, size_t histlen); +GetLine *del_GetLine(GetLine *gl); + +/* + * Read a line into an internal buffer of gl. + */ +char *gl_get_line(GetLine *gl, const char *prompt, const char *start_line, + int start_pos); + +/*....................................................................... + * Prompt the user for a single-character reply. + * + * Input: + * gl GetLine * A resource object returned by new_GetLine(). + * prompt char * The prompt to prefix the query with, or NULL + * to reuse the previous prompt. + * defchar char The character to substitute if the + * user simply hits return, or '\n' if you don't + * need to substitute anything. + * Output: + * return int The character that was read, or EOF if the read + * had to be aborted (in which case you can call + * gl_return_status() to find out why). + */ +int gl_query_char(GetLine *gl, const char *prompt, char defchar); + +/*....................................................................... + * Read a single uninterpretted character from the user, without + * displaying anything. + * + * Input: + * gl GetLine * A resource object previously returned by + * new_GetLine(). + * Output: + * return int The character that was read, or EOF if the read + * had to be aborted (in which case you can call + * gl_return_status() to find out why). + */ +int gl_read_char(GetLine *gl); + +/* + * Configure the application specific and/or user-specific behavior of + * gl_get_line(). + */ +int gl_configure_getline(GetLine *gl, const char *app_string, + const char *app_file, const char *user_file); + +/* + * The following enumerators specify the origin of a key binding, and + * are listed in order of decreasing priority, such that user-specified + * key-bindings take precedence over application default bindings. + */ +typedef enum { + GL_USER_KEY, /* A key-binding specified by the user */ + GL_APP_KEY /* A key-binding specified by the application */ +} GlKeyOrigin; + +/* + * Bind a key sequence to a given action. If action==NULL, unbind the + * key-sequence. + */ +int gl_bind_keyseq(GetLine *gl, GlKeyOrigin origin, const char *keyseq, + const char *action); + +/*----------------------------------------------------------------------- + * The file-expansion module provides facilities for expanding ~user/ and + * $envvar expressions, and for expanding glob-style wildcards. + * See the ef_expand_file(3) man page for more details. + *-----------------------------------------------------------------------*/ + +/* + * ExpandFile objects contain the resources needed to expand pathnames. + */ +typedef struct ExpandFile ExpandFile; + +/* + * The following functions are used to create and delete the resource + * objects that are used by the ef_expand_file() function. + */ +ExpandFile *new_ExpandFile(void); +ExpandFile *del_ExpandFile(ExpandFile *ef); + +/* + * A container of the following type is returned by ef_expand_file(). + */ +typedef struct { + int exists; /* True if the files in files[] currently exist. */ + /* This only time that this may not be true is if */ + /* the input filename didn't contain any wildcards */ + /* and thus wasn't matched against existing files. */ + /* In this case the single entry in 'nfile' may not */ + /* refer to an existing file. */ + int nfile; /* The number of files in files[] */ + char **files; /* An array of 'nfile' filenames. */ +} FileExpansion; + +/* + * The ef_expand_file() function expands a specified pathname, converting + * ~user/ and ~/ patterns at the start of the pathname to the + * corresponding home directories, replacing $envvar with the value of + * the corresponding environment variable, and then, if there are any + * wildcards, matching these against existing filenames. + * + * If no errors occur, a container is returned containing the array of + * files that resulted from the expansion. If there were no wildcards + * in the input pathname, this will contain just the original pathname + * after expansion of ~ and $ expressions. If there were any wildcards, + * then the array will contain the files that matched them. Note that + * if there were any wildcards but no existing files match them, this + * is counted as an error and NULL is returned. + * + * The supported wildcards and their meanings are: + * * - Match any sequence of zero or more characters. + * ? - Match any single character. + * [chars] - Match any single character that appears in 'chars'. + * If 'chars' contains an expression of the form a-b, + * then any character between a and b, including a and b, + * matches. The '-' character looses its special meaning + * as a range specifier when it appears at the start + * of the sequence of characters. + * [^chars] - The same as [chars] except that it matches any single + * character that doesn't appear in 'chars'. + * + * Wildcard expressions are applied to individual filename components. + * They don't match across directory separators. A '.' character at + * the beginning of a filename component must also be matched + * explicitly by a '.' character in the input pathname, since these + * are UNIX's hidden files. + * + * Input: + * fe ExpandFile * The pathname expansion resource object. + * path const char * The path name to be expanded. + * pathlen int The length of the suffix of path[] that + * constitutes the filename to be expanded, + * or -1 to specify that the whole of the + * path string should be used. + * Output: + * return FileExpansion * A pointer to a results container within the + * given ExpandFile object. This contains an + * array of the pathnames that resulted from + * expanding ~ and $ expressions and from + * matching any wildcards, sorted into lexical + * order. + * + * This container and its contents will be + * recycled on subsequent calls, so if you need + * to keep the results of two successive runs, + * you will either have to allocate a private + * copy of the array, or use two ExpandFile + * objects. + * + * On error, NULL is returned. A description + * of the error can be acquired by calling the + * ef_last_error() function. + */ +FileExpansion *ef_expand_file(ExpandFile *ef, const char *path, int pathlen); + +/*....................................................................... + * Print out an array of matching files. + * + * Input: + * result FileExpansion * The container of the sorted array of + * expansions. + * fp FILE * The output stream to write to. + * term_width int The width of the terminal. + * Output: + * return int 0 - OK. + * 1 - Error. + */ +int ef_list_expansions(FileExpansion *result, FILE *fp, int term_width); + +/* + * The ef_last_error() function returns a description of the last error + * that occurred in a call ef_expand_file(). Note that this message is + * contained in an array which is allocated as part of *ef, and its + * contents thus potentially change on every call to ef_expand_file(). + */ +const char *ef_last_error(ExpandFile *ef); + +/*----------------------------------------------------------------------- + * The WordCompletion module is used for completing incomplete words, such + * as filenames. Programs can use functions within this module to register + * their own customized completion functions. + *-----------------------------------------------------------------------*/ + +/* + * Ambiguous completion matches are recorded in objects of the + * following type. + */ +typedef struct WordCompletion WordCompletion; + +/* + * Create a new completion object. + */ +WordCompletion *new_WordCompletion(void); + +/* + * Delete a redundant completion object. + */ +WordCompletion *del_WordCompletion(WordCompletion *cpl); + +/*....................................................................... + * Callback functions declared and prototyped using the following macro + * are called upon to return an array of possible completion suffixes + * for the token that precedes a specified location in the given + * input line. It is up to this function to figure out where the token + * starts, and to call cpl_add_completion() to register each possible + * completion before returning. + * + * Input: + * cpl WordCompletion * An opaque pointer to the object that will + * contain the matches. This should be filled + * via zero or more calls to cpl_add_completion(). + * data void * The anonymous 'data' argument that was + * passed to cpl_complete_word() or + * gl_customize_completion()). + * line const char * The current input line. + * word_end int The index of the character in line[] which + * follows the end of the token that is being + * completed. + * Output + * return int 0 - OK. + * 1 - Error. + */ +#define CPL_MATCH_FN(fn) int (fn)(WordCompletion *cpl, void *data, \ + const char *line, int word_end) +typedef CPL_MATCH_FN(CplMatchFn); + +/*....................................................................... + * Optional callback functions declared and prototyped using the + * following macro are called upon to return non-zero if a given + * file, specified by its pathname, is to be included in a list of + * completions. + * + * Input: + * data void * The application specified pointer which + * was specified when this callback function + * was registered. This can be used to have + * anything you like passed to your callback. + * pathname const char * The pathname of the file to be checked to + * see if it should be included in the list + * of completions. + * Output + * return int 0 - Ignore this file. + * 1 - Do include this file in the list + * of completions. + */ +#define CPL_CHECK_FN(fn) int (fn)(void *data, const char *pathname) +typedef CPL_CHECK_FN(CplCheckFn); + +/* + * You can use the following CplCheckFn callback function to only + * have executables included in a list of completions. + */ +CPL_CHECK_FN(cpl_check_exe); + +/* + * cpl_file_completions() is the builtin filename completion callback + * function. This can also be called by your own custom CPL_MATCH_FN() + * callback functions. To do this pass on all of the arguments of your + * custom callback function to cpl_file_completions(), with the exception + * of the (void *data) argument. The data argument should either be passed + * NULL to request the default behaviour of the file-completion function, + * or be passed a pointer to a CplFileConf structure (see below). In the + * latter case the contents of the structure modify the behavior of the + * file-completer. + */ +CPL_MATCH_FN(cpl_file_completions); + +/* + * Objects of the following type can be used to change the default + * behavior of the cpl_file_completions() callback function. + */ +typedef struct CplFileConf CplFileConf; + +/* + * If you want to change the behavior of the cpl_file_completions() + * callback function, call the following function to allocate a + * configuration object, then call one or more of the subsequent + * functions to change any of the default configuration parameters + * that you don't want. This function returns NULL when there is + * insufficient memory. + */ +CplFileConf *new_CplFileConf(void); + +/* + * If backslashes in the prefix being passed to cpl_file_completions() + * should be treated as literal characters, call the following function + * with literal=1. Otherwise the default is to treat them as escape + * characters which remove the special meanings of spaces etc.. + */ +void cfc_literal_escapes(CplFileConf *cfc, int literal); + +/* + * Before calling cpl_file_completions(), call this function if you + * know the index at which the filename prefix starts in the input line. + * Otherwise by default, or if you specify start_index to be -1, the + * filename is taken to start after the first unescaped space preceding + * the cursor, or the start of the line, which ever comes first. + */ +void cfc_file_start(CplFileConf *cfc, int start_index); + +/* + * If you only want certain types of files to be included in the + * list of completions, use the following function to specify a + * callback function which will be called to ask whether a given file + * should be included. The chk_data argument is will be passed to the + * callback function whenever it is called and can be anything you want. + */ +void cfc_set_check_fn(CplFileConf *cfc, CplCheckFn *chk_fn, void *chk_data); + +/* + * The following function deletes a CplFileConf objects previously + * returned by new_CplFileConf(). It always returns NULL. + */ +CplFileConf *del_CplFileConf(CplFileConf *cfc); + +/* + * The following configuration structure is deprecated. Do not change + * its contents, since this will break any programs that still use it, + * and don't use it in new programs. Instead use opaque CplFileConf + * objects as described above. cpl_file_completions() figures out + * what type of structure you pass it, by virtue of a magic int code + * placed at the start of CplFileConf object by new_CplFileConf(). + */ +typedef struct { + int escaped; /* Opposite to the argument of cfc_literal_escapes() */ + int file_start; /* Equivalent to the argument of cfc_file_start() */ +} CplFileArgs; +/* + * This initializes the deprecated CplFileArgs structures. + */ +void cpl_init_FileArgs(CplFileArgs *cfa); + +/*....................................................................... + * When an error occurs while performing a completion, custom completion + * callback functions should register a terse description of the error + * by calling cpl_record_error(). This message will then be returned on + * the next call to cpl_last_error() and used by getline to display an + * error message to the user. + * + * Input: + * cpl WordCompletion * The string-completion resource object that was + * originally passed to the callback. + * errmsg const char * The description of the error. + */ +void cpl_record_error(WordCompletion *cpl, const char *errmsg); + +/*....................................................................... + * This function can be used to replace the builtin filename-completion + * function with one of the user's choice. The user's completion function + * has the option of calling the builtin filename-completion function + * if it believes that the token that it has been presented with is a + * filename (see cpl_file_completions() above). + * + * Input: + * gl GetLine * The resource object of the command-line input + * module. + * data void * This is passed to match_fn() whenever it is + * called. It could, for example, point to a + * symbol table that match_fn() would look up + * matches in. + * match_fn CplMatchFn * The function that will identify the prefix + * to be completed from the input line, and + * report matching symbols. + * Output: + * return int 0 - OK. + * 1 - Error. + */ +int gl_customize_completion(GetLine *gl, void *data, CplMatchFn *match_fn); + +/*....................................................................... + * This function allows you to install alternate completion action + * functions or completion listing functions, or to change the + * completion function of an existing action of the same type. This + * should preferably be called before the first call to gl_get_line() + * so that the name of the action becomes defined before the user's + * configuration file is read. + * + * Input: + * gl GetLine * The resource object of the command-line input + * module. + * data void * This is passed to match_fn() whenever it is + * called. It could, for example, point to a + * symbol table that match_fn() would look up + * matches in. + * match_fn CplMatchFn * The function that will identify the prefix + * to be completed from the input line, and + * report matching symbols. + * list_only int If non-zero, install an action that only lists + * possible completions, rather than attempting + * to perform the completion. + * name const char * The name with which users can refer to the + * binding in tecla configuration files. + * keyseq const char * The key sequence with which to invoke + * the binding. This should be specified in the + * same manner as key-sequences in tecla + * configuration files (eg. "M-^I"). + * Output: + * return int 0 - OK. + * 1 - Error. + */ +int gl_completion_action(GetLine *gl, void *data, CplMatchFn *match_fn, + int list_only, const char *name, const char *keyseq); + +/*....................................................................... + * Change the terminal (or stream) that getline interacts with. + * + * Input: + * gl GetLine * The resource object of the command-line input + * module. + * input_fp FILE * The stdio stream to read from. + * output_fp FILE * The stdio stream to write to. + * term const char * The terminal type. This can be NULL if + * either or both of input_fp and output_fp don't + * refer to a terminal. Otherwise it should refer + * to an entry in the terminal information database. + * Output: + * return int 0 - OK. + * 1 - Error. + */ +int gl_change_terminal(GetLine *gl, FILE *input_fp, FILE *output_fp, + const char *term); + +/*....................................................................... + * The following functions can be used to save and restore the contents + * of the history buffer. + * + * Input: + * gl GetLine * The resource object of the command-line input + * module. + * filename const char * The name of the new file to write to. + * comment const char * Extra information such as timestamps will + * be recorded on a line started with this + * string, the idea being that the file can + * double as a command file. Specify "" if + * you don't care. Be sure to specify the + * same string to both functions. + * max_lines int The maximum number of lines to save, or -1 + * to save all of the lines in the history + * list. + * Output: + * return int 0 - OK. + * 1 - Error. + */ +int gl_save_history(GetLine *gl, const char *filename, const char *comment, + int max_lines); +int gl_load_history(GetLine *gl, const char *filename, const char *comment); + +/* + * Enumerate file-descriptor events that can be waited for. + */ +typedef enum { + GLFD_READ, /* Watch for data waiting to be read from a file descriptor */ + GLFD_WRITE, /* Watch for ability to write to a file descriptor */ + GLFD_URGENT /* Watch for urgent out-of-band data on the file descriptor */ +} GlFdEvent; + +/* + * The following enumeration is used for the return status of file + * descriptor event callbacks. + */ +typedef enum { + GLFD_ABORT, /* Cause gl_get_line() to abort with an error */ + GLFD_REFRESH, /* Redraw the input line and continue waiting for input */ + GLFD_CONTINUE /* Continue to wait for input, without redrawing the line */ +} GlFdStatus; + +/*....................................................................... + * On systems that have the select() system call, while gl_get_line() + * is waiting for terminal input, it can also be asked to listen for + * activity on arbitrary file descriptors. Callback functions of the + * following type can be registered to be called when activity is + * seen. If your callback needs to write to the terminal or use + * signals, please see the gl_get_line(3) man page. + * + * Input: + * gl GetLine * The gl_get_line() resource object. You can use + * this safely to call gl_watch_fd() or + * gl_inactivity_timeout(). The effect of calling other + * functions that take a gl argument is undefined, + * and must be avoided. + * data void * A pointer to arbitrary callback data, as originally + * registered with gl_watch_fd(). + * fd int The file descriptor that has activity. + * event GlFdEvent The activity seen on the file descriptor. The + * inclusion of this argument allows the same + * callback to be registered for multiple events. + * Output: + * return GlFdStatus GLFD_ABORT - Cause gl_get_line() to abort with + * an error (set errno if you need it). + * GLFD_REFRESH - Redraw the input line and continue + * waiting for input. Use this if you + * wrote something to the terminal. + * GLFD_CONTINUE - Continue to wait for input, without + * redrawing the line. + */ +#define GL_FD_EVENT_FN(fn) GlFdStatus (fn)(GetLine *gl, void *data, int fd, \ + GlFdEvent event) +typedef GL_FD_EVENT_FN(GlFdEventFn); + +/*....................................................................... + * Where possible, register a function and associated data to be called + * whenever a specified event is seen on a file descriptor. + * + * Input: + * gl GetLine * The resource object of the command-line input + * module. + * fd int The file descriptor to watch. + * event GlFdEvent The type of activity to watch for. + * callback GlFdEventFn * The function to call when the specified + * event occurs. Setting this to 0 removes + * any existing callback. + * data void * A pointer to arbitrary data to pass to the + * callback function. + * Output: + * return int 0 - OK. + * 1 - Either gl==NULL, or this facility isn't + * available on the the host system + * (ie. select() isn't available). No + * error message is generated in the latter + * case. + */ +int gl_watch_fd(GetLine *gl, int fd, GlFdEvent event, + GlFdEventFn *callback, void *data); + +/* + * Enumerators from the following list are returned by activity + * timeout callbacks registered by gl_inactivity_timeout(). They tell + * gl_get_line() whether and how to procede. + */ +typedef enum { + GLTO_ABORT, /* Cause gl_get_line() to abort with an error */ + GLTO_REFRESH, /* Redraw the input line and continue waiting for input */ + GLTO_CONTINUE /* Continue to wait for input, without redrawing the line */ +} GlAfterTimeout; + +/*....................................................................... + * On systems that have the select() system call, the application has + * the option of providing a callback function of the following type, + * which is called whenever no terminal input or other I/O activity is + * seen for the timeout duration specified in the last call to + * gl_inactivity_timeout(). + * + * Input: + * gl GetLine * The gl_get_line() resource object. You can use + * this safely to call gl_watch_fd() or + * gl_inactivity_timeout(). The effect of calling other + * functions that take a gl argument is undefined, + * and must be avoided. + * data void * A pointer to arbitrary callback data, as + * originally registered with gl_inactivity_timeout(). + * Output: + * return GlAfterTimeout GLTO_ABORT - Cause gl_get_line() to + * abort with an error (set + * errno if you need it). + * GLTO_REFRESH - Redraw the input line and + * continue waiting for + * input. Use this if you + * wrote something to the + * terminal. + * GLTO_CONTINUE - Continue to wait for + * input, without redrawing + * the line. + */ +#define GL_TIMEOUT_FN(fn) GlAfterTimeout (fn)(GetLine *gl, void *data) +typedef GL_TIMEOUT_FN(GlTimeoutFn); + +/*....................................................................... + * On systems with the select() system call, the gl_inactivity_timeout() + * function provides the option of setting (or cancelling) an + * inactivity timeout. Inactivity, in this case, refers both to + * terminal input received from the user, and to I/O on any file + * descriptors registered by calls to gl_watch_fd(). If at any time, + * no activity is seen for the requested time period, the specified + * timeout callback function is called. On returning, this callback + * returns a code which tells gl_get_line() what to do next. Note that + * each call to gl_inactivity_timeout() replaces any previously installed + * timeout callback, and that specifying a callback of 0, turns off + * inactivity timing. + * + * Beware that although the timeout argument includes a nano-second + * component, few computer clocks presently have resolutions finer + * than a few milliseconds, so asking for less than a few milliseconds + * is equivalent to zero on a lot of systems. + * + * Input: + * gl GetLine * The resource object of the command-line input + * module. + * callback GlTimeoutFn * The function to call when the inactivity + * timeout is exceeded. To turn off + * inactivity timeouts altogether, send 0. + * data void * A pointer to arbitrary data to pass to the + * callback function. + * sec unsigned long The number of whole seconds in the timeout. + * nsec unsigned long The fractional number of seconds in the + * timeout, expressed in nano-seconds (see + * the caveat above). + * Output: + * return int 0 - OK. + * 1 - Either gl==NULL, or this facility isn't + * available on the the host system + * (ie. select() isn't available). No + * error message is generated in the latter + * case. + */ +int gl_inactivity_timeout(GetLine *gl, GlTimeoutFn *timeout_fn, void *data, + unsigned long sec, unsigned long nsec); + +/*....................................................................... + * Switch history streams. History streams represent separate history + * lists recorded within a single history buffer. Different streams + * are distinguished by integer identifiers chosen by the calling + * appplicaton. Initially new_GetLine() sets the stream identifier to + * 0. Whenever a new line is appended to the history list, the current + * stream identifier is recorded with it, and history lookups only + * consider lines marked with the current stream identifier. + * + * Input: + * gl GetLine * The resource object of gl_get_line(). + * id unsigned The new history stream identifier. + * Output: + * return int 0 - OK. + * 1 - Error. + */ +int gl_group_history(GetLine *gl, unsigned id); + +/*....................................................................... + * Display the contents of the history list. + * + * Input: + * gl GetLine * The resource object of gl_get_line(). + * fp FILE * The stdio output stream to write to. + * fmt const char * A format string. This containing characters to be + * written verbatim, plus any of the following + * format directives: + * %D - The date, formatted like 2001-11-20 + * %T - The time of day, formatted like 23:59:59 + * %N - The sequential entry number of the + * line in the history buffer. + * %G - The number of the history group that + * the line belongs to. + * %% - A literal % character. + * %H - The history line itself. + * Note that a '\n' newline character is not + * appended by default. + * all_groups int If true, display history lines from all + * history groups. Otherwise only display + * those of the current history group. + * max_lines int If max_lines is < 0, all available lines + * are displayed. Otherwise only the most + * recent max_lines lines will be displayed. + * Output: + * return int 0 - OK. + * 1 - Error. + */ +int gl_show_history(GetLine *gl, FILE *fp, const char *fmt, int all_groups, + int max_lines); + +/*....................................................................... + * Resize or delete the history buffer. + * + * Input: + * gl GetLine * The resource object of gl_get_line(). + * bufsize size_t The number of bytes in the history buffer, or 0 + * to delete the buffer completely. + * Output: + * return int 0 - OK. + * 1 - Insufficient memory (the previous buffer + * will have been retained). No error message + * will be displayed. + */ +int gl_resize_history(GetLine *gl, size_t bufsize); + +/*....................................................................... + * Set an upper limit to the number of lines that can be recorded in the + * history list, or remove a previously specified limit. + * + * Input: + * gl GetLine * The resource object of gl_get_line(). + * max_lines int The maximum number of lines to allow, or -1 to + * cancel a previous limit and allow as many lines + * as will fit in the current history buffer size. + */ +void gl_limit_history(GetLine *gl, int max_lines); + +/*....................................................................... + * Discard either all historical lines, or just those associated with the + * current history group. + * + * Input: + * gl GetLine * The resource object of gl_get_line(). + * all_groups int If true, clear all of the history. If false, + * clear only the stored lines associated with the + * currently selected history group. + */ +void gl_clear_history(GetLine *gl, int all_groups); + +/*....................................................................... + * Temporarily enable or disable the gl_get_line() history mechanism. + * + * Input: + * gl GetLine * The resource object of gl_get_line(). + * enable int If true, turn on the history mechanism. If + * false, disable it. + */ +void gl_toggle_history(GetLine *gl, int enable); + +/* + * Objects of the following type are returned by gl_terminal_size(). + */ +typedef struct { + int nline; /* The terminal has nline lines */ + int ncolumn; /* The terminal has ncolumn columns */ +} GlTerminalSize; + +/*....................................................................... + * Update if necessary, and return the current size of the terminal. + * + * Input: + * gl GetLine * The resource object of gl_get_line(). + * def_ncolumn int If the number of columns in the terminal + * can't be determined, substitute this number. + * def_nline int If the number of lines in the terminal can't + * be determined, substitute this number. + * Output: + * return GlTerminalSize The current terminal size. + */ +GlTerminalSize gl_terminal_size(GetLine *gl, int def_ncolumn, int def_nline); + +/*....................................................................... + * Tell gl_get_line() the current terminal size. Note that this is only + * necessary on systems where changes in terminal size aren't reported + * via SIGWINCH. + * + * Input: + * gl GetLine * The resource object of gl_get_line(). + * ncolumn int The number of columns in the terminal. + * nline int The number of rows in the terminal. + * Output: + * return int 0 - OK. + * 1 - Error. + */ +int gl_set_term_size(GetLine *gl, int ncolumn, int nline); + +/* + * The gl_lookup_history() function returns information in an + * argument of the following type. + */ +typedef struct { + const char *line; /* The requested history line */ + unsigned group; /* The history group to which the */ + /* line belongs. */ + time_t timestamp; /* The date and time at which the */ + /* line was originally entered. */ +} GlHistoryLine; + +/*....................................................................... + * Lookup a history line by its sequential number of entry in the + * history buffer. + * + * Input: + * gl GetLine * The resource object of gl_get_line(). + * id unsigned long The identification number of the line to + * be returned, where 0 denotes the first line + * that was entered in the history list, and + * each subsequently added line has a number + * one greater than the previous one. For + * the range of lines currently in the list, + * see the gl_range_of_history() function. + * Input/Output: + * line GlHistoryLine * A pointer to the variable in which to + * return the details of the line. + * Output: + * return int 0 - The line is no longer in the history + * list, and *line has not been changed. + * 1 - The requested line can be found in + * *line. Note that the string in + * line->line is part of the history + * buffer and will change, so a private + * copy should be made if you wish to + * use it after subsequent calls to any + * functions that take gl as an argument. + */ +int gl_lookup_history(GetLine *gl, unsigned long id, GlHistoryLine *line); + +/* + * The gl_state_of_history() function returns information in an argument + * of the following type. + */ +typedef struct { + int enabled; /* True if history is enabled */ + unsigned group; /* The current history group */ + int max_lines; /* The current upper limit on the number of lines */ + /* in the history list, or -1 if unlimited. */ +} GlHistoryState; + +/*....................................................................... + * Query the state of the history list. Note that any of the input/output + * pointers can be specified as NULL. + * + * Input: + * gl GetLine * The resource object of gl_get_line(). + * Input/Output: + * state GlHistoryState * A pointer to the variable in which to record + * the return values. + */ +void gl_state_of_history(GetLine *gl, GlHistoryState *state); + +/* + * The gl_range_of_history() function returns information in an argument + * of the following type. + */ +typedef struct { + unsigned long oldest; /* The sequential entry number of the oldest */ + /* line in the history list. */ + unsigned long newest; /* The sequential entry number of the newest */ + /* line in the history list. */ + int nlines; /* The number of lines in the history list */ +} GlHistoryRange; + +/*....................................................................... + * Query the number and range of lines in the history buffer. + * + * Input: + * gl GetLine * The resource object of gl_get_line(). + * range GlHistoryRange * A pointer to the variable in which to record + * the return values. If range->nline=0, the + * range of lines will be given as 0-0. + */ +void gl_range_of_history(GetLine *gl, GlHistoryRange *range); + +/* + * The gl_size_of_history() function returns information in an argument + * of the following type. + */ +typedef struct { + size_t size; /* The size of the history buffer (bytes) */ + size_t used; /* The number of bytes of the history buffer */ + /* that are currently occupied. */ +} GlHistorySize; + +/*....................................................................... + * Return the size of the history buffer and the amount of the + * buffer that is currently in use. + * + * Input: + * gl GetLine * The resource object of gl_get_line(). + * Input/Output: + * GlHistorySize size * A pointer to the variable in which to return + * the results. + */ +void gl_size_of_history(GetLine *gl, GlHistorySize *size); + +/*....................................................................... + * Enable or disable the automatic addition of newly entered lines to the + * history list. + * + * Input: + * gl GetLine * The resource object of gl_get_line(). + * enable int If true, subsequently entered lines will + * automatically be added to the history list + * before they are returned to the caller of + * gl_get_line(). If 0, the choice of how and + * when to archive lines in the history list, + * is left up to the calling application, which + * can do so via calls to gl_append_history(). + * Output: + * return int 0 - OK. + * 1 - Error. + */ +int gl_automatic_history(GetLine *gl, int enable); + +/*....................................................................... + * Append a specified line to the history list. + * + * Input: + * gl GetLine * The resource object of gl_get_line(). + * line const char * The line to be added. + * Output: + * return int 0 - OK. + * 1 - Error. + */ +int gl_append_history(GetLine *gl, const char *line); + +/*....................................................................... + * Specify whether text that users type should be displayed or hidden. + * In the latter case, only the prompt is displayed, and the final + * input line is not archived in the history list. + * + * Input: + * gl GetLine * The input-line history maintenance object. + * enable int 0 - Disable echoing. + * 1 - Enable echoing. + * -1 - Just query the mode without changing it. + * Output: + * return int The echoing disposition that was in effect + * before this function was called: + * 0 - Echoing was disabled. + * 1 - Echoing was enabled. + */ +int gl_echo_mode(GetLine *gl, int enable); + +/*....................................................................... + * This function can be called from gl_get_line() callbacks to have + * the prompt changed when they return. It has no effect if gl_get_line() + * is not currently being invoked. + * + * Input: + * gl GetLine * The resource object of gl_get_line(). + * prompt const char * The new prompt. + */ +void gl_replace_prompt(GetLine *gl, const char *prompt); + +/* + * Enumerate the available prompt formatting styles. + */ +typedef enum { + GL_LITERAL_PROMPT, /* Display the prompt string literally */ + GL_FORMAT_PROMPT /* The prompt string can contain any of the */ + /* following formatting directives: */ + /* %B - Display subsequent characters */ + /* with a bold font. */ + /* %b - Stop displaying characters */ + /* with the bold font. */ + /* %U - Underline subsequent characters. */ + /* %u - Stop underlining characters. */ + /* %S - Highlight subsequent characters */ + /* (also known as standout mode). */ + /* %s - Stop highlighting characters */ + /* %% - Display a single % character. */ +} GlPromptStyle; + +/*....................................................................... + * Specify whether to heed text attribute directives within prompt + * strings. + * + * Input: + * gl GetLine * The resource object of gl_get_line(). + * style GlPromptStyle The style of prompt (see the definition of + * GlPromptStyle in libtecla.h for details). + */ +void gl_prompt_style(GetLine *gl, GlPromptStyle style); + +/*....................................................................... + * Remove a signal from the list of signals that gl_get_line() traps. + * + * Input: + * gl GetLine * The resource object of gl_get_line(). + * signo int The number of the signal to be ignored. + * Output: + * return int 0 - OK. + * 1 - Error. + */ +int gl_ignore_signal(GetLine *gl, int signo); + +/* + * A bitwise union of the following enumerators is passed to + * gl_trap_signal() to specify the environment in which the + * application's signal handler is to be called. + */ +typedef enum { + GLS_RESTORE_SIG=1, /* Restore the caller's signal environment */ + /* while handling the signal. */ + GLS_RESTORE_TTY=2, /* Restore the caller's terminal settings */ + /* while handling the signal. */ + GLS_RESTORE_LINE=4, /* Move the cursor to the start of the next line */ + GLS_REDRAW_LINE=8, /* Redraw the input line when the signal handler */ + /* returns. */ + GLS_UNBLOCK_SIG=16, /* Normally a signal who's delivery is found to */ + /* be blocked by the calling application is not */ + /* trapped by gl_get_line(). Including this flag */ + /* causes it to be temporarily unblocked and */ + /* trapped while gl_get_line() is executing. */ + GLS_DONT_FORWARD=32,/* Don't forward the signal to the signal handler */ + /* of the calling program. */ + GLS_RESTORE_ENV = GLS_RESTORE_SIG | GLS_RESTORE_TTY | GLS_REDRAW_LINE, + GLS_SUSPEND_INPUT = GLS_RESTORE_ENV | GLS_RESTORE_LINE +} GlSignalFlags; + +/* + * The following enumerators are passed to gl_trap_signal() to tell + * it what to do after the application's signal handler has been called. + */ +typedef enum { + GLS_RETURN, /* Return the line as though the user had pressed the */ + /* return key. */ + GLS_ABORT, /* Cause gl_get_line() to return NULL */ + GLS_CONTINUE /* After handling the signal, resume command line editing */ +} GlAfterSignal; + +/*....................................................................... + * Tell gl_get_line() how to respond to a given signal. This can be used + * both to override the default responses to signals that gl_get_line() + * normally catches and to add new signals to the list that are to be + * caught. + * + * Input: + * gl GetLine * The resource object of gl_get_line(). + * signo int The number of the signal to be caught. + * flags unsigned A bitwise union of GlSignalFlags enumerators. + * after GlAfterSignal What to do after the application's signal + * handler has been called. + * errno_value int The value to set errno to. + * Output: + * return int 0 - OK. + * 1 - Insufficient memory to record the + * new signal disposition. + */ +int gl_trap_signal(GetLine *gl, int signo, unsigned flags, + GlAfterSignal after, int errno_value); + +/*....................................................................... + * By default, gl_get_line() doesn't trap signals that are blocked + * when it is called. This default can be changed either on a + * per-signal basis by calling gl_trap_signal(), or on a global basis + * by calling this function. What this function does is add the + * GLS_UNBLOCK_SIG flag to all signals that are currently configured + * to be trapped by gl_get_line(), such that when subsequent calls to + * gl_get_line() wait for I/O, these signals are temporarily + * unblocked. This behavior is useful in non-blocking server-I/O mode, + * where it is used to avoid race conditions related to handling these + * signals externally to gl_get_line(). See the demonstration code in + * demo3.c, or the gl_handle_signal() man page for further + * information. + * + * Input: + * gl GetLine * The resource object of gl_get_line(). + */ +void gl_catch_blocked(GetLine *gl); + +/*....................................................................... + * In server-I/O mode the terminal is left in raw mode between calls + * to gl_get_line(), so it is necessary for the application to install + * terminal restoring signal handlers for signals that could terminate + * or suspend the process, plus a terminal reconfiguration handler to + * be called when a process resumption signal is received, and finally + * a handler to be called when a terminal-resize signal is received. + * + * Since there are many signals that by default terminate or suspend + * processes, and different systems support different sub-sets of + * these signals, this function provides a convenient wrapper around + * sigaction() for assigning the specified handlers to all appropriate + * signals. It also arranges that when any one of these signals is + * being handled, all other catchable signals are blocked. This is + * necessary so that the specified signal handlers can safely call + * gl_raw_io(), gl_normal_io() and gl_update_size() without reentrancy + * issues. + * + * Input: + * term_handler void (*)(int) The signal handler to invoke when + * a process terminating signal is + * received. + * susp_handler void (*)(int) The signal handler to invoke when + * a process suspending signal is + * received. + * cont_handler void (*)(int) The signal handler to invoke when + * a process resumption signal is + * received (ie. SIGCONT). + * size_handler void (*)(int) The signal handler to invoke when + * a terminal-resize signal (ie. SIGWINCH) + * is received. + * Output: + * return int 0 - OK. + * 1 - Error. + */ +int gl_tty_signals(void (*term_handler)(int), void (*susp_handler)(int), + void (*cont_handler)(int), void (*size_handler)(int)); + +/*....................................................................... + * Return the last signal that was caught by the most recent call to + * gl_get_line(), or -1 if no signals were caught. This is useful if + * gl_get_line() returns errno=EINTR and you need to find out what signal + * caused it to abort. + * + * Input: + * gl GetLine * The resource object of gl_get_line(). + * Output: + * return int The last signal caught by the most recent + * call to gl_get_line(), or -1 if no signals + * were caught. + */ +int gl_last_signal(GetLine *gl); + +/*....................................................................... + * Return the signal mask used by gl_get_line(). This is the set of + * signals that gl_get_line() is currently configured to trap. + * + * Input: + * gl GetLine * The resource object of gl_get_line(). + * Input/Output: + * set sigset_t * The set of signals will be returned in *set, + * in the form of a signal process mask, as + * used by sigaction(), sigprocmask(), + * sigpending(), sigsuspend(), sigsetjmp() and + * other standard POSIX signal-aware + * functions. + * Output: + * return int 0 - OK. + * 1 - Error (examine errno for reason). + */ +int gl_list_signals(GetLine *gl, sigset_t *set); + +/*....................................................................... + * Respond to signals who's default effects have important + * consequences to gl_get_line(). This is intended for use in + * non-blocking server mode, where the external event loop is + * responsible for catching signals. Signals that are handled include + * those that by default terminate or suspend the process, and the + * signal that indicates that the terminal size has changed. Note that + * this function is not signal safe and should thus not be called from + * a signal handler itself. See the gl_io_mode() man page for how it + * should be used. + * + * In the case of signals that by default terminate or suspend + * processes, command-line editing will be suspended, the terminal + * returned to a usable state, then the default disposition of the + * signal restored and the signal resent, in order to suspend or + * terminate the process. If the process subsequently resumes, + * command-line editing is resumed. + * + * In the case of signals that indicate that the terminal has been + * resized, the new size will be queried, and any input line that is + * being edited will be redrawn to fit the new dimensions of the + * terminal. + * + * Input: + * signo int The number of the signal to respond to. + * gl GetLine * The first element of an array of 'ngl' GetLine + * objects. + * ngl int The number of elements in the gl[] array. Normally + * this will be one. + */ +void gl_handle_signal(int signo, GetLine *gl, int ngl); + +/*....................................................................... + * Return extra information (ie. in addition to that provided by errno) + * about the last error to occur in either gl_get_line() or its + * associated public functions. + * + * Input: + * gl GetLine * The resource object of gl_get_line(). + * Input/Output: + * buff char * An optional output buffer. Note that if the + * calling application calls any gl_*() + * functions from signal handlers, it should + * provide a buffer here, so that a copy of + * the latest error message can safely be made + * while signals are blocked. + * n size_t The allocated size of buff[]. + * Output: + * return const char * A pointer to the error message. This will + * be the buff argument, unless buff==NULL, in + * which case it will be a pointer to an + * internal error buffer. In the latter case, + * note that the contents of the returned buffer + * will change on subsequent calls to any gl_*() + * functions. + */ +const char *gl_error_message(GetLine *gl, char *buff, size_t n); + +/*....................................................................... + * Clear the terminal and leave the cursor at the home position. In + * server I/O mode, arrange for the input line to be redrawn from scratch + * when gl_get_line() is next called. + * + * Input: + * gl GetLine * The resource object of gl_get_line(). + * Output: + * return int 0 - OK. + * 1 - Error. + */ +int gl_erase_terminal(GetLine *gl); + +/*....................................................................... + * Display a left-justified string over multiple terminal lines, + * taking account of the current width of the terminal. Optional + * indentation and an optional prefix string can be specified to be + * displayed at the start of each new terminal line used. Similarly, + * an optional suffix can be specified to be displayed at the end of + * each terminal line. If needed, a single paragraph can be broken + * across multiple calls. Note that literal newlines in the input + * string can be used to force a newline at any point and that you + * should use this feature to explicitly end all paragraphs, including + * at the end of the last string that you write. Note that when a new + * line is started between two words that are separated by spaces, + * those spaces are not output, whereas when a new line is started + * because a newline character was found in the string, only the + * spaces before the newline character are discarded. + * + * Input: + * gl GetLine * The resource object of gl_get_line(). + * indentation int The number of spaces of indentation to write + * at the beginning of each new terminal line. + * prefix const char * An optional prefix string to write after the + * indentation margin at the start of each new + * terminal line. You can specify NULL if no + * prefix is required. + * suffix const char * An optional suffix string to draw at the end + * of the terminal line. Spaces will be added + * where necessary to ensure that the suffix ends + * in the last column of the terminal line. If + * no suffix is desired, specify NULL. + * fill_char int The padding character to use when indenting + * the line or padding up to the suffix. + * def_width int If the terminal width isn't known, such as when + * writing to a pipe or redirecting to a file, + * this number specifies what width to assume. + * start int The number of characters already written to + * the start of the current terminal line. This + * is primarily used to allow individual + * paragraphs to be written over multiple calls + * to this function, but can also be used to + * allow you to start the first line of a + * paragraph with a different prefix or + * indentation than those specified above. + * string const char * The string to be written. + * Output: + * return int On error -1 is returned. Otherwise the + * return value is the terminal column index at + * which the cursor was left after writing the + * final word in the string. Successful return + * values can thus be passed verbatim to the + * 'start' arguments of subsequent calls to + * gl_display_text() to allow the printing of a + * paragraph to be broken across multiple calls + * to gl_display_text(). + */ +int gl_display_text(GetLine *gl, int indentation, const char *prefix, + const char *suffix, int fill_char, int def_width, + int start, const char *string); + + +/* + * Enumerate the I/O modes supported by gl_get_line(). + */ +typedef enum { + GL_NORMAL_MODE, /* Normal line-at-a-time mode using gl_get_line()'s */ + /* internal event loop. */ + GL_SERVER_MODE /* Non-blocking server mode, driven by an external */ + /* event loop. */ +} GlIOMode; + +/*....................................................................... + * Select the I/O mode to be used by gl_get_line(). + * + * Input: + * gl GetLine * The resource object of gl_get_line(). + * mode GlIOMode The I/O mode to establish. Note that + * when server mode, the terminal is placed + * in raw mode, as though gl_raw_io() had + * been called. + * Output: + * return int 0 - OK. + * 1 - Error. + */ +int gl_io_mode(GetLine *gl, GlIOMode mode); + +/*....................................................................... + * In server mode, this function configures the terminal for non-blocking + * raw terminal I/O. In normal I/O mode it does nothing. + * + * Callers of this function must be careful to trap all signals that + * terminate or suspend the program, and call gl_normal_io() + * from the corresponding signal handlers in order to restore the + * terminal to its original settings before the program is terminated + * or suspended. They should also trap the SIGCONT signal to detect + * when the program resumes, and ensure that its signal handler + * call gl_raw_io() to redisplay the line and resume editing. + * + * Input: + * gl GetLine * The line editor resource object. + * Output: + * return int 0 - OK. + * 1 - Error. + */ +int gl_raw_io(GetLine *gl); + +/*....................................................................... + * Restore the terminal to the state that it had when gl_raw_io() was + * last called. After calling gl_raw_io(), this function must be called + * before terminating or suspending the program, and before attempting + * other uses of the terminal from within the program. See gl_raw_io() + * for more details. + * + * Input: + * gl GetLine * The line editor resource object. + * Output: + * return int 0 - OK. + * 1 - Error. + */ +int gl_normal_io(GetLine *gl); + +/*....................................................................... + * When in non-blocking server mode, this function can be used to abandon + * the current incompletely entered input line, and prepare to start + * editing a new line on the next call to gl_get_line(). + * + * Input: + * gl GetLine * The line editor resource object. + * Output: + * return int 0 - OK. + * 1 - Error. + */ +void gl_abandon_line(GetLine *gl); + +/* + * Enumerators of the following type are used to report why + * gl_get_line() returned. This is most useful in non-blocking + * server mode, since in that mode a NULL return value can mean + * either that an error occurred, or that I/O blocked. + */ +typedef enum { + GLR_NEWLINE, /* A new input line was returned */ + GLR_BLOCKED, /* The terminal was in non-blocking mode, and input */ + /* or output would have blocked. */ + GLR_SIGNAL, /* A signal caused gl_get_line() to return. */ + GLR_TIMEOUT, /* An application timeout callback returned GLTO_ABORT */ + GLR_FDABORT, /* An application I/O callack returned GLFD_ABORT */ + GLR_EOF, /* End of file reached */ + GLR_ERROR /* An unexpected error caused gl_get_line() to abort */ +} GlReturnStatus; + +/*....................................................................... + * Ask gl_get_line() what caused it to return. + * + * Input: + * gl GetLine * The line editor resource object. + * Output: + * return GlReturnStatus The return status of the last call to + * gl_get_line(). + */ +GlReturnStatus gl_return_status(GetLine *gl); + +/* + * Enumerate the types of I/O that gl_get_line() can be waiting for + * in non-blocking sedrver I/O mode. + */ +typedef enum { + GLP_READ, /* gl_get_line() is waiting to write to the terminal */ + GLP_WRITE /* gl_get_line() is waiting to read from the terminal */ +} GlPendingIO; + +/*....................................................................... + * In non-blocking server-I/O mode, this function should be called + * from the application's external event loop to see what type of + * terminal I/O is being waited for by gl_get_line(), and thus what + * direction of I/O to wait for with select() or poll(). + * + * Input: + * gl GetLine * The resource object of gl_get_line(). + * Output: + * return GlPendingIO The type of pending I/O being waited for. + */ +GlPendingIO gl_pending_io(GetLine *gl); + +/* + * The following enumerators are returned by externally defined action + * functions to tell gl_get_line() how to procede after the action + * function returns. + */ +typedef enum { + GLA_ABORT, /* Cause gl_get_line() to return NULL */ + GLA_RETURN, /* Return the line as though the user had pressed the */ + /* return key. */ + GLA_CONTINUE /* Resume command-line editing */ +} GlAfterAction; + +/*....................................................................... + * Functions of the following form implement external + * application-specific action functions, which can then be bound to + * sequences of terminal keys. + * + * Input: + * gl GetLine * The line editor resource object. + * data void * The anonymous 'data' argument that was + * passed to gl_external_action() when the + * callback function was registered. + * count int A positive repeat count specified by the user, + * or 1 if not specified. Action functions should + * ignore this if repeating the action multiple + * times isn't appropriate. Alternatively they + * can interpret it as a general numeric + * argument. + * curpos size_t The position of the cursor within the input + * line, expressed as the index of the + * corresponding character within the line[] + * array. + * line const char * A read-only copy of the current input line. + * Output + * return GlAfterAction What should gl_get_line() do when the action + * function returns? + * GLA_ABORT - Cause gl_get_line() to + * abort with an error (set + * errno if you need it). + * GLA_RETURN - Return the input line as + * though the user had typed + * the return key. + * GLA_CONTINUE - Resume waiting for keyboard + * input. + */ +#define GL_ACTION_FN(fn) GlAfterAction (fn)(GetLine *gl, void *data, \ + int count, size_t curpos, const char *line) + +typedef GL_ACTION_FN(GlActionFn); + +/*....................................................................... + * Register an application-provided function as an action function. + * This should preferably be called before the first call to gl_get_line() + * so that the name of the action becomes defined before the user's + * configuration file is read. + * + * Input: + * gl GetLine * The resource object of the command-line input + * module. + * data void * Arbitrary application-specific callback + * data to be passed to the callback + * function, fn(). + * fn GlActionFn * The application-specific function that + * implements the action. This will be invoked + * whenever the user presses any + * key-sequence which is bound to this action. + * name const char * The name with which users can refer to the + * binding in tecla configuration files. + * keyseq const char * The key sequence with which to invoke + * the binding. This should be specified in the + * same manner as key-sequences in tecla + * configuration files (eg. "M-^I"). + * Output: + * return int 0 - OK. + * 1 - Error. + */ +int gl_register_action(GetLine *gl, void *data, GlActionFn *fn, + const char *name, const char *keyseq); + +/*....................................................................... + * This function is designed to be called by CPL_MATCH_FN() callback + * functions. It adds one possible completion of the token that is being + * completed to an array of completions. If the completion needs any + * special quoting to be valid when displayed in the input line, this + * quoting must be included in the string. + * + * Input: + * cpl WordCompletion * The argument of the same name that was passed + * to the calling CPL_MATCH_FN() callback function. + * line const char * The input line, as received by the callback + * function. + * word_start int The index within line[] of the start of the + * word that is being completed. If an empty + * string is being completed, set this to be + * the same as word_end. + * word_end int The index within line[] of the character which + * follows the incomplete word, as received by the + * callback function. + * suffix const char * The appropriately quoted string that could + * be appended to the incomplete token to complete + * it. A copy of this string will be allocated + * internally. + * type_suffix const char * When listing multiple completions, gl_get_line() + * appends this string to the completion to indicate + * its type to the user. If not pertinent pass "". + * Otherwise pass a literal or static string. + * cont_suffix const char * If this turns out to be the only completion, + * gl_get_line() will append this string as + * a continuation. For example, the builtin + * file-completion callback registers a directory + * separator here for directory matches, and a + * space otherwise. If the match were a function + * name you might want to append an open + * parenthesis, etc.. If not relevant pass "". + * Otherwise pass a literal or static string. + * Output: + * return int 0 - OK. + * 1 - Error. + */ +int cpl_add_completion(WordCompletion *cpl, const char *line, + int word_start, int word_end, const char *suffix, + const char *type_suffix, const char *cont_suffix); + +/* + * Each possible completion string is recorded in an array element of + * the following type. + */ +typedef struct { + char *completion; /* The matching completion string */ + char *suffix; /* The pointer into completion[] at which the */ + /* string was extended. */ + const char *type_suffix; /* A suffix to be added when listing completions */ + /* to indicate the type of the completion. */ +} CplMatch; + +/* + * Completions are returned in a container of the following form. + */ +typedef struct { + char *suffix; /* The common initial part of all of the */ + /* completion suffixes. */ + const char *cont_suffix; /* Optional continuation string to be appended to */ + /* the sole completion when nmatch==1. */ + CplMatch *matches; /* The array of possible completion strings, */ + /* sorted into lexical order. */ + int nmatch; /* The number of elements in matches[] */ +} CplMatches; + +/*....................................................................... + * Given an input line and the point at which completion is to be + * attempted, return an array of possible completions. + * + * Input: + * cpl WordCompletion * The word-completion resource object. + * line const char * The current input line. + * word_end int The index of the character in line[] which + * follows the end of the token that is being + * completed. + * data void * Anonymous 'data' to be passed to match_fn(). + * match_fn CplMatchFn * The function that will identify the prefix + * to be completed from the input line, and + * record completion suffixes. + * Output: + * return CplMatches * The container of the array of possible + * completions. The returned pointer refers + * to a container owned by the parent Completion + * object, and its contents thus potentially + * change on every call to cpl_complete_word(). + */ +CplMatches *cpl_complete_word(WordCompletion *cpl, const char *line, + int word_end, void *data, + CplMatchFn *match_fn); + +/*....................................................................... + * Recall the return value of the last call to cpl_complete_word(). + * + * Input: + * cpl WordCompletion * The completion resource object. + * Output: + * return CplMatches * The container of the array of possible + * completions, as returned by the last call to + * cpl_complete_word(). The returned pointer refers + * to a container owned by the parent WordCompletion + * object, and its contents thus potentially + * change on every call to cpl_complete_word(). + * On error, either in the execution of this + * function, or in the last call to + * cpl_complete_word(), NULL is returned, and a + * description of the error can be acquired by + * calling cpl_last_error(cpl). + */ +CplMatches *cpl_recall_matches(WordCompletion *cpl); + +/*....................................................................... + * Print out an array of matching completions. + * + * Input: + * result CplMatches * The container of the sorted array of + * completions. + * fp FILE * The output stream to write to. + * term_width int The width of the terminal. + * Output: + * return int 0 - OK. + * 1 - Error. + */ +int cpl_list_completions(CplMatches *result, FILE *fp, int term_width); + +/*....................................................................... + * Return a description of the error that occurred on the last call to + * cpl_complete_word() or cpl_add_completion(). + * + * Input: + * cpl WordCompletion * The string-completion resource object. + * Output: + * return const char * The description of the last error. + */ +const char *cpl_last_error(WordCompletion *cpl); + +/* + * PathCache objects encapsulate the resources needed to record + * files of interest from comma-separated lists of directories. + */ +typedef struct PathCache PathCache; + +/*....................................................................... + * Create an object who's function is to maintain a cache of filenames + * found within a list of directories, and provide quick lookup and + * completion of selected files in this cache. + * + * Output: + * return PathCache * The new, initially empty cache, or NULL + * on error. + */ +PathCache *new_PathCache(void); + +/*....................................................................... + * Delete a given cache of files, returning the resources that it + * was using to the system. + * + * Input: + * pc PathCache * The cache to be deleted (can be NULL). + * Output: + * return PathCache * The deleted object (ie. allways NULL). + */ +PathCache *del_PathCache(PathCache *pc); + +/*....................................................................... + * Return a description of the last path-caching error that occurred. + * + * Input: + * pc PathCache * The filename cache that suffered the error. + * Output: + * return char * The description of the last error. + */ +const char *pca_last_error(PathCache *pc); + +/*....................................................................... + * Build the list of files of interest contained in a given + * colon-separated list of directories. + * + * Input: + * pc PathCache * The cache in which to store the names of + * the files that are found in the list of + * directories. + * path const char * A colon-separated list of directory + * paths. Under UNIX, when searching for + * executables, this should be the return + * value of getenv("PATH"). + * Output: + * return int 0 - OK. + * 1 - An error occurred. + */ +int pca_scan_path(PathCache *pc, const char *path); + +/*....................................................................... + * If you want subsequent calls to pca_lookup_file() and + * pca_path_completions() to only return the filenames of certain + * types of files, for example executables, or filenames ending in + * ".ps", call this function to register a file-selection callback + * function. This callback function takes the full pathname of a file, + * plus application-specific data, and returns 1 if the file is of + * interest, and zero otherwise. + * + * Input: + * pc PathCache * The filename cache. + * check_fn CplCheckFn * The function to call to see if the name of + * a given file should be included in the + * cache. This determines what type of files + * will reside in the cache. To revert to + * selecting all files, regardless of type, + * pass 0 here. + * data void * You can pass a pointer to anything you + * like here, including NULL. It will be + * passed to your check_fn() callback + * function, for its private use. + */ +void pca_set_check_fn(PathCache *pc, CplCheckFn *check_fn, void *data); + +/*....................................................................... + * Given the simple name of a file, search the cached list of files + * in the order in which they where found in the list of directories + * previously presented to pca_scan_path(), and return the pathname + * of the first file which has this name. + * + * Input: + * pc PathCache * The cached list of files. + * name const char * The name of the file to lookup. + * name_len int The length of the filename substring at the + * beginning of name[], or -1 to assume that the + * filename occupies the whole of the string. + * literal int If this argument is zero, lone backslashes + * in name[] are ignored during comparison + * with filenames in the cache, under the + * assumption that they were in the input line + * soley to escape the special significance of + * characters like spaces. To have them treated + * as normal characters, give this argument a + * non-zero value, such as 1. + * Output: + * return char * The pathname of the first matching file, + * or NULL if not found. Note that the returned + * pointer points to memory owned by *pc, and + * will become invalid on the next call. + */ +char *pca_lookup_file(PathCache *pc, const char *name, int name_len, + int literal); + +/* + * Objects of the following type can be used to change the default + * behavior of the pca_path_completions() callback function. + */ +typedef struct PcaPathConf PcaPathConf; + +/* + * pca_path_completions() is a completion callback function for use directly + * with cpl_complete_word() or gl_customize_completions(), or indirectly + * from your own completion callback function. It requires that a PcaPathConf + * object be passed via its 'void *data' argument (see below). + */ +CPL_MATCH_FN(pca_path_completions); + +/*....................................................................... + * Allocate and initialize a pca_path_completions() configuration object. + * + * Input: + * pc PathCache * The filename cache in which to look for + * file name completions. + * Output: + * return PcaPathConf * The new configuration structure, or NULL + * on error. + */ +PcaPathConf *new_PcaPathConf(PathCache *pc); + +/*....................................................................... + * Deallocate memory, previously allocated by new_PcaPathConf(). + * + * Input: + * ppc PcaPathConf * Any pointer previously returned by + * new_PcaPathConf() [NULL is allowed]. + * Output: + * return PcaPathConf * The deleted structure (always NULL). + */ +PcaPathConf *del_PcaPathConf(PcaPathConf *ppc); + +/* + * If backslashes in the prefix being passed to pca_path_completions() + * should be treated as literal characters, call the following function + * with literal=1. Otherwise the default is to treat them as escape + * characters which remove the special meanings of spaces etc.. + */ +void ppc_literal_escapes(PcaPathConf *ppc, int literal); + +/* + * Before calling pca_path_completions, call this function if you know + * the index at which the filename prefix starts in the input line. + * Otherwise by default, or if you specify start_index to be -1, the + * filename is taken to start after the first unescaped space preceding + * the cursor, or the start of the line, whichever comes first. + */ +void ppc_file_start(PcaPathConf *ppc, int start_index); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/libtecla-1.6.1/libtecla.map b/libtecla-1.6.1/libtecla.map new file mode 100644 index 0000000..a63378e --- /dev/null +++ b/libtecla-1.6.1/libtecla.map @@ -0,0 +1,155 @@ +# This mapfile (or version script) lists the public symbols that are +# publically exported by each version of the tecla library. This file +# has the format required by the Sun and Linux linkers, and also acts +# as a template from which map files for other systems can be derived +# with awk or sed. +# +# Under Solaris and Linux, this map file is used by ld during shared +# library creation. It has two purposes: +# +# 1. It specifies which symbols in the library are to be made visible +# to applications. This has the dual benefits of reducing namespace +# polution, and of preventing applications from using private +# internal library functions that might change or disappear in +# future releases. +# +# 2. The information listed in this file is recorded in the shared +# library, such that when an application is linked against it, the +# linker can record a dependency in the application which says +# which is the earliest library version which included all of the +# symbols that the application needs. This means that if the +# application is copied to another system that has an earlier +# version of the library, the linker can quickly determine whether +# the earlier version contains all of the symbols that it needs. +# +# Under Linux, mapfiles can also be used to allow multiple +# incompatible versions of a given function to exist in a library, +# thus supporting applications that were compiled against different +# incompatible versions of the library. Since this feature (and the +# inclusion of .symver directives) isn't supported by Solaris, it +# can't be included in this file. Non backwards compatibility in the +# ABI must instead be handled in the more traditional way, by +# incrementing the major version number. +# +# When a new minor release is made, a new tecla_1.x specification +# should be added which inherits the symbols of the previous release +# and lists newly added functions. For example, below you will find +# the following clause: +# +# tecla_1.3 { +# global: +# ef_list_expansions; +# } tecla_1.2; +# +# This says that ef_list_expansions is the name of a public function +# that was added in the 1.3 release, and that the symbols defined in +# the previous tecla_1.2 clause have been inherited by tecla_1.3. +# +# For more details see the following URL: +# +# http://www.usenix.org/publications/library/proceedings/als2000/browndavid.html +#------------------------------------------------------------------------------- + +tecla_1.2 { + global: + cfc_file_start; + cfc_literal_escapes; + cfc_set_check_fn; + cpl_add_completion; + cpl_check_exe; + cpl_complete_word; + cpl_file_completions; + cpl_init_FileArgs; + cpl_last_error; + cpl_list_completions; + cpl_record_error; + del_CplFileConf; + del_ExpandFile; + del_GetLine; + del_PathCache; + del_PcaPathConf; + del_WordCompletion; + ef_expand_file; + ef_last_error; + gl_change_terminal; + gl_customize_completion; + gl_get_line; + new_CplFileConf; + new_ExpandFile; + new_GetLine; + new_PathCache; + new_PcaPathConf; + new_WordCompletion; + pca_last_error; + pca_lookup_file; + pca_path_completions; + pca_scan_path; + pca_set_check_fn; + ppc_file_start; + ppc_literal_escapes; + + local: + *; +}; + +tecla_1.3 { + global: + ef_list_expansions; +} tecla_1.2; + +tecla_1.4 { + global: + gl_configure_getline; + gl_save_history; + gl_load_history; + gl_group_history; + gl_show_history; + gl_resize_history; + gl_limit_history; + gl_clear_history; + gl_toggle_history; + gl_watch_fd; + libtecla_version; + gl_terminal_size; + gl_state_of_history; + gl_range_of_history; + gl_size_of_history; + gl_lookup_history; + gl_echo_mode; + gl_replace_prompt; + gl_prompt_style; + gl_ignore_signal; + gl_trap_signal; + gl_last_signal; +} tecla_1.3; + +tecla_l.5 { + global: + gl_inactivity_timeout; + gl_completion_action; + gl_register_action; + gl_display_text; + gl_error_message; + gl_return_status; + gl_set_term_size; + gl_list_signals; + gl_catch_blocked; + gl_io_mode; + gl_raw_io; + gl_normal_io; + gl_tty_signals; + gl_abandon_line; + gl_handle_signal; + gl_pending_io; + gl_bind_keyseq; + cpl_recall_matches; + gl_erase_terminal; +} tecla_1.4; + +tecla_1.6 { + global: + gl_append_history; + gl_automatic_history; + gl_query_char; + gl_read_char; +} tecla_l.5; diff --git a/libtecla-1.6.1/man/file/teclarc.in b/libtecla-1.6.1/man/file/teclarc.in new file mode 100644 index 0000000..b5ee705 --- /dev/null +++ b/libtecla-1.6.1/man/file/teclarc.in @@ -0,0 +1 @@ +.so @MISC_MANDIR@/tecla.@MISC_MANEXT@ diff --git a/libtecla-1.6.1/man/func/cfc_file_start.in b/libtecla-1.6.1/man/func/cfc_file_start.in new file mode 100644 index 0000000..734f281 --- /dev/null +++ b/libtecla-1.6.1/man/func/cfc_file_start.in @@ -0,0 +1 @@ +.so @FUNC_MANDIR@/cpl_complete_word.@FUNC_MANEXT@ diff --git a/libtecla-1.6.1/man/func/cfc_literal_escapes.in b/libtecla-1.6.1/man/func/cfc_literal_escapes.in new file mode 100644 index 0000000..734f281 --- /dev/null +++ b/libtecla-1.6.1/man/func/cfc_literal_escapes.in @@ -0,0 +1 @@ +.so @FUNC_MANDIR@/cpl_complete_word.@FUNC_MANEXT@ diff --git a/libtecla-1.6.1/man/func/cfc_set_check_fn.in b/libtecla-1.6.1/man/func/cfc_set_check_fn.in new file mode 100644 index 0000000..734f281 --- /dev/null +++ b/libtecla-1.6.1/man/func/cfc_set_check_fn.in @@ -0,0 +1 @@ +.so @FUNC_MANDIR@/cpl_complete_word.@FUNC_MANEXT@ diff --git a/libtecla-1.6.1/man/func/cpl_add_completion.in b/libtecla-1.6.1/man/func/cpl_add_completion.in new file mode 100644 index 0000000..734f281 --- /dev/null +++ b/libtecla-1.6.1/man/func/cpl_add_completion.in @@ -0,0 +1 @@ +.so @FUNC_MANDIR@/cpl_complete_word.@FUNC_MANEXT@ diff --git a/libtecla-1.6.1/man/func/cpl_complete_word.in b/libtecla-1.6.1/man/func/cpl_complete_word.in new file mode 100644 index 0000000..d5331e9 --- /dev/null +++ b/libtecla-1.6.1/man/func/cpl_complete_word.in @@ -0,0 +1,441 @@ +.\" Copyright (c) 2000, 2001, 2002, 2003, 2004 by Martin C. Shepherd +.\" +.\" All rights reserved. +.\" +.\" Permission is hereby granted, free of charge, to any person obtaining a +.\" copy of this software and associated documentation files (the +.\" "Software"), to deal in the Software without restriction, including +.\" without limitation the rights to use, copy, modify, merge, publish, +.\" distribute, and/or sell copies of the Software, and to permit persons +.\" to whom the Software is furnished to do so, provided that the above +.\" copyright notice(s) and this permission notice appear in all copies of +.\" the Software and that both the above copyright notice(s) and this +.\" permission notice appear in supporting documentation. +.\" +.\" THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +.\" OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +.\" MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT +.\" OF THIRD PARTY RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR +.\" HOLDERS INCLUDED IN THIS NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL +.\" INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING +.\" FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, +.\" NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION +.\" WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +.\" +.\" Except as contained in this notice, the name of a copyright holder +.\" shall not be used in advertising or otherwise to promote the sale, use +.\" or other dealings in this Software without prior written authorization +.\" of the copyright holder. +.TH cpl_complete_word @FUNC_MANEXT@ +.SH NAME +cpl_complete_word, cfc_file_start, cfc_literal_escapes, cfc_set_check_fn, cpl_add_completion, cpl_file_completions, cpl_last_error, cpl_list_completions, cpl_recall_matches, cpl_record_error, del_CplFileConf, del_WordCompletion, new_CplFileConf, new_WordCompletion \- lookup possible completions for a word +.SH SYNOPSIS +.nf +#include +#include + +WordCompletion *new_WordCompletion(void); + +WordCompletion *del_WordCompletion(WordCompletion *cpl); + + +#define CPL_MATCH_FN(fn) int (fn)(WordCompletion *cpl, \\ + void *data, \\ + const char *line, \\ + int word_end) +typedef CPL_MATCH_FN(CplMatchFn); + +CPL_MATCH_FN(cpl_file_completions); + + +CplMatches *cpl_complete_word(WordCompletion *cpl, + const char *line, + int word_end, void *data, + CplMatchFn *match_fn); + +CplMatches *cpl_recall_matches(WordCompletion *cpl); + +int cpl_list_completions(CplMatches *result, FILE *fp, + int term_width); + +int cpl_add_completion(WordCompletion *cpl, + const char *line, int word_start, + int word_end, const char *suffix, + const char *type_suffix, + const char *cont_suffix); + +void cpl_record_error(WordCompletion *cpl, + const char *errmsg); + +const char *cpl_last_error(WordCompletion *cpl); + + +#define CPL_CHECK_FN(fn) int (fn)(void *data, \\ + const char *pathname) + +typedef CPL_CHECK_FN(CplCheckFn); + +CPL_CHECK_FN(cpl_check_exe); + +CplFileConf *new_CplFileConf(void); + +CplFileConf *del_CplFileConf(CplFileConf *cfc); + +void cfc_literal_escapes(CplFileConf *cfc, int literal); + +void cfc_file_start(CplFileConf *cfc, int start_index); + +void cfc_set_check_fn(CplFileConf *cfc, CplCheckFn *chk_fn, + void *chk_data); + +.fi + +.SH DESCRIPTION + +The \f3cpl_complete_word()\f1 function is part of the tecla library +(see the libtecla(@LIBR_MANEXT@) man page). It is usually called behind the scenes +by \f3gl_get_line(@FUNC_MANEXT@)\f1, but can also be called separately. + +Given an input line containing an incomplete word to be completed, it +calls a user-provided callback function (or the provided +file-completion callback function) to look up all possible completion +suffixes for that word. The callback function is expected to look +backward in the line, starting from the specified cursor position, to +find the start of the word to be completed, then to look up all +possible completions of that word and record them, one at a time by +calling \f3cpl_add_completion()\f1. + +.sp +Descriptions of the functions of this module are as follows: +.sp +.nf + WordCompletion *new_WordCompletion(void) +.fi +.sp +This function creates the resources used by the \f3cpl_complete_word()\f1 +function. In particular, it maintains the memory that is used to +return the results of calling \f3cpl_complete_word()\f1. +.sp +.nf + WordCompletion *del_WordCompletion(WordCompletion *cpl) +.fi +.sp +This function deletes the resources that were returned by a previous +call to \f3new_WordCompletion()\f1. It always returns \f3NULL\f1 (ie. a +deleted object). It does nothing if the \f3cpl\f1 argument is +\f3NULL\f1. +.sp +The callback functions which lookup possible completions should be +defined with the following macro (which is defined in libtecla.h). +.sp +.nf + #define CPL_MATCH_FN(fn) int (fn)(WordCompletion *cpl, \\ + void *data, \\ + const char *line, \\ + int word_end) +.fi +.sp +Functions of this type are called by \f3cpl_complete_word()\f1, and +all of the arguments of the callback are those that were passed to +said function. In particular, the \f3line\f1 argument contains the +input line containing the word to be completed, and \f3word_end\f1 is +the index of the character that follows the last character of the +incomplete word within this string. The callback is expected to look +backwards from \f3word_end\f1 for the start of the incomplete +word. What constitutes the start of a word clearly depends on the +application, so it makes sense for the callback to take on this +responsibility. For example, the builtin filename completion function +looks backwards until it hits an unescaped space, or the start of the +line. Having found the start of the word, the callback should then +lookup all possible completions of this word, and record each +completion via separate calls to \f3cpl_add_completion()\f1. If the +callback needs access to an application-specific symbol table, it can +pass it and any other data that it needs, via the \f3data\f1 +argument. This removes any need for globals. +.sp +The callback function should return 0 if no errors occur. On failure +it should return 1, and register a terse description of the error by +calling \f3cpl_record_error()\f1. +.sp +.nf + void cpl_record_error(WordCompletion *cpl, + const char *errmsg); +.fi +.sp +The last error message recorded by calling \f3cpl_record_error()\f1, +can subsequently be queried by calling \f3cpl_last_error()\f1, as +described later. +.sp +.nf + int cpl_add_completion(WordCompletion *cpl, + const char *line, int word_start, + int word_end, const char *suffix, + const char *type_suffix, + const char *cont_suffix); +.fi +.sp +The \f3cpl_add_completion()\f1 function is called zero or more times +by the completion callback function to record each possible completion +in the specified \f3WordCompletion\f1 object. These completions are +subsequently returned by \f3cpl_complete_word()\f1, as described +later. The \f3cpl\f1, \f3line\f1, and \f3word_end\f1 arguments should +be those that were passed to the callback function. The +\f3word_start\f1 argument should be the index within the input line +string of the start of the word that is being completed. This should +equal \f3word_end\f1 if a zero-length string is being completed. The +\f3suffix\f1 argument is the string that would have to be appended to +the incomplete word to complete it. If this needs any quoting +(eg. the addition of backslashes before special charaters) to be valid +within the displayed input line, this should be included. A copy of +the suffix string is allocated internally, so there is no need to +maintain your copy of the string after \f3cpl_add_completion()\f1 +returns. +.sp +Note that in the array of possible completions which the +\f3cpl_complete_word()\f1 function returns, the suffix recorded by +\f3cpl_add_completion()\f1 is listed along with the concatentation of +this suffix with the word that lies between \f3word_start\f1 and +\f3word_end\f1 in the input line. +.sp +The \f3type_suffix\f1 argument specifies an optional string to be +appended to the completion if it is displayed as part of a list of +completions by \f3cpl_list_completions()\f1. The intention is that +this indicate to the user the type of each completion. For example, +the file completion function places a directory separator after +completions that are directories, to indicate their nature to the +user. Similary, if the completion were a function, you could indicate +this to the user by setting \f3type_suffix\f1 to "()". Note that the +\f3type_suffix\f1 string isn't copied, so if the argument isn't a +literal string between speech marks, be sure that the string remains +valid for at least as long as the results of \f3cpl_complete_word()\f1 +are needed. +.sp +The \f3cont_suffix\f1 is a continuation suffix to append to the +completed word in the input line if this is the only completion. This +is something that isn't part of the completion itself, but that gives +the user an indication about how they might continue to extend the +token. For example, the file-completion callback function adds a +directory separator if the completed word is a directory. If the +completed word were a function name, you could similarly aid the user +by arranging for an open parenthesis to be appended. +.sp +.nf + CplMatches *cpl_complete_word(WordCompletion *cpl, + const char *line, + int word_end, void *data, + CplMatchFn *match_fn); +.fi +.sp +The \f3cpl_complete_word()\f1 is normally called behind the scenes by +\f3gl_get_line(@FUNC_MANEXT@)\f1, but can also be called separately if you +separately allocate a \f3WordCompletion\f1 object. It performs word +completion, as described at the beginning of this section. Its first +argument is a resource object previously returned by +\f3new_WordCompletion()\f1. The \f3line\f1 argument is the input line +string, containing the word to be completed. The \f3word_end\f1 +argument contains the index of the character in the input line, that +just follows the last character of the word to be completed. When +called by \f3gl_get_line()\f1, this is the character over which the +user pressed \f3TAB\f1. The \f3match_fn\f3 argument is the function +pointer of the callback function which will lookup possible +completions of the word, as described above, and the \f3data\f1 +argument provides a way for the application to pass arbitrary data to +the callback function. +.sp +If no errors occur, the \f3cpl_complete_word()\f1 function returns a +pointer to a \f3CplMatches\f1 container, as defined below. This +container is allocated as part of the \f3cpl\f1 object that was passed +to \f3cpl_complete_word()\f1, and will thus change on each call which +uses the same \f3cpl\f1 argument. +.sp +.nf + typedef struct { + char *completion; /* A matching completion */ + /* string */ + char *suffix; /* The part of the */ + /* completion string which */ + /* would have to be */ + /* appended to complete the */ + /* original word. */ + const char *type_suffix; /* A suffix to be added when */ + /* listing completions, to */ + /* indicate the type of the */ + /* completion. */ + } CplMatch; + + typedef struct { + char *suffix; /* The common initial part */ + /* of all of the completion */ + /* suffixes. */ + const char *cont_suffix; /* Optional continuation */ + /* string to be appended to */ + /* the sole completion when */ + /* nmatch==1. */ + CplMatch *matches; /* The array of possible */ + /* completion strings, */ + /* sorted into lexical */ + /* order. */ + int nmatch; /* The number of elements in */ + /* the above matches[] */ + /* array. */ + } CplMatches; +.fi +.sp +If an error occurs during completion, \f3cpl_complete_word()\f1 +returns NULL. A description of the error can be acquired by calling +the \f3cpl_last_error()\f3 function. +.sp +.nf + const char *cpl_last_error(WordCompletion *cpl); +.fi +.sp +The \f3cpl_last_error()\f3 function returns a terse description of the +error which occurred on the last call to \f3cpl_complete_word()\f1 or +\f3cpl_add_completion()\f1. +.sp +.nf + CplMatches *cpl_recall_matches(WordCompletion *cpl); +.fi +.sp +As a convenience, the return value of the last call to +\f3cpl_complete_word()\f1 can be recalled at a later time by calling +\f3cpl_recall_matches()\f1. If \f3cpl_complete_word()\f1 returned +\f3NULL\f1, so will \f3cpl_recall_matches()\f1. +.sp +.nf + int cpl_list_completions(CplMatches *result, FILE *fp, + int terminal_width); +.fi +.sp +When the \f3cpl_complete_word()\f1 function returns multiple possible +completions, the \f3cpl_list_completions()\f1 function can be called +upon to list them, suitably arranged across the available width of the +terminal. It arranges for the displayed columns of completions to all +have the same width, set by the longest completion. It also appends +the \f3type_suffix\f1 strings that were recorded with each completion, +thus indicating their types to the user. + +.SH THE BUILT-IN FILENAME-COMPLETION CALLBACK + +By default the \f3gl_get_line(@FUNC_MANEXT@)\f1 function, passes the following +completion callback function to \f3cpl_complete_word()\f1. This +function can also be used separately, either by sending it to +\f3cpl_complete_word()\f1, or by calling it directly from your +own completion callback function. +.sp +.nf + CPL_MATCH_FN(cpl_file_completions); +.fi +.sp +Certain aspects of the behavior of this callback can be changed via +its \f3data\f1 argument. If you are happy with its default behavior +you can pass \f3NULL\f1 in this argument. Otherwise it should be a +pointer to a \f3CplFileConf\f1 object, previously allocated by calling +\f3new_CplFileConf()\f1. +.sp +.nf + CplFileConf *new_CplFileConf(void); +.fi +.sp +\f3CplFileConf\f1 objects encapsulate the configuration parameters of +\f3cpl_file_completions()\f1. These parameters, which start out with +default values, can be changed by calling the accessor functions +described below. +.sp +By default, the \f3cpl_file_completions()\f3 callback function +searches backwards for the start of the filename being completed, +looking for the first un-escaped space or the start of the input +line. If you wish to specify a different location, call +\f3cfc_file_start()\f1 with the index at which the filename starts in +the input line. Passing start_index=-1 re-enables the default +behavior. +.sp +.nf + void cfc_file_start(CplFileConf *cfc, int start_index); +.fi +.sp +By default, when \f3cpl_file_completions()\f1 looks at a filename in +the input line, each lone backslash in the input line is interpreted +as being a special character which removes any special significance of +the character which follows it, such as a space which should be taken +as part of the filename rather than delimiting the start of the +filename. These backslashes are thus ignored while looking for +completions, and subsequently added before spaces, tabs and literal +backslashes in the list of completions. To have unescaped backslashes +treated as normal characters, call \f3cfc_literal_escapes()\f1 with a +non-zero value in its \f3literal\f1 argument. +.sp +.nf + void cfc_literal_escapes(CplFileConf *cfc, int literal); +.fi +.sp +By default, \f3cpl_file_completions()\f1 reports all files who's names +start with the prefix that is being completed. If you only want a +selected subset of these files to be reported in the list of +completions, you can arrange this by providing a callback function +which takes the full pathname of a file, and returns \f30\f1 if the +file should be ignored, or \f31\f1 if the file should be included in +the list of completions. To register such a function for use by +\f3cpl_file_completions()\f1, call \f3cfc_set_check_fn()\f1, and pass +it a pointer to the function, together with a pointer to any data that +you would like passed to this callback whenever it is called. Your +callback can make its decisions based on any property of the file, +such as the filename itself, whether the file is readable, writable or +executable, or even based on what the file contains. +.sp +.nf + #define CPL_CHECK_FN(fn) int (fn)(void *data, \\ + const char *pathname) + typedef CPL_CHECK_FN(CplCheckFn); + + void cfc_set_check_fn(CplFileConf *cfc, + CplCheckFn *chk_fn, void *chk_data); +.fi +.sp +The \f3cpl_check_exe()\f1 function is a provided callback of the above +type, for use with \f3cpl_file_completions()\f1. It returns non-zero +if the filename that it is given represents a normal file that the +user has execute permission to. You could use this to have +\f3cpl_file_completions()\f1 only list completions of executable +files. +.sp +When you have finished with a \f3CplFileConf\f1 variable, you can pass +it to the \f3del_CplFileConf()\f1 destructor function to reclaim its +memory. +.sp +.nf + CplFileConf *del_CplFileConf(CplFileConf *cfc); +.fi +.sp + +.SH THREAD SAFETY + +In multi-threaded programs, you should use the \f3libtecla_r.a\f1 +version of the library. This uses POSIX reentrant functions where +available (hence the \f3_r\f1 suffix), and disables features that rely +on non-reentrant system functions. In the case of this module, the +only disabled feature is username completion in \f3~username/\f1 +expressions, in \f3cpl_file_completions()\f1. + +Using the \f3libtecla_r.a\f1 version of the library, it is safe to use +the facilities of this module in multiple threads, provided that each +thread uses a separately allocated \f3WordCompletion\f1 object. In +other words, if two threads want to do word completion, they should +each call \f3new_WordCompletion()\f1 to allocate their own completion +objects. + +.SH FILES +.nf +libtecla.a - The tecla library +libtecla.h - The tecla header file. +.fi + +.SH SEE ALSO + +.nf +libtecla(@LIBR_MANEXT@), gl_get_line(@FUNC_MANEXT@), ef_expand_file(@FUNC_MANEXT@), +pca_lookup_file(@FUNC_MANEXT@) +.fi + +.SH AUTHOR +Martin Shepherd (mcs@astro.caltech.edu) diff --git a/libtecla-1.6.1/man/func/cpl_file_completions.in b/libtecla-1.6.1/man/func/cpl_file_completions.in new file mode 100644 index 0000000..734f281 --- /dev/null +++ b/libtecla-1.6.1/man/func/cpl_file_completions.in @@ -0,0 +1 @@ +.so @FUNC_MANDIR@/cpl_complete_word.@FUNC_MANEXT@ diff --git a/libtecla-1.6.1/man/func/cpl_last_error.in b/libtecla-1.6.1/man/func/cpl_last_error.in new file mode 100644 index 0000000..734f281 --- /dev/null +++ b/libtecla-1.6.1/man/func/cpl_last_error.in @@ -0,0 +1 @@ +.so @FUNC_MANDIR@/cpl_complete_word.@FUNC_MANEXT@ diff --git a/libtecla-1.6.1/man/func/cpl_list_completions.in b/libtecla-1.6.1/man/func/cpl_list_completions.in new file mode 100644 index 0000000..734f281 --- /dev/null +++ b/libtecla-1.6.1/man/func/cpl_list_completions.in @@ -0,0 +1 @@ +.so @FUNC_MANDIR@/cpl_complete_word.@FUNC_MANEXT@ diff --git a/libtecla-1.6.1/man/func/cpl_recall_matches.in b/libtecla-1.6.1/man/func/cpl_recall_matches.in new file mode 100644 index 0000000..734f281 --- /dev/null +++ b/libtecla-1.6.1/man/func/cpl_recall_matches.in @@ -0,0 +1 @@ +.so @FUNC_MANDIR@/cpl_complete_word.@FUNC_MANEXT@ diff --git a/libtecla-1.6.1/man/func/cpl_record_error.in b/libtecla-1.6.1/man/func/cpl_record_error.in new file mode 100644 index 0000000..734f281 --- /dev/null +++ b/libtecla-1.6.1/man/func/cpl_record_error.in @@ -0,0 +1 @@ +.so @FUNC_MANDIR@/cpl_complete_word.@FUNC_MANEXT@ diff --git a/libtecla-1.6.1/man/func/del_CplFileConf.in b/libtecla-1.6.1/man/func/del_CplFileConf.in new file mode 100644 index 0000000..734f281 --- /dev/null +++ b/libtecla-1.6.1/man/func/del_CplFileConf.in @@ -0,0 +1 @@ +.so @FUNC_MANDIR@/cpl_complete_word.@FUNC_MANEXT@ diff --git a/libtecla-1.6.1/man/func/del_ExpandFile.in b/libtecla-1.6.1/man/func/del_ExpandFile.in new file mode 100644 index 0000000..3d0a884 --- /dev/null +++ b/libtecla-1.6.1/man/func/del_ExpandFile.in @@ -0,0 +1 @@ +.so @FUNC_MANDIR@/ef_expand_file.@FUNC_MANEXT@ diff --git a/libtecla-1.6.1/man/func/del_GetLine.in b/libtecla-1.6.1/man/func/del_GetLine.in new file mode 100644 index 0000000..6e46fc6 --- /dev/null +++ b/libtecla-1.6.1/man/func/del_GetLine.in @@ -0,0 +1 @@ +.so @FUNC_MANDIR@/gl_get_line.@FUNC_MANEXT@ diff --git a/libtecla-1.6.1/man/func/del_PathCache.in b/libtecla-1.6.1/man/func/del_PathCache.in new file mode 100644 index 0000000..dbc4da7 --- /dev/null +++ b/libtecla-1.6.1/man/func/del_PathCache.in @@ -0,0 +1 @@ +.so @FUNC_MANDIR@/pca_lookup_file.@FUNC_MANEXT@ diff --git a/libtecla-1.6.1/man/func/del_PcaPathConf.in b/libtecla-1.6.1/man/func/del_PcaPathConf.in new file mode 100644 index 0000000..dbc4da7 --- /dev/null +++ b/libtecla-1.6.1/man/func/del_PcaPathConf.in @@ -0,0 +1 @@ +.so @FUNC_MANDIR@/pca_lookup_file.@FUNC_MANEXT@ diff --git a/libtecla-1.6.1/man/func/del_WordCompletion.in b/libtecla-1.6.1/man/func/del_WordCompletion.in new file mode 100644 index 0000000..734f281 --- /dev/null +++ b/libtecla-1.6.1/man/func/del_WordCompletion.in @@ -0,0 +1 @@ +.so @FUNC_MANDIR@/cpl_complete_word.@FUNC_MANEXT@ diff --git a/libtecla-1.6.1/man/func/ef_expand_file.in b/libtecla-1.6.1/man/func/ef_expand_file.in new file mode 100644 index 0000000..f23f3eb --- /dev/null +++ b/libtecla-1.6.1/man/func/ef_expand_file.in @@ -0,0 +1,248 @@ +.\" Copyright (c) 2000, 2001, 2002, 2003, 2004 by Martin C. Shepherd +.\" +.\" All rights reserved. +.\" +.\" Permission is hereby granted, free of charge, to any person obtaining a +.\" copy of this software and associated documentation files (the +.\" "Software"), to deal in the Software without restriction, including +.\" without limitation the rights to use, copy, modify, merge, publish, +.\" distribute, and/or sell copies of the Software, and to permit persons +.\" to whom the Software is furnished to do so, provided that the above +.\" copyright notice(s) and this permission notice appear in all copies of +.\" the Software and that both the above copyright notice(s) and this +.\" permission notice appear in supporting documentation. +.\" +.\" THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +.\" OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +.\" MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT +.\" OF THIRD PARTY RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR +.\" HOLDERS INCLUDED IN THIS NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL +.\" INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING +.\" FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, +.\" NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION +.\" WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +.\" +.\" Except as contained in this notice, the name of a copyright holder +.\" shall not be used in advertising or otherwise to promote the sale, use +.\" or other dealings in this Software without prior written authorization +.\" of the copyright holder. +.TH ef_expand_file @FUNC_MANEXT@ +.SH NAME +ef_expand_file, del_ExpandFile, ef_last_error, ef_list_expansions, new_ExpandFile \- expand filenames containing ~user/$envvar and wildcard expressions +.SH SYNOPSIS +.nf +#include + +ExpandFile *new_ExpandFile(void); + +ExpandFile *del_ExpandFile(ExpandFile *ef); + +FileExpansion *ef_expand_file(ExpandFile *ef, + const char *path, + int pathlen); + +int ef_list_expansions(FileExpansion *result, FILE *fp, + int term_width); + +const char *ef_last_error(ExpandFile *ef); +.fi + +.SH DESCRIPTION + +The \f3ef_expand_file()\f1 function is part of the tecla library +(see the libtecla(@LIBR_MANEXT@) man page). It expands a specified filename, +converting \f3~user/\f1 and \f3~/\f1 expressions at the start of the +filename to the corresponding home directories, replacing +\f3$envvar\f1 with the value of the corresponding environment +variable, and then, if there are any wildcards, matching these against +existing filenames. Backslashes in the input filename are interpreted +as escaping any special meanings of the characters that follow them. +Only backslahes that are themselves preceded by backslashes are +preserved in the expanded filename. +.sp +In the presence of wildcards, the returned list of filenames only +includes the names of existing files which match the +wildcards. Otherwise, the original filename is returned after +expansion of tilde and dollar expressions, and the result is not +checked against existing files. This mimics the file-globbing behavior +of the unix \f3tcsh\f1 shell. +.sp +The supported wildcards and their meanings are: +.nf + * - Match any sequence of zero or more characters. + ? - Match any single character. + [chars] - Match any single character that appears in + 'chars'. If 'chars' contains an expression of + the form a-b, then any character between a and + b, including a and b, matches. The '-' + character looses its special meaning as a + range specifier when it appears at the start + of the sequence of characters. The ']' + character also looses its significance as the + terminator of the range expression if it + appears immediately after the opening '[', at + which point it is treated one of the + characters of the range. If you want both '-' + and ']' to be part of the range, the '-' + should come first and the ']' second. + + [^chars] - The same as [chars] except that it matches any + single character that doesn't appear in + 'chars'. +.fi + +Note that wildcards never match the initial dot in filenames that +start with '.'. The initial '.' must be explicitly specified in the +filename. This again mimics the globbing behavior of most unix shells, +and its rational is based in the fact that in unix, files with names +that start with '.' are usually hidden configuration files, which are +not listed by default by the ls command. +.sp +The following is a complete example of how to use the file expansion +function. + +.nf + #include + #include + + int main(int argc, char *argv[]) + { + ExpandFile *ef; /* The expansion resource object */ + char *filename; /* The filename being expanded */ + FileExpansion *expn; /* The results of the expansion */ + int i; + + ef = new_ExpandFile(); + if(!ef) + return 1; + + for(arg = *(argv++); arg; arg = *(argv++)) { + if((expn = ef_expand_file(ef, arg, -1)) == NULL) { + fprintf(stderr, "Error expanding %s (%s).\\n", arg, + ef_last_error(ef)); + } else { + printf("%s matches the following files:\\n", arg); + for(i=0; infile; i++) + printf(" %s\\n", expn->files[i]); + } + } + + ef = del_ExpandFile(ef); + return 0; + } +.fi +.sp +Descriptions of the functions used above are as follows: +.sp +.nf + ExpandFile *new_ExpandFile(void) +.fi +.sp +This function creates the resources used by the \f3ef_expand_file()\f1 +function. In particular, it maintains the memory that is used to record the +array of matching filenames that is returned by \f3ef_expand_file()\f1. This +array is expanded as needed, so there is no built in limit to the number of +files that can be matched. +.sp +.nf + ExpandFile *del_ExpandFile(ExpandFile *ef) +.fi +.sp +This function deletes the resources that were returned by a previous call to +\f3new_ExpandFile()\f1. It always returns \f3NULL\f1 (ie a deleted object). It +does nothing if the \f3ef\f1 argument is \f3NULL\f1. +.sp +A container of the following type is returned by \f3ef_expand_file()\f1. +.sp +.nf + typedef struct { + int exists; /* True if the files in files[] exist */ + int nfile; /* The number of files in files[] */ + char **files; /* An array of 'nfile' filenames. */ + } FileExpansion; +.fi +.sp +.nf + FileExpansion *ef_expand_file(ExpandFile *ef, + const char *path, + int pathlen) +.fi +.sp +The \f3ef_expand_file()\f1 function performs filename expansion, as documented +at the start of this section. Its first argument is a resource object returned +by \f3new_ExpandFile()\f1. A pointer to the start of the filename to be matched +is passed via the \f3path\f1 argument. This must be a normal \f3NUL\f1 +terminated string, but unless a length of -1 is passed in \f3pathlen\f1, only +the first \f3pathlen\f1 characters will be used in the filename expansion. If +the length is specified as -1, the whole of the string will be +expanded. +.sp +The function returns a pointer to a container who's contents are the +results of the expansion. If there were no wildcards in the filename, +the \f3nfile\f1 member will be 1, and the \f3exists\f1 member should +be queried if it is important to know if the expanded file currently +exists or not. If there were wildcards, then the contained +\f3files[]\f1 array will contain the names of the \f3nfile\f1 existing +files that matched the wildcarded filename, and the \f3exists\f1 +member will have the value 1. Note that the returned container belongs +to the specified \f3ef\f1 object, and its contents will change on each +call, so if you need to retain the results of more than one call to +\f3ef_expand_file()\f1, you should either make a private copy of the +returned results, or create multiple file-expansion resource objects +via multiple calls to \f3new_ExpandFile()\f1. +.sp +On error, \f3NULL\f1 is returned, and an explanation of the error can +be determined by calling \f3ef_last_error(ef)\f1. +.sp +.nf + const char *ef_last_error(ExpandFile *ef) +.fi +.sp +This function returns the message which describes the error that +occurred on the last call to \f3ef_expand_file()\f1, for the given +\f3(ExpandFile *ef)\f1 resource object. +.sp +.nf + int ef_list_expansions(FileExpansion *result, FILE *fp, + int terminal_width); +.fi +.sp +The \f3ef_list_expansions()\f1 function provides a convenient way to +list the filename expansions returned by \f3ef_expand_file()\f1. Like +the unix \f3ls\f1 command, it arranges the filenames into equal width +columns, each column having the width of the largest file. The number +of columns used is thus determined by the length of the longest +filename, and the specified terminal width. Beware that filenames that +are longer than the specified terminal width are printed without being +truncated, so output longer than the specified terminal width can +occur. The list is written to the stdio stream specified by the +\f3fp\f1 argument. + +.SH THREAD SAFETY + +In multi-threaded programs, you should use the \f3libtecla_r.a\f1 +version of the library. This uses POSIX reentrant functions where +available (hence the \f3_r\f1 suffix), and disables features that rely +on non-reentrant system functions. Currently there are no features +disabled in this module. + +Using the \f3libtecla_r.a\f1 version of the library, it is safe to use +the facilities of this module in multiple threads, provided that each +thread uses a separately allocated \f3ExpandFile\f1 object. In other +words, if two threads want to do file expansion, they should each call +\f3new_ExpandFile()\f1 to allocate their own file-expansion objects. + +.SH FILES +.nf +libtecla.a - The tecla library +libtecla.h - The tecla header file. +.fi + +.SH SEE ALSO +.nf +libtecla(@LIBR_MANEXT@), gl_get_line(@FUNC_MANEXT@), cpl_complete_word(@FUNC_MANEXT@), +pca_lookup_file(@FUNC_MANEXT@) +.fi + +.SH AUTHOR +Martin Shepherd (mcs@astro.caltech.edu) diff --git a/libtecla-1.6.1/man/func/ef_last_error.in b/libtecla-1.6.1/man/func/ef_last_error.in new file mode 100644 index 0000000..3d0a884 --- /dev/null +++ b/libtecla-1.6.1/man/func/ef_last_error.in @@ -0,0 +1 @@ +.so @FUNC_MANDIR@/ef_expand_file.@FUNC_MANEXT@ diff --git a/libtecla-1.6.1/man/func/ef_list_expansions.in b/libtecla-1.6.1/man/func/ef_list_expansions.in new file mode 100644 index 0000000..3d0a884 --- /dev/null +++ b/libtecla-1.6.1/man/func/ef_list_expansions.in @@ -0,0 +1 @@ +.so @FUNC_MANDIR@/ef_expand_file.@FUNC_MANEXT@ diff --git a/libtecla-1.6.1/man/func/gl_abandon_line.in b/libtecla-1.6.1/man/func/gl_abandon_line.in new file mode 100644 index 0000000..24798bc --- /dev/null +++ b/libtecla-1.6.1/man/func/gl_abandon_line.in @@ -0,0 +1 @@ +.so @FUNC_MANDIR@/gl_io_mode.@FUNC_MANEXT@ diff --git a/libtecla-1.6.1/man/func/gl_bind_keyseq.in b/libtecla-1.6.1/man/func/gl_bind_keyseq.in new file mode 100644 index 0000000..6e46fc6 --- /dev/null +++ b/libtecla-1.6.1/man/func/gl_bind_keyseq.in @@ -0,0 +1 @@ +.so @FUNC_MANDIR@/gl_get_line.@FUNC_MANEXT@ diff --git a/libtecla-1.6.1/man/func/gl_catch_blocked.in b/libtecla-1.6.1/man/func/gl_catch_blocked.in new file mode 100644 index 0000000..6e46fc6 --- /dev/null +++ b/libtecla-1.6.1/man/func/gl_catch_blocked.in @@ -0,0 +1 @@ +.so @FUNC_MANDIR@/gl_get_line.@FUNC_MANEXT@ diff --git a/libtecla-1.6.1/man/func/gl_change_terminal.in b/libtecla-1.6.1/man/func/gl_change_terminal.in new file mode 100644 index 0000000..6e46fc6 --- /dev/null +++ b/libtecla-1.6.1/man/func/gl_change_terminal.in @@ -0,0 +1 @@ +.so @FUNC_MANDIR@/gl_get_line.@FUNC_MANEXT@ diff --git a/libtecla-1.6.1/man/func/gl_clear_history.in b/libtecla-1.6.1/man/func/gl_clear_history.in new file mode 100644 index 0000000..6e46fc6 --- /dev/null +++ b/libtecla-1.6.1/man/func/gl_clear_history.in @@ -0,0 +1 @@ +.so @FUNC_MANDIR@/gl_get_line.@FUNC_MANEXT@ diff --git a/libtecla-1.6.1/man/func/gl_completion_action.in b/libtecla-1.6.1/man/func/gl_completion_action.in new file mode 100644 index 0000000..6e46fc6 --- /dev/null +++ b/libtecla-1.6.1/man/func/gl_completion_action.in @@ -0,0 +1 @@ +.so @FUNC_MANDIR@/gl_get_line.@FUNC_MANEXT@ diff --git a/libtecla-1.6.1/man/func/gl_configure_getline.in b/libtecla-1.6.1/man/func/gl_configure_getline.in new file mode 100644 index 0000000..6e46fc6 --- /dev/null +++ b/libtecla-1.6.1/man/func/gl_configure_getline.in @@ -0,0 +1 @@ +.so @FUNC_MANDIR@/gl_get_line.@FUNC_MANEXT@ diff --git a/libtecla-1.6.1/man/func/gl_customize_completion.in b/libtecla-1.6.1/man/func/gl_customize_completion.in new file mode 100644 index 0000000..6e46fc6 --- /dev/null +++ b/libtecla-1.6.1/man/func/gl_customize_completion.in @@ -0,0 +1 @@ +.so @FUNC_MANDIR@/gl_get_line.@FUNC_MANEXT@ diff --git a/libtecla-1.6.1/man/func/gl_display_text.in b/libtecla-1.6.1/man/func/gl_display_text.in new file mode 100644 index 0000000..6e46fc6 --- /dev/null +++ b/libtecla-1.6.1/man/func/gl_display_text.in @@ -0,0 +1 @@ +.so @FUNC_MANDIR@/gl_get_line.@FUNC_MANEXT@ diff --git a/libtecla-1.6.1/man/func/gl_echo_mode.in b/libtecla-1.6.1/man/func/gl_echo_mode.in new file mode 100644 index 0000000..6e46fc6 --- /dev/null +++ b/libtecla-1.6.1/man/func/gl_echo_mode.in @@ -0,0 +1 @@ +.so @FUNC_MANDIR@/gl_get_line.@FUNC_MANEXT@ diff --git a/libtecla-1.6.1/man/func/gl_erase_terminal.in b/libtecla-1.6.1/man/func/gl_erase_terminal.in new file mode 100644 index 0000000..6e46fc6 --- /dev/null +++ b/libtecla-1.6.1/man/func/gl_erase_terminal.in @@ -0,0 +1 @@ +.so @FUNC_MANDIR@/gl_get_line.@FUNC_MANEXT@ diff --git a/libtecla-1.6.1/man/func/gl_error_message.in b/libtecla-1.6.1/man/func/gl_error_message.in new file mode 100644 index 0000000..6e46fc6 --- /dev/null +++ b/libtecla-1.6.1/man/func/gl_error_message.in @@ -0,0 +1 @@ +.so @FUNC_MANDIR@/gl_get_line.@FUNC_MANEXT@ diff --git a/libtecla-1.6.1/man/func/gl_get_line.in b/libtecla-1.6.1/man/func/gl_get_line.in new file mode 100644 index 0000000..1995108 --- /dev/null +++ b/libtecla-1.6.1/man/func/gl_get_line.in @@ -0,0 +1,2236 @@ +.\" Copyright (c) 2000, 2001, 2002, 2003, 2004 by Martin C. Shepherd +.\" +.\" All rights reserved. +.\" +.\" Permission is hereby granted, free of charge, to any person obtaining a +.\" copy of this software and associated documentation files (the +.\" "Software"), to deal in the Software without restriction, including +.\" without limitation the rights to use, copy, modify, merge, publish, +.\" distribute, and/or sell copies of the Software, and to permit persons +.\" to whom the Software is furnished to do so, provided that the above +.\" copyright notice(s) and this permission notice appear in all copies of +.\" the Software and that both the above copyright notice(s) and this +.\" permission notice appear in supporting documentation. +.\" +.\" THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +.\" OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +.\" MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT +.\" OF THIRD PARTY RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR +.\" HOLDERS INCLUDED IN THIS NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL +.\" INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING +.\" FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, +.\" NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION +.\" WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +.\" +.\" Except as contained in this notice, the name of a copyright holder +.\" shall not be used in advertising or otherwise to promote the sale, use +.\" or other dealings in this Software without prior written authorization +.\" of the copyright holder. +.TH gl_get_line @FUNC_MANEXT@ +.SH NAME +gl_get_line, new_GetLine, del_GetLine, gl_customize_completion, +gl_change_terminal, gl_configure_getline, gl_load_history, +gl_save_history, gl_group_history, gl_show_history, gl_watch_fd, +gl_inactivity_timeout, gl_terminal_size, gl_set_term_size, +gl_resize_history, gl_limit_history, gl_clear_history, +gl_toggle_history, gl_lookup_history, gl_state_of_history, +gl_range_of_history, gl_size_of_history, gl_echo_mode, +gl_replace_prompt, gl_prompt_style, gl_ignore_signal, gl_trap_signal, +gl_last_signal, gl_completion_action, gl_display_text, +gl_return_status, gl_error_message, gl_catch_blocked, gl_list_signals, +gl_bind_keyseq, gl_erase_terminal, gl_automatic_history, gl_append_history, +gl_query_char, gl_read_char \- allow the user to compose an input line +.SH SYNOPSIS +.nf +#include +#include + +GetLine *new_GetLine(size_t linelen, size_t histlen); + +GetLine *del_GetLine(GetLine *gl); + +char *gl_get_line(GetLine *gl, const char *prompt, + const char *start_line, int start_pos); + +int gl_query_char(GetLine *gl, const char *prompt, + char defchar); + +int gl_read_char(GetLine *gl); + +int gl_customize_completion(GetLine *gl, void *data, + CplMatchFn *match_fn); + +int gl_change_terminal(GetLine *gl, FILE *input_fp, + FILE *output_fp, const char *term); + +int gl_configure_getline(GetLine *gl, + const char *app_string, + const char *app_file, + const char *user_file); + +int gl_bind_keyseq(GetLine *gl, GlKeyOrigin origin, + const char *keyseq, const char *action); + +int gl_save_history(GetLine *gl, const char *filename, + const char *comment, int max_lines); + +int gl_load_history(GetLine *gl, const char *filename, + const char *comment); + +int gl_watch_fd(GetLine *gl, int fd, GlFdEvent event, + GlFdEventFn *callback, void *data); + +int gl_inactivity_timeout(GetLine *gl, GlTimeoutFn *callback, + void *data, unsigned long sec, + unsigned long nsec); + +int gl_group_history(GetLine *gl, unsigned stream); + +int gl_show_history(GetLine *gl, FILE *fp, + const char *fmt, int all_groups, + int max_lines); + +int gl_resize_history(GetLine *gl, size_t bufsize); + +void gl_limit_history(GetLine *gl, int max_lines); + +void gl_clear_history(GetLine *gl, int all_groups); + +void gl_toggle_history(GetLine *gl, int enable); + +GlTerminalSize gl_terminal_size(GetLine *gl, + int def_ncolumn, + int def_nline); + +int gl_set_term_size(GetLine *gl, int ncolumn, int nline); + +int gl_lookup_history(GetLine *gl, unsigned long id, + GlHistoryLine *hline); + +void gl_state_of_history(GetLine *gl, + GlHistoryState *state); + +void gl_range_of_history(GetLine *gl, + GlHistoryRange *range); + +void gl_size_of_history(GetLine *gl, GlHistorySize *size); + +void gl_echo_mode(GetLine *gl, int enable); + +void gl_replace_prompt(GetLine *gl, const char *prompt); + +void gl_prompt_style(GetLine *gl, GlPromptStyle style); + +int gl_ignore_signal(GetLine *gl, int signo); + +int gl_trap_signal(GetLine *gl, int signo, unsigned flags, + GlAfterSignal after, int errno_value); + +int gl_last_signal(GetLine *gl); + +int gl_completion_action(GetLine *gl, + void *data, CplMatchFn *match_fn, + int list_only, const char *name, + const char *keyseq); + +int gl_register_action(GetLine *gl, void *data, + GlActionFn *fn, const char *name, + const char *keyseq); + +int gl_display_text(GetLine *gl, int indentation, + const char *prefix, + const char *suffix, int fill_char, + int def_width, int start, + const char *string); + +GlReturnStatus gl_return_status(GetLine *gl); + +const char *gl_error_message(GetLine *gl, char *buff, + size_t n); + +void gl_catch_blocked(GetLine *gl); + +int gl_list_signals(GetLine *gl, sigset_t *set); + +int gl_append_history(GetLine *gl, const char *line); + +int gl_automatic_history(GetLine *gl, int enable); + +.fi + +.SH DESCRIPTION + +The \f3gl_get_line()\f1 function is part of the tecla library (see the +\f3libtecla(@LIBR_MANEXT@)\f1 man page). If the user is typing at a terminal, each +call prompts them for an line of input, then provides interactive +editing facilities, similar to those of the unix \f3tcsh\f1 shell. In +addition to simple command-line editing, it supports recall of +previously entered command lines, TAB completion of file names, and +in-line wild-card expansion of filenames. Documentation of both the +user-level command-line editing features and all user configuration +options, can be found in the \f3tecla(@MISC_MANEXT@)\f1 man page. This man page +concerns itself with documentation for programmers interested in using +this library in their application. +.sp +.SH AN EXAMPLE + +The following shows a complete example of how to use the +\f3gl_get_line()\f1 function to get input from the user: + +.nf + #include + #include + #include + + int main(int argc, char *argv[]) + { + char *line; /* The line that the user typed */ + GetLine *gl; /* The gl_get_line() resource object */ + + setlocale(LC_CTYPE, ""); /* Adopt the user's choice */ + /* of character set. */ + + gl = new_GetLine(1024, 2048); + if(!gl) + return 1; + + while((line=gl_get_line(gl, "$ ", NULL, -1)) != NULL && + strcmp(line, "exit\\n") != 0) + printf("You typed: %s\\n", line); + + gl = del_GetLine(gl); + return 0; + } +.fi +.sp +In the example, first the resources needed by the \f3gl_get_line()\f1 function +are created by calling \f3new_GetLine()\f1. This allocates the memory used in +subsequent calls to the \f3gl_get_line()\f1 function, including the history +buffer for recording previously entered lines. Then one or more lines are read +from the user, until either an error occurs, or the user types \f3exit\f1. Then +finally the resources that were allocated by \f3new_GetLine()\f1, are returned +to the system by calling \f3del_GetLine()\f1. Note the use of the \f3NULL\f1 +return value of \f3del_GetLine()\f1 to make \f3gl\f1 \f3NULL\f1. This is a +safety precaution. If the program subsequently attempts to pass \f3gl\f1 to +\f3gl_get_line()\f1, said function will complain, and return an error, instead of +attempting to use the deleted resource object. + +.sp +.SH THE FUNCTIONS USED IN THE EXAMPLE +The descriptions of the functions used in the example are as follows: +.sp +.nf + GetLine *new_GetLine(size_t linelen, size_t histlen) +.fi +.sp +This function creates the resources used by the \f3gl_get_line()\f1 +function and returns an opaque pointer to the object that contains +them. The maximum length of an input line is specified via the +\f3linelen\f1 argument, and the number of bytes to allocate for +storing history lines is set by the \f3histlen\f1 argument. History +lines are stored back-to-back in a single buffer of this size. Note +that this means that the number of history lines that can be stored at +any given time, depends on the lengths of the individual lines. If +you want to place an upper limit on the number of lines that can be +stored, see the \f3gl_limit_history()\f1 function described later. If +you don't want history at all, specify \f3histlen\f1 as zero, and no +history buffer will be allocated. +.sp +On error, a message is printed to \f3stderr\f1 and \f3NULL\f1 is returned. +.sp +.nf + GetLine *del_GetLine(GetLine *gl) +.fi +.sp +This function deletes the resources that were returned by a previous +call to \f3new_GetLine()\f1. It always returns \f3NULL\f1 (ie a +deleted object). It does nothing if the \f3gl\f1 argument is +\f3NULL\f1. +.sp +.nf + char *gl_get_line(GetLine *gl, const char *prompt, + const char *start_line, int start_pos); +.fi +.sp +The \f3gl_get_line()\f1 function can be called any number of +times to read input from the user. The \f3gl\f1 argument +must have been previously returned by a call to +\f3new_GetLine()\f1. The \f3prompt\f1 argument should be a +normal \f3NUL\f1 terminated string, specifying the prompt to +present the user with. By default prompts are displayed +literally, but if enabled with the \f3gl_prompt_style()\f1 +function (see later), prompts can contain directives to do +underlining, switch to and from bold fonts, or turn +highlighting on and off. + +If you want to specify the initial contents of the line, for the user +to edit, pass the desired string via the \f3start_line\f1 +argument. You can then specify which character of this line the cursor +is initially positioned over, using the \f3start_pos\f1 argument. This +should be -1 if you want the cursor to follow the last character of +the start line. If you don't want to preload the line in this manner, +send \f3start_line\f1 as \f3NULL\f1, and set \f3start_pos\f1 to +-1. Note that the line pointer returned by one call to +\f3gl_get_line()\f1 can be passed back to the next call to +\f3gl_get_line()\f1 via the \f3start_line\f1. This allows the +application to take the last entered line, and if it contains an +error, to then present it back to the user for re-editing, with the +cursor initially positioned where the error was encountered. + +The \f3gl_get_line()\f1 function returns a pointer to the line entered +by the user, or \f3NULL\f1 on error or at the end of the input. The +returned pointer is part of the specified \f3gl\f1 resource object, +and thus should not be free'd by the caller, or assumed to be +unchanging from one call to the next. When reading from a user at a +terminal, there will always be a newline character at the end of the +returned line. When standard input is being taken from a pipe or a +file, there will similarly be a newline unless the input line was too +long to store in the internal buffer. In the latter case you should +call \f3gl_get_line()\f1 again to read the rest of the line. Note that +this behavior makes \f3gl_get_line()\f1 similar to \f3fgets()\f1. In +fact when \f3stdin\f1 isn't connected to a terminal,\f3gl_get_line()\f1 +just calls \f3fgets()\f1. + +.SH THE RETURN STATUS OF GL_GET_LINE + +As described above, the \f3gl_get_line()\f1 function has two possible +return values; a pointer to the completed input line, or +\f3NULL\f1. Extra information about what caused \f3gl_get_line()\f1 to +return is available both by inspecting \f3errno\f1, and by calling the +\f3gl_return_status()\f1 function. + +.sp +.nf + GlReturnStatus gl_return_status(GetLine *gl); +.fi +.sp + +The following are the possible enumerated values that this +function returns. + +.sp +.nf + GLR_NEWLINE - The last call to \f3gl_get_line()\f1 + successfully returned a completed + input line. + + GLR_BLOCKED - \f3gl_get_line()\f1 was in non-blocking + server mode, and returned early to + avoid blocking the process while + waiting for terminal I/O. The + \f3gl_pending_io()\f1 function can be + used to see what type of I/O + \f3gl_get_line()\f1 was waiting for. + (see the \f3gl_io_mode(@FUNC_MANEXT@)\f1 man page + for details). + + GLR_SIGNAL - A signal was caught by + \f3gl_get_line()\f1 that had an + after-signal disposition of + \f3GLS_ABORT\f1 (See \f3gl_trap_signal()\f1). + + GLR_TIMEOUT - The inactivity timer expired while + \f3gl_get_line()\f1 was waiting for + input, and the timeout callback + function returned \f3GLTO_ABORT\f1. + See \f3gl_inactivity_timeout()\f1 for + information about timeouts. + + GLR_FDABORT - An application I/O callack returned + \f3GLFD_ABORT\f1 (see \f3gl_watch_fd()\f1). + + GLR_EOF - End of file reached. This can happen + when input is coming from a file or a + pipe, instead of the terminal. It also + occurs if the user invokes the + \f3list-or-eof\f1 or \f3del-char-or-list-or-eof\f1 + actions at the start of a new line. + + GLR_ERROR - An unexpected error caused + \f3gl_get_line()\f1 to abort (consult + \f3errno\f1 and/or + \f3gl_error_message()\f1 for details. +.fi +.sp + +When \f3gl_return_status()\f1 returns \f3GLR_ERROR\f1, and the +value of \f3errno\f1 isn't sufficient to explain what +happened, you can use the \f3gl_error_message()\f1 function +to request a description of the last error that occurred. + +.sp +.nf + const char *gl_error_message(GetLine *gl, char *buff, + size_t n); +.fi +.sp + +The return value is a pointer to the message that +occurred. If the \f3buff\f1 argument is \f3NULL\f1, this +will be a pointer to a buffer within \f3gl\f1, who's value +will probably change on the next call to any function +associated with \f3gl_get_line()\f1. Otherwise, if a +non-\f3NULL\f1 \f3buff\f1 argument is provided, the error +message, including a \f3'\\0'\f1 terminator, will be written +within the first \f3n\f1 elements of this buffer, and the +return value will be a pointer to the first element of this +buffer. If the message won't fit in the provided buffer, it +will be truncated to fit. + +.SH OPTIONAL PROMPT FORMATTING + +Whereas by default the prompt string that you specify is +displayed literally, without any special interpretation of +the characters within it, the \f3gl_prompt_style()\f1 +function can be used to enable optional formatting +directives within the prompt. +.sp +.nf + void gl_prompt_style(GetLine *gl, GlPromptStyle style); +.fi +.sp +The \f3style\f1 argument, which specifies the formatting +style, can take any of the following values: +.sp +.nf + GL_FORMAT_PROMPT - In this style, the formatting + directives described below, when + included in prompt strings, are + interpreted as follows: + + %B - Display subsequent + characters with a bold + font. + %b - Stop displaying characters + with the bold font. + %F - Make subsequent characters + flash. + %f - Turn off flashing + characters. + %U - Underline subsequent + characters. + %u - Stop underlining + characters. + %P - Switch to a pale (half + brightness) font. + %p - Stop using the pale font. + %S - Highlight subsequent + characters (also known as + standout mode). + %s - Stop highlighting + characters. + %V - Turn on reverse video. + %v - Turn off reverse video. + %% - Display a single % + character. + + For example, in this mode, a prompt + string like \f3"%UOK%u$ "\f1 would + display the prompt \f3"OK$ "\f1, + but with the \f3OK\f1 part + underlined. + + Note that although a pair of + characters that starts with a % + character, but doesn't match any of + the above directives is displayed + literally, if a new directive is + subsequently introduced which does + match, the displayed prompt will + change, so it is better to always + use %% to display a literal %. + + Also note that not all terminals + support all of these text + attributes, and that some substitute + a different attribute for missing + ones. + + GL_LITERAL_PROMPT - In this style, the prompt string is + printed literally. This is the + default style. +.fi + +.SH ALTERNATE CONFIGURATION SOURCES + +As mentioned above, by default users have the option of configuring +the behavior of \f3gl_get_line()\f1 via a configuration file called +\f3\&.teclarc\f1 in their home directories. The fact that all +applications share this same configuration file is both an advantage +and a disadvantage. In most cases it is an advantage, since it +encourages uniformity, and frees the user from having to configure +each application separately. In some applications, however, this +single means of configuration is a problem. This is particularly true +of embedded software, where there's no filesystem to read a +configuration file from, and also in applications where a radically +different choice of keybindings is needed to emulate a legacy keyboard +interface. To cater for such cases, the following function allows the +application to control where configuration information is read from. + +.sp +.nf + int gl_configure_getline(GetLine *gl, + const char *app_string, + const char *app_file, + const char *user_file); +.fi +.sp + +It allows the configuration commands that would normally be read from +a user's \f3~/.teclarc\f1 file, to be read from any or none of, a +string, an application specific configuration file, and/or a +user-specific configuration file. If this function is called before +the first call to \f3gl_get_line()\f1, the default behavior of +reading \f3~/.teclarc\f1 on the first call to \f3gl_get_line()\f1 is +disabled, so all configuration must be achieved using the +configuration sources specified with this function. + +If \f3app_string != NULL\f1, then it is interpreted as a string +containing one or more configuration commands, separated from each +other in the string by embedded newline characters. If \f3app_file != +NULL\f1 then it is interpreted as the full pathname of an +application-specific configuration file. If \f3user_file != NULL\f1 +then it is interpreted as the full pathname of a user-specific +configuration file, such as \f3~/.teclarc\f1. For example, in the +following call, + +.sp +.nf + gl_configure_getline(gl, "edit-mode vi \\n nobeep", + "/usr/share/myapp/teclarc", + "~/.teclarc"); +.fi +.sp + +the \f3app_string\f1 argument causes the calling application to start +in vi edit-mode, instead of the default emacs mode, and turns off the +use of the terminal bell by the library. It then attempts to read +system-wide configuration commands from an optional file called +\f3/usr/share/myapp/teclarc\f1, then finally reads user-specific +configuration commands from an optional \f3\&.teclarc\f1 file in the +user's home directory. Note that the arguments are listed in ascending +order of priority, with the contents of \f3app_string\f1 being +potentially overriden by commands in \f3app_file\f1, and commands in +\f3app_file\f1 potentially being overriden by commands in +\f3user_file\f1. +.sp +You can call this function as many times as needed, the results being +cumulative, but note that copies of any filenames specified via the +\f3app_file\f1 and \f3user_file\f1 arguments are recorded internally +for subsequent use by the \f3read-init-files\f1 key-binding function, +so if you plan to call this function multiple times, be sure that the +last call specifies the filenames that you want re-read when the user +requests that the configuration files be re-read. +.sp +Individual key sequences can also be bound and unbound using the +\f3gl_bind_keyseq()\f1 function. + +.sp +.nf + int gl_bind_keyseq(GetLine *gl, GlKeyOrigin origin, + const char *keyseq, + const char *action); +.fi +.sp + +The \f3origin\f1 argument specifies the priority of the binding, +according to who it is being established for, and must be one of +the following two values. +.sp +.nf + GL_USER_KEY - The user requested this key-binding. + GL_APP_KEY - This is a default binding set by the + application. +.fi +.sp +When both user and application bindings for a given key-sequence have +been specified, the user binding takes precedence. The application's +binding is subsequently reinstated if the user's binding is later +unbound via either another to this function, or a call to +\f3gl_configure_getline()\f1. + +The \f3keyseq\f1 argument specifies the key-sequence to be bound or +unbound, and is expressed in the same way as in a \f3~/.teclarc\f1 +configuration file. The \f3action\f1 argument must either be a string +containing the name of the action to bind the key-sequence to, or it +must be \f3NULL\f1 or "" to unbind the key-sequence. + +.SH CUSTOMIZED WORD COMPLETION + +If in your application, you would like to have TAB completion complete +other things in addition to or instead of filenames, you can arrange +this by registering an alternate completion callback function, via a +call to the \f3gl_customize_completion()\f1 function. +.sp +.nf + int gl_customize_completion(GetLine *gl, void *data, + CplMatchFn *match_fn); +.fi +.sp +The \f3data\f1 argument provides a way for your application to pass +arbitrary, application-specific information to the callback +function. This is passed to the callback every time that it is +called. It might for example, point to the symbol table from which +possible completions are to be sought. The \f3match_fn\f1 argument +specifies the callback function to be called. The \f3CplMatchFn\f1 +function type is defined in \f3libtecla.h\f1, as is a +\f3CPL_MATCH_FN()\f1 macro that you can use to declare and prototype +callback functions. The declaration and responsibilities of callback +functions are described in depth in the \f1cpl_complete_word(@FUNC_MANEXT@)\f1 man +page. +.sp +In brief, the callback function is responsible for looking backwards +in the input line, back from the point at which the user pressed TAB, +to find the start of the word being completed. It then must lookup +possible completions of this word, and record them one by one in the +\f3WordCompletion\f1 object that is passed to it as an argument, by +calling the \f3cpl_add_completion()\f1 function. If the callback +function wishes to provide filename completion in addition to its own +specific completions, it has the option of itself calling the builtin +file-name completion callback. This also, is documented in the +\f3cpl_complete_word(@FUNC_MANEXT@)\f1 man page. +.sp +Note that if you would like \f3gl_get_line()\f1 to return the current +input line when a successful completion is been made, you can arrange +this when you call \f3cpl_add_completion()\f1, by making the last +character of the continuation suffix a newline character. If you do +this, the input line will be updated to display the completion, +together with any contiuation suffix up to the newline character, then +\f3gl_get_line()\f1 will return this input line. +.sp + +If, for some reason, your callback function needs to write something +to the terminal, it must call \f3gl_normal_io()\f1 before doing +so. This will start a new line after the input line that is currently +being edited, reinstate normal terminal I/O, and tell +\f3gl_get_line()\f1 that the input line will need to be redrawn when +the callback returns. + +.SH ADDING COMPLETION ACTIONS + +In the previous section the ability to customize the behavior of the +only default completion action, \f3complete-word\f1, was described. +In this section the ability to install additional action functions, so +that different types of word completion can be bound to different +key-sequences, is described. This is achieved by using the +\f3gl_completion_action()\f1 function. + +.sp +.nf + int gl_completion_action(GetLine *gl, + void *data, CplMatchFn *match_fn, + int list_only, const char *name, + const char *keyseq); +.fi +.sp + +The \f3data\f1 and \f3match_fn\f1 arguments are as described +in the \f3cpl_complete_word\f1 man page, and specify the +callback function that should be invoked to identify +possible completions. The \f3list_only\f1 argument +determines whether the action that is being defined should +attempt to complete the word as far as possible in the input +line before displaying any possible ambiguous completions, +or whether it should simply display the list of possible +completions without touching the input line. The former +option is selected by specifying a value of \f30\f1, and the +latter by specifying a value of \f31\f1. The \f3name\f1 +argument specifies the name by which configuration files and +future invokations of this function should refer to the +action. This must either be the name of an existing +completion action to be changed, or be a new unused name for +a new action. Finally, the \f3keyseq\f1 argument specifies +the default key-sequence to bind the action to. If this is +\f3NULL\f1, no new keysequence will be bound to the action. + +Beware that in order for the user to be able to change the +key-sequence that is bound to actions that are installed in +this manner, when you call \f3gl_completion_action()\f1 to +install a given action for the first time, you should do +this between calling \f3new_GetLine()\f1 and the first call +to \f3gl_get_line()\f1. Otherwise, when the user's +configuration file is read on the first call to +\f3gl_get_line()\f1, the name of the your additional action +won't be known, and any reference to it in the configuration +file will generate an error. + +As discussed for \f3gl_customize_completion()\f1, if your callback +function, for some reason, needs to write anything to the terminal, it +must call \f3gl_normal_io()\f1 before doing so. + +.SH DEFINING CUSTOM ACTIONS + +Although the built-in key-binding actions are sufficient for the needs +of most applications, occasionally a specialized application may need +to define one or more custom actions, bound to application-specific +key-sequences. For example, a sales application would benefit from +having a key-sequence that displayed the part name that corresponded +to a part number preceding the cursor. Such a feature is clearly +beyond the scope of the built-in action functions. So for such special +cases, the \f3gl_register_action()\f1 function is provided. + +.sp +.nf + int gl_register_action(GetLine *gl, void *data, + GlActionFn *fn, const char *name, + const char *keyseq); +.fi +.sp + +This function lets the application register an external function, +\f3fn\f1, that will thereafter be called whenever either the specified +key-sequence, \f3keyseq\f1, is entered by the user, or the user enters +any other key-sequence that the user subsequently binds to the +specified action name, \f3name\f1, in their configuration file. The +\f3data\f1 argument can be a pointer to anything that the application +wishes to have passed to the action function, \f3fn\f1, whenever that +function is invoked. + +The action function, \f3fn\f1, should be declared using the following +macro, which is defined in \f3libtecla.h\f1. + +.sp +.nf + #define GL_ACTION_FN(fn) GlAfterAction (fn)(GetLine *gl, \\ + void *data, int count, size_t curpos, \\ + const char *line) +.fi +.sp + +The \f3gl\f1 and \f3data\f1 arguments are those that were previously +passed to \f3gl_register_action()\f1 when the action function was +registered. The \f3count\f1 argument is a numeric argument which the +user has the option of entering using the \f3digit-argument\f1 action, +before invoking the action. If the user doesn't enter a number, then +the \f3count\f1 argument is set to 1. Nominally this argument is +interpreted as a repeat count, meaning that the action should be +repeated that many times. In practice however, for some actions a +repeat count makes little sense. In such cases, actions can either +simply ignore the \f3count\f1 argument, or use its value for a +different purpose. + +A copy of the current input line is passed in the read-only \f3line\f1 +argument. The current cursor position within this string is given by +the index contained in the \f3curpos\f1 argument. Note that direct +manipulation of the input line and the cursor position is not +permitted. This is because the rules dicated by various modes, such as +vi mode versus emacs mode, no-echo mode, and insert mode versus +overstrike mode etc, make it too complex for an application writer to +write a conforming editing action, as well as constrain future changes +to the internals of \f3gl_get_line()\f1. A potential solution to this +dilema would be to allow the action function to edit the line using +the existing editing actions. This is currently under consideration. + +If the action function wishes to write text to the terminal, without +this getting mixed up with the displayed text of the input line, or +read from the terminal without having to handle raw terminal I/O, then +before doing either of these operations, it must temporarily suspend +line editing by calling the \f3gl_normal_io()\f1 function. This +function flushes any pending output to the terminal, moves the cursor +to the start of the line that follows the last terminal line of the +input line, then restores the terminal to a state that is suitable for +use with the C stdio facilities. The latter includes such things as +restoring the normal mapping of \f3\\n\f1 to \f3\\r\\n\f1, and, when +in server mode, restoring the normal blocking form of terminal +I/O. Having called this function, the action function can read from +and write to the terminal without the fear of creating a mess. It +isn't necessary for the action function to restore the original +editing environment before it returns. This is done automatically by +\f3gl_get_line()\f1 after the action function returns. The following +is a simple example of an action function which writes the sentence +"Hello world" on a new terminal line after the line being edited. When +this function returns, the input line is redrawn on the line that +follows the "Hello world" line, and line editing resumes. + +.sp +.nf + static GL_ACTION_FN(say_hello_fn) + { + if(gl_normal_io(gl)) /* Temporarily suspend editing */ + return GLA_ABORT; + printf("Hello world\\n"); + return GLA_CONTINUE; + } +.fi +.sp + +Action functions must return one of the following values, to tell +\f3gl_get_line()\f1 how to procede. + +.sp +.nf + GLA_ABORT - Cause gl_get_line() to return NULL. + GLA_RETURN - Cause gl_get_line() to return the + completed input line. + GLA_CONTINUE - Resume command-line editing. +.fi +.sp + +Note that the \f3name\f1 argument of \f3gl_register_action()\f1 +specifies the name by which a user can refer to the action in their +configuration file. This allows them to re-bind the action to an +alternate key-seqeunce. In order for this to work, it is necessary to +call \f3gl_register_action()\f1 between calling \f3new_GetLine()\f1 +and the first call to \f3gl_get_line()\f1. + +.SH HISTORY FILES + +To save the contents of the history buffer before quitting your +application, and subsequently restore them when you next start the +application, the following functions are provided. + +.sp +.nf + int gl_save_history(GetLine *gl, const char *filename, + const char *comment, int max_lines); + int gl_load_history(GetLine *gl, const char *filename, + const char *comment); +.fi +.sp + +The \f3filename\f1 argument specifies the name to give the history +file when saving, or the name of an existing history file, when +loading. This may contain home-directory and environment variable +expressions, such as "~/.myapp_history" or "$HOME/.myapp_history". +.sp +Along with each history line, extra information about it, such as when +it was entered by the user, and what its nesting level is, is recorded +as a comment preceding the line in the history file. Writing this as a +comment allows the history file to double as a command file, just in +case you wish to replay a whole session using it. Since comment +prefixes differ in different languages, the \f3comment\f1 argument is +provided for specifying the comment prefix. For example, if your +application were a unix shell, such as the bourne shell, you would +specify "#" here. Whatever you choose for the comment character, you +must specify the same prefix to \f3gl_load_history()\f1 that you used +when you called \f3gl_save_history()\f1 to write the history file. +.sp +The \f3max_lines\f1 must be either -1 to specify that all lines in the +history list be saved, or a positive number specifying a ceiling on +how many of the most recent lines should be saved. +.sp +Both fuctions return non-zero on error, after writing an error message +to stderr. Note that \f3gl_load_history()\f1 does not consider the +non-existence of a file to be an error. + +.SH MULTIPLE HISTORY LISTS + +If your application uses a single \f3GetLine\f1 object for entering +many different types of input lines, you may wish \f3gl_get_line()\f1 +to distinguish the different types of lines in the history list, and +only recall lines that match the current type of line. To support this +requirement, \f3gl_get_line()\f1 marks lines being recorded in the +history list with an integer identifier chosen by the application. +Initially this identifier is set to \f10\f3 by \f3new_GetLine()\f1, +but it can be changed subsequently by calling +\f3gl_group_history()\f1. + +.sp +.nf + int gl_group_history(GetLine *gl, unsigned id); +.fi +.sp + +The integer identifier \f3id\f1 can be any number chosen by the +application, but note that \f3gl_save_history()\f1 and +\f3gl_load_history()\f1 preserve the association between identifiers +and historical input lines between program invokations, so you should +choose fixed identifiers for the different types of input line used by +your application. +.sp +Whenever \f3gl_get_line()\f1 appends a new input line to the history +list, the current history identifier is recorded with it, and when it +is asked to recall a historical input line, it only recalls lines that +are marked with the current identifier. + +.SH DISPLAYING HISTORY + +The history list can be displayed by calling \f3gl_show_history()\f1. + +.sp +.nf + int gl_show_history(GetLine *gl, FILE *fp, + const char *fmt, + int all_groups, + int max_lines); +.fi +.sp + +This displays the current contents of the history list to the stdio +output stream \f3fp\f1. If the \f3max_lines\f1 argument is greater +than or equal to zero, then no more than this number of the most +recent lines will be displayed. If the \f3all_groups\f1 argument is +non-zero, lines from all history groups are displayed. Otherwise just +those of the currently selected history group are displayed. The +format string argument, \f3fmt\f1, determines how the line is +displayed. This can contain arbitrary characters which are written +verbatim, interleaved with any of the following format directives: + +.nf + %D - The date on which the line was originally + entered, formatted like 2001-11-20. + %T - The time of day when the line was entered, + formatted like 23:59:59. + %N - The sequential entry number of the line in + the history buffer. + %G - The number of the history group which the + line belongs to. + %% - A literal % character. + %H - The history line itself. +.fi + +Thus a format string like \f3"%D %T %H\n"\f1 would output something like: + +.nf + 2001-11-20 10:23:34 Hello world +.fi + +Note the inclusion of an explicit newline character in the format +string. + +.SH LOOKING UP HISTORY + +The \f3gl_lookup_history()\f1 function allows the calling application +to look up lines in the history list. + +.sp +.nf + typedef struct { + const char *line; /* The requested historical */ + /* line. */ + unsigned group; /* The history group to which */ + /* the line belongs. */ + time_t timestamp; /* The date and time at which */ + /* the line was originally */ + /* entered. */ + } GlHistoryLine; + + int gl_lookup_history(GetLine *gl, unsigned long id, + GlHistoryLine *hline); +.fi +.sp + +The \f3id\f1 argument indicates which line to look up, where the first +line that was entered in the history list after \f3new_GetLine()\f1 +was called, is denoted by 0, and subsequently entered lines are +denoted with successively higher numbers. Note that the range of lines +currently preserved in the history list can be queried by calling the +\f3gl_range_of_history()\f1 function, described later. If the +requested line is in the history list, the details of the line are +recorded in the variable pointed to by the \f3hline\f1 argument, and +\f31\f1 is returned. Otherwise \f30\f1 is returned, and the variable +pointed to by \f3hline\f1 is left unchanged. +.sp +Beware that the string returned in \f3hline->line\f1 is part of the +history buffer, so it must not be modified by the caller, and will be +recycled on the next call to any function that takes \f3gl\f1 as its +argument. Therefore you should make a private copy of this string if +you need to keep it around. + +.SH MANUAL HISTORY ARCHIVAL + +By default, whenever a line is entered by the user, it is +automatically appended to the history list, just before +\f3gl_get_line()\f1 returns the line to the caller. This is convenient +for the majority of applications, but there are also applications that +need finer grained control over what gets added to the history +list. In such cases, the automatic addition of entered lines to the +history list can be turned off by calling the +\f3gl_automatic_history()\f1 function. + +.sp +.nf + int gl_automatic_history(GetLine *gl, int enable); +.fi +.sp + +If this function is called with its \f3enable\f1 argument set to +\f30\f1, \f3gl_get_line()\f1 won't automatically archive subsequently +entered lines. Automatic archiving can be reenabled at a later time, +by calling this function again, with its \f3enable\f1 argument set to +1. While automatic history archiving is disabled, the calling +application can use the \f3gl_append_history()\f1 to append lines to +the history list as needed. + +.sp +.nf + int gl_append_history(GetLine *gl, const char *line); +.fi +.sp + +The \f3line\f1 argument specifies the line to be added to the history +list. This must be a normal \f3'\0'\f1 terminated string. If this +string contains any newline characters, the line that gets archived in +the history list will be terminated by the first of these. Otherwise +it will be terminated by the \f3'\0'\f1 terminator. If the line is +longer than the maximum input line length, that was specified when +\f3new_GetLine()\f1 was called, when the line is recalled, it will get +truncated to the actual \f3gl_get_line()\f1 line length. + +If successful, \f3gl_append_history()\f1 returns 0. Otherwise it +returns non-zero, and sets \f3errno\f1 to one of the following values. + +.sp +.nf + EINVAL - One of the arguments passed to + gl_append_history() was NULL. + ENOMEM - The specified line was longer than the allocated + size of the history buffer (as specified when + new_GetLine() was called), so it couldn't be + archived. +.fi +.sp + +A textual description of the error can optionally be obtained by +calling \f3gl_error_message()\f1. Note that after such an error, the +history list remains in a valid state to receive new history lines, so +there is little harm in simply ignoring the return status of +\f3gl_append_history()\f1. + +.SH MISCELLANEOUS HISTORY CONFIGURATION + +If you wish to change the size of the history buffer that was +originally specified in the call to \f3new_GetLine()\f1, you can do so +with the \f3gl_resize_history()\f1 function. + +.sp +.nf + int gl_resize_history(GetLine *gl, size_t histlen); +.fi +.sp + +The \f3histlen\f1 argument specifies the new size in bytes, and if you +specify this as 0, the buffer will be deleted. +.sp +As mentioned in the discussion of \f3new_GetLine()\f1, the number of +lines that can be stored in the history buffer, depends on the lengths +of the individual lines. For example, a 1000 byte buffer could equally +store 10 lines of average length 100 bytes, or 2 lines of average +length 50 bytes. Although the buffer is never expanded when new lines +are added, a list of pointers into the buffer does get expanded when +needed to accomodate the number of lines currently stored in the +buffer. To place an upper limit on the number of lines in the buffer, +and thus a ceiling on the amount of memory used in this list, you can +call the \f3gl_limit_history()\f1 function. + +.sp +.nf + void gl_limit_history(GetLine *gl, int max_lines); +.fi +.sp + +The \f3max_lines\f1 should either be a positive number \f3>= 0\f1, +specifying an upper limit on the number of lines in the buffer, or be +\f3-1\f1 to cancel any previously specified limit. When a limit is in +effect, only the \f3max_lines\f1 most recently appended lines are kept +in the buffer. Older lines are discarded. +.sp +To discard lines from the history buffer, use the +\f3gl_clear_history()\f1 function. +.sp +.nf + void gl_clear_history(GetLine *gl, int all_groups); +.fi +.sp +The \f3all_groups\f1 argument tells the function whether to delete +just the lines associated with the current history group (see +\f3gl_group_history()\f1), or all historical lines in the buffer. +.sp +The \f3gl_toggle_history()\f1 function allows you to toggle history on +and off without losing the current contents of the history list. + +.sp +.nf + void gl_toggle_history(GetLine *gl, int enable); +.fi +.sp + +Setting the \f3enable\f1 argument to 0 turns off the history +mechanism, and setting it to 1 turns it back on. When history is +turned off, no new lines will be added to the history list, and +history lookup key-bindings will act as though there is nothing in the +history buffer. + +.SH QUERYING HISTORY INFORMATION + +The configured state of the history list can be queried with the +\f3gl_history_state()\f1 function. + +.sp +.nf + typedef struct { + int enabled; /* True if history is enabled */ + unsigned group; /* The current history group */ + int max_lines; /* The current upper limit on the */ + /* number of lines in the history */ + /* list, or -1 if unlimited. */ + } GlHistoryState; + + void gl_state_of_history(GetLine *gl, + GlHistoryState *state); +.fi +.sp +On return, the status information is recorded in the variable pointed +to by the \f3state\f1 argument. +.sp +The \f3gl_range_of_history()\f1 function returns the number and +range of lines in the history list. + +.sp +.nf +typedef struct { + unsigned long oldest; /* The sequential entry number */ + /* of the oldest line in the */ + /* history list. */ + unsigned long newest; /* The sequential entry number */ + /* of the newest line in the */ + /* history list. */ + int nlines; /* The number of lines in the */ + /* history list. */ +} GlHistoryRange; + +void gl_range_of_history(GetLine *gl, GlHistoryRange *range); +.fi +.sp +The return values are recorded in the variable pointed to by the +\f3range\f1 argument. If the \f3nlines\f1 member of this structure is +greater than zero, then the \f3oldest\f1 and \f3newest\f1 members +report the range of lines in the list, and \f3newest=oldest+nlines-1\f1. +Otherwise they are both zero. +.sp +The \f3gl_size_of_history()\f1 function returns the total size of the +history buffer and the amount of the buffer that is currently +occupied. +.sp +.nf + typedef struct { + size_t size; /* The size of the history buffer */ + /* (bytes). */ + size_t used; /* The number of bytes of the */ + /* history buffer that are */ + /* currently occupied. */ + } GlHistorySize; + + void gl_size_of_history(GetLine *gl, GlHistorySize *size); +.fi +.sp +On return, the size information is recorded in the variable pointed to +by the \f3size\f1 argument. + +.SH CHANGING TERMINALS + +The \f3new_GetLine()\f1 constructor function assumes that input is to +be read from \f3stdin\f1, and output written to \f3stdout\f1. The +following function allows you to switch to different input and output +streams. +.sp +.nf + int gl_change_terminal(GetLine *gl, FILE *input_fp, + FILE *output_fp, const char *term); +.fi +.sp +The \f3gl\f1 argument is the object that was returned by +\f3new_GetLine()\f1. The \f3input_fp\f1 argument specifies the stream +to read from, and \f3output_fp\f1 specifies the stream to be written +to. Only if both of these refer to a terminal, will interactive +terminal input be enabled. Otherwise \f3gl_get_line()\f1 will simply +call \f3fgets()\f1 to read command input. If both streams refer to a +terminal, then they must refer to the same terminal, and the type of +this terminal must be specified via the \f3term\f1 argument. The value +of the \f3term\f1 argument is looked up in the terminal information +database (terminfo or termcap), in order to determine which special +control sequences are needed to control various aspects of the +terminal. \f3new_GetLine()\f1 for example, passes the return value of +\f3getenv("TERM")\f1 in this argument. Note that if one or both of +\f3input_fp\f1 and \f3output_fp\f1 don't refer to a terminal, then it +is legal to pass \f3NULL\f1 instead of a terminal type. +.sp +Note that if you want to pass file descriptors to +\f3gl_change_terminal()\f1, you can do this by creating stdio stream +wrappers using the POSIX \f3fdopen()\f1 function. + +.SH EXTERNAL EVENT HANDLING + +By default, \f3gl_get_line()\f1 doesn't return until either a complete +input line has been entered by the user, or an error occurs. In +programs that need to watch for I/O from other sources than the +terminal, there are two options. + +.sp +.nf + 1. Use the functions described in the + \f3gl_io_mode(@FUNC_MANEXT@)\f1 man page to switch + \f3gl_get_line()\f1 into non-blocking server mode. In this mode, + \f3gl_get_line()\f1 becomes a non-blocking, incremental + line-editing function that can safely be called from + an external event loop. Although this is a very + versatile method, it involves taking on some + responsibilities that are normally performed behind + the scenes by \f3gl_get_line()\f1. + + 2. While \f3gl_get_line()\f1 is waiting for keyboard + input from the user, you can ask it to also watch for + activity on arbitrary file descriptors, such as + network sockets, pipes etc, and have it call functions + of your choosing when activity is seen. This works on + any system that has the \f3select()\f1 system call, + which is most, if not all flavors of unix. +.fi +.sp + +Registering a file descriptor to be watched by +\f3gl_get_line()\f1 involves calling the \f3gl_watch_fd()\f1 function. + +.sp +.nf + int gl_watch_fd(GetLine *gl, int fd, GlFdEvent event, + GlFdEventFn *callback, void *data); +.fi +.sp + +If this returns non-zero, then it means that either your arguments are +invalid, or that this facility isn't supported on the host system. +.sp +The \f3fd\f1 argument is the file descriptor to be watched. The +\f3event\f1 argument specifies what type of activity is of interest, +chosen from the following enumerated values: + +.sp +.nf + GLFD_READ - Watch for the arrival of data to be read. + GLFD_WRITE - Watch for the ability to write to the file + descriptor without blocking. + GLFD_URGENT - Watch for the arrival of urgent + out-of-band data on the file descriptor. +.fi +.sp + +The \f3callback\f1 argument is the function to call when the selected +activity is seen. It should be defined with the following macro, which +is defined in libtecla.h. + +.sp +.nf + #define GL_FD_EVENT_FN(fn) GlFdStatus (fn)(GetLine *gl, \\ + void *data, int fd, \\ + GlFdEvent event) +.fi +.sp +The \f3data\f1 argument of the \f3gl_watch_fd()\f1 function is passed +to the callback function for its own use, and can point to anything +you like, including \f3NULL\f1. The file descriptor and the event +argument are also passed to the callback function, and this +potentially allows the same callback function to be registered to more +than one type of event and/or more than one file descriptor. The +return value of the callback function should be one of the following +values. + +.sp +.nf + GLFD_ABORT - Tell gl_get_line() to abort. When this + happens, \f3gl_get_line()\f1 returns + \f3NULL\f1, and a following call to + \f3gl_return_status()\f1 will return + \f3GLR_FDABORT\f1. Note that if the + application needs \f3errno\f1 always to + have a meaningful value when + \f3gl_get_line()\f1 returns \f3NULL\f1, + the callback function should set + \f3errno\f1 appropriately. + GLFD_REFRESH - Redraw the input line then continue + waiting for input. Return this if + your callback wrote to the terminal. + GLFD_CONTINUE - Continue to wait for input, without + redrawing the line. +.fi +.sp +Note that before calling the callback, \f3gl_get_line()\f1 blocks most +signals, and leaves its own signal handlers installed, so if you need +to catch a particular signal you will need to both temporarily install +your own signal handler, and unblock the signal. Be sure to re-block +the signal (if it was originally blocked) and reinstate the original +signal handler, if any, before returning. + +.sp + +If the callback function needs to read or write to the terminal, it +should ideally first call \f3gl_normal_io(gl)\f1 to temporarily +suspend line editing. This will restore the terminal to canonical, +blocking-I/O, mode, and move the cursor to the start of a new terminal +line. Later, when the callback returns, \f3gl_get_line()\f1 will +notice that \f3gl_normal_io()\f1 was called, redisplay the input line +and resume editing. Note that in this case the return values, +\f3GLFD_REFRESH\f1 and \f3GLFD_CONTINUE\f1 are equivalent. + +.sp + +To support cases where the callback function calls a third-party +function which occasionally and unpredictably writes to the terminal, +the automatic conversion of \f3"\n"\f1 to \f3"\r\n"\f1 is re-enabled +before the callback function is called. If the callack knows that the +third-party function wrote to the terminal, it should then return the +\f3GLFD_REFRESH\f1 return value, to tell \f3gl_get_line()\f1 to +redisplay the input line. + +.sp + +To remove a callback function that you previously registered for a +given file descriptor and event, simply call \f3gl_watch_fd()\f1 with +the same file descriptor and \f3event\f1 arguments, but with a +\f3callback\f1 argument of \f30\f1. The \f3data\f1 argument is ignored +in this case. + +.SH SETTING AN INACTIVITY TIMEOUT + +On systems with the \f3select()\f1 system call, the +\f3gl_inactivity_timeout()\f1 function can be used to set or cancel an +inactivity timeout. Inactivity in this case refers both to keyboard +input, and to I/O on any file descriptors registered by prior and +subsequent calls to \f3gl_watch_fd()\f1. On oddball systems that don't +have \f3select()\f1, this call has no effect. + +.sp +.nf + int gl_inactivity_timeout(GetLine *gl, GlTimeoutFn *callback, + void *data, unsigned long sec, + unsigned long nsec); +.fi +.sp + +The timeout is specified in the form of an integral number of seconds +and an integral number of nanoseconds, via the \f3sec\f1 and +\f3nsec\f1 arguments respectively. Subsequently, whenever no activity +is seen for this time period, the function specified via the +\f3callback\f1 argument is called. The \f3data\f1 argument of +\f3gl_inactivity_timeout()\f1 is passed verbatim to this callback function +whenever it is invoked, and can thus be used to pass arbitrary +application-specific information to the callback. The following macro +is provided in \f3libtecla.h\f1 for applications to use to declare and +prototype timeout callback functions. + +.sp +.nf + #define GL_TIMEOUT_FN(fn) \\ + GlAfterTimeout (fn)(GetLine *gl, void *data) +.fi +.sp + +On returning, the application's callback is expected to return one of +the following enumerators to tell \f3gl_get_line()\f1 how to procede +after the timeout has been handled by the callback. + +.sp +.nf + GLTO_ABORT - Tell gl_get_line() to abort. When + this happens, \f3gl_get_line()\f1 will + return \f3NULL\f1, and a following call + to \f3gl_return_status()\f1 will return + \f3GLR_TIMEOUT\f1. Note that if the + application needs \f3errno\f1 always to + have a meaningful value when + \f3gl_get_line()\f1 returns \f3NULL\f1, + the callback function should set + \f3errno\f1 appropriately. + GLTO_REFRESH - Redraw the input line, then continue + waiting for input. You should return + this value if your callback wrote to the + terminal without having first called + \f3gl_normal_io(gl)\f1. + GLTO_CONTINUE - In normal blocking-I/O mode, continue to + wait for input, without redrawing the + user's input line. + In non-blocking server I/O mode (see + gl_io_mode(@FUNC_MANEXT@)), cause \f3gl_get_line()\f1 + to act as though I/O blocked. This means + that \f3gl_get_line()\f1 will immediately + return \f3NULL\f1, and a following call + to \f3gl_return_status()\f1 will return + \f3GLR_BLOCKED\f1. +.fi +.sp + +Note that before calling the callback, \f3gl_get_line()\f1 blocks most +signals, and leaves its own signal handlers installed, so if you need +to catch a particular signal you will need to both temporarily install +your own signal handler, and unblock the signal. Be sure to re-block +the signal (if it was originally blocked) and reinstate the original +signal handler, if any, before returning. + +.sp + +If the callback function needs to read or write to the terminal, it +should ideally first call \f3gl_normal_io(gl)\f1 to temporarily +suspend line editing. This will restore the terminal to canonical, +blocking-I/O, mode, and move the cursor to the start of a new terminal +line. Later, when the callback returns, \f3gl_get_line()\f1 will +notice that \f3gl_normal_io()\f1 was called, redisplay the input line +and resume editing. Note that in this case the return values, +\f3GLTO_REFRESH\f1 and \f3GLTO_CONTINUE\f1 are equivalent. + +.sp + +To support cases where the callback function calls a third-party +function which occasionally and unpredictably writes to the terminal, +the automatic conversion of \f3"\n"\f1 to \f3"\r\n"\f1 is re-enabled +before the callback function is called. If the callack knows that the +third-party function wrote to the terminal, it should then return the +\f3GLTO_REFRESH\f1 return value, to tell \f3gl_get_line()\f1 to +redisplay the input line. + +.sp + +Note that although the timeout argument includes a nano-second +component, few computer clocks presently have resolutions that are +finer than a few milliseconds, so asking for less than a few +milliseconds is equivalent to requesting zero seconds on a lot of +systems. If this would be a problem, you should base your timeout +selection on the actual resolution of the host clock (eg. by calling +\f3sysconf(_SC_CLK_TCK)\f1). + +.sp + +To turn off timeouts, simply call \f3gl_inactivity_timeout()\f1 with a +\f3callback\f1 argument of \f30\f1. The \f3data\f1 argument is ignored +in this case. + +.SH SIGNAL HANDLING DEFAULTS + +By default, the \f3gl_get_line()\f1 function intercepts a +number of signals. This is particularly important for +signals which would by default terminate the process, since +the terminal needs to be restored to a usable state before +this happens. In this section, the signals that are trapped +by default, and how \f3gl_get_line()\f1 responds to them, is +described. Changing these defaults is the topic of the +following section. +.sp +When the following subset of signals are caught, \f3gl_get_line()\f1 +first restores the terminal settings and signal handling to how they +were before \f3gl_get_line()\f1 was called, resends the signal, to +allow the calling application's signal handlers to handle it, then if +the process still exists, \f3gl_get_line()\f1 returns \f3NULL\f1 and +sets \f3errno\f1 as specified below. + +.sp +.nf + SIGINT - This signal is generated both by the keyboard + interrupt key (usually ^C), and the keyboard + break key. + + errno=EINTR + + SIGHUP - This signal is generated when the controlling + terminal exits. + + errno=ENOTTY + + SIGPIPE - This signal is generated when a program attempts + to write to a pipe who's remote end isn't being + read by any process. This can happen for example + if you have called \f3gl_change_terminal()\f1 to + redirect output to a pipe hidden under a pseudo + terminal. + + errno=EPIPE + + SIGQUIT - This signal is generated by the keyboard quit + key (usually ^\\). + + errno=EINTR + + SIGABRT - This signal is generated by the standard C, + abort() function. By default it both + terminates the process and generates a core + dump. + + errno=EINTR + + SIGTERM - This is the default signal that the UN*X + kill command sends to processes. + + errno=EINTR +.fi +.sp +Note that in the case of all of the above signals, POSIX mandates that +by default the process is terminated, with the addition of a core dump +in the case of the \f3SIGQUIT\f1 signal. In other words, if the +calling application doesn't override the default handler by supplying +its own signal handler, receipt of the corresponding signal will +terminate the application before \f3gl_get_line()\f1 returns. +.sp +If gl_get_line() aborts with errno set to EINTR, you can find out what +signal caused it to abort, by calling the following function. +.sp +.nf + int gl_last_signal(const GetLine *gl); +.fi +.sp +This returns the numeric code (eg. \f3SIGINT\f1) of the last signal +that was received during the most recent call to \f3gl_get_line()\f1, +or \f3-1\f1 if no signals were received. +.sp +On systems that support it, when a SIGWINCH (window change) signal is +received, \f3gl_get_line()\f1 queries the terminal to find out its new +size, redraws the current input line to accomodate the new size, then +returns to waiting for keyboard input from the user. Unlike other +signals, this signal isn't resent to the application. +.sp +Finally, the following signals cause \f3gl_get_line()\f1 to first +restore the terminal and signal environment to that which prevailed +before \f3gl_get_line()\f1 was called, then resend the signal to the +application. If the process still exists after the signal has been +delivered, then \f3gl_get_line()\f1 then re-establishes its own signal +handlers, switches the terminal back to raw mode, redisplays the input +line, and goes back to awaiting terminal input from the user. +.sp +.nf + SIGCONT - This signal is generated when a suspended + process is resumed. + + SIGPOLL - On SVR4 systems, this signal notifies the + process of an asynchronous I/O event. Note + that under 4.3+BSD, SIGIO and SIGPOLL are + the same. On other systems, SIGIO is ignored + by default, so \f3gl_get_line()\f1 doesn't + trap it by default. + + SIGPWR - This signal is generated when a power failure + occurs (presumably when the system is on a + UPS). + + SIGALRM - This signal is generated when a timer + expires. + + SIGUSR1 - An application specific signal. + + SIGUSR2 - Another application specific signal. + + SIGVTALRM - This signal is generated when a virtual + timer expires (see man setitimer(2)). + + SIGXCPU - This signal is generated when a process + exceeds its soft CPU time limit. + + SIGXFSZ - This signal is generated when a process + exceeds its soft file-size limit. + + SIGTSTP - This signal is generated by the terminal + suspend key, which is usually ^Z, or the + delayed terminal suspend key, which is + usually ^Y. + + SIGTTIN - This signal is generated if the program + attempts to read from the terminal while the + program is running in the background. + + SIGTTOU - This signal is generated if the program + attempts to write to the terminal while the + program is running in the background. +.fi +.sp + +Obviously not all of the above signals are supported on all systems, +so code to support them is conditionally compiled into the tecla +library. +.sp +Note that if \f3SIGKILL\f1 or \f3SIGPOLL\f1, which by definition can't +be caught, or any of the hardware generated exception signals, such as +\f3SIGSEGV\f1, \f3SIGBUS\f1 and \f3SIGFPE\f1, are received and +unhandled while \f3gl_get_line()\f1 has the terminal in raw mode, the +program will be terminated without the terminal having been restored +to a usable state. In practice, job-control shells usually reset the +terminal settings when a process relinquishes the controlling +terminal, so this is only a problem with older shells. + +.SH CUSTOMIZED SIGNAL HANDLING + +The previous section listed the signals that +\f3gl_get_line()\f1 traps by default, and described how it +responds to them. This section describes how to both add and +remove signals from the list of trapped signals, and how to +specify how \f3gl_get_line()\f1 should respond to a given +signal. +.sp +If you don't need \f3gl_get_line()\f1 to do anything in +response to a signal that it normally traps, you can tell to +\f3gl_get_line()\f1 to ignore that signal by calling +\f3gl_ignore_signal()\f1. +.sp +.nf + int gl_ignore_signal(GetLine *gl, int signo); +.fi +.sp +The \f3signo\f1 argument is the number of the signal +(eg. \f3SIGINT\f1) that you want to have ignored. If the +specified signal isn't currently one of those being trapped, +this function does nothing. +.sp +The \f3gl_trap_signal()\f1 function allows you to either add +a new signal to the list that \f3gl_get_line()\f1 traps, or +modify how it responds to a signal that it already traps. +.sp +.nf + int gl_trap_signal(GetLine *gl, int signo, unsigned flags, + GlAfterSignal after, int errno_value); +.fi +.sp +The \f3signo\f1 argument is the number of the signal that +you wish to have trapped. The \f3flags\f1 argument is a set +of flags which determine the environment in which the +application's signal handler is invoked, the \f3after\f1 +argument tells \f3gl_get_line()\f1 what to do after the +application's signal handler returns, and \f3errno_value\f1 +tells \f3gl_get_line()\f1 what to set \f3errno\f1 to if told +to abort. +.sp +The \f3flags\f1 argument is a bitwise OR of zero or more of +the following enumerators: +.sp +.nf + GLS_RESTORE_SIG - Restore the caller's signal + environment while handling the + signal. + + GLS_RESTORE_TTY - Restore the caller's terminal settings + while handling the signal. + + GLS_RESTORE_LINE - Move the cursor to the start of the + line following the input line before + invoking the application's signal + handler. + + GLS_REDRAW_LINE - Redraw the input line when the + application's signal handler returns. + + GLS_UNBLOCK_SIG - Normally, if the calling program has + a signal blocked (man sigprocmask), + gl_get_line() does not trap that + signal. This flag tells gl_get_line() + to trap the signal and unblock it for + the duration of the call to + gl_get_line(). + + GLS_DONT_FORWARD - If this flag is included, the signal + will not be forwarded to the signal + handler of the calling program. +.fi +.sp +Two commonly useful flag combinations are also enumerated as +follows: +.sp +.nf + GLS_RESTORE_ENV = GLS_RESTORE_SIG | GLS_RESTORE_TTY | + GLS_REDRAW_LINE + + GLS_SUSPEND_INPUT = GLS_RESTORE_ENV | GLS_RESTORE_LINE +.fi +.sp + +If your signal handler, or the default system signal +handler for this signal, if you haven't overridden it, never +either writes to the terminal, nor suspends or terminates +the calling program, then you can safely set the \f3flags\f1 +argument to \f30\f1. +.sp +If your signal handler always writes to the terminal, reads +from it, or suspends or terminates the program, you should +specify the \f3flags\f1 argument as \f3GL_SUSPEND_INPUT\f1, +so that: +.sp +.nf +1. The cursor doesn't get left in the middle of the input + line. +2. So that the user can type in input and have it echoed. +3. So that you don't need to end each output line with + \f3\\r\\n\f1, instead of just \f3\\n\f1. +.fi +.sp +The \f3GL_RESTORE_ENV\f1 combination is the same as +\f3GL_SUSPEND_INPUT\f1, except that it doesn't move the +cursor, and if your signal handler doesn't read or write +anything to the terminal, the user won't see any visible +indication that a signal was caught. This can be useful if +you have a signal handler that only occasionally writes to +the terminal, where using \f3GL_SUSPEND_LINE\f1 would cause +the input line to be unnecessarily duplicated when nothing +had been written to the terminal. Such a signal handler, +when it does write to the terminal, should be sure to start +a new line at the start of its first write, by writing a +'\\n' character, and should be sure to leave the cursor on a +new line before returning. If the signal arrives while the +user is entering a line that only occupies a signal terminal +line, or if the cursor is on the last terminal line of a +longer input line, this will have the same effect as +\f3GL_SUSPEND_INPUT\f1. Otherwise it will start writing on a +line that already contains part of the displayed input line. +This doesn't do any harm, but it looks a bit ugly, which is +why the \f3GL_SUSPEND_INPUT\f1 combination is better if you +know that you are always going to be writting to the +terminal. +.sp +The \f3after\f1 argument, which determines what +\f3gl_get_line()\f1 does after the application's signal +handler returns (if it returns), can take any one of the +following values: +.sp +.nf + GLS_RETURN - Return the completed input line, just as + though the user had pressed the return + key. + + GLS_ABORT - Cause \f3gl_get_line()\f1 to abort. When + this happens, \f3gl_get_line()\f1 returns + \f3NULL\f1, and a following call to + \f3gl_return_status()\f1 will return + \f3GLR_SIGNAL\f1. Note that if the + application needs \f3errno\f1 always to + have a meaningful value when + \f3gl_get_line()\f1 returns \f3NULL\f1, + the callback function should set + \f3errno\f1 appropriately. + GLS_CONTINUE - Resume command line editing. +.fi +.sp +The \f3errno_value\f1 argument is intended to be combined +with the \f3GLS_ABORT\f1 option, telling \f3gl_get_line()\f1 +what to set the standard \f3errno\f1 variable to before +returning \f3NULL\f1 to the calling program. It can also, +however, be used with the \f3GL_RETURN\f1 option, in case +you wish to have a way to distinguish between an input line +that was entered using the return key, and one that was +entered by the receipt of a signal. + +.SH RELIABLE SIGNAL HANDLING + +Signal handling is suprisingly hard to do reliably without race +conditions. In \f3gl_get_line()\f1 a lot of care has been taken to +allow applications to perform reliable signal handling around +\f3gl_get_line()\f1. This section explains how to make use of this. + +As an example of the problems that can arise if the application isn't +written correctly, imagine that one's application has a SIGINT signal +handler that sets a global flag. Now suppose that the application +tests this flag just before invoking \f3gl_get_line()\f1. If a SIGINT +signal happens to be received in the small window of time between the +statement that tests the value of this flag, and the statement that +calls \f3gl_get_line()\f1, then \f3gl_get_line()\f1 will not see the +signal, and will not be interrupted. As a result, the application +won't be able to respond to the signal until the user gets around to +finishing entering the input line and \f3gl_get_line()\f1 +returns. Depending on the application, this might or might not be a +disaster, but at the very least it would puzzle the user. + +The way to avoid such problems is to do the following. + +1. If needed, use the \f3gl_trap_signal()\f1 function to + configure \f3gl_get_line()\f1 to abort when important + signals are caught. + +2. Configure \f3gl_get_line()\f1 such that if any of the + signals that it catches are blocked when + \f3gl_get_line()\f1 is called, they will be unblocked + automatically during times when \f3gl_get_line()\f1 is + waiting for I/O. This can be done either + on a per signal basis, by calling the + \f3gl_trap_signal()\f1 function, and specifying the + \f3GLS_UNBLOCK\f1 attribute of the signal, or globally by + calling the \f3gl_catch_blocked()\f1 function. + +.sp +.nf + void gl_catch_blocked(GetLine *gl); +.fi +.sp + + This function simply adds the \f3GLS_UNBLOCK\f1 attribute + to all of the signals that it is currently configured to + trap. + +3. Just before calling \f3gl_get_line()\f1, block delivery + of all of the signals that \f3gl_get_line()\f1 is + configured to trap. This can be done using the POSIX + \f3sigprocmask()\f1 function in conjunction with the + \f3gl_list_signals()\f1 function. + +.sp +.nf + int gl_list_signals(GetLine *gl, sigset_t *set); +.fi +.sp + + This function returns the set of signals that it is + currently configured to catch in the \f3set\f1 argument, + which is in the form required by \f3sigprocmask()\f1. + +4. In the example, one would now test the global flag that + the signal handler sets, knowing that there is now no + danger of this flag being set again until + \f3gl_get_line()\f1 unblocks its signals while performing + I/O. + +5. Eventually \f3gl_get_line()\f1 returns, either because + a signal was caught, an error occurred, or the user + finished entering their input line. + +6. Now one would check the global signal flag again, and if + it is set, respond to it, and zero the flag. + +7. Use \f3sigprocmask()\f1 to unblock the signals that were + blocked in step 3. + +The same technique can be used around certain POSIX +signal-aware functions, such as \f3sigsetjmp()\f1 and +\f3sigsuspend()\f1, and in particular, the former of these +two functions can be used in conjunction with +\f3siglongjmp()\f1 to implement race-condition free signal +handling around other long-running system calls. The way to +do this, is explained next, by showing how +\f3gl_get_line()\f1 manages to reliably trap signals around +calls to functions like \f3read()\f1 and \f3select()\f1 +without race conditions. + +The first thing that \f3gl_get_line()\f1 does, whenever it +is called, is to use the POSIX \f3sigprocmask()\f1 function +to block the delivery of all of the signals that it is +currently configured to catch. This is redundant if the +application has already blocked them, but it does no +harm. It undoes this step just before returning. + +Whenever \f3gl_get_line()\f1 needs to call \f3read()\f1 or +\f3select()\f1 to wait for input from the user, it first +calls the POSIX \f3sigsetjmp()\f1 function, being sure to +specify a non-zero value for its \f3savesigs\f1 argument. +The reason for the latter argument will become clear +shortly. + +If \f3sigsetjmp()\f1 returns zero, \f3gl_get_line()\f1 then +does the following. + +.sp +.nf +a. It uses the POSIX \f3sigaction()\f1 function to register + a temporary signal handler to all of the signals that it + is configured to catch. This signal handler does two + things. + + 1. It records the number of the signal that was received + in a file-scope variable. + + 2. It then calls the POSIX \f3siglongjmp()\f1 + function using the buffer that was passed to + \f3sigsetjmp()\f1 for its first argument, and + a non-zero value for its second argument. + + When this signal handler is registered, the \f3sa_mask\f1 + member of the \f3struct sigaction act\f1 argument of the + call to \f3sigaction()\f1 is configured to contain all of + the signals that \f3gl_get_line()\f1 is catching. This + ensures that only one signal will be caught at once by + our signal handler, which in turn ensures that multiple + instances of our signal handler don't tread on each + other's toes. + +b. Now that the signal handler has been set up, + \f3gl_get_line()\f1 unblocks all of the signals that it + is configured to catch. + +c. It then calls the \f3read()\f1 or \f3select()\f1 system + calls to wait for keyboard input. + +d. If this system call returns (ie. no signal is received), + \f3gl_get_line()\f1 blocks delivery of the signals of + interest again. + +e. It then reinstates the signal handlers that were + displaced by the one that was just installed. +.fi +.sp + +Alternatively, if \f3sigsetjmp()\f1 returns non-zero, this +means that one of the signals being trapped was caught while +the above steps were executing. When this happens, +\f3gl_get_line()\f1 does the following. + +First, note that when a call to \f3siglongjmp()\f1 causes +\f3sigsetjmp()\f1 to return, provided that the +\f3savesigs\f1 argument of \f3sigsetjmp()\f1 was non-zero, +as specified above, the signal process mask is restored to +how it was when \f3sigsetjmp()\f1 was called. This is the +important difference between \f3sigsetjmp()\f1 and the older +problematic \f3setjmp()\f1, and is the essential ingredient +that makes it possible to avoid signal handling race +conditions. Because of this we are guaranteed that all of +the signals that we blocked before calling \f3sigsetjmp()\f1 +are blocked again as soon as any signal is caught. The +following statements, which are then executed, are thus +guaranteed to be executed without any further signals being +caught. + +1. If so instructed by the \f3gl_get_line()\f1 configuration + attributes of the signal that was caught, + \f3gl_get_line()\f1 restores the terminal attributes to + the state that they had when \f3gl_get_line()\f1 was + called. This is particularly important for signals that + suspend or terminate the process, since otherwise the + terminal would be left in an unusable state. + +2. It then reinstates the application's signal handlers. + +3. Then it uses the C standard-library \f3raise()\f1 + function to re-send the application the signal that + was caught. + +3. Next it unblocks delivery of the signal that we just + sent. This results in the signal that was just sent + via \f3raise()\f1, being caught by the application's + original signal handler, which can now handle it as it + sees fit. + +4. If the signal handler returns (ie. it doesn't terminate + the process), \f3gl_get_line()\f1 blocks delivery of the + above signal again. + +5. It then undoes any actions performed in the first of the + above steps, and redisplays the line, if the signal + configuration calls for this. + +6. \f3gl_get_line()\f1 then either resumes trying to + read a character, or aborts, depending on the + configuration of the signal that was caught. + +What the above steps do in essence is to take asynchronously +delivered signals and handle them synchronously, one at a +time, at a point in the code where \f3gl_get_line()\f1 has +complete control over its environment. + +.SH THE TERMINAL SIZE + +On most systems the combination of the \f3TIOCGWINSZ\f1 ioctl and the +\f3SIGWINCH\f1 signal is used to maintain an accurate idea of the +terminal size. The terminal size is newly queried every time that +\f3gl_get_line()\f1 is called and whenever a \f3SIGWINCH\f1 signal is +received. +.sp +On the few systems where this mechanism isn't available, at +startup \f3new_GetLine()\f1 first looks for the \f3LINES\f1 +and \f3COLUMNS\f1 environment variables. If these aren't +found, or they contain unusable values, then if a terminal +information database like terminfo or termcap is available, +the default size of the terminal is looked up in this +database. If this too fails to provide the terminal size, a +default size of 80 columns by 24 lines is used. +.sp +Even on systems that do support \f3ioctl(TIOCGWINSZ)\f1, if the +terminal is on the other end of a serial line, the terminal driver +generally has no way of detecting when a resize occurs or of querying +what the current size is. In such cases no \f3SIGWINCH\f1 is sent to +the process, and the dimensions returned by \f3ioctl(TIOCGWINSZ)\f1 +aren't correct. The only way to handle such instances is to provide a +way for the user to enter a command that tells the remote system what +the new size is. This command would then call the +\f3gl_set_term_size()\f1 function to tell \f3gl_get_line()\f1 about +the change in size. + +.sp +.nf + int gl_set_term_size(GetLine *gl, int ncolumn, int nline); +.fi +.sp + +The \f3ncolumn\f1 and \f3nline\f1 arguments are used to specify the +new dimensions of the terminal, and must not be less than 1. On +systems that do support \f3ioctl(TIOCGWINSZ)\f1, this function first +calls \f3ioctl(TIOCSWINSZ)\f1 to tell the terminal driver about the +change in size. In non-blocking server-I/O mode, if a line is +currently being input, the input line is then redrawn to accomodate +the changed size. Finally the new values are recorded in \f3gl\f1 for +future use by \f3gl_get_line()\f1. +.sp +The \f3gl_terminal_size()\f1 function allows you to query +the current size of the terminal, and install an alternate +fallback size for cases where the size isn't available. +Beware that the terminal size won't be available if reading +from a pipe or a file, so the default values can be +important even on systems that do support ways of finding +out the terminal size. +.sp +.nf + typedef struct { + int nline; /* The terminal has nline lines */ + int ncolumn; /* The terminal has ncolumn columns */ + } GlTerminalSize; + + GlTerminalSize gl_terminal_size(GetLine *gl, + int def_ncolumn, + int def_nline); +.fi +.sp +This function first updates \f3gl_get_line()\f1's fallback terminal +dimensions, then records its findings in the return value. +.sp +The \f3def_ncolumn\f1 and \f3def_nline\f1 specify the +default number of terminal columns and lines to use if the +terminal size can't be determined via \f3ioctl(TIOCGWINSZ)\f1 or +environment variables. + +.SH HIDING WHAT YOU TYPE + +When entering sensitive information, such as passwords, it is best not +to have the text that you are entering echoed on the terminal. +Furthermore, such text should not be recorded in the history list, +since somebody finding your terminal unattended could then recall it, +or somebody snooping through your directories could see it in your +history file. With this in mind, the \f3gl_echo_mode()\f1 +function allows you to toggle on and off the display and archival of +any text that is subsequently entered in calls to \f3gl_get_line()\f1. + +.sp +.nf + int gl_echo_mode(GetLine *gl, int enable); +.fi +.sp + +The \f3enable\f1 argument specifies whether entered text +should be visible or not. If it is \f30\f1, then +subsequently entered lines will not be visible on the +terminal, and will not be recorded in the history list. If +it is \f31\f1, then subsequent input lines will be displayed +as they are entered, and provided that history hasn't been +turned off via a call to \f3gl_toggle_history()\f1, then +they will also be archived in the history list. Finally, if +the \f3enable\f1 argument is \f3-1\f1, then the echoing mode +is left unchanged, which allows you to non-destructively +query the current setting via the return value. In all +cases, the return value of the function is \f30\f1 if +echoing was disabled before the function was called, and +\f31\f1 if it was enabled. +.sp +When echoing is turned off, note that although tab +completion will invisibly complete your prefix as far as +possible, ambiguous completions will not be displayed. + +.SH SINGLE CHARACTER QUERIES + +Using \f3gl_get_line()\f1 to query the user for a single character +reply, is inconvenient for the user, since they must hit the enter or +return key before the character that they typed is returned to the +program. Thus the \f3gl_query_char()\f1 function has been provided for +single character queries like this. + +.sp +.nf + int gl_query_char(GetLine *gl, const char *prompt, + char defchar); +.fi +.sp + +This function displays the specified prompt at the start of a new +line, and waits for the user to type a character. When the user types +a character, \f3gl_query_char()\f1 displays it to the right of the +prompt, starts a newline, then returns the character to the calling +program. The return value of the function is the character that was +typed. If the read had to be aborted for some reason, \f3EOF\f1 is +returned instead. In the latter case, the application can call the +previously documented \f3gl_return_status()\f1, to find out what went +wrong. This could, for example, have been the reception of a signal, +or the optional inactivity timer going off. + +If the user simply hits enter, the value of the \f3defchar\f1 argument +is substituted. This means that when the user hits either newline or +return, the character specified in \f3defchar\f1, is displayed after +the prompt, as though the user had typed it, as well as being returned +to the calling application. If such a replacement is not important, +simply pass \f3'\n'\f1 as the value of \f3defchar\f1. + +If the entered character is an unprintable character, it is displayed +symbolically. For example, control-A is displayed as ^A, and +characters beyond 127 are displayed in octal, preceded by a +backslash. + +As with \f3gl_get_line()\f1, echoing of the entered character can be +disabled using the \f3gl_echo_mode()\f1 function. + +If the calling process is suspended while waiting for the user to type +their response, the cursor is moved to the line following the prompt +line, then when the process resumes, the prompt is redisplayed, and +\f3gl_query_char()\f1 resumes waiting for the user to type a +character. + +Note that in non-blocking server mode, (see +gl_io_mode(@FUNC_MANEXT@)), if an incomplete input line is in the +process of being read when \f3gl_query_char()\f1 is called, the +partial input line is discarded, and erased from the terminal, before +the new prompt is displayed. The next call to \f3gl_get_line()\f1 will +thus start editing a new line. + +.SH READING RAW CHARACTERS + +Whereas the \f3gl_query_char()\f1 function visibly prompts the user +for a character, and displays what they typed, the +\f3gl_read_char()\f1 function reads a signal character from the user, +without writing anything to the terminal, or perturbing any +incompletely entered input line. This means that it can be called not +only from between calls to \f3gl_get_line()\f1, but also from callback +functions that the application has registered to be called by +\f3gl_get_line()\f1. + +.sp +.nf + int gl_read_char(GetLine *gl); +.fi +.sp + +On success, the return value of \f3gl_read_char()\f1 is the character +that was read. On failure, \f3EOF\f1 is returned, and the +\f3gl_return_status()\f1 function can be called to find out what went +wrong. Possibilities include the optional inactivity timer going off, +the receipt of a signal that is configured to abort gl_get_line(), or +terminal I/O blocking, when in non-blocking server-I/O mode. + +Beware that certain keyboard keys, such as function keys, and cursor +keys, usually generate at least 3 characters each, so a single call to +\f3gl_read_char()\f1 won't be enough to identify such keystrokes. + +.SH CLEARING THE TERMINAL + +The calling program can clear the terminal by calling +\f3gl_erase_terminal()\f1. In non-blocking server-I/O mode, this +function also arranges for the current input line to be redrawn from +scratch when \f3gl_get_line()\f1 is next called. + +.sp +.nf + int gl_erase_terminal(GetLine *gl); +.fi +.sp + +.SH DISPLAYING TEXT DYNAMICALLY + +Between calls to \f3gl_get_line()\f1, the \f3gl_display_text()\f1 +function provides a convenient way to display paragraphs of text, +left-justified and split over one or more terminal lines according to +the constraints of the current width of the terminal. Examples of the +use of this function may be found in the demo programs, where it is +used to display introductions. In those examples the advanced use of +optional prefixes, suffixes and filled lines to draw a box around the +text is also illustrated. + +.sp +.nf + int gl_display_text(GetLine *gl, int indentation, + const char *prefix, + const char *suffix, int fill_char, + int def_width, int start, + const char *string); +.fi +.sp +If \f3gl\f1 isn't currently connected to a terminal, for example if +the output of a program that uses \f3gl_get_line()\f1 is being piped +to another program or redirected to a file, then the value of the +\f3def_width\f1 parameter is used as the terminal width. + +The \f3indentation\f1 argument specifies the number of characters to +use to indent each line of ouput. The \f3fill_char\f1 argument +specifies the character that will be used to perform this indentation. + +The \f3prefix\f1 argument can either be \f3NULL\f1, or be a string to +place at the beginning of each new line (after any indentation). +Similarly, the \f3suffix\f1 argument can either be \f3NULL\f1, or be a +string to place at the end of each line. The suffix is placed flush +against the right edge of the terminal, and any space between its +first character and the last word on that line is filled with the +character specified via the \f3fill_char\f1 argument. Normally the +fill-character is a space. + +The \f3start\f1 argument tells \f3gl_display_text()\f1 how many +characters have already been written to the current terminal line, and +thus tells it the starting column index of the cursor. Since the +return value of \f3gl_display_text()\f1 is the ending column index of +the cursor, by passing the return value of one call to the \f3start\f1 +argument of the next call, a paragraph that is broken between more +than one string can be composed by calling \f3gl_display_text()\f1 for +each successive portion of the paragraph. Note that literal newline +characters are necessary at the end of each paragraph to force a new +line to be started. + +On error, \f3gl_display_text()\f1 returns -1. + +.SH CALLBACK FUNCTION FACILITIES + +Unless otherwise stated, callback functions, such as tab +completion callbacks and event callbacks should not call any +functions in this module. The following functions, however, +are designed specifically to be used by callback functions. +.sp +Calling the \f3gl_replace_prompt()\f1 function from a +callback tells \f3gl_get_line()\f1 to display a different +prompt when the callback returns. Except in non-blocking +server mode, it has no effect if used between calls to +\f3gl_get_line()\f1. In non-blocking server mode (see the +\f3gl_io_mode(@FUNC_MANEXT@)\f1 man page, when used between two calls to +\f3gl_get_line()\f1 that are operating on the same input +line, the current input line will be re-drawn with the new +prompt on the following call to \f3gl_get_line()\f1. + +.sp +.nf + void gl_replace_prompt(GetLine *gl, const char *prompt); +.fi +.sp + +.SH INTERNATIONAL CHARACTER SETS + +Since libtecla version 1.4.0, \f3gl_get_line()\f1 has been 8-bit +clean. This means that all 8-bit characters that are printable in the +user's current locale are now displayed verbatim and included in the +returned input line. Assuming that the calling program correctly +contains a call like the following, +.sp +.nf + setlocale(LC_CTYPE, ""); +.fi +.sp +then the current locale is determined by the first of the environment +variables \f3LC_CTYPE\f1, \f3LC_ALL\f1, and \f3LANG\f1, that is found +to contain a valid locale name. If none of these variables are +defined, or the program neglects to call setlocale, then the default +\f3C\f1 locale is used, which is US 7-bit ASCII. On most unix-like +platforms, you can get a list of valid locales by typing the command: +.sp +.nf + locale -a +.fi +.sp +at the shell prompt. Further documentation on how the user can make use +of this to enter international characters can be found in the +\f3tecla(@MISC_MANEXT@)\f1 man page. + +.SH THREAD SAFETY + +In a multi-threaded program, you should use the libtecla_r.a version +of the library. This uses reentrant versions of system functions, +where available. Unfortunately neither terminfo nor termcap were +designed to be reentrant, so you can't safely use the functions of the +getline module in multiple threads (you can use the separate +file-expansion and word-completion modules in multiple threads, see +the corresponding man pages for details). However due to the use of +POSIX reentrant functions for looking up home directories etc, it is +safe to use this module from a single thread of a multi-threaded +program, provided that your other threads don't use any termcap or +terminfo functions. + +.SH FILES +.nf +libtecla.a - The tecla library +libtecla.h - The tecla header file. +~/.teclarc - The personal tecla customization file. +.fi + +.SH SEE ALSO +.nf +libtecla(@LIBR_MANEXT@), gl_io_mode(@FUNC_MANEXT@), tecla(@MISC_MANEXT@), ef_expand_file(@FUNC_MANEXT@), +cpl_complete_word(@FUNC_MANEXT@), pca_lookup_file(@FUNC_MANEXT@) +.fi + +.SH AUTHOR +Martin Shepherd (mcs@astro.caltech.edu) diff --git a/libtecla-1.6.1/man/func/gl_group_history.in b/libtecla-1.6.1/man/func/gl_group_history.in new file mode 100644 index 0000000..6e46fc6 --- /dev/null +++ b/libtecla-1.6.1/man/func/gl_group_history.in @@ -0,0 +1 @@ +.so @FUNC_MANDIR@/gl_get_line.@FUNC_MANEXT@ diff --git a/libtecla-1.6.1/man/func/gl_handle_signal.in b/libtecla-1.6.1/man/func/gl_handle_signal.in new file mode 100644 index 0000000..24798bc --- /dev/null +++ b/libtecla-1.6.1/man/func/gl_handle_signal.in @@ -0,0 +1 @@ +.so @FUNC_MANDIR@/gl_io_mode.@FUNC_MANEXT@ diff --git a/libtecla-1.6.1/man/func/gl_ignore_signal.in b/libtecla-1.6.1/man/func/gl_ignore_signal.in new file mode 100644 index 0000000..6e46fc6 --- /dev/null +++ b/libtecla-1.6.1/man/func/gl_ignore_signal.in @@ -0,0 +1 @@ +.so @FUNC_MANDIR@/gl_get_line.@FUNC_MANEXT@ diff --git a/libtecla-1.6.1/man/func/gl_inactivity_timeout.in b/libtecla-1.6.1/man/func/gl_inactivity_timeout.in new file mode 100644 index 0000000..6e46fc6 --- /dev/null +++ b/libtecla-1.6.1/man/func/gl_inactivity_timeout.in @@ -0,0 +1 @@ +.so @FUNC_MANDIR@/gl_get_line.@FUNC_MANEXT@ diff --git a/libtecla-1.6.1/man/func/gl_io_mode.in b/libtecla-1.6.1/man/func/gl_io_mode.in new file mode 100644 index 0000000..5789666 --- /dev/null +++ b/libtecla-1.6.1/man/func/gl_io_mode.in @@ -0,0 +1,571 @@ +.\" Copyright (c) 2002, 2003, 2004 by Martin C. Shepherd +.\" +.\" All rights reserved. +.\" +.\" Permission is hereby granted, free of charge, to any person obtaining a +.\" copy of this software and associated documentation files (the +.\" "Software"), to deal in the Software without restriction, including +.\" without limitation the rights to use, copy, modify, merge, publish, +.\" distribute, and/or sell copies of the Software, and to permit persons +.\" to whom the Software is furnished to do so, provided that the above +.\" copyright notice(s) and this permission notice appear in all copies of +.\" the Software and that both the above copyright notice(s) and this +.\" permission notice appear in supporting documentation. +.\" +.\" THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +.\" OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +.\" MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT +.\" OF THIRD PARTY RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR +.\" HOLDERS INCLUDED IN THIS NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL +.\" INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING +.\" FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, +.\" NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION +.\" WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +.\" +.\" Except as contained in this notice, the name of a copyright holder +.\" shall not be used in advertising or otherwise to promote the sale, use +.\" or other dealings in this Software without prior written authorization +.\" of the copyright holder. +.TH gl_io_mode @FUNC_MANEXT@ +.SH NAME + gl_io_mode, gl_raw_io, gl_normal_io, gl_tty_signals, gl_abandon_line, + gl_handle_signal, gl_pending_io \- How to use gl_get_line() from an external event loop. +.SH SYNOPSIS +.nf +#include + +int gl_io_mode(GetLine *gl, GlIOMode mode); + +int gl_raw_io(GetLine *gl); + +int gl_normal_io(GetLine *gl); + +int gl_tty_signals(void (*term_handler)(int), + void (*susp_handler)(int), + void (*cont_handler)(int), + void (*size_handler)(int)); + +void gl_abandon_line(GetLine *gl); + +void gl_handle_signal(int signo, GetLine *gl, int ngl); + +GlPendingIO gl_pending_io(GetLine *gl); + +.fi + +.SH DESCRIPTION + +The \f3gl_get_line()\f1 function, which is documented separately in +the \f3gl_get_line(@FUNC_MANEXT@)\f1 man page, supports two different I/O modes. +These are selected by calling the \f3gl_io_mode()\f1 function. + +.sp +.nf + int gl_io_mode(GetLine *gl, GlIOMode mode); +.fi +.sp + +The \f3mode\f1 argument of this function specifies the new I/O mode, +and must be one of the following. + +.sp +.nf + GL_NORMAL_MODE - Select the normal blocking-I/O mode. + In this mode \f3gl_get_line()\f1 + doesn't return until either an error + occurs of the user finishes entering a + new line. This mode is the focus of + the \f3gl_get_line(@FUNC_MANEXT@)\f1 man page. + + GL_SERVER_MODE - Select non-blocking server I/O mode. + In this mode, since non-blocking + terminal I/O is used, the entry of + each new input line typically requires + many calls to \f3gl_get_line()\f1 from + an external I/O-driven event loop. + This mode is the focus of this man + page. +.fi +.sp + +Newly created \f3GetLine\f1 objects start in normal I/O +mode, so to switch to non-blocking server mode requires an +initial call to \f3gl_io_mode()\f1. + +.SH SERVER I/O MODE + +In non-blocking server I/O mode, the application is required +to have an event loop which calls \f3gl_get_line()\f1 +whenever the terminal file descriptor can do the type I/O +that \f3gl_get_line()\f1 is waiting for. To determine which +type of I/O \f3gl_get_line()\f1 is waiting for, the +application calls the \f3gl_pending_io()\f1 function. + +.sp +.nf + GlPendingIO gl_pending_io(GetLine *gl); +.fi +.sp + +The return value of this function is one of the following two +enumerated values. + +.sp +.nf + GLP_READ - gl_get_line() is waiting to write a + character to the terminal. + + GLP_WRITE - gl_get_line() is waiting to read a + character from the keyboad. +.fi +.sp + +If the application is using either the \f3select()\f1 or \f3poll()\f1 +system calls to watch for I/O on a group of file descriptors, then it +should call the \f3gl_pending_io()\f1 function before each call to +these functions to see which direction of I/O it should tell them to +watch for, and configure their arguments accordingly. In the case of +the \f3select()\f1 system call, this means using the \f3FD_SET()\f1 +macro to add the terminal file descriptor either to the set of file +descriptors to be watched for readability, or the set to be watched +for writability. + +As in normal I/O mode, the return value of \f3gl_get_line()\f1 is +either a pointer to a completed input line, or \f3NULL\f1. However, +whereas in normal I/O mode a \f3NULL\f1 return value always means that +an error occurred, in non-blocking server mode, \f3NULL\f1 is also +returned when \f3gl_get_line()\f1 can't read or write to the terminal +without blocking. Thus in non-blocking server mode, in order to +determine when a \f3NULL\f1 return value signifies that an error +occurred or not, it is necessary to call the \f3gl_return_status()\f1 +function. If this function returns the enumerated value, +\f3GLR_BLOCKED\f1, as documented in the \f3gl_get_line(@FUNC_MANEXT@)\f1 man page, +this means that \f3gl_get_line()\f1 is waiting for I/O, and no error +has occurred. + +When \f3gl_get_line()\f1 returns \f3NULL\f1 and +\f3gl_return_status()\f1 indicates that this is due to blocked +terminal I/O, the application should call \f3gl_get_line()\f1 again +when the type of I/O reported by \f3gl_pending_io()\f1 becomes +possible. The \f3prompt\f1, \f3start_line\f1 and \f3start_pos\f1 +arguments of \f3gl_get_line()\f1 will be ignored on these calls. If +you need to change the prompt of the line that is currently being +edited, then you can call the \f3gl_replace_prompt()\f1 function +(documented in the \f3gl_get_line(@FUNC_MANEXT@) man page) between calls to +\f3gl_get_line()\f1. + +.SH GIVING UP THE TERMINAL + +A complication that is unique to non-blocking server mode is that it +requires that the terminal be left in raw mode between calls to +\f3gl_get_line()\f1. If this weren't the case, the external event loop +wouldn't be able to detect individual key-presses, and the basic line +editing implemented by the terminal driver would clash with the +editing provided by \f3gl_get_line()\f1. What this means is that any +time that the terminal needs to be used for other things than entering +a new input line with \f3gl_get_line()\f1, it needs to be restored to +a usable state. In particular, whenever the process is suspended or +terminated, the terminal must be returned to a normal state. If this +isn't done, then depending on the characteristics of the shell that +was used to invoke the program, the user may end up with a hung +terminal. To this end, the \f3gl_normal_io()\f1 function is provided +for switching the terminal back to the state that it was in when raw +mode was last established. + +.sp +.nf + int gl_normal_io(GetLine *gl); +.fi +.sp + +What this function does is first flush any pending output to the +terminal, then move the cursor to the start of the terminal line which +follows the end of the incompletely entered input line. At this point +it is safe to suspend or terminate the process, and it is safe for the +application to read and write to the terminal. To resume entry of the +input line, the application should call the \f3gl_raw_io()\f1 +function. + +.sp +.nf + int gl_raw_io(GetLine *gl); +.fi +.sp + +This function starts a new line, redisplays the partially completed +input line (if any), restores the cursor position within this line to +where it was when \f3gl_normal_io()\f1 was called, then switches back +to raw, non-blocking terminal mode ready to continue entry of the +input line when \f3gl_get_line()\f1 is next called. + +Note that in non-blocking server mode, if \f3gl_get_line()\f1 is +called after a call to \f3gl_normal_io()\f1, without an intervening +call to \f3gl_raw_io()\f1, \f3gl_get_line()\f1 will call +\f3gl_raw_mode()\f1 itself, and the terminal will remain in this mode +when \f3gl_get_line()\f1 returns. + +.SH SIGNAL HANDLING + +In the previous section it was pointed out that in non-blocking server +mode, the terminal must be restored to a sane state whenever a signal +is received that either suspends or terminates the process. In normal +I/O mode, this is done for you by \f3gl_get_line()\f1, but in +non-blocking server mode, since the terminal is left in raw mode +between calls to \f3gl_get_line()\f1, this signal handling has to be +done by the application. Since there are many signals that can suspend +or terminate a process, as well as other signals that are important to +\f3gl_get_line()\f1, such as the \f3SIGWINCH\f1 signal, which tells it +when the terminal size has changed, the \f3gl_tty_signals()\f1 +function is provided for installing signal handlers for all pertinent +signals. + +.sp +.nf + int gl_tty_signals(void (*term_handler)(int), + void (*susp_handler)(int), + void (*cont_handler)(int), + void (*size_handler)(int)); +.fi +.sp + +What this does is use \f3gl_get_line()\f1's internal list of signals +to assign specified signal handlers to groups of signals. The +arguments of this function are as follows. + +.sp +.nf + term_handler - This is the signal handler that is to be + used to trap signals that by default + terminate any process that receives + them (eg. SIGINT or SIGTERM). + + susp_handler - This is the signal handler that is to be + used to trap signals that by default + suspend any process that receives them, + (eg. SIGTSTP or SIGTTOU). + + cont_handler - This is the signal handler that is to be + used to trap signals that are usually + sent when a process resumes after being + suspended (usually SIGCONT). Beware that there is + nothing to stop a user from sending one of these + signals at other times. + + size_handler - This signal handler is used to trap + signals that are sent to processes when + their controlling terminals are resized + by the user (eg. SIGWINCH). +.fi +.sp + +These arguments can all be the same, if so desired, and you can +specify \f3SIG_IGN\f1 (ignore this signal) or \f3SIG_DFL\f1 (use the +system-provided default signal handler) instead of a function where +pertinent. In particular, it is rarely useful to trap \f3SIGCONT\f1, +so the \f3cont_handler\f1 argument will usually be \f3SIG_DFL\f1 or +\f3SIG_IGN\f1. + +The \f3gl_tty_signals()\f1 function uses the POSIX \f3sigaction()\f1 +function to install these signal handlers, and it is careful to use +the \f3sa_mask\f1 member of each sigaction structure to ensure that +only one of these signals is ever delivered at a time. This guards +against different instances of these signal handlers from +simultaneously trying to write to common global data, such as a shared +\f3sigsetjmp()\f1 buffer or a signal-received flag. + +The signal handlers that are installed by this function, should call +the \f3gl_handle_signal(). + +.sp +.nf + void gl_handle_signal(int signo, GetLine *gl, int ngl); +.fi +.sp + +The \f3signo\f1 argument tells this function which signal it is being +asked to respond to, and the \f3gl\f1 argument should be a pointer to +the first element of an array of \f3ngl\f1 \f3GetLine\f1 objects. If +your application only has one of these objects, just pass its pointer +as the \f3gl\f1 argument and specify \f3ngl\f1 as \f31\f1. + +Depending on the signal that is being handled, this function does +different things. + +.SS Terminal resize signals (SIGWINCH) + +If the signal indicates that the terminal was resized, then it +arranges for the next call to \f3gl_get_line()\f1 to ask the terminal +for its new size and redraw the input line accordingly. In order that +\f3gl_get_line()\f1 be called as soon as possible to do this, +\f3gl_handle_signal()\f1 also arranges that the next call to +\f3gl_pending_io()\f1 will return \f3GLP_WRITE\f1. Thus if the +application waits for I/O in \f3select()\f1 or \f3poll()\f1, then the +application needs to ensure that these functions will be reliably +aborted when a signal is caught and handled by the application. More +on this below. + +.SH Process termination signals. + +If the signal that was caught is one of those that by default +terminates any process that receives it, then \f3gl_handle_signal()\f1 +does the following steps. + +1. First it blocks the delivery of all signals that can be + blocked (ie. \f3SIGKILL\f1 and \f3SIGSTOP\f1 can't be blocked) + +2. Next it calls \f3gl_normal_io()\f1 for each of the \f3ngl\f1 + \f3GetLine\f1 objects. Note that this does nothing to any of the + \f3GetLine\f1 objects that aren't currently in raw mode. + +3. Next it sets the signal handler of the signal to its default, + process-termination disposition. + +4. Next it re-sends the process the signal that was caught. + +5. Finally it unblocks delivery of this signal, which + results in the process being terminated. + +.SH Process suspension signals. + +If the default disposition of the signal is to suspend the process, +the same steps are executed as for process termination signals, except +that when the process is later resumed, \f3gl_handle_signal()\f1 +continues, and does the following steps. + +6. It re-blocks delivery of the signal. + +7. It reinstates the signal handler of the signal to the one + that was displaced when its default disposition was substituted. + +8. For any of the \f3GetLine\f1 objects that were in raw mode when + \f3gl_handle_signal()\f1 was called, \f3gl_handle_signal()\f1 then + calls \f3gl_raw_io()\f1, to resume entry of the input lines on + those terminals. + +9. Finally, it restores the signal process mask to how it + was when \f3gl_handle_signal()\f1 was called. + +Note that the process is suspended or terminated using the original +signal that was caught, rather than using the uncatchable +\f3SIGSTOP\f1 and \f3SIGKILL\f1 signals. This is important, because +when a process is suspended or terminated, the parent of the process +may wish to use the status value returned by the \f3wait()\f1 system +call to figure out which signal was responsible. In particular, most +shells use this information to print a corresponding message to the +terminal. Users would be rightly confused if when their process +received a \f3SIGPIPE\f1 signal, the program responded by sending +itself a \f3SIGKILL\f1 signal, and the shell then printed out the +provocative statement, "Killed!". + +.SH INTERRUPTING THE EVENT LOOP + +If a signal is caught and handled when the application's event loop is +waiting in \f3select()\f1 or \f3poll()\f1, these functions will be +aborted with \f3errno\f1 set to \f3EINTR\f1. When this happens the +event loop should call \f3gl_pending_io()\f1, before calling +\f3select()\f1 or \f3poll()\f1 again. It should then arrange for +\f3select()\f1 or \f3poll()\f1 to wait for the type of I/O that this +reports. This is necessary, because any signal handler which calls +\f3gl_handle_signal()\f1, will frequently change the type of I/O that +\f3gl_get_line()\f1 is waiting for. + +Unfortunately, if a signal arrives between the statements which +configure the arguments of \f3select()\f1 or \f3poll()\f1 and the +calls to these functions, then the signal will not be seen by these +functions, which will then not be aborted. If these functions are +waiting for keyboard input from the user when the signal is received, +and the signal handler arranges to redraw the input line to accomodate +a terminal resize or the resumption of the process, then this +redisplay will be end up being delayed until the user hits the next +key. Apart from puzzling the user, this clearly isn't a serious +problem. However there is a way, albeit complicated, to completely +avoid this race condition. The following steps illustrate this. + +1. Block all of the signals that \f3gl_get_line()\f1 catches, + by passing the signal set returned by \f3gl_list_signals()\f1 to + \f3sigprocmask()\f1. + +2. Call \f3gl_pending_io()\f1 and set up the arguments of + \f3select()\f1 or \f3poll()\f1 accordingly. + +3. Call \f3sigsetjmp()\f1 with a non-zero \f3savesigs\f1 argument. + +4. Initially this \f3sigsetjmp()\f1 statement will return zero, + indicating that control isn't resuming there after a matching + call to \f3siglongjmp()\f1. + +5. Replace all of the handlers of the signals that \f3gl_get_line()\f1 + is configured to catch, with a signal handler that first records + the number of the signal that was caught, in a file-scope variable, + then calls \f3siglongjmp()\f1 with a non-zero value argument, to + return execution to the above \f3sigsetjmp()\f1 + statement. Registering these signal handlers can conveniently be + done using the \f3gl_tty_signals()\f1 function. + +6. Set the file-scope variable that the above signal handler uses to + record any signal that is caught to -1, so that we can check + whether a signal was caught by seeing if it contains a valid signal + number. + +7. Now unblock the signals that were blocked in step 1. Any signal + that was received by the process in between step 1 and now will + now be delivered, and trigger our signal handler, as will any + signal that is received until we block these signals again. + +8. Now call \f3select()\f1 or \f3poll()\f1. + +9. When \f3select()\f1 returns, again block the signals that were + unblocked in step 7. + +If a signal is arrived any time during the above steps, our signal +handler will be triggered and cause control to return to the +\f3sigsetjmp()\f1 statement, where this time, \f3sigsetjmp()\f1 will +return non-zero, indicating that a signal was caught. When this +happens we simply skip the above block of statements, and continue +with the following statements, which are executed regardless of +whether or not a signal is caught. Note that when \f3sigsetjmp()\f1 +returns, regardless of why it returned, the process signal mask is +returned to how it was when \f3sigsetjmp()\f1 was called. Thus the +following statements are always executed with all of our signals +blocked. + +9. Reinstate the signal handlers that were displaced in step 5. + +10. Check wether a signal was caught, by checking the file-scope + variable that the signal handler records signal numbers in. + +11. If a signal was caught, send this signal to the application + again, and unblock just this signal, so that it invokes the + signal handler which we just reinstated in step 10. + +12. Unblock all of the signals that were blocked in step 7. + +Since this is complicated, note that \f3demo3.c\f1 includes a working +example of how to do this. The method used there however, is more +general than the above. What it provides is a wrapper function around +\f3select()\f1 which encompasses steps 3 to 11. In this wrapper, +rather than use \f3gl_list_signals()\f1 to figure out the signals to +block, and and \f3gl_tty_signals()\f1 to assign and revert signal +handlers, one of its arguments is a \f3sigset_t\f1 which specifies +which signals to block and assign signal handlers to. This function +thus doesn't depend on \f3gl_get_line()\f1 and can thus be used in +other situations where race-condition-free signal handling is +required. + +.SH SIGNALS CAUGHT BY GL_GET_LINE + +Since the application is expected to handle signals in non-blocking +server mode, \f3gl_get_line()\f1 doesn't attempt to duplicate this +when it is being called. If one of the signals that it is configured +to catch is sent to the application while \f3gl_get_line()\f1 is being +called, \f3gl_get_line()\f1 reinstates the caller's signal handlers, +then just before returning, re-sends the signal to the process to let +the application's signal handler handle it. If the process isn't +terminated by this signal, \f3gl_get_line()\f1 returns \f3NULL\f1, and +a following call to \f3gl_return_status()\f1 returns the enumerated +value \f3GLR_SIGNAL\f1. + +.SH ABORTING LINE INPUT + +Often, rather than letting it terminate the process, applications +respond to the SIGINT user-interrupt signal by aborting the current +input line. The way to do this in non-blocking server-I/O mode is to +not call \f3gl_handle_signal()\f1 when this signal is caught, but +instead to call the \f3gl_abandon_line()\f1. + +.sp +.nf + void gl_abandon_line(GetLine *gl); +.fi +.sp + +This function arranges that when \f3gl_get_line()\f1 is next called, +it first flushes any pending output to the terminal, then discardes +the current input line, outputs a new prompt on the next line, and +finally starts accepting input of a new input line from the user. + +.SH SIGNAL SAFE FUNCTIONS + +Provided that certain rules are followed, the following functions can +have been written to be safely callable from signal handlers. Other +functions in this library should not be called from signal handlers. + +.sp +.nf + gl_normal_io() + gl_raw_io() + gl_handle_signal() + gl_abandon_line() +.fi +.sp + +In order for this to be true, all signal handlers that call these +functions must be registered in such a way that only one instance of +any one of them can be running at one time. The way to do this is to +use the POSIX \f3sigaction()\f1 function to register all signal +handlers, and when doing this, use the \f3sa_mask\f1 member of the +corresponding sigaction structure, to indicate that all of the signals +who's handlers invoke the above functions, should be blocked when the +current signal is being handled. This prevents two signal handlers +from operating on a \f3GetLine\f1 object at the same time. + +To prevent signal handlers from accessing a \f3GetLine\f1 object while +\f3gl_get_line()\f1 or any of its associated public functions are +operating on it, all public functions associated with +\f3gl_get_line()\f1, including \f3gl_get_line()\f1 itself, temporarily +block the delivery of signals when they are accessing \f3GetLine\f1 +objects. Beware that the only signals that they block are the signals +that \f3gl_get_line()\f1 is currently configured to catch, so be sure +that if you call any of the above functions from signal handlers, that +the signals that these handlers are assigned to are configured to be +caught by \f3gl_get_line()\f1 (see \f3gl_trap_signal()\f1). + +.SH USING TIMEOUTS TO POLL + +If instead of using \f3select()\f1 or \f3poll()\f1 to wait for I/O, +your application just needs to get out of \f3gl_get_line()\f1 +periodically to briefly do something else before returning to accept +input from the user, this can be done in non-blocking server mode by +using the \f3gl_inactivity_timeout()\f1 function (see +\f3gl_get_line(@FUNC_MANEXT@)\f1), to specify that a callback function that +returns \f3GLTO_CONTINUE\f1 should be called whenever +\f3gl_get_line()\f1 has been waiting for I/O for more than a specified +amount of time. + +When this callback is triggered, \f3gl_get_line()\f1 will return +\f3NULL\f1, and a following call to \f3gl_return_status()\f1 will +return \f3GLR_BLOCKED\f1. + +Beware that \f3gl_get_line()\f1 won't return until the user +hasn't typed a key for the specified interval, so if the +interval is long, and the user keeps typing, +\f3gl_get_line()\f1 may not return for a while. In other +words there is no guarantee that it will return in the time +specified. + +.SH THE SERVER DEMO PROGRAM + +The \f3demo3\f1 program that is distributed with the library, provides +a working example of how to use non-blocking server I/O mode in a real +program. As far as the user is concerned, this program operates +identically to the main demo program (called \f3demo\f1), except that +whereas the main demo program uses the normal blocking I/O mode, +\f3demo3\f1 using non-blocking I/O and an external event loop. The +source code can be found in \f3demo3.c\f1, and the comments therein +explain the various steps. + +.SH FILES +.nf +libtecla.a - The tecla library +libtecla.h - The tecla header file. +.fi + +.SH SEE ALSO + +.nf +libtecla(@LIBR_MANEXT@), gl_get_line(@FUNC_MANEXT@), tecla(@MISC_MANEXT@), ef_expand_file(@FUNC_MANEXT@), +cpl_complete_word(@FUNC_MANEXT@), pca_lookup_file(@FUNC_MANEXT@) +.fi + +.SH AUTHOR +Martin Shepherd (mcs@astro.caltech.edu) diff --git a/libtecla-1.6.1/man/func/gl_last_signal.in b/libtecla-1.6.1/man/func/gl_last_signal.in new file mode 100644 index 0000000..6e46fc6 --- /dev/null +++ b/libtecla-1.6.1/man/func/gl_last_signal.in @@ -0,0 +1 @@ +.so @FUNC_MANDIR@/gl_get_line.@FUNC_MANEXT@ diff --git a/libtecla-1.6.1/man/func/gl_limit_history.in b/libtecla-1.6.1/man/func/gl_limit_history.in new file mode 100644 index 0000000..6e46fc6 --- /dev/null +++ b/libtecla-1.6.1/man/func/gl_limit_history.in @@ -0,0 +1 @@ +.so @FUNC_MANDIR@/gl_get_line.@FUNC_MANEXT@ diff --git a/libtecla-1.6.1/man/func/gl_list_signals.in b/libtecla-1.6.1/man/func/gl_list_signals.in new file mode 100644 index 0000000..6e46fc6 --- /dev/null +++ b/libtecla-1.6.1/man/func/gl_list_signals.in @@ -0,0 +1 @@ +.so @FUNC_MANDIR@/gl_get_line.@FUNC_MANEXT@ diff --git a/libtecla-1.6.1/man/func/gl_load_history.in b/libtecla-1.6.1/man/func/gl_load_history.in new file mode 100644 index 0000000..6e46fc6 --- /dev/null +++ b/libtecla-1.6.1/man/func/gl_load_history.in @@ -0,0 +1 @@ +.so @FUNC_MANDIR@/gl_get_line.@FUNC_MANEXT@ diff --git a/libtecla-1.6.1/man/func/gl_lookup_history.in b/libtecla-1.6.1/man/func/gl_lookup_history.in new file mode 100644 index 0000000..6e46fc6 --- /dev/null +++ b/libtecla-1.6.1/man/func/gl_lookup_history.in @@ -0,0 +1 @@ +.so @FUNC_MANDIR@/gl_get_line.@FUNC_MANEXT@ diff --git a/libtecla-1.6.1/man/func/gl_normal_io.in b/libtecla-1.6.1/man/func/gl_normal_io.in new file mode 100644 index 0000000..24798bc --- /dev/null +++ b/libtecla-1.6.1/man/func/gl_normal_io.in @@ -0,0 +1 @@ +.so @FUNC_MANDIR@/gl_io_mode.@FUNC_MANEXT@ diff --git a/libtecla-1.6.1/man/func/gl_pending_io.in b/libtecla-1.6.1/man/func/gl_pending_io.in new file mode 100644 index 0000000..24798bc --- /dev/null +++ b/libtecla-1.6.1/man/func/gl_pending_io.in @@ -0,0 +1 @@ +.so @FUNC_MANDIR@/gl_io_mode.@FUNC_MANEXT@ diff --git a/libtecla-1.6.1/man/func/gl_prompt_style.in b/libtecla-1.6.1/man/func/gl_prompt_style.in new file mode 100644 index 0000000..6e46fc6 --- /dev/null +++ b/libtecla-1.6.1/man/func/gl_prompt_style.in @@ -0,0 +1 @@ +.so @FUNC_MANDIR@/gl_get_line.@FUNC_MANEXT@ diff --git a/libtecla-1.6.1/man/func/gl_query_char.in b/libtecla-1.6.1/man/func/gl_query_char.in new file mode 100644 index 0000000..6e46fc6 --- /dev/null +++ b/libtecla-1.6.1/man/func/gl_query_char.in @@ -0,0 +1 @@ +.so @FUNC_MANDIR@/gl_get_line.@FUNC_MANEXT@ diff --git a/libtecla-1.6.1/man/func/gl_range_of_history.in b/libtecla-1.6.1/man/func/gl_range_of_history.in new file mode 100644 index 0000000..6e46fc6 --- /dev/null +++ b/libtecla-1.6.1/man/func/gl_range_of_history.in @@ -0,0 +1 @@ +.so @FUNC_MANDIR@/gl_get_line.@FUNC_MANEXT@ diff --git a/libtecla-1.6.1/man/func/gl_raw_io.in b/libtecla-1.6.1/man/func/gl_raw_io.in new file mode 100644 index 0000000..24798bc --- /dev/null +++ b/libtecla-1.6.1/man/func/gl_raw_io.in @@ -0,0 +1 @@ +.so @FUNC_MANDIR@/gl_io_mode.@FUNC_MANEXT@ diff --git a/libtecla-1.6.1/man/func/gl_read_char.in b/libtecla-1.6.1/man/func/gl_read_char.in new file mode 100644 index 0000000..6e46fc6 --- /dev/null +++ b/libtecla-1.6.1/man/func/gl_read_char.in @@ -0,0 +1 @@ +.so @FUNC_MANDIR@/gl_get_line.@FUNC_MANEXT@ diff --git a/libtecla-1.6.1/man/func/gl_register_action.in b/libtecla-1.6.1/man/func/gl_register_action.in new file mode 100644 index 0000000..6e46fc6 --- /dev/null +++ b/libtecla-1.6.1/man/func/gl_register_action.in @@ -0,0 +1 @@ +.so @FUNC_MANDIR@/gl_get_line.@FUNC_MANEXT@ diff --git a/libtecla-1.6.1/man/func/gl_resize_history.in b/libtecla-1.6.1/man/func/gl_resize_history.in new file mode 100644 index 0000000..6e46fc6 --- /dev/null +++ b/libtecla-1.6.1/man/func/gl_resize_history.in @@ -0,0 +1 @@ +.so @FUNC_MANDIR@/gl_get_line.@FUNC_MANEXT@ diff --git a/libtecla-1.6.1/man/func/gl_return_status.in b/libtecla-1.6.1/man/func/gl_return_status.in new file mode 100644 index 0000000..6e46fc6 --- /dev/null +++ b/libtecla-1.6.1/man/func/gl_return_status.in @@ -0,0 +1 @@ +.so @FUNC_MANDIR@/gl_get_line.@FUNC_MANEXT@ diff --git a/libtecla-1.6.1/man/func/gl_save_history.in b/libtecla-1.6.1/man/func/gl_save_history.in new file mode 100644 index 0000000..6e46fc6 --- /dev/null +++ b/libtecla-1.6.1/man/func/gl_save_history.in @@ -0,0 +1 @@ +.so @FUNC_MANDIR@/gl_get_line.@FUNC_MANEXT@ diff --git a/libtecla-1.6.1/man/func/gl_set_term_size.in b/libtecla-1.6.1/man/func/gl_set_term_size.in new file mode 100644 index 0000000..6e46fc6 --- /dev/null +++ b/libtecla-1.6.1/man/func/gl_set_term_size.in @@ -0,0 +1 @@ +.so @FUNC_MANDIR@/gl_get_line.@FUNC_MANEXT@ diff --git a/libtecla-1.6.1/man/func/gl_show_history.in b/libtecla-1.6.1/man/func/gl_show_history.in new file mode 100644 index 0000000..6e46fc6 --- /dev/null +++ b/libtecla-1.6.1/man/func/gl_show_history.in @@ -0,0 +1 @@ +.so @FUNC_MANDIR@/gl_get_line.@FUNC_MANEXT@ diff --git a/libtecla-1.6.1/man/func/gl_size_of_history.in b/libtecla-1.6.1/man/func/gl_size_of_history.in new file mode 100644 index 0000000..6e46fc6 --- /dev/null +++ b/libtecla-1.6.1/man/func/gl_size_of_history.in @@ -0,0 +1 @@ +.so @FUNC_MANDIR@/gl_get_line.@FUNC_MANEXT@ diff --git a/libtecla-1.6.1/man/func/gl_state_of_history.in b/libtecla-1.6.1/man/func/gl_state_of_history.in new file mode 100644 index 0000000..6e46fc6 --- /dev/null +++ b/libtecla-1.6.1/man/func/gl_state_of_history.in @@ -0,0 +1 @@ +.so @FUNC_MANDIR@/gl_get_line.@FUNC_MANEXT@ diff --git a/libtecla-1.6.1/man/func/gl_terminal_size.in b/libtecla-1.6.1/man/func/gl_terminal_size.in new file mode 100644 index 0000000..6e46fc6 --- /dev/null +++ b/libtecla-1.6.1/man/func/gl_terminal_size.in @@ -0,0 +1 @@ +.so @FUNC_MANDIR@/gl_get_line.@FUNC_MANEXT@ diff --git a/libtecla-1.6.1/man/func/gl_toggle_history.in b/libtecla-1.6.1/man/func/gl_toggle_history.in new file mode 100644 index 0000000..6e46fc6 --- /dev/null +++ b/libtecla-1.6.1/man/func/gl_toggle_history.in @@ -0,0 +1 @@ +.so @FUNC_MANDIR@/gl_get_line.@FUNC_MANEXT@ diff --git a/libtecla-1.6.1/man/func/gl_trap_signal.in b/libtecla-1.6.1/man/func/gl_trap_signal.in new file mode 100644 index 0000000..6e46fc6 --- /dev/null +++ b/libtecla-1.6.1/man/func/gl_trap_signal.in @@ -0,0 +1 @@ +.so @FUNC_MANDIR@/gl_get_line.@FUNC_MANEXT@ diff --git a/libtecla-1.6.1/man/func/gl_tty_signals.in b/libtecla-1.6.1/man/func/gl_tty_signals.in new file mode 100644 index 0000000..24798bc --- /dev/null +++ b/libtecla-1.6.1/man/func/gl_tty_signals.in @@ -0,0 +1 @@ +.so @FUNC_MANDIR@/gl_io_mode.@FUNC_MANEXT@ diff --git a/libtecla-1.6.1/man/func/gl_watch_fd.in b/libtecla-1.6.1/man/func/gl_watch_fd.in new file mode 100644 index 0000000..6e46fc6 --- /dev/null +++ b/libtecla-1.6.1/man/func/gl_watch_fd.in @@ -0,0 +1 @@ +.so @FUNC_MANDIR@/gl_get_line.@FUNC_MANEXT@ diff --git a/libtecla-1.6.1/man/func/libtecla_version.in b/libtecla-1.6.1/man/func/libtecla_version.in new file mode 100644 index 0000000..31867c4 --- /dev/null +++ b/libtecla-1.6.1/man/func/libtecla_version.in @@ -0,0 +1 @@ +.so @LIBR_MANDIR@/libtecla.@LIBR_MANEXT@ diff --git a/libtecla-1.6.1/man/func/new_CplFileConf.in b/libtecla-1.6.1/man/func/new_CplFileConf.in new file mode 100644 index 0000000..734f281 --- /dev/null +++ b/libtecla-1.6.1/man/func/new_CplFileConf.in @@ -0,0 +1 @@ +.so @FUNC_MANDIR@/cpl_complete_word.@FUNC_MANEXT@ diff --git a/libtecla-1.6.1/man/func/new_ExpandFile.in b/libtecla-1.6.1/man/func/new_ExpandFile.in new file mode 100644 index 0000000..3d0a884 --- /dev/null +++ b/libtecla-1.6.1/man/func/new_ExpandFile.in @@ -0,0 +1 @@ +.so @FUNC_MANDIR@/ef_expand_file.@FUNC_MANEXT@ diff --git a/libtecla-1.6.1/man/func/new_GetLine.in b/libtecla-1.6.1/man/func/new_GetLine.in new file mode 100644 index 0000000..6e46fc6 --- /dev/null +++ b/libtecla-1.6.1/man/func/new_GetLine.in @@ -0,0 +1 @@ +.so @FUNC_MANDIR@/gl_get_line.@FUNC_MANEXT@ diff --git a/libtecla-1.6.1/man/func/new_PathCache.in b/libtecla-1.6.1/man/func/new_PathCache.in new file mode 100644 index 0000000..dbc4da7 --- /dev/null +++ b/libtecla-1.6.1/man/func/new_PathCache.in @@ -0,0 +1 @@ +.so @FUNC_MANDIR@/pca_lookup_file.@FUNC_MANEXT@ diff --git a/libtecla-1.6.1/man/func/new_PcaPathConf.in b/libtecla-1.6.1/man/func/new_PcaPathConf.in new file mode 100644 index 0000000..dbc4da7 --- /dev/null +++ b/libtecla-1.6.1/man/func/new_PcaPathConf.in @@ -0,0 +1 @@ +.so @FUNC_MANDIR@/pca_lookup_file.@FUNC_MANEXT@ diff --git a/libtecla-1.6.1/man/func/new_WordCompletion.in b/libtecla-1.6.1/man/func/new_WordCompletion.in new file mode 100644 index 0000000..734f281 --- /dev/null +++ b/libtecla-1.6.1/man/func/new_WordCompletion.in @@ -0,0 +1 @@ +.so @FUNC_MANDIR@/cpl_complete_word.@FUNC_MANEXT@ diff --git a/libtecla-1.6.1/man/func/pca_last_error.in b/libtecla-1.6.1/man/func/pca_last_error.in new file mode 100644 index 0000000..dbc4da7 --- /dev/null +++ b/libtecla-1.6.1/man/func/pca_last_error.in @@ -0,0 +1 @@ +.so @FUNC_MANDIR@/pca_lookup_file.@FUNC_MANEXT@ diff --git a/libtecla-1.6.1/man/func/pca_lookup_file.in b/libtecla-1.6.1/man/func/pca_lookup_file.in new file mode 100644 index 0000000..861d205 --- /dev/null +++ b/libtecla-1.6.1/man/func/pca_lookup_file.in @@ -0,0 +1,365 @@ +.\" Copyright (c) 2001, 2002, 2003, 2004 by Martin C. Shepherd +.\" +.\" All rights reserved. +.\" +.\" Permission is hereby granted, free of charge, to any person obtaining a +.\" copy of this software and associated documentation files (the +.\" "Software"), to deal in the Software without restriction, including +.\" without limitation the rights to use, copy, modify, merge, publish, +.\" distribute, and/or sell copies of the Software, and to permit persons +.\" to whom the Software is furnished to do so, provided that the above +.\" copyright notice(s) and this permission notice appear in all copies of +.\" the Software and that both the above copyright notice(s) and this +.\" permission notice appear in supporting documentation. +.\" +.\" THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +.\" OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +.\" MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT +.\" OF THIRD PARTY RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR +.\" HOLDERS INCLUDED IN THIS NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL +.\" INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING +.\" FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, +.\" NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION +.\" WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +.\" +.\" Except as contained in this notice, the name of a copyright holder +.\" shall not be used in advertising or otherwise to promote the sale, use +.\" or other dealings in this Software without prior written authorization +.\" of the copyright holder. +.TH pca_lookup_file @FUNC_MANEXT@ +.SH NAME +pca_lookup_file, del_PathCache, del_PcaPathConf, new_PathCache, new_PcaPathConf, pca_last_error, pca_path_completions, pca_scan_path, pca_set_check_fn, ppc_file_start, ppc_literal_escapes \- lookup a file in a list of directories +.SH SYNOPSIS +.nf +#include + +PathCache *new_PathCache(void); + +PathCache *del_PathCache(PathCache *pc); + +int pca_scan_path(PathCache *pc, const char *path); + +void pca_set_check_fn(PathCache *pc, CplCheckFn *check_fn, + void *data); + +char *pca_lookup_file(PathCache *pc, const char *name, + int name_len, int literal); + +const char *pca_last_error(PathCache *pc); + +CPL_MATCH_FN(pca_path_completions); + +.fi + +.SH DESCRIPTION + +The \f3PathCache\f1 object is part of the tecla library (see the +libtecla(@LIBR_MANEXT@) man page). +.sp +\f3PathCache\f1 objects allow an application to search for files in +any colon separated list of directories, such as the unix execution +PATH environment variable. Files in absolute directories are cached in +a \f3PathCache\f1 object, whereas relative directories are scanned as +needed. Using a \f3PathCache\f1 object, you can look up the full +pathname of a simple filename, or you can obtain a list of the +possible completions of a given filename prefix. By default all files +in the list of directories are targets for lookup and completion, but +a versatile mechanism is provided for only selecting specific types of +files. The obvious application of this facility is to provide +Tab-completion and lookup of executable commands in the unix PATH, so +an optional callback which rejects all but executable files, is +provided. +.sp +.SH AN EXAMPLE + +Under UNIX, the following example program looks up and displays the +full pathnames of each of the command names on the command line. +.sp +.nf + #include + #include + #include + + int main(int argc, char *argv[]) + { + int i; + /* + * Create a cache for executable files. + */ + PathCache *pc = new_PathCache(); + if(!pc) + exit(1); + /* + * Scan the user's PATH for executables. + */ + if(pca_scan_path(pc, getenv("PATH"))) { + fprintf(stderr, "%s\\n", pca_last_error(pc)); + exit(1); + } + /* + * Arrange to only report executable files. + */ + pca_set_check_fn(pc, cpl_check_exe, NULL); + /* + * Lookup and display the full pathname of each of the + * commands listed on the command line. + */ + for(i=1; i 1 is specified, + only that many of the most + recent lines are displayed. + See the "ENTERING REPEAT + COUNTS" section. + read-from-file - Temporarily switch to reading + input from the file who's + name precedes the cursor. + read-init-files - Re-read teclarc configuration + files. + beginning-of-history - Move to the oldest line in the + history list. Note that in vi + mode you are left in command + mode. + end-of-history - Move to the newest line in the + history list (ie. the current + line). Note that in vi mode + this leaves you in command + mode. + digit-argument - Enter a repeat count for the + next key-binding function. + For details, see the ENTERING + REPEAT COUNTS section. + newline - Terminate and return the + current contents of the + line, after appending a + newline character. The newline + character is normally '\\n', + but will be the first + character of the key-sequence + that invoked the newline + action, if this happens to be + a printable character. If the + action was invoked by the + '\\n' newline character or the + '\\r' carriage return + character, the line is + appended to the history + buffer. + repeat-history - Return the line that is being + edited, then arrange for the + next most recent entry in the + history buffer to be recalled + when Tecla is next called. + Repeatedly invoking this + action causes successive + historical input lines to be + re-executed. Note that this + action is equivalent to the + 'Operate' action in ksh. + ring-bell - Ring the terminal bell, unless + the bell has been silenced via + the \f3nobeep\f1 configuration + option (see the THE TECLA + CONFIGURATION FILE section). + forward-copy-char - Copy the next character into + the cut buffer (NB. use repeat + counts to copy more than one). + backward-copy-char - Copy the previous character + into the cut buffer. + forward-copy-word - Copy the next word into the cut + buffer. + backward-copy-word - Copy the previous word into the + cut buffer. + forward-find-char - Move the cursor to the next + occurrence of the next + character that you type. + backward-find-char - Move the cursor to the last + occurrence of the next + character that you type. + forward-to-char - Move the cursor to the + character just before the next + occurrence of the next + character that the user types. + backward-to-char - Move the cursor to the + character just after the last + occurrence before the cursor + of the next character that the + user types. + repeat-find-char - Repeat the last + backward-find-char, + forward-find-char, + backward-to-char or + forward-to-char. + invert-refind-char - Repeat the last + backward-find-char, + forward-find-char, + backward-to-char, or + forward-to-char in the + opposite direction. + delete-to-column - Delete the characters from the + cursor up to the column that + is specified by the repeat + count. + delete-to-parenthesis - Delete the characters from the + cursor up to and including + the matching parenthesis, or + next close parenthesis. + forward-delete-find - Delete the characters from the + cursor up to and including the + following occurence of the + next character typed. + backward-delete-find - Delete the characters from the + cursor up to and including the + preceding occurence of the + next character typed. + forward-delete-to - Delete the characters from the + cursor up to, but not + including, the following + occurence of the next + character typed. + backward-delete-to - Delete the characters from the + cursor up to, but not + including, the preceding + occurence of the next + character typed. + delete-refind - Repeat the last *-delete-find + or *-delete-to action. + delete-invert-refind - Repeat the last *-delete-find + or *-delete-to action, in the + opposite direction. + copy-to-column - Copy the characters from the + cursor up to the column that + is specified by the repeat + count, into the cut buffer. + copy-to-parenthesis - Copy the characters from the + cursor up to and including + the matching parenthesis, or + next close parenthesis, into + the cut buffer. + forward-copy-find - Copy the characters from the + cursor up to and including the + following occurence of the + next character typed, into the + cut buffer. + backward-copy-find - Copy the characters from the + cursor up to and including the + preceding occurence of the + next character typed, into the + cut buffer. + forward-copy-to - Copy the characters from the + cursor up to, but not + including, the following + occurence of the next + character typed, into the cut + buffer. + backward-copy-to - Copy the characters from the + cursor up to, but not + including, the preceding + occurence of the next + character typed, into the cut + buffer. + copy-refind - Repeat the last *-copy-find + or *-copy-to action. + copy-invert-refind - Repeat the last *-copy-find + or *-copy-to action, in the + opposite direction. + vi-mode - Switch to vi mode from emacs + mode. + emacs-mode - Switch to emacs mode from vi + mode. + vi-insert - From vi command mode, switch to + insert mode. + vi-overwrite - From vi command mode, switch to + overwrite mode. + vi-insert-at-bol - From vi command mode, move the + cursor to the start of the line + and switch to insert mode. + vi-append-at-eol - From vi command mode, move the + cursor to the end of the line + and switch to append mode. + vi-append - From vi command mode, move the + cursor one position right, and + switch to insert mode. + vi-replace-char - From vi command mode, replace + the character under the cursor + with the the next character + entered. + vi-forward-change-char - From vi command mode, delete + the next character then enter + insert mode. + vi-backward-change-char - From vi command mode, delete + the preceding character then + enter insert mode. + vi-forward-change-word - From vi command mode, delete + the next word then enter + insert mode. + vi-backward-change-word - From vi command mode, delete + the preceding word then + enter insert mode. + vi-change-rest-of-line - From vi command mode, delete + from the cursor to the end of + the line, then enter insert + mode. + vi-change-line - From vi command mode, delete + the current line, then enter + insert mode. + vi-change-to-bol - From vi command mode, delete + all characters between the + cursor and the beginning of + the line, then enter insert + mode. + vi-change-to-column - From vi command mode, delete + the characters from the cursor + up to the column that is + specified by the repeat count, + then enter insert mode. + vi-change-to-parenthesis - Delete the characters from the + cursor up to and including + the matching parenthesis, or + next close parenthesis, then + enter vi insert mode. + vi-forward-change-find - From vi command mode, delete + the characters from the + cursor up to and including the + following occurence of the + next character typed, then + enter insert mode. + vi-backward-change-find - From vi command mode, delete + the characters from the + cursor up to and including the + preceding occurence of the + next character typed, then + enter insert mode. + vi-forward-change-to - From vi command mode, delete + the characters from the + cursor up to, but not + including, the following + occurence of the next + character typed, then enter + insert mode. + vi-backward-change-to - From vi command mode, delete + the characters from the + cursor up to, but not + including, the preceding + occurence of the next + character typed, then enter + insert mode. + vi-change-refind - Repeat the last + vi-*-change-find or + vi-*-change-to action. + vi-change-invert-refind - Repeat the last + vi-*-change-find or + vi-*-change-to action, in the + opposite direction. + vi-undo - In vi mode, undo the last + editing operation. + vi-repeat-change - In vi command mode, repeat the + last command that modified the + line. +.fi + +.SH DEFAULT KEY BINDINGS IN EMACS MODE + +The following default key bindings, which can be overriden by +the Tecla configuration file, are designed to mimic most of +the bindings of the unix \f3tcsh\f1 shell, when it is in +emacs editing mode. +.sp +This is the default editing mode of the Tecla library. +.sp +Under UNIX the terminal driver sets a number of special keys for certain +functions. The tecla library attempts to use the same keybindings to maintain +consistency. The key sequences shown for the following 6 bindings are thus just +examples of what they will probably be set to. If you have used the \f3stty\f1 +command to change these keys, then the default bindings should match. + +.nf + ^C -> user-interrupt + ^\\ -> abort + ^Z -> suspend + ^Q -> start-output + ^S -> stop-output + ^V -> literal-next +.fi + +The cursor keys are refered to by name, as follows. This is necessary +because different types of terminals generate different key sequences +when their cursor keys are pressed. + + right -> cursor-right + left -> cursor-left + up -> up-history + down -> down-history + +The remaining bindings don't depend on the terminal setttings. + +.nf + ^F -> cursor-right + ^B -> cursor-left + M-i -> insert-mode + ^A -> beginning-of-line + ^E -> end-of-line + ^U -> delete-line + ^K -> kill-line + M-f -> forward-word + M-b -> backward-word + ^D -> del-char-or-list-or-eof + ^H -> backward-delete-char + ^? -> backward-delete-char + M-d -> forward-delete-word + M-^H -> backward-delete-word + M-^? -> backward-delete-word + M-u -> upcase-word + M-l -> downcase-word + M-c -> capitalize-word + ^R -> redisplay + ^L -> clear-screen + ^T -> transpose-chars + ^@ -> set-mark + ^X^X -> exchange-point-and-mark + ^W -> kill-region + M-w -> copy-region-as-kill + ^Y -> yank + ^P -> up-history + ^N -> down-history + M-p -> history-search-backward + M-n -> history-search-forward + ^I -> complete-word + ^X* -> expand-filename + ^X^F -> read-from-file + ^X^R -> read-init-files + ^Xg -> list-glob + ^Xh -> list-history + M-< -> beginning-of-history + M-> -> end-of-history + \\n -> newline + \\r -> newline + M-o -> repeat-history + M-^V -> vi-mode + + M-0, M-1, ... M-9 -> digit-argument (see below) +.fi + +Note that \f3^I\f1 is what the TAB key generates, and that \f3^@\f1 +can be generated not only by pressing the control key and the \f3@\f1 +key simultaneously, but also by pressing the control key and the space +bar at the same time. + +.SH DEFAULT KEY BINDINGS IN VI MODE + +The following default key bindings are designed to mimic the +vi style of editing as closely as possible. This means that +very few editing functions are provided in the initial +character input mode, editing functions instead being +provided by the vi command mode. Vi command mode is entered +whenever the escape character is pressed, or whenever a +key-sequence that starts with a meta character is entered. In +addition to mimicing vi, libtecla provides bindings for tab +completion, wild-card expansion of file names, and historical +line recall. +.sp +To learn how to tell the Tecla library to use vi mode instead +of the default emacs editing mode, see the earlier section entitled +THE TECLA CONFIGURATION FILE. +.sp +Under UNIX the terminal driver sets a number of special keys +for certain functions. The Tecla library attempts to use the +same keybindings to maintain consistency, binding them both +in input mode and in command mode. The key sequences shown +for the following 6 bindings are thus just examples of what +they will probably be set to. If you have used the \f3stty\f1 +command to change these keys, then the default bindings +should match. + +.nf + ^C -> user-interrupt + ^\\ -> abort + ^Z -> suspend + ^Q -> start-output + ^S -> stop-output + ^V -> literal-next + M-^C -> user-interrupt + M-^\\ -> abort + M-^Z -> suspend + M-^Q -> start-output + M-^S -> stop-output +.fi + +Note that above, most of the bindings are defined twice, once +as a raw control code like \f3^C\f1 and then a second time as +a meta character like \f3M-^C\f1. The former is the binding +for vi input mode, whereas the latter is the binding for vi +command mode. Once in command mode all key-sequences that the +user types that they don't explicitly start with an escape or +a meta key, have their first key secretly converted to a meta +character before the key sequence is looked up in the key +binding table. Thus, once in command mode, when you type the +letter \f3i\f1, for example, the Tecla library actually looks +up the binding for \f3M-i\f1. + +The cursor keys are refered to by name, as follows. This is necessary +because different types of terminals generate different key sequences +when their cursor keys are pressed. + + right -> cursor-right + left -> cursor-left + up -> up-history + down -> down-history + +The cursor keys normally generate a keysequence that start +with an escape character, so beware that using the arrow keys +will put you into command mode (if you aren't already in +command mode). +.sp +The following are the terminal-independent key bindings for vi input +mode. + +.nf + ^D -> list-or-eof + ^G -> list-glob + ^H -> backward-delete-char + ^I -> complete-word + \\r -> newline + \\n -> newline + ^L -> clear-screen + ^N -> down-history + ^P -> up-history + ^R -> redisplay + ^U -> backward-kill-line + ^W -> backward-delete-word + ^X* -> expand-filename + ^X^F -> read-from-file + ^X^R -> read-init-files + ^? -> backward-delete-char +.fi + +The following are the key bindings that are defined in vi +command mode, this being specified by them all starting with +a meta character. As mentioned above, once in command mode +the initial meta character is optional. For example, you +might enter command mode by typing Esc, and then press h +twice to move the cursor two positions to the left. Both h +characters get quietly converted to M-h before being compared +to the key-binding table, the first one because Escape +followed by a character is always converted to the equivalent +meta character, and the second because command mode was +already active. + +.nf + M-\\ -> cursor-right (Meta-space) + M-$ -> end-of-line + M-* -> expand-filename + M-+ -> down-history + M-- -> up-history + M-< -> beginning-of-history + M-> -> end-of-history + M-^ -> beginning-of-line + M-; -> repeat-find-char + M-, -> invert-refind-char + M-| -> goto-column + M-~ -> change-case + M-. -> vi-repeat-change + M-% -> find-parenthesis + M-a -> vi-append + M-A -> vi-append-at-eol + M-b -> backward-word + M-B -> backward-word + M-C -> vi-change-rest-of-line + M-cb -> vi-backward-change-word + M-cB -> vi-backward-change-word + M-cc -> vi-change-line + M-ce -> vi-forward-change-word + M-cE -> vi-forward-change-word + M-cw -> vi-forward-change-word + M-cW -> vi-forward-change-word + M-cF -> vi-backward-change-find + M-cf -> vi-forward-change-find + M-cT -> vi-backward-change-to + M-ct -> vi-forward-change-to + M-c; -> vi-change-refind + M-c, -> vi-change-invert-refind + M-ch -> vi-backward-change-char + M-c^H -> vi-backward-change-char + M-c^? -> vi-backward-change-char + M-cl -> vi-forward-change-char + M-c\\ -> vi-forward-change-char (Meta-c-space) + M-c^ -> vi-change-to-bol + M-c0 -> vi-change-to-bol + M-c$ -> vi-change-rest-of-line + M-c| -> vi-change-to-column + M-c% -> vi-change-to-parenthesis + M-dh -> backward-delete-char + M-d^H -> backward-delete-char + M-d^? -> backward-delete-char + M-dl -> forward-delete-char + M-d -> forward-delete-char (Meta-d-space) + M-dd -> delete-line + M-db -> backward-delete-word + M-dB -> backward-delete-word + M-de -> forward-delete-word + M-dE -> forward-delete-word + M-dw -> forward-delete-word + M-dW -> forward-delete-word + M-dF -> backward-delete-find + M-df -> forward-delete-find + M-dT -> backward-delete-to + M-dt -> forward-delete-to + M-d; -> delete-refind + M-d, -> delete-invert-refind + M-d^ -> backward-kill-line + M-d0 -> backward-kill-line + M-d$ -> kill-line + M-D -> kill-line + M-d| -> delete-to-column + M-d% -> delete-to-parenthesis + M-e -> forward-word + M-E -> forward-word + M-f -> forward-find-char + M-F -> backward-find-char + M-- -> up-history + M-h -> cursor-left + M-H -> beginning-of-history + M-i -> vi-insert + M-I -> vi-insert-at-bol + M-j -> down-history + M-J -> history-search-forward + M-k -> up-history + M-K -> history-search-backward + M-l -> cursor-right + M-L -> end-of-history + M-n -> history-re-search-forward + M-N -> history-re-search-backward + M-p -> append-yank + M-P -> yank + M-r -> vi-replace-char + M-R -> vi-overwrite + M-s -> vi-forward-change-char + M-S -> vi-change-line + M-t -> forward-to-char + M-T -> backward-to-char + M-u -> vi-undo + M-w -> forward-to-word + M-W -> forward-to-word + M-x -> forward-delete-char + M-X -> backward-delete-char + M-yh -> backward-copy-char + M-y^H -> backward-copy-char + M-y^? -> backward-copy-char + M-yl -> forward-copy-char + M-y\\ -> forward-copy-char (Meta-y-space) + M-ye -> forward-copy-word + M-yE -> forward-copy-word + M-yw -> forward-copy-word + M-yW -> forward-copy-word + M-yb -> backward-copy-word + M-yB -> backward-copy-word + M-yf -> forward-copy-find + M-yF -> backward-copy-find + M-yt -> forward-copy-to + M-yT -> backward-copy-to + M-y; -> copy-refind + M-y, -> copy-invert-refind + M-y^ -> copy-to-bol + M-y0 -> copy-to-bol + M-y$ -> copy-rest-of-line + M-yy -> copy-line + M-Y -> copy-line + M-y| -> copy-to-column + M-y% -> copy-to-parenthesis + M-^E -> emacs-mode + M-^H -> cursor-left + M-^? -> cursor-left + M-^L -> clear-screen + M-^N -> down-history + M-^P -> up-history + M-^R -> redisplay + M-^D -> list-or-eof + M-^I -> complete-word + M-\\r -> newline + M-\\n -> newline + M-^X^R -> read-init-files + M-^Xh -> list-history + + M-0, M-1, ... M-9 -> digit-argument (see below) +.fi + +Note that \f3^I\f1 is what the TAB key generates. + +.SH ENTERING REPEAT COUNTS + +Many of the key binding functions described previously, take an +optional count, typed in before the target keysequence. This is +interpreted as a repeat count by most bindings. A notable exception is +the goto-column binding, which interprets the count as a column +number. +.sp +By default you can specify this count argument by pressing the meta +key while typing in the numeric count. This relies on the +\f3digit-argument\f1 action being bound to Meta-0, Meta-1 etc. Once +any one of these bindings has been activated, you can optionally take +your finger off the meta key to type in the rest of the number, since +every numeric digit thereafter is treated as part of the number, +unless it is preceded by the \f3literal-next\f1 binding. As soon as a +non-digit, or literal digit key is pressed the repeat count is +terminated and either causes the just typed character to be added to +the line that many times, or causes the next key-binding function to +be given that argument. +.sp +For example, in emacs mode, typing: +.sp +.nf + M-12a +.fi +.sp +causes the letter 'a' to be added to the line 12 times, +whereas +.sp +.nf + M-4M-c +.fi +.sp +Capitalizes the next 4 words. +.sp +In vi command mode the Meta modifier is automatically added to all +characters typed in, so to enter a count in vi command-mode, just +involves typing in the number, just as it does in the vi editor +itself. So for example, in vi command mode, typing: +.sp +.nf + 4w2x +.fi +.sp +moves the cursor four words to the right, then deletes two characters. +.sp +You can also bind \f3digit-argument\f1 to other key sequences. If +these end in a numeric digit, that digit gets appended to the current +repeat count. If it doesn't end in a numeric digit, a new repeat count +is started with a value of zero, and can be completed by typing in the +number, after letting go of the key which triggered the digit-argument +action. + +.SH FILES +.nf +libtecla.a - The Tecla library +libtecla.h - The Tecla header file. +~/.teclarc - The personal Tecla customization file. +.fi + +.SH SEE ALSO + +.nf +libtecla(@LIBR_MANEXT@), gl_get_line(@LIBR_MANEXT@), gl_io_mode(@LIBR_MANEXT@), ef_expand_file(@LIBR_MANEXT@), +cpl_complete_word(@LIBR_MANEXT@), pca_lookup_file(@LIBR_MANEXT@) +.fi + +.SH AUTHOR +Martin Shepherd (mcs@astro.caltech.edu) diff --git a/libtecla-1.6.1/man/prog/enhance.in b/libtecla-1.6.1/man/prog/enhance.in new file mode 100644 index 0000000..a5c51a6 --- /dev/null +++ b/libtecla-1.6.1/man/prog/enhance.in @@ -0,0 +1,89 @@ +.\" Copyright (c) 2000, 2001, 2002, 2003, 2004 by Martin C. Shepherd +.\" +.\" All rights reserved. +.\" +.\" Permission is hereby granted, free of charge, to any person obtaining a +.\" copy of this software and associated documentation files (the +.\" "Software"), to deal in the Software without restriction, including +.\" without limitation the rights to use, copy, modify, merge, publish, +.\" distribute, and/or sell copies of the Software, and to permit persons +.\" to whom the Software is furnished to do so, provided that the above +.\" copyright notice(s) and this permission notice appear in all copies of +.\" the Software and that both the above copyright notice(s) and this +.\" permission notice appear in supporting documentation. +.\" +.\" THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +.\" OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +.\" MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT +.\" OF THIRD PARTY RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR +.\" HOLDERS INCLUDED IN THIS NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL +.\" INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING +.\" FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, +.\" NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION +.\" WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +.\" +.\" Except as contained in this notice, the name of a copyright holder +.\" shall not be used in advertising or otherwise to promote the sale, use +.\" or other dealings in this Software without prior written authorization +.\" of the copyright holder. +.TH enhance @PROG_MANEXT@ +.SH NAME +enhance - A program that adds command-line editing to third party programs. +.SH SYNOPSIS +.nf +enhance command [ argument ... ] +.fi + +.SH DESCRIPTION + +The \f3enhance\f1 program provides enhanced command-line editing +facilities to users of third party applications, to which one doesn't +have any source code. It does this by placing a pseudo-terminal +between the application and the real terminal. It uses the tecla +command-line editing library to read input from the real terminal, +then forwards each just completed input line to the application via +the pseudo-terminal. All output from the application is forwarded +back unchanged to the real terminal. +.sp +Whenever the application stops generating output for more than a tenth +of a second, the \f3enhance\f1 program treats the latest incomplete +output line as the prompt, and redisplays any incompleted input line +that the user has typed after it. Note that the small delay, which is +imperceptible to the user, isn't necessary for correct operation of +the program. It is just an optimization, designed to stop the input +line from being redisplayed so often that it slows down output. +.sp +Note that the user-level command-line editing facilities provided by +the Tecla library are documented in the \f3tecla(@MISC_MANEXT@)\f1 man page + +.SH DEFICIENCIES + +The one major problem that hasn't been solved yet, is how to deal with +applications that change whether typed input is echo'd by their +controlling terminal. For example, programs that ask for a password, +such as ftp and telnet, temporarily tell their controlling terminal +not to echo what the user types. Since this request goes to the +application side of the psuedo terminal, the \f3enhance\f1 program has +no way of knowing that this has happened, and continues to echo typed +input to its controlling terminal, while the user types their +password. +.sp +Furthermore, before executing the host application, the \f3enhance\f1 +program initially sets the pseudo terminal to noecho mode, so that +everything that it sends to the program doesn't get redundantly +echoed. If a program that switches to noecho mode explicitly restores +echoing afterwards, rather than restoring the terminal modes that were +previously in force, then subsequently, every time that you enter a +new input line, a duplicate copy will be displayed on the next line. + +.SH FILES +.nf +libtecla.a - The tecla library. +~/.teclarc - The tecla personal customization file. +.fi + +.SH SEE ALSO +tecla(@MISC_MANEXT@), libtecla(@LIBR_MANEXT@) + +.SH AUTHOR +Martin Shepherd (mcs@astro.caltech.edu) diff --git a/libtecla-1.6.1/pathutil.c b/libtecla-1.6.1/pathutil.c new file mode 100644 index 0000000..7d3e95c --- /dev/null +++ b/libtecla-1.6.1/pathutil.c @@ -0,0 +1,539 @@ +/* + * Copyright (c) 2000, 2001, 2002, 2003, 2004 by Martin C. Shepherd. + * + * All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, and/or sell copies of the Software, and to permit persons + * to whom the Software is furnished to do so, provided that the above + * copyright notice(s) and this permission notice appear in all copies of + * the Software and that both the above copyright notice(s) and this + * permission notice appear in supporting documentation. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT + * OF THIRD PARTY RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR + * HOLDERS INCLUDED IN THIS NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL + * INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING + * FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, + * NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION + * WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + * Except as contained in this notice, the name of a copyright holder + * shall not be used in advertising or otherwise to promote the sale, use + * or other dealings in this Software without prior written authorization + * of the copyright holder. + */ + +/* + * If file-system access is to be excluded, this module has no function, + * so all of its code should be excluded. + */ +#ifndef WITHOUT_FILE_SYSTEM + +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "pathutil.h" + +/*....................................................................... + * Create a new PathName object. + * + * Output: + * return PathName * The new object, or NULL on error. + */ +PathName *_new_PathName(void) +{ + PathName *path; /* The object to be returned */ +/* + * Allocate the container. + */ + path = (PathName *) malloc(sizeof(PathName)); + if(!path) { + errno = ENOMEM; + return NULL; + }; +/* + * Before attempting any operation that might fail, initialize the + * container at least up to the point at which it can safely be passed + * to _del_PathName(). + */ + path->name = NULL; + path->dim = 0; +/* + * Figure out the maximum length of an expanded pathname. + */ + path->dim = _pu_pathname_dim(); + if(path->dim == 0) + return _del_PathName(path); +/* + * Allocate the pathname buffer. + */ + path->name = (char *)malloc(path->dim * sizeof(char)); + if(!path->name) { + errno = ENOMEM; + return _del_PathName(path); + }; + return path; +} + +/*....................................................................... + * Delete a PathName object. + * + * Input: + * path PathName * The object to be deleted. + * Output: + * return PathName * The deleted object (always NULL). + */ +PathName *_del_PathName(PathName *path) +{ + if(path) { + if(path->name) + free(path->name); + free(path); + }; + return NULL; +} + +/*....................................................................... + * Return the pathname to a zero-length string. + * + * Input: + * path PathName * The pathname container. + * Output: + * return char * The cleared pathname buffer, or NULL on error. + */ +char *_pn_clear_path(PathName *path) +{ +/* + * Check the arguments. + */ + if(!path) { + errno = EINVAL; + return NULL; + }; + path->name[0] = '\0'; + return path->name; +} + +/*....................................................................... + * Append a string to a pathname, increasing the size of the pathname + * buffer if needed. + * + * Input: + * path PathName * The pathname container. + * string const char * The string to be appended to the pathname. + * Note that regardless of the slen argument, + * this should be a '\0' terminated string. + * slen int The maximum number of characters to append + * from string[], or -1 to append the whole + * string. + * remove_escapes int If true, remove the backslashes that escape + * spaces, tabs, backslashes etc.. + * Output: + * return char * The pathname string path->name[], which may + * have been reallocated, or NULL if there was + * insufficient memory to extend the pathname. + */ +char *_pn_append_to_path(PathName *path, const char *string, int slen, + int remove_escapes) +{ + int pathlen; /* The length of the pathname */ + int i; +/* + * Check the arguments. + */ + if(!path || !string) { + errno = EINVAL; + return NULL; + }; +/* + * Get the current length of the pathname. + */ + pathlen = strlen(path->name); +/* + * How many characters should be appended? + */ + if(slen < 0 || slen > strlen(string)) + slen = strlen(string); +/* + * Resize the pathname if needed. + */ + if(!_pn_resize_path(path, pathlen + slen)) + return NULL; +/* + * Append the string to the output pathname, removing any escape + * characters found therein. + */ + if(remove_escapes) { + int is_escape = 0; + for(i=0; iname[pathlen++] = string[i]; + }; +/* + * Terminate the string. + */ + path->name[pathlen] = '\0'; + } else { +/* + * Append the string directly to the pathname. + */ + memcpy(path->name + pathlen, string, slen); + path->name[pathlen + slen] = '\0'; + }; + return path->name; +} + +/*....................................................................... + * Prepend a string to a pathname, increasing the size of the pathname + * buffer if needed. + * + * Input: + * path PathName * The pathname container. + * string const char * The string to be prepended to the pathname. + * Note that regardless of the slen argument, + * this should be a '\0' terminated string. + * slen int The maximum number of characters to prepend + * from string[], or -1 to append the whole + * string. + * remove_escapes int If true, remove the backslashes that escape + * spaces, tabs, backslashes etc.. + * Output: + * return char * The pathname string path->name[], which may + * have been reallocated, or NULL if there was + * insufficient memory to extend the pathname. + */ +char *_pn_prepend_to_path(PathName *path, const char *string, int slen, + int remove_escapes) +{ + int pathlen; /* The length of the pathname */ + int shift; /* The number of characters to shift the suffix by */ + int i,j; +/* + * Check the arguments. + */ + if(!path || !string) { + errno = EINVAL; + return NULL; + }; +/* + * Get the current length of the pathname. + */ + pathlen = strlen(path->name); +/* + * How many characters should be appended? + */ + if(slen < 0 || slen > strlen(string)) + slen = strlen(string); +/* + * Work out how far we need to shift the original path string to make + * way for the new prefix. When removing escape characters, we need + * final length of the new prefix, after unescaped backslashes have + * been removed. + */ + if(remove_escapes) { + int is_escape = 0; + for(shift=0,i=0; iname + shift, path->name, pathlen+1); +/* + * Copy the new prefix into the vacated space at the beginning of the + * output pathname, removing any escape characters if needed. + */ + if(remove_escapes) { + int is_escape = 0; + for(i=j=0; iname[j++] = string[i]; + }; + } else { + memcpy(path->name, string, slen); + }; + return path->name; +} + +/*....................................................................... + * If needed reallocate a given pathname buffer to allow a string of + * a given length to be stored in it. + * + * Input: + * path PathName * The pathname container object. + * length size_t The required length of the pathname buffer, + * not including the terminating '\0'. + * Output: + * return char * The pathname buffer, or NULL if there was + * insufficient memory. + */ +char *_pn_resize_path(PathName *path, size_t length) +{ +/* + * Check the arguments. + */ + if(!path) { + errno = EINVAL; + return NULL; + }; +/* + * If the pathname buffer isn't large enough to accomodate a string + * of the specified length, attempt to reallocate it with the new + * size, plus space for a terminating '\0'. Also add a bit of + * head room to prevent too many reallocations if the initial length + * turned out to be very optimistic. + */ + if(length + 1 > path->dim) { + size_t dim = length + 1 + PN_PATHNAME_INC; + char *name = (char *) realloc(path->name, dim); + if(!name) + return NULL; + path->name = name; + path->dim = dim; + }; + return path->name; +} + +/*....................................................................... + * Estimate the largest amount of space needed to store a pathname. + * + * Output: + * return size_t The number of bytes needed, including space for the + * terminating '\0'. + */ +size_t _pu_pathname_dim(void) +{ + int maxlen; /* The return value excluding space for the '\0' */ +/* + * If the POSIX PATH_MAX macro is defined in limits.h, use it. + */ +#ifdef PATH_MAX + maxlen = PATH_MAX; +/* + * If we have pathconf, use it. + */ +#elif defined(_PC_PATH_MAX) + errno = 0; + maxlen = pathconf(FS_ROOT_DIR, _PC_PATH_MAX); + if(maxlen <= 0 || errno) + maxlen = MAX_PATHLEN_FALLBACK; +/* + * None of the above approaches worked, so substitute our fallback + * guess. + */ +#else + maxlen = MAX_PATHLEN_FALLBACK; +#endif +/* + * Return the amount of space needed to accomodate a pathname plus + * a terminating '\0'. + */ + return maxlen + 1; +} + +/*....................................................................... + * Return non-zero if the specified path name refers to a directory. + * + * Input: + * pathname const char * The path to test. + * Output: + * return int 0 - Not a directory. + * 1 - pathname[] refers to a directory. + */ +int _pu_path_is_dir(const char *pathname) +{ + struct stat statbuf; /* The file-statistics return buffer */ +/* + * Look up the file attributes. + */ + if(stat(pathname, &statbuf) < 0) + return 0; +/* + * Is the file a directory? + */ + return S_ISDIR(statbuf.st_mode) != 0; +} + +/*....................................................................... + * Return non-zero if the specified path name refers to a regular file. + * + * Input: + * pathname const char * The path to test. + * Output: + * return int 0 - Not a regular file. + * 1 - pathname[] refers to a regular file. + */ +int _pu_path_is_file(const char *pathname) +{ + struct stat statbuf; /* The file-statistics return buffer */ +/* + * Look up the file attributes. + */ + if(stat(pathname, &statbuf) < 0) + return 0; +/* + * Is the file a regular file? + */ + return S_ISREG(statbuf.st_mode) != 0; +} + +/*....................................................................... + * Return non-zero if the specified path name refers to an executable. + * + * Input: + * pathname const char * The path to test. + * Output: + * return int 0 - Not an executable file. + * 1 - pathname[] refers to an executable file. + */ +int _pu_path_is_exe(const char *pathname) +{ + struct stat statbuf; /* The file-statistics return buffer */ +/* + * Look up the file attributes. + */ + if(stat(pathname, &statbuf) < 0) + return 0; +/* + * Is the file a regular file which is executable by the current user. + */ + return S_ISREG(statbuf.st_mode) != 0 && + (statbuf.st_mode & (S_IXOTH | S_IXGRP | S_IXUSR)) && + access(pathname, X_OK) == 0; +} + +/*....................................................................... + * Search backwards for the potential start of a filename. This + * looks backwards from the specified index in a given string, + * stopping at the first unescaped space or the start of the line. + * + * Input: + * string const char * The string to search backwards in. + * back_from int The index of the first character in string[] + * that follows the pathname. + * Output: + * return char * The pointer to the first character of + * the potential pathname, or NULL on error. + */ +char *_pu_start_of_path(const char *string, int back_from) +{ + int i, j; +/* + * Check the arguments. + */ + if(!string || back_from < 0) { + errno = EINVAL; + return NULL; + }; +/* + * Search backwards from the specified index. + */ + for(i=back_from-1; i>=0; i--) { + int c = string[i]; +/* + * Stop on unescaped spaces. + */ + if(isspace((int)(unsigned char)c)) { +/* + * The space can't be escaped if we are at the start of the line. + */ + if(i==0) + break; +/* + * Find the extent of the escape characters which precedes the space. + */ + for(j=i-1; j>=0 && string[j]=='\\'; j--) + ; +/* + * If there isn't an odd number of escape characters before the space, + * then the space isn't escaped. + */ + if((i - 1 - j) % 2 == 0) + break; + }; + }; + return (char *)string + i + 1; +} + +/*....................................................................... + * Find the length of a potential filename starting from a given + * point. This looks forwards from the specified index in a given string, + * stopping at the first unescaped space or the end of the line. + * + * Input: + * string const char * The string to search backwards in. + * start_from int The index of the first character of the pathname + * in string[]. + * Output: + * return char * The pointer to the character that follows + * the potential pathname, or NULL on error. + */ +char *_pu_end_of_path(const char *string, int start_from) +{ + int c; /* The character being examined */ + int escaped = 0; /* True when the next character is escaped */ + int i; +/* + * Check the arguments. + */ + if(!string || start_from < 0) { + errno = EINVAL; + return NULL; + }; +/* + * Search forwards from the specified index. + */ + for(i=start_from; (c=string[i]) != '\0'; i++) { + if(escaped) { + escaped = 0; + } else if(isspace(c)) { + break; + } else if(c == '\\') { + escaped = 1; + }; + }; + return (char *)string + i; +} + +/*....................................................................... + * Return non-zero if the specified path name refers to an existing file. + * + * Input: + * pathname const char * The path to test. + * Output: + * return int 0 - The file doesn't exist. + * 1 - The file does exist. + */ +int _pu_file_exists(const char *pathname) +{ + struct stat statbuf; + return stat(pathname, &statbuf) == 0; +} + +#endif /* ifndef WITHOUT_FILE_SYSTEM */ diff --git a/libtecla-1.6.1/pathutil.h b/libtecla-1.6.1/pathutil.h new file mode 100644 index 0000000..5454d91 --- /dev/null +++ b/libtecla-1.6.1/pathutil.h @@ -0,0 +1,122 @@ +#ifndef pathutil_h +#define pathutil_h + +/* + * Copyright (c) 2000, 2001, 2002, 2003, 2004 by Martin C. Shepherd. + * + * All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, and/or sell copies of the Software, and to permit persons + * to whom the Software is furnished to do so, provided that the above + * copyright notice(s) and this permission notice appear in all copies of + * the Software and that both the above copyright notice(s) and this + * permission notice appear in supporting documentation. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT + * OF THIRD PARTY RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR + * HOLDERS INCLUDED IN THIS NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL + * INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING + * FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, + * NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION + * WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + * Except as contained in this notice, the name of a copyright holder + * shall not be used in advertising or otherwise to promote the sale, use + * or other dealings in this Software without prior written authorization + * of the copyright holder. + */ + +/* + * The following object encapsulates a buffer designed to be used to + * store pathnames. The pathname member of the object is initially + * allocated with the size that _pu_pathname_dim() returns, and then + * if this turns out to be pessimistic, the pathname can be reallocated + * via calls to pb_append_to_path() and/or pb_resize_path(). + */ +typedef struct { + char *name; /* The path buffer */ + size_t dim; /* The current allocated size of buffer[] */ +} PathName; + +PathName *_new_PathName(void); +PathName *_del_PathName(PathName *path); + +char *_pn_clear_path(PathName *path); +char *_pn_append_to_path(PathName *path, const char *string, int slen, + int remove_escapes); +char *_pn_prepend_to_path(PathName *path, const char *string, int slen, + int remove_escapes); +char *_pn_resize_path(PathName *path, size_t length); + +/* + * Search backwards for the potential start of a filename. This + * looks backwards from the specified index in a given string, + * stopping at the first unescaped space or the start of the line. + */ +char *_pu_start_of_path(const char *string, int back_from); + +/* + * Find the end of a potential filename, starting from a given index + * in the string. This looks forwards from the specified index in a + * given string, stopping at the first unescaped space or the end + * of the line. + */ +char *_pu_end_of_path(const char *string, int start_from); + + +/* + * Return an estimate of the the length of the longest pathname + * on the local system. + */ +size_t _pu_pathname_dim(void); + +/* + * Return non-zero if the specified path name refers to a directory. + */ +int _pu_path_is_dir(const char *pathname); + +/* + * Return non-zero if the specified path name refers to a regular file. + */ +int _pu_path_is_file(const char *pathname); + +/* + * Return non-zero if the specified path name refers to an executable. + */ +int _pu_path_is_exe(const char *pathname); + +/* + * Return non-zero if a file exists with the specified pathname. + */ +int _pu_file_exists(const char *pathname); + +/* + * If neither the POSIX PATH_MAX macro nor the pathconf() function + * can be used to find out the maximum pathlength on the target + * system, the following fallback maximum length is used. + */ +#define MAX_PATHLEN_FALLBACK 1024 + +/* + * If the pathname buffer turns out to be too small, it will be extended + * in chunks of the following amount (plus whatever is needed at the time). + */ +#define PN_PATHNAME_INC 100 + +/* + * Define the special character-sequences of the filesystem. + */ +#define FS_ROOT_DIR "/" /* The root directory */ +#define FS_ROOT_DIR_LEN (sizeof(FS_ROOT_DIR) - 1) +#define FS_PWD "." /* The current working directory */ +#define FS_PWD_LEN (sizeof(FS_PWD_LEN) - 1) +#define FS_DIR_SEP "/" /* The directory separator string */ +#define FS_DIR_SEP_LEN (sizeof(FS_DIR_SEP) - 1) + +#endif diff --git a/libtecla-1.6.1/pcache.c b/libtecla-1.6.1/pcache.c new file mode 100644 index 0000000..74e4ef5 --- /dev/null +++ b/libtecla-1.6.1/pcache.c @@ -0,0 +1,1710 @@ +/* + * Copyright (c) 2000, 2001, 2002, 2003, 2004 by Martin C. Shepherd. + * + * All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, and/or sell copies of the Software, and to permit persons + * to whom the Software is furnished to do so, provided that the above + * copyright notice(s) and this permission notice appear in all copies of + * the Software and that both the above copyright notice(s) and this + * permission notice appear in supporting documentation. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT + * OF THIRD PARTY RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR + * HOLDERS INCLUDED IN THIS NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL + * INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING + * FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, + * NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION + * WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + * Except as contained in this notice, the name of a copyright holder + * shall not be used in advertising or otherwise to promote the sale, use + * or other dealings in this Software without prior written authorization + * of the copyright holder. + */ + +/* + * If file-system access is to be excluded, this module has no function, + * so all of its code should be excluded. + */ +#ifndef WITHOUT_FILE_SYSTEM + +#include +#include +#include +#include + +#include "libtecla.h" +#include "pathutil.h" +#include "homedir.h" +#include "freelist.h" +#include "direader.h" +#include "stringrp.h" +#include "errmsg.h" + +/* + * The new_PcaPathConf() constructor sets the integer first member of + * the returned object to the following magic number. This is then + * checked for by pca_path_completions() as a sanity check. + */ +#define PPC_ID_CODE 4567 + +/* + * A pointer to a structure of the following type can be passed to + * the builtin path-completion callback function to modify its behavior. + */ +struct PcaPathConf { + int id; /* This is set to PPC_ID_CODE by new_PcaPathConf() */ + PathCache *pc; /* The path-list cache in which to look up the executables */ + int escaped; /* If non-zero, backslashes in the input line are */ + /* interpreted as escaping special characters and */ + /* spaces, and any special characters and spaces in */ + /* the listed completions will also be escaped with */ + /* added backslashes. This is the default behaviour. */ + /* If zero, backslashes are interpreted as being */ + /* literal parts of the file name, and none are added */ + /* to the completion suffixes. */ + int file_start; /* The index in the input line of the first character */ + /* of the file name. If you specify -1 here, */ + /* pca_path_completions() identifies the */ + /* the start of the file by looking backwards for */ + /* an unescaped space, or the beginning of the line. */ +}; + +/* + * Prepended to each chached filename is a character which contains + * one of the following status codes. When a given filename (minus + * this byte) is passed to the application's check_fn(), the result + * is recorded in this byte, such that the next time it is looked + * up, we don't have to call check_fn() again. These codes are cleared + * whenever the path is scanned and whenever the check_fn() callback + * is changed. + */ +typedef enum { + PCA_F_ENIGMA='?', /* The file remains to be checked */ + PCA_F_WANTED='+', /* The file has been selected by the caller's callback */ + PCA_F_IGNORE='-' /* The file has been rejected by the caller's callback */ +} PcaFileStatus; + +/* + * Encapsulate the memory management objects which supply memoy for + * the arrays of filenames. + */ +typedef struct { + StringGroup *sg; /* The memory used to record the names of files */ + size_t files_dim; /* The allocated size of files[] */ + char **files; /* Memory for 'files_dim' pointers to files */ + size_t nfiles; /* The number of filenames currently in files[] */ +} CacheMem; + +static CacheMem *new_CacheMem(void); +static CacheMem *del_CacheMem(CacheMem *cm); +static void rst_CacheMem(CacheMem *cm); + +/* + * Lists of nodes of the following type are used to record the + * names and contents of individual directories. + */ +typedef struct PathNode PathNode; +struct PathNode { + PathNode *next; /* The next directory in the path */ + int relative; /* True if the directory is a relative pathname */ + CacheMem *mem; /* The memory used to store dir[] and files[] */ + char *dir; /* The directory pathname (stored in pc->sg) */ + int nfile; /* The number of filenames stored in 'files' */ + char **files; /* Files of interest in the current directory, */ + /* or NULL if dir[] is a relative pathname */ + /* who's contents can't be cached. This array */ + /* and its contents are taken from pc->abs_mem */ + /* or pc->rel_mem */ +}; + +/* + * Append a new node to the list of directories in the path. + */ +static int add_PathNode(PathCache *pc, const char *dirname); + +/* + * Set the maximum length allowed for usernames. + * names. + */ +#define USR_LEN 100 + +/* + * PathCache objects encapsulate the resources needed to record + * files of interest from comma-separated lists of directories. + */ +struct PathCache { + ErrMsg *err; /* The error reporting buffer */ + FreeList *node_mem; /* A free-list of PathNode objects */ + CacheMem *abs_mem; /* Memory for the filenames of absolute paths */ + CacheMem *rel_mem; /* Memory for the filenames of relative paths */ + PathNode *head; /* The head of the list of directories in the */ + /* path, or NULL if no path has been scanned yet. */ + PathNode *tail; /* The tail of the list of directories in the */ + /* path, or NULL if no path has been scanned yet. */ + PathName *path; /* The fully qualified name of a file */ + HomeDir *home; /* Home-directory lookup object */ + DirReader *dr; /* A portable directory reader */ + CplFileConf *cfc; /* Configuration parameters to pass to */ + /* cpl_file_completions() */ + CplCheckFn *check_fn; /* The callback used to determine if a given */ + /* filename should be recorded in the cache. */ + void *data; /* Annonymous data to be passed to pc->check_fn() */ + char usrnam[USR_LEN+1];/* The buffer used when reading the names of */ + /* users. */ +}; + +/* + * Empty the cache. + */ +static void pca_clear_cache(PathCache *pc); + +/* + * Read a username from string[] and record it in pc->usrnam[]. + */ +static int pca_read_username(PathCache *pc, const char *string, int slen, + int literal, const char **nextp); + +/* + * Extract the next component of a colon separated list of directory + * paths. + */ +static int pca_extract_dir(PathCache *pc, const char *path, + const char **nextp); + +/* + * Scan absolute directories for files of interest, recording their names + * in mem->sg and recording pointers to these names in mem->files[]. + */ +static int pca_scan_dir(PathCache *pc, const char *dirname, CacheMem *mem); + +/* + * A qsort() comparison function for comparing the cached filename + * strings pointed to by two (char **) array elements. Note that + * this ignores the initial cache-status byte of each filename. + */ +static int pca_cmp_matches(const void *v1, const void *v2); + +/* + * A qsort() comparison function for comparing a filename + * against an element of an array of pointers to filename cache + * entries. + */ +static int pca_cmp_file(const void *v1, const void *v2); + +/* + * Initialize a PcaPathConf configuration objects with the default + * options. + */ +static int pca_init_PcaPathConf(PcaPathConf *ppc, PathCache *pc); + +/* + * Make a copy of a completion suffix, suitable for passing to + * cpl_add_completion(). + */ +static int pca_prepare_suffix(PathCache *pc, const char *suffix, + int add_escapes); + +/* + * Return non-zero if the specified string appears to start with a pathname. + */ +static int cpa_cmd_contains_path(const char *prefix, int prefix_len); + +/* + * Return a given prefix with escapes optionally removed. + */ +static const char *pca_prepare_prefix(PathCache *pc, const char *prefix, + size_t prefix_len, int escaped); + +/* + * If there is a tilde expression at the beginning of the specified path, + * place the corresponding home directory into pc->path. Otherwise + * just clear pc->path. + */ +static int pca_expand_tilde(PathCache *pc, const char *path, int pathlen, + int literal, const char **endp); + +/* + * Clear the filename status codes that are recorded before each filename + * in the cache. + */ +static void pca_remove_marks(PathCache *pc); + +/* + * Specify how many PathNode's to allocate at a time. + */ +#define PATH_NODE_BLK 30 + +/* + * Specify the amount by which the files[] arrays are to be extended + * whenever they are found to be too small. + */ +#define FILES_BLK_FACT 256 + +/*....................................................................... + * Create a new object who's function is to maintain a cache of + * filenames found within a list of directories, and provide quick + * lookup and completion of selected files in this cache. + * + * Output: + * return PathCache * The new, initially empty cache, or NULL + * on error. + */ +PathCache *new_PathCache(void) +{ + PathCache *pc; /* The object to be returned */ +/* + * Allocate the container. + */ + pc = (PathCache *)malloc(sizeof(PathCache)); + if(!pc) { + errno = ENOMEM; + return NULL; + }; +/* + * Before attempting any operation that might fail, initialize the + * container at least up to the point at which it can safely be passed + * to del_PathCache(). + */ + pc->err = NULL; + pc->node_mem = NULL; + pc->abs_mem = NULL; + pc->rel_mem = NULL; + pc->head = NULL; + pc->tail = NULL; + pc->path = NULL; + pc->home = NULL; + pc->dr = NULL; + pc->cfc = NULL; + pc->check_fn = 0; + pc->data = NULL; + pc->usrnam[0] = '\0'; +/* + * Allocate a place to record error messages. + */ + pc->err = _new_ErrMsg(); + if(!pc->err) + return del_PathCache(pc); +/* + * Allocate the freelist of directory list nodes. + */ + pc->node_mem = _new_FreeList(sizeof(PathNode), PATH_NODE_BLK); + if(!pc->node_mem) + return del_PathCache(pc); +/* + * Allocate memory for recording names of files in absolute paths. + */ + pc->abs_mem = new_CacheMem(); + if(!pc->abs_mem) + return del_PathCache(pc); +/* + * Allocate memory for recording names of files in relative paths. + */ + pc->rel_mem = new_CacheMem(); + if(!pc->rel_mem) + return del_PathCache(pc); +/* + * Allocate a pathname buffer. + */ + pc->path = _new_PathName(); + if(!pc->path) + return del_PathCache(pc); +/* + * Allocate an object for looking up home-directories. + */ + pc->home = _new_HomeDir(); + if(!pc->home) + return del_PathCache(pc); +/* + * Allocate an object for reading directories. + */ + pc->dr = _new_DirReader(); + if(!pc->dr) + return del_PathCache(pc); +/* + * Allocate a cpl_file_completions() configuration object. + */ + pc->cfc = new_CplFileConf(); + if(!pc->cfc) + return del_PathCache(pc); +/* + * Configure cpl_file_completions() to use check_fn() to select + * files of interest. + */ + cfc_set_check_fn(pc->cfc, pc->check_fn, pc->data); +/* + * Return the cache, ready for use. + */ + return pc; +} + +/*....................................................................... + * Delete a given cache of files, returning the resources that it + * was using to the system. + * + * Input: + * pc PathCache * The cache to be deleted (can be NULL). + * Output: + * return PathCache * The deleted object (ie. allways NULL). + */ +PathCache *del_PathCache(PathCache *pc) +{ + if(pc) { +/* + * Delete the error message buffer. + */ + pc->err = _del_ErrMsg(pc->err); +/* + * Delete the memory of the list of path nodes. + */ + pc->node_mem = _del_FreeList(pc->node_mem, 1); +/* + * Delete the memory used to record filenames. + */ + pc->abs_mem = del_CacheMem(pc->abs_mem); + pc->rel_mem = del_CacheMem(pc->rel_mem); +/* + * The list of PathNode's was already deleted when node_mem was + * deleted. + */ + pc->head = NULL; + pc->tail = NULL; +/* + * Delete the pathname buffer. + */ + pc->path = _del_PathName(pc->path); +/* + * Delete the home-directory lookup object. + */ + pc->home = _del_HomeDir(pc->home); +/* + * Delete the directory reader. + */ + pc->dr = _del_DirReader(pc->dr); +/* + * Delete the cpl_file_completions() config object. + */ + pc->cfc = del_CplFileConf(pc->cfc); +/* + * Delete the container. + */ + free(pc); + }; + return NULL; +} + +/*....................................................................... + * If you want subsequent calls to pca_lookup_file() and + * pca_path_completions() to only return the filenames of certain + * types of files, for example executables, or filenames ending in + * ".ps", call this function to register a file-selection callback + * function. This callback function takes the full pathname of a file, + * plus application-specific data, and returns 1 if the file is of + * interest, and zero otherwise. + * + * Input: + * pc PathCache * The filename cache. + * check_fn CplCheckFn * The function to call to see if the name of + * a given file should be included in the + * cache. This determines what type of files + * will reside in the cache. To revert to + * selecting all files, regardless of type, + * pass 0 here. + * data void * You can pass a pointer to anything you + * like here, including NULL. It will be + * passed to your check_fn() callback + * function, for its private use. + */ +void pca_set_check_fn(PathCache *pc, CplCheckFn *check_fn, void *data) +{ + if(pc) { +/* + * If the callback or its data pointer have changed, clear the cached + * statuses of files that were accepted or rejected by the previous + * calback. + */ + if(check_fn != pc->check_fn || data != pc->data) + pca_remove_marks(pc); +/* + * Record the new callback locally. + */ + pc->check_fn = check_fn; + pc->data = data; +/* + * Configure cpl_file_completions() to use the same callback to + * select files of interest. + */ + cfc_set_check_fn(pc->cfc, check_fn, data); + }; + return; +} + +/*....................................................................... + * Return a description of the last path-caching error that occurred. + * + * Input: + * pc PathCache * The filename cache that suffered the error. + * Output: + * return char * The description of the last error. + */ +const char *pca_last_error(PathCache *pc) +{ + return pc ? _err_get_msg(pc->err) : "NULL PathCache argument"; +} + +/*....................................................................... + * Discard all cached filenames. + * + * Input: + * pc PathCache * The cache to be cleared. + */ +static void pca_clear_cache(PathCache *pc) +{ + if(pc) { +/* + * Return all path-nodes to the freelist. + */ + _rst_FreeList(pc->node_mem); + pc->head = pc->tail = NULL; +/* + * Delete all filename strings. + */ + rst_CacheMem(pc->abs_mem); + rst_CacheMem(pc->rel_mem); + }; + return; +} + +/*....................................................................... + * Build the list of files of interest contained in a given + * colon-separated list of directories. + * + * Input: + * pc PathCache * The cache in which to store the names of + * the files that are found in the list of + * directories. + * path const char * A colon-separated list of directory + * paths. Under UNIX, when searching for + * executables, this should be the return + * value of getenv("PATH"). + * Output: + * return int 0 - OK. + * 1 - An error occurred. A description of + * the error can be acquired by calling + * pca_last_error(pc). + */ +int pca_scan_path(PathCache *pc, const char *path) +{ + const char *pptr; /* A pointer to the next unprocessed character in path[] */ + PathNode *node; /* A node in the list of directory paths */ + char **fptr; /* A pointer into pc->abs_mem->files[] */ +/* + * Check the arguments. + */ + if(!pc) + return 1; +/* + * Clear the outdated contents of the cache. + */ + pca_clear_cache(pc); +/* + * If no path list was provided, there is nothing to be added to the + * cache. + */ + if(!path) + return 0; +/* + * Extract directories from the path list, expanding tilde expressions + * on the fly into pc->pathname, then add them to the list of path + * nodes, along with a sorted list of the filenames of interest that + * the directories hold. + */ + pptr = path; + while(*pptr) { +/* + * Extract the next pathname component into pc->path->name. + */ + if(pca_extract_dir(pc, pptr, &pptr)) + return 1; +/* + * Add a new node to the list of paths, containing both the + * directory name and, if not a relative pathname, the list of + * files of interest in the directory. + */ + if(add_PathNode(pc, pc->path->name)) + return 1; + }; +/* + * The file arrays in each absolute directory node are sections of + * pc->abs_mem->files[]. Record pointers to the starts of each + * of these sections in each directory node. Note that this couldn't + * be done in add_PathNode(), because pc->abs_mem->files[] may + * get reallocated in subsequent calls to add_PathNode(), thus + * invalidating any pointers to it. + */ + fptr = pc->abs_mem->files; + for(node=pc->head; node; node=node->next) { + node->files = fptr; + fptr += node->nfile; + }; + return 0; +} + +/*....................................................................... + * Extract the next directory path from a colon-separated list of + * directories, expanding tilde home-directory expressions where needed. + * + * Input: + * pc PathCache * The cache of filenames. + * path const char * A pointer to the start of the next component + * in the path list. + * Input/Output: + * nextp const char ** A pointer to the next unprocessed character + * in path[] will be assigned to *nextp. + * Output: + * return int 0 - OK. The extracted path is in pc->path->name. + * 1 - Error. A description of the error will + * have been left in pc->err. + */ +static int pca_extract_dir(PathCache *pc, const char *path, const char **nextp) +{ + const char *pptr; /* A pointer into path[] */ + const char *sptr; /* The path following tilde expansion */ + int escaped = 0; /* True if the last character was a backslash */ +/* + * If there is a tilde expression at the beginning of the specified path, + * place the corresponding home directory into pc->path. Otherwise + * just clear pc->path. + */ + if(pca_expand_tilde(pc, path, strlen(path), 0, &pptr)) + return 1; +/* + * Keep a record of the current location in the path. + */ + sptr = pptr; +/* + * Locate the end of the directory name in the pathname string, stopping + * when either the end of the string is reached, or an un-escaped colon + * separator is seen. + */ + while(*pptr && (escaped || *pptr != ':')) + escaped = !escaped && *pptr++ == '\\'; +/* + * Append the rest of the directory path to the pathname buffer. + */ + if(_pn_append_to_path(pc->path, sptr, pptr - sptr, 1) == NULL) { + _err_record_msg(pc->err, "Insufficient memory to record directory name", + END_ERR_MSG); + return 1; + }; +/* + * To facilitate subsequently appending filenames to the directory + * path name, make sure that the recorded directory name ends in a + * directory separator. + */ + { + int dirlen = strlen(pc->path->name); + if(dirlen < FS_DIR_SEP_LEN || + strncmp(pc->path->name + dirlen - FS_DIR_SEP_LEN, FS_DIR_SEP, + FS_DIR_SEP_LEN) != 0) { + if(_pn_append_to_path(pc->path, FS_DIR_SEP, FS_DIR_SEP_LEN, 0) == NULL) { + _err_record_msg(pc->err, "Insufficient memory to record directory name", + END_ERR_MSG); + return 1; + }; + }; + }; +/* + * Skip the separator unless we have reached the end of the path. + */ + if(*pptr==':') + pptr++; +/* + * Return the unprocessed tail of the path-list string. + */ + *nextp = pptr; + return 0; +} + +/*....................................................................... + * Read a username, stopping when a directory separator is seen, a colon + * separator is seen, the end of the string is reached, or the username + * buffer overflows. + * + * Input: + * pc PathCache * The cache of filenames. + * string char * The string who's prefix contains the name. + * slen int The max number of characters to read from string[]. + * literal int If true, treat backslashes as literal characters + * instead of escapes. + * Input/Output: + * nextp char ** A pointer to the next unprocessed character + * in string[] will be assigned to *nextp. + * Output: + * return int 0 - OK. The username can be found in pc->usrnam. + * 1 - Error. A description of the error message + * can be found in pc->err. + */ +static int pca_read_username(PathCache *pc, const char *string, int slen, + int literal, const char **nextp) +{ + int usrlen; /* The number of characters in pc->usrnam[] */ + const char *sptr; /* A pointer into string[] */ + int escaped = 0; /* True if the last character was a backslash */ +/* + * Extract the username. + */ + for(sptr=string,usrlen=0; usrlen < USR_LEN && (sptr-string) < slen; sptr++) { +/* + * Stop if the end of the string is reached, or a directory separator + * or un-escaped colon separator is seen. + */ + if(!*sptr || strncmp(sptr, FS_DIR_SEP, FS_DIR_SEP_LEN)==0 || + (!escaped && *sptr == ':')) + break; +/* + * Escape the next character? + */ + if(!literal && !escaped && *sptr == '\\') { + escaped = 1; + } else { + escaped = 0; + pc->usrnam[usrlen++] = *sptr; + }; + }; +/* + * Did the username overflow the buffer? + */ + if(usrlen >= USR_LEN) { + _err_record_msg(pc->err, "Username too long", END_ERR_MSG); + return 1; + }; +/* + * Terminate the string. + */ + pc->usrnam[usrlen] = '\0'; +/* + * Indicate where processing of the input string should continue. + */ + *nextp = sptr; + return 0; +} + + +/*....................................................................... + * Create a new CacheMem object. + * + * Output: + * return CacheMem * The new object, or NULL on error. + */ +static CacheMem *new_CacheMem(void) +{ + CacheMem *cm; /* The object to be returned */ +/* + * Allocate the container. + */ + cm = (CacheMem *)malloc(sizeof(CacheMem)); + if(!cm) { + errno = ENOMEM; + return NULL; + }; +/* + * Before attempting any operation that might fail, initialize the + * container at least up to the point at which it can safely be passed + * to del_CacheMem(). + */ + cm->sg = NULL; + cm->files_dim = 0; + cm->files = NULL; + cm->nfiles = 0; +/* + * Allocate a list of string segments for storing filenames. + */ + cm->sg = _new_StringGroup(_pu_pathname_dim()); + if(!cm->sg) + return del_CacheMem(cm); +/* + * Allocate an array of pointers to filenames. + * This will be extended later if needed. + */ + cm->files_dim = FILES_BLK_FACT; + cm->files = (char **) malloc(sizeof(*cm->files) * cm->files_dim); + if(!cm->files) { + errno = ENOMEM; + return del_CacheMem(cm); + }; + return cm; +} + +/*....................................................................... + * Delete a CacheMem object. + * + * Input: + * cm CacheMem * The object to be deleted. + * Output: + * return CacheMem * The deleted object (always NULL). + */ +static CacheMem *del_CacheMem(CacheMem *cm) +{ + if(cm) { +/* + * Delete the memory that was used to record filename strings. + */ + cm->sg = _del_StringGroup(cm->sg); +/* + * Delete the array of pointers to filenames. + */ + cm->files_dim = 0; + if(cm->files) { + free(cm->files); + cm->files = NULL; + }; +/* + * Delete the container. + */ + free(cm); + }; + return NULL; +} + +/*....................................................................... + * Re-initialize the memory used to allocate filename strings. + * + * Input: + * cm CacheMem * The memory cache to be cleared. + */ +static void rst_CacheMem(CacheMem *cm) +{ + _clr_StringGroup(cm->sg); + cm->nfiles = 0; + return; +} + +/*....................................................................... + * Append a new directory node to the list of directories read from the + * path. + * + * Input: + * pc PathCache * The filename cache. + * dirname const char * The name of the new directory. + * Output: + * return int 0 - OK. + * 1 - Error. + */ +static int add_PathNode(PathCache *pc, const char *dirname) +{ + PathNode *node; /* The new directory list node */ + int relative; /* True if dirname[] is a relative pathname */ +/* + * Have we been passed a relative pathname or an absolute pathname? + */ + relative = strncmp(dirname, FS_ROOT_DIR, FS_ROOT_DIR_LEN) != 0; +/* + * If it's an absolute pathname, ignore it if the corresponding + * directory doesn't exist. + */ + if(!relative && !_pu_path_is_dir(dirname)) + return 0; +/* + * Allocate a new list node to record the specifics of the new directory. + */ + node = (PathNode *) _new_FreeListNode(pc->node_mem); + if(!node) { + _err_record_msg(pc->err, "Insufficient memory to cache new directory.", + END_ERR_MSG); + return 1; + }; +/* + * Initialize the node. + */ + node->next = NULL; + node->relative = relative; + node->mem = relative ? pc->rel_mem : pc->abs_mem; + node->dir = NULL; + node->nfile = 0; + node->files = NULL; +/* + * Make a copy of the directory pathname. + */ + node->dir = _sg_store_string(pc->abs_mem->sg, dirname, 0); + if(!node->dir) { + _err_record_msg(pc->err, "Insufficient memory to store directory name.", + END_ERR_MSG); + return 1; + }; +/* + * Scan absolute directories for files of interest, recording their names + * in node->mem->sg and appending pointers to these names to the + * node->mem->files[] array. + */ + if(!node->relative) { + int nfile = node->nfile = pca_scan_dir(pc, node->dir, node->mem); + if(nfile < 1) { /* No files matched or an error occurred */ + node = (PathNode *) _del_FreeListNode(pc->node_mem, node); + return nfile < 0; + }; + }; +/* + * Append the new node to the list. + */ + if(pc->head) { + pc->tail->next = node; + pc->tail = node; + } else { + pc->head = pc->tail = node; + }; + return 0; +} + +/*....................................................................... + * Scan a given directory for files of interest, record their names + * in mem->sg and append pointers to them to the mem->files[] array. + * + * Input: + * pc PathCache * The filename cache. + * dirname const char * The pathname of the directory to be scanned. + * mem CacheMem * The memory in which to store filenames of + * interest. + * Output: + * return int The number of files recorded, or -1 if a + * memory error occurs. Note that the + * inability to read the contents of the + * directory is not counted as an error. + */ +static int pca_scan_dir(PathCache *pc, const char *dirname, CacheMem *mem) +{ + int nfile = 0; /* The number of filenames recorded */ + const char *filename; /* The name of the file being looked at */ +/* + * Attempt to open the directory. If the directory can't be read then + * there are no accessible files of interest in the directory. + */ + if(_dr_open_dir(pc->dr, dirname, NULL)) + return 0; +/* + * Record the names of all files in the directory in the cache. + */ + while((filename = _dr_next_file(pc->dr))) { + char *copy; /* A copy of the filename */ +/* + * Make a temporary copy of the filename with an extra byte prepended. + */ + _pn_clear_path(pc->path); + if(_pn_append_to_path(pc->path, " ", 1, 0) == NULL || + _pn_append_to_path(pc->path, filename, -1, 1) == NULL) { + _err_record_msg(pc->err, "Insufficient memory to record filename", + END_ERR_MSG); + return -1; + }; +/* + * Store the filename. + */ + copy = _sg_store_string(mem->sg, pc->path->name, 0); + if(!copy) { + _err_record_msg(pc->err, "Insufficient memory to cache file name.", + END_ERR_MSG); + return -1; + }; +/* + * Mark the filename as unchecked. + */ + copy[0] = PCA_F_ENIGMA; +/* + * Make room to store a pointer to the copy in mem->files[]. + */ + if(mem->nfiles + 1 > mem->files_dim) { + int needed = mem->files_dim + FILES_BLK_FACT; + char **files = (char **) realloc(mem->files, sizeof(*mem->files)*needed); + if(!files) { + _err_record_msg(pc->err, + "Insufficient memory to extend filename cache.", + END_ERR_MSG); + return 1; + }; + mem->files = files; + mem->files_dim = needed; + }; +/* + * Record a pointer to the copy of the filename at the end of the files[] + * array. + */ + mem->files[mem->nfiles++] = copy; +/* + * Keep a record of the number of files matched so far. + */ + nfile++; + }; +/* + * Sort the list of files into lexical order. + */ + qsort(mem->files + mem->nfiles - nfile, nfile, sizeof(*mem->files), + pca_cmp_matches); +/* + * Return the number of files recorded in mem->files[]. + */ + return nfile; +} + +/*....................................................................... + * A qsort() comparison function for comparing the cached filename + * strings pointed to by two (char **) array elements. Note that + * this ignores the initial cache-status byte of each filename. + * + * Input: + * v1, v2 void * Pointers to the pointers of two strings to be compared. + * Output: + * return int -1 -> v1 < v2. + * 0 -> v1 == v2 + * 1 -> v1 > v2 + */ +static int pca_cmp_matches(const void *v1, const void *v2) +{ + const char **s1 = (const char **) v1; + const char **s2 = (const char **) v2; + return strcmp(*s1+1, *s2+1); +} + +/*....................................................................... + * Given the simple name of a file, search the cached list of files + * in the order in which they where found in the list of directories + * previously presented to pca_scan_path(), and return the pathname + * of the first file which has this name. If a pathname to a file is + * given instead of a simple filename, this is returned without being + * looked up in the cache, but with any initial ~username expression + * expanded, and optionally, unescaped backslashes removed. + * + * Input: + * pc PathCache * The cached list of files. + * name const char * The name of the file to lookup. + * name_len int The length of the filename string at the + * beginning of name[], or -1 to indicate that + * the filename occupies the whole of the + * string. + * literal int If this argument is zero, lone backslashes + * in name[] are ignored during comparison + * with filenames in the cache, under the + * assumption that they were in the input line + * soley to escape the special significance of + * characters like spaces. To have them treated + * as normal characters, give this argument a + * non-zero value, such as 1. + * Output: + * return char * The pathname of the first matching file, + * or NULL if not found. Note that the returned + * pointer points to memory owned by *pc, and + * will become invalid on the next call to any + * function in the PathCache module. + */ +char *pca_lookup_file(PathCache *pc, const char *name, int name_len, + int literal) +{ + PathNode *node; /* A node in the list of directories in the path */ + char **match; /* A pointer to a matching filename string in the cache */ +/* + * Check the arguments. + */ + if(!pc || !name || name_len==0) + return NULL; +/* + * If no length was specified, determine the length of the string to + * be looked up. + */ + if(name_len < 0) + name_len = strlen(name); +/* + * If the word starts with a ~username expression, the root directory, + * of it contains any directory separators, then treat it isn't a simple + * filename that can be looked up in the cache, but rather appears to + * be the pathname of a file. If so, return a copy of this pathname with + * escapes removed, if requested, and any initial ~username expression + * expanded. + */ + if(cpa_cmd_contains_path(name, name_len)) { + const char *nptr; + if(pca_expand_tilde(pc, name, name_len, literal, &nptr) || + _pn_append_to_path(pc->path, nptr, name_len - (nptr-name), + !literal) == NULL) + return NULL; + return pc->path->name; + }; +/* + * Look up the specified filename in each of the directories of the path, + * in the same order that they were listed in the path, and stop as soon + * as an instance of the file is found. + */ + for(node=pc->head; node; node=node->next) { +/* + * If the directory of the latest node is a relative pathname, + * scan it for files of interest. + */ + if(node->relative) { + rst_CacheMem(node->mem); + if(pca_scan_dir(pc, node->dir, node->mem) < 1) + continue; + node->files = node->mem->files; + node->nfile = node->mem->nfiles; + }; +/* + * Copy the filename into a temporary buffer, while interpretting + * escape characters if needed. + */ + _pn_clear_path(pc->path); + if(_pn_append_to_path(pc->path, name, name_len, !literal) == NULL) + return NULL; +/* + * Perform a binary search for the requested filename. + */ + match = (char **)bsearch(pc->path->name, node->files, node->nfile, + sizeof(*node->files), pca_cmp_file); + if(match) { +/* + * Prepend the pathname in which the directory was found, which we have + * guaranteed to end in a directory separator, to the located filename. + */ + if(_pn_prepend_to_path(pc->path, node->dir, -1, 0) == NULL) + return NULL; +/* + * Return the matching pathname unless it is rejected by the application. + */ + if(!pc->check_fn || (*match)[0] == PCA_F_WANTED || + ((*match)[0]==PCA_F_ENIGMA && pc->check_fn(pc->data, pc->path->name))){ + (*match)[0] = PCA_F_WANTED; + return pc->path->name; + } else { + *(match)[0] = PCA_F_IGNORE; + }; + }; + }; +/* + * File not found. + */ + return NULL; +} + +/*....................................................................... + * A qsort() comparison function for comparing a filename string to + * a cached filename string pointed to by a (char **) array element. + * This ignores the initial code byte at the start of the cached filename + * string. + * + * Input: + * v1, v2 void * Pointers to the pointers of two strings to be compared. + * Output: + * return int -1 -> v1 < v2. + * 0 -> v1 == v2 + * 1 -> v1 > v2 + */ +static int pca_cmp_file(const void *v1, const void *v2) +{ + const char *file_name = (const char *) v1; + const char **cache_name = (const char **) v2; + return strcmp(file_name, *cache_name + 1); +} + +/*....................................................................... + * The PcaPathConf structure may have options added to it in the future. + * To allow your application to be linked against a shared version of the + * tecla library, without these additions causing your application to + * crash, you should use new_PcaPathConf() to allocate such structures. + * This will set all of the configuration options to their default values, + * which you can then change before passing the structure to + * pca_path_completions(). + * + * Input: + * pc PathCache * The filename cache in which to look for + * file name completions. + * Output: + * return PcaPathConf * The new configuration structure, or NULL + * on error. A descripition of the error + * can be found by calling pca_last_error(pc). + */ +PcaPathConf *new_PcaPathConf(PathCache *pc) +{ + PcaPathConf *ppc; /* The object to be returned */ +/* + * Check the arguments. + */ + if(!pc) + return NULL; +/* + * Allocate the container. + */ + ppc = (PcaPathConf *)malloc(sizeof(PcaPathConf)); + if(!ppc) { + _err_record_msg(pc->err, "Insufficient memory.", END_ERR_MSG); + return NULL; + }; +/* + * Before attempting any operation that might fail, initialize the + * container at least up to the point at which it can safely be passed + * to del_PcaPathConf(). + */ + if(pca_init_PcaPathConf(ppc, pc)) + return del_PcaPathConf(ppc); + return ppc; +} + +/*....................................................................... + * Initialize a PcaPathConf configuration structure with defaults. + * + * Input: + * ppc PcaPathConf * The structre to be initialized. + * pc PathCache * The cache in which completions will be looked up. + * Output: + * return int 0 - OK. + * 1 - Error. A description of the error can be + * obtained by calling pca_last_error(pc). + */ +static int pca_init_PcaPathConf(PcaPathConf *ppc, PathCache *pc) +{ +/* + * Check the arguments. + */ + if(!pc) + return 1; +/* + * Set the default options. + */ + ppc->id = PPC_ID_CODE; + ppc->pc = pc; + ppc->escaped = 1; + ppc->file_start = -1; + return 0; +} + +/*....................................................................... + * Delete a PcaPathConf object. + * + * Input: + * ppc PcaPathConf * The object to be deleted. + * Output: + * return PcaPathConf * The deleted object (always NULL). + */ +PcaPathConf *del_PcaPathConf(PcaPathConf *ppc) +{ + if(ppc) { + ppc->pc = NULL; /* It is up to the caller to delete the cache */ +/* + * Delete the container. + */ + free(ppc); + }; + return NULL; +} + +/*....................................................................... + * pca_path_completions() is a completion callback function for use + * directly with cpl_complete_word() or gl_customize_completions(), or + * indirectly from your own completion callback function. It requires + * that a CpaPathArgs object be passed via its 'void *data' argument. + */ +CPL_MATCH_FN(pca_path_completions) +{ + PcaPathConf *ppc; /* The configuration arguments */ + PathCache *pc; /* The cache in which to look for completions */ + PathNode *node; /* A node in the list of directories in the path */ + const char *filename; /* The name of the file being looked at */ + const char *start_path; /* The pointer to the start of the pathname */ + /* in line[]. */ + int word_start; /* The index in line[] corresponding to start_path */ + const char *prefix; /* The file-name prefix being searched for */ + size_t prefix_len; /* The length of the prefix being completed */ + int bot; /* The lowest index of the array not searched yet */ + int top; /* The highest index of the array not searched yet */ +/* + * Check the arguments. + */ + if(!cpl) + return 1; + if(!line || word_end < 0 || !data) { + cpl_record_error(cpl, "pca_path_completions: Invalid arguments."); + return 1; + }; +/* + * Get the configuration arguments. + */ + ppc = (PcaPathConf *) data; +/* + * Check that the callback data is a PcaPathConf structure returned + * by new_PcaPathConf(). + */ + if(ppc->id != PPC_ID_CODE) { + cpl_record_error(cpl, + "Invalid callback data passed to pca_path_completions()"); + return 1; + }; +/* + * Get the filename cache. + */ + pc = ppc->pc; +/* + * Get the start of the file name. If not specified by the caller, + * identify it by searching backwards in the input line for an + * unescaped space or the start of the line. + */ + if(ppc->file_start < 0) { + start_path = _pu_start_of_path(line, word_end); + if(!start_path) { + cpl_record_error(cpl, "Unable to find the start of the file name."); + return 1; + }; + } else { + start_path = line + ppc->file_start; + }; +/* + * Get the index of the start of the word being completed. + */ + word_start = start_path - line; +/* + * Work out the length of the prefix that is bein completed. + */ + prefix_len = word_end - word_start; +/* + * If the word starts with a ~username expression or the root directory, + * of it contains any directory separators, then completion must be + * delegated to cpl_file_completions(). + */ + if(cpa_cmd_contains_path(start_path, prefix_len)) { + cfc_file_start(pc->cfc, word_start); + return cpl_file_completions(cpl, pc->cfc, line, word_end); + }; +/* + * Look up the specified file name in each of the directories of the path, + * in the same order that they were listed in the path, and stop as soon + * as an instance of the file is found. + */ + for(node=pc->head; node; node=node->next) { +/* + * If the directory of the latest node is a relative pathname, + * scan it for files of interest. + */ + if(node->relative) { + rst_CacheMem(node->mem); + if(pca_scan_dir(pc, node->dir, node->mem) < 1) + continue; + node->files = node->mem->files; + node->nfile = node->mem->nfiles; + }; +/* + * If needed, make a copy of the file-name being matched, with + * escapes removed. Note that we need to do this anew every loop + * iteration, because the above call to pca_scan_dir() uses + * pc->path. + */ + prefix = pca_prepare_prefix(pc, start_path, prefix_len, ppc->escaped); + if(!prefix) + return 1; +/* + * The directory entries are sorted, so we can perform a binary + * search for an instance of the prefix being searched for. + */ + bot = 0; + top = node->nfile - 1; + while(top >= bot) { + int mid = (top + bot)/2; + int test = strncmp(node->files[mid]+1, prefix, prefix_len); + if(test > 0) + top = mid - 1; + else if(test < 0) + bot = mid + 1; + else { + top = bot = mid; + break; + }; + }; +/* + * If we found a match, look to see if any of its neigbors also match. + */ + if(top == bot) { + while(--bot >= 0 && strncmp(node->files[bot]+1, prefix, prefix_len) == 0) + ; + while(++top < node->nfile && + strncmp(node->files[top]+1, prefix, prefix_len) == 0) + ; +/* + * We will have gone one too far in each direction. + */ + bot++; + top--; +/* + * Add the completions to the list after checking them against the + * callers requirements. + */ + for( ; bot<=top; bot++) { + char *match = node->files[bot]; +/* + * Form the full pathname of the file. + */ + _pn_clear_path(pc->path); + if(_pn_append_to_path(pc->path, node->dir, -1, 0) == NULL || + _pn_append_to_path(pc->path, match+1, -1, 0) == NULL) { + _err_record_msg(pc->err, "Insufficient memory to complete file name", + END_ERR_MSG); + return 1; + }; +/* + * Should the file be included in the list of completions? + */ + if(!pc->check_fn || match[0] == PCA_F_WANTED || + (match[0]==PCA_F_ENIGMA && pc->check_fn(pc->data, pc->path->name))) { + match[0] = PCA_F_WANTED; +/* + * Copy the completion suffix into the work pathname pc->path->name, + * adding backslash escapes if needed. + */ + if(pca_prepare_suffix(pc, match + 1 + prefix_len, + ppc->escaped)) + return 1; +/* + * Record the completion. + */ + if(cpl_add_completion(cpl, line, word_start, word_end, pc->path->name, + "", " ")) + return 1; +/* + * The file was rejected by the application. + */ + } else { + match[0] = PCA_F_IGNORE; + }; + }; + }; + }; +/* + * We now need to search for subdirectories of the current directory which + * have matching prefixes. First, if needed, make a copy of the word being + * matched, with escapes removed. + */ + prefix = pca_prepare_prefix(pc, start_path, prefix_len, ppc->escaped); + if(!prefix) + return 1; +/* + * Now open the current directory. + */ + if(_dr_open_dir(pc->dr, FS_PWD, NULL)) + return 0; +/* + * Scan the current directory for sub-directories whos names start with + * the prefix that we are completing. + */ + while((filename = _dr_next_file(pc->dr))) { +/* + * Does the latest filename match the prefix, and is it a directory? + */ + if(strncmp(filename, prefix, prefix_len) == 0 && _pu_path_is_dir(filename)){ +/* + * Record the completion. + */ + if(pca_prepare_suffix(pc, filename + prefix_len, ppc->escaped) || + cpl_add_completion(cpl, line, word_start, word_end, pc->path->name, + FS_DIR_SEP, FS_DIR_SEP)) + return 1; +/* + * The prefix in pc->path->name will have been overwritten by + * pca_prepare_suffix(). Restore it here. + */ + prefix = pca_prepare_prefix(pc, start_path, prefix_len, ppc->escaped); + if(!prefix) + return 1; + }; + }; + _dr_close_dir(pc->dr); + return 0; +} + +/*....................................................................... + * Using the work buffer pc->path, make a suitably escaped copy of a + * given completion suffix, ready to be passed to cpl_add_completion(). + * + * Input: + * pc PathCache * The filename cache resource object. + * suffix char * The suffix to be copied. + * add_escapes int If true, escape special characters. + * Output: + * return int 0 - OK. + * 1 - Error. + */ +static int pca_prepare_suffix(PathCache *pc, const char *suffix, + int add_escapes) +{ + const char *sptr; /* A pointer into suffix[] */ + int nbsl; /* The number of backslashes to add to the suffix */ + int i; +/* + * How long is the suffix? + */ + int suffix_len = strlen(suffix); +/* + * Clear the work buffer. + */ + _pn_clear_path(pc->path); +/* + * Count the number of backslashes that will have to be added to + * escape spaces, tabs, backslashes and wildcard characters. + */ + nbsl = 0; + if(add_escapes) { + for(sptr = suffix; *sptr; sptr++) { + switch(*sptr) { + case ' ': case '\t': case '\\': case '*': case '?': case '[': + nbsl++; + break; + }; + }; + }; +/* + * Arrange for the output path buffer to have sufficient room for the + * both the suffix and any backslashes that have to be inserted. + */ + if(_pn_resize_path(pc->path, suffix_len + nbsl) == NULL) { + _err_record_msg(pc->err, "Insufficient memory to complete file name", + END_ERR_MSG); + return 1; + }; +/* + * If the suffix doesn't need any escapes, copy it directly into the + * work buffer. + */ + if(nbsl==0) { + strcpy(pc->path->name, suffix); + } else { +/* + * Make a copy with special characters escaped? + */ + if(nbsl > 0) { + const char *src = suffix; + char *dst = pc->path->name; + for(i=0; i= FS_ROOT_DIR_LEN && + strncmp(prefix, FS_ROOT_DIR, FS_ROOT_DIR_LEN) == 0) + return 1; +/* + * Search the prefix for directory separators, returning as soon as + * any are found, since their presence indicates that the filename + * starts with a pathname specification (valid or otherwise). + */ + for(i=0; i= FS_DIR_SEP_LEN && + strncmp(prefix + i, FS_DIR_SEP, FS_DIR_SEP_LEN) == 0) + return 1; + }; +/* + * The file name doesn't appear to start with a pathname specification. + */ + return 0; +} + +/*....................................................................... + * If needed make a new copy of the prefix being matched, in pc->path->name, + * but with escapes removed. If no escapes are to be removed, simply return + * the original prefix string. + * + * Input: + * pc PathCache * The cache being searched. + * prefix const char * The prefix to be processed. + * prefix_len size_t The length of the prefix. + * escaped int If true, return a copy with escapes removed. + * Output: + * return const char * The prepared prefix, or NULL on error, in + * which case an error message will have been + * left in pc->err. + */ +static const char *pca_prepare_prefix(PathCache *pc, const char *prefix, + size_t prefix_len, int escaped) +{ +/* + * Make a copy with escapes removed? + */ + if(escaped) { + _pn_clear_path(pc->path); + if(_pn_append_to_path(pc->path, prefix, prefix_len, 1) == NULL) { + _err_record_msg(pc->err, "Insufficient memory to complete filename", + END_ERR_MSG); + return NULL; + }; + return pc->path->name; + }; + return prefix; +} + +/*....................................................................... + * If backslashes in the filename should be treated as literal + * characters, call the following function with literal=1. Otherwise + * the default is to treat them as escape characters, used for escaping + * spaces etc.. + * + * Input: + * ppc PcaPathConf * The pca_path_completions() configuration object + * to be configured. + * literal int Pass non-zero here to enable literal interpretation + * of backslashes. Pass 0 to turn off literal + * interpretation. + */ +void ppc_literal_escapes(PcaPathConf *ppc, int literal) +{ + if(ppc) + ppc->escaped = !literal; +} + +/*....................................................................... + * Call this function if you know where the index at which the + * filename prefix starts in the input line. Otherwise by default, + * or if you specify start_index to be -1, the filename is taken + * to start after the first unescaped space preceding the cursor, + * or the start of the line, which ever comes first. + * + * Input: + * ppc PcaPathConf * The pca_path_completions() configuration object + * to be configured. + * start_index int The index of the start of the filename in + * the input line, or -1 to select the default. + */ +void ppc_file_start(PcaPathConf *ppc, int start_index) +{ + if(ppc) + ppc->file_start = start_index; +} + +/*....................................................................... + * Expand any ~user expression found at the start of a path, leaving + * either an empty string in pc->path if there is no ~user expression, + * or the corresponding home directory. + * + * Input: + * pc PathCache * The filename cache. + * path const char * The path to expand. + * pathlen int The max number of characters to look at in path[]. + * literal int If true, treat backslashes as literal characters + * instead of escapes. + * Input/Output: + * endp const char * A pointer to the next unprocessed character in + * path[] will be assigned to *endp. + * Output: + * return int 0 - OK + * 1 - Error (a description will have been placed + * in pc->err). + */ +static int pca_expand_tilde(PathCache *pc, const char *path, int pathlen, + int literal, const char **endp) +{ + const char *pptr = path; /* A pointer into path[] */ + const char *homedir=NULL; /* A home directory */ +/* + * Clear the pathname buffer. + */ + _pn_clear_path(pc->path); +/* + * If the first character is a tilde, then perform home-directory + * interpolation. + */ + if(*pptr == '~') { +/* + * Skip the tilde character and attempt to read the username that follows + * it, into pc->usrnam[]. + */ + if(pca_read_username(pc, ++pptr, pathlen-1, literal, &pptr)) + return 1; +/* + * Attempt to lookup the home directory of the user. + */ + homedir = _hd_lookup_home_dir(pc->home, pc->usrnam); + if(!homedir) { + _err_record_msg(pc->err, _hd_last_home_dir_error(pc->home), END_ERR_MSG); + return 1; + }; +/* + * Append the home directory to the pathname string. + */ + if(_pn_append_to_path(pc->path, homedir, -1, 0) == NULL) { + _err_record_msg(pc->err, + "Insufficient memory for home directory expansion", + END_ERR_MSG); + return 1; + }; + }; +/* + * ~user and ~ are usually followed by a directory separator to + * separate them from the file contained in the home directory. + * If the home directory is the root directory, then we don't want + * to follow the home directory by a directory separator, so we should + * skip over it so that it doesn't get copied into the output pathname + */ + if(homedir && strcmp(homedir, FS_ROOT_DIR) == 0 && + (pptr-path) + FS_DIR_SEP_LEN < pathlen && + strncmp(pptr, FS_DIR_SEP, FS_DIR_SEP_LEN) == 0) { + pptr += FS_DIR_SEP_LEN; + }; +/* + * Return a pointer to the next unprocessed character. + */ + *endp = pptr; + return 0; +} + +/*....................................................................... + * Clear the filename status codes that are recorded before each filename + * in the cache. + * + * Input: + * pc PathCache * The filename cache. + */ +static void pca_remove_marks(PathCache *pc) +{ + PathNode *node; /* A node in the list of directories in the path */ + int i; +/* + * Traverse the absolute directories of the path, clearing the + * filename status marks that precede each filename. + */ + for(node=pc->head; node; node=node->next) { + if(!node->relative) { + for(i=0; infile; i++) + *node->files[i] = PCA_F_ENIGMA; + }; + }; + return; +} + +#endif /* ifndef WITHOUT_FILE_SYSTEM */ diff --git a/libtecla-1.6.1/stringrp.c b/libtecla-1.6.1/stringrp.c new file mode 100644 index 0000000..2d84766 --- /dev/null +++ b/libtecla-1.6.1/stringrp.c @@ -0,0 +1,286 @@ +/* + * Copyright (c) 2000, 2001, 2002, 2003, 2004 by Martin C. Shepherd. + * + * All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, and/or sell copies of the Software, and to permit persons + * to whom the Software is furnished to do so, provided that the above + * copyright notice(s) and this permission notice appear in all copies of + * the Software and that both the above copyright notice(s) and this + * permission notice appear in supporting documentation. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT + * OF THIRD PARTY RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR + * HOLDERS INCLUDED IN THIS NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL + * INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING + * FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, + * NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION + * WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + * Except as contained in this notice, the name of a copyright holder + * shall not be used in advertising or otherwise to promote the sale, use + * or other dealings in this Software without prior written authorization + * of the copyright holder. + */ +#include +#include +#include +#include + +#include "freelist.h" +#include "stringrp.h" + +/* + * StringSegment objects store lots of small strings in larger + * character arrays. Since the total length of all of the strings can't + * be known in advance, an extensible list of large character arrays, + * called string-segments are used. + */ +typedef struct StringSegment StringSegment; +struct StringSegment { + StringSegment *next; /* A pointer to the next segment in the list */ + char *block; /* An array of characters to be shared between strings */ + int unused; /* The amount of unused space at the end of block[] */ +}; + +/* + * StringGroup is typedef'd in stringrp.h. + */ +struct StringGroup { + FreeList *node_mem; /* The StringSegment free-list */ + int block_size; /* The dimension of each character array block */ + StringSegment *head; /* The list of character arrays */ +}; + +/* + * Specify how many StringSegment's to allocate at a time. + */ +#define STR_SEG_BLK 20 + +/*....................................................................... + * Create a new StringGroup object. + * + * Input: + * segment_size int The length of each of the large character + * arrays in which multiple strings will be + * stored. This sets the length of longest + * string that can be stored, and for efficiency + * should be at least 10 times as large as + * the average string that will be stored. + * Output: + * return StringGroup * The new object, or NULL on error. + */ +StringGroup *_new_StringGroup(int segment_size) +{ + StringGroup *sg; /* The object to be returned */ +/* + * Check the arguments. + */ + if(segment_size < 1) { + errno = EINVAL; + return NULL; + }; +/* + * Allocate the container. + */ + sg = (StringGroup *) malloc(sizeof(StringGroup)); + if(!sg) { + errno = ENOMEM; + return NULL; + }; +/* + * Before attempting any operation that might fail, initialize the + * container at least up to the point at which it can safely be passed + * to _del_StringGroup(). + */ + sg->node_mem = NULL; + sg->head = NULL; + sg->block_size = segment_size; +/* + * Allocate the free list that is used to allocate list nodes. + */ + sg->node_mem = _new_FreeList(sizeof(StringSegment), STR_SEG_BLK); + if(!sg->node_mem) + return _del_StringGroup(sg); + return sg; +} + +/*....................................................................... + * Delete a StringGroup object. + * + * Input: + * sg StringGroup * The object to be deleted. + * Output: + * return StringGroup * The deleted object (always NULL). + */ +StringGroup *_del_StringGroup(StringGroup *sg) +{ + if(sg) { + StringSegment *node; +/* + * Delete the character arrays. + */ + for(node=sg->head; node; node=node->next) { + if(node->block) + free(node->block); + node->block = NULL; + }; +/* + * Delete the list nodes that contained the string segments. + */ + sg->node_mem = _del_FreeList(sg->node_mem, 1); + sg->head = NULL; /* Already deleted by deleting sg->node_mem */ +/* + * Delete the container. + */ + free(sg); + }; + return NULL; +} + +/*....................................................................... + * Make a copy of a string in the specified string group, and return + * a pointer to the copy. + * + * Input: + * sg StringGroup * The group to store the string in. + * string const char * The string to be recorded. + * remove_escapes int If true, omit backslashes which escape + * other characters when making the copy. + * Output: + * return char * The pointer to the copy of the string, + * or NULL if there was insufficient memory. + */ +char *_sg_store_string(StringGroup *sg, const char *string, int remove_escapes) +{ + char *copy; /* The recorded copy of string[] */ +/* + * Check the arguments. + */ + if(!sg || !string) + return NULL; +/* + * Get memory for the string. + */ + copy = _sg_alloc_string(sg, strlen(string)); + if(copy) { +/* + * If needed, remove backslash escapes while copying the input string + * into the cache string. + */ + if(remove_escapes) { + int escaped = 0; /* True if the next character should be */ + /* escaped. */ + const char *src = string; /* A pointer into the input string */ + char *dst = copy; /* A pointer into the cached copy of the */ + /* string. */ + while(*src) { + if(!escaped && *src == '\\') { + escaped = 1; + src++; + } else { + escaped = 0; + *dst++ = *src++; + }; + }; + *dst = '\0'; +/* + * If escapes have already been removed, copy the input string directly + * into the cache. + */ + } else { + strcpy(copy, string); + }; + }; +/* + * Return a pointer to the copy of the string (or NULL if the allocation + * failed). + */ + return copy; +} + +/*....................................................................... + * Allocate memory for a string of a given length. + * + * Input: + * sg StringGroup * The group to store the string in. + * length int The required length of the string. + * Output: + * return char * The pointer to the copy of the string, + * or NULL if there was insufficient memory. + */ +char *_sg_alloc_string(StringGroup *sg, int length) +{ + StringSegment *node; /* A node of the list of string segments */ + char *copy; /* The allocated string */ +/* + * If the string is longer than block_size, then we can't record it. + */ + if(length > sg->block_size || length < 0) + return NULL; +/* + * See if there is room to record the string in one of the existing + * string segments. Do this by advancing the node pointer until we find + * a node with length+1 bytes unused, or we get to the end of the list. + */ + for(node=sg->head; node && node->unused <= length; node=node->next) + ; +/* + * If there wasn't room, allocate a new string segment. + */ + if(!node) { + node = (StringSegment *) _new_FreeListNode(sg->node_mem); + if(!node) + return NULL; +/* + * Initialize the segment. + */ + node->next = NULL; + node->block = NULL; + node->unused = sg->block_size; +/* + * Attempt to allocate the string segment character array. + */ + node->block = (char *) malloc(sg->block_size); + if(!node->block) + return NULL; +/* + * Prepend the node to the list. + */ + node->next = sg->head; + sg->head = node; + }; +/* + * Get memory for the string. + */ + copy = node->block + sg->block_size - node->unused; + node->unused -= length + 1; +/* + * Return a pointer to the string memory. + */ + return copy; +} + +/*....................................................................... + * Delete all of the strings that are currently stored by a specified + * StringGroup object. + * + * Input: + * sg StringGroup * The group of strings to clear. + */ +void _clr_StringGroup(StringGroup *sg) +{ + StringSegment *node; /* A node in the list of string segments */ +/* + * Mark all of the string segments as unoccupied. + */ + for(node=sg->head; node; node=node->next) + node->unused = sg->block_size; + return; +} diff --git a/libtecla-1.6.1/stringrp.h b/libtecla-1.6.1/stringrp.h new file mode 100644 index 0000000..c5fdd3a --- /dev/null +++ b/libtecla-1.6.1/stringrp.h @@ -0,0 +1,84 @@ +#ifndef stringrp_h +#define stringrp_h +/* + * Copyright (c) 2000, 2001, 2002, 2003, 2004 by Martin C. Shepherd. + * + * All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, and/or sell copies of the Software, and to permit persons + * to whom the Software is furnished to do so, provided that the above + * copyright notice(s) and this permission notice appear in all copies of + * the Software and that both the above copyright notice(s) and this + * permission notice appear in supporting documentation. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT + * OF THIRD PARTY RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR + * HOLDERS INCLUDED IN THIS NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL + * INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING + * FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, + * NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION + * WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + * Except as contained in this notice, the name of a copyright holder + * shall not be used in advertising or otherwise to promote the sale, use + * or other dealings in this Software without prior written authorization + * of the copyright holder. + */ + +/* + * StringGroup objects provide memory for modules that need to + * allocate lots of small strings without needing to free any of them + * individually, but rather is happy to free them all at the same + * time. Taking advantage of these properties, StringGroup objects + * avoid the heap fragmentation that tends to occur when lots of small + * strings are allocated directly from the heap and later free'd. They + * do this by allocating a list of large character arrays in each of + * which multiple strings are stored. Thus instead of allocating lots + * of small strings, a few large character arrays are allocated. When + * the strings are free'd on mass, this list of character arrays is + * maintained, ready for subsequent use in recording another set of + * strings. + */ +typedef struct StringGroup StringGroup; + +/* + * The following constructor allocates a string-allocation object. + * The segment_size argument specifies how long each string segment + * array should be. This should be at least 10 times the length of + * the average string to be recorded in the string group, and + * sets the length of the longest string that can be stored. + */ +StringGroup *_new_StringGroup(int segment_size); + +/* + * Delete all of the strings that are currently stored by a specified + * StringGroup object. + */ +void _clr_StringGroup(StringGroup *sg); + +/* + * Make a copy of the specified string, returning a pointer to + * the copy, or NULL if there was insufficient memory. If the + * remove_escapes argument is non-zero, backslashes that escape + * other characters will be removed. + */ +char *_sg_store_string(StringGroup *sg, const char *string, int remove_escapes); + +/* + * Allocate memory for a string of a given length. + */ +char *_sg_alloc_string(StringGroup *sg, int length); + +/* + * Delete a StringGroup object (and all of the strings that it + * contains). + */ +StringGroup *_del_StringGroup(StringGroup *sg); + +#endif diff --git a/libtecla-1.6.1/strngmem.c b/libtecla-1.6.1/strngmem.c new file mode 100644 index 0000000..c2637ec --- /dev/null +++ b/libtecla-1.6.1/strngmem.c @@ -0,0 +1,218 @@ +/* + * Copyright (c) 2000, 2001, 2002, 2003, 2004 by Martin C. Shepherd. + * + * All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, and/or sell copies of the Software, and to permit persons + * to whom the Software is furnished to do so, provided that the above + * copyright notice(s) and this permission notice appear in all copies of + * the Software and that both the above copyright notice(s) and this + * permission notice appear in supporting documentation. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT + * OF THIRD PARTY RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR + * HOLDERS INCLUDED IN THIS NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL + * INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING + * FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, + * NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION + * WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + * Except as contained in this notice, the name of a copyright holder + * shall not be used in advertising or otherwise to promote the sale, use + * or other dealings in this Software without prior written authorization + * of the copyright holder. + */ + +#include +#include +#include + +#include "strngmem.h" +#include "freelist.h" + +struct StringMem { + unsigned long nmalloc; /* The number of strings allocated with malloc */ + FreeList *fl; /* The free-list */ +}; + +/*....................................................................... + * Create a string free-list container and the first block of its free-list. + * + * Input: + * blocking_factor int The blocking_factor argument specifies how + * many strings of length SM_STRLEN + * bytes (see stringmem.h) are allocated in each + * free-list block. + * For example if blocking_factor=64 and + * SM_STRLEN=16, then each new + * free-list block will take 1K of memory. + * Output: + * return StringMem * The new free-list container, or NULL on + * error. + */ +StringMem *_new_StringMem(unsigned blocking_factor) +{ + StringMem *sm; /* The container to be returned. */ +/* + * Check arguments. + */ + if(blocking_factor < 1) { + errno = EINVAL; + return NULL; + }; +/* + * Allocate the container. + */ + sm = (StringMem *) malloc(sizeof(StringMem)); + if(!sm) { + errno = ENOMEM; + return NULL; + }; +/* + * Before attempting any operation that might fail, initialize + * the container at least up to the point at which it can safely + * be passed to _del_StringMem(). + */ + sm->nmalloc = 0; + sm->fl = NULL; +/* + * Allocate the free-list. + */ + sm->fl = _new_FreeList(SM_STRLEN, blocking_factor); + if(!sm->fl) + return _del_StringMem(sm, 1); +/* + * Return the free-list container. + */ + return sm; +} + +/*....................................................................... + * Delete a string free-list. + * + * Input: + * sm StringMem * The string free-list to be deleted, or NULL. + * force int If force==0 then _del_StringMem() will complain + * and refuse to delete the free-list if any + * of nodes have not been returned to the free-list. + * If force!=0 then _del_StringMem() will not check + * whether any nodes are still in use and will + * always delete the list. + * Output: + * return StringMem * Always NULL (even if the list couldn't be + * deleted). + */ +StringMem *_del_StringMem(StringMem *sm, int force) +{ + if(sm) { +/* + * Check whether any strings have not been returned to the free-list. + */ + if(!force && (sm->nmalloc > 0 || _busy_FreeListNodes(sm->fl) > 0)) { + errno = EBUSY; + return NULL; + }; +/* + * Delete the free-list. + */ + sm->fl = _del_FreeList(sm->fl, force); +/* + * Delete the container. + */ + free(sm); + }; + return NULL; +} + +/*....................................................................... + * Allocate an array of 'length' chars. + * + * Input: + * sm StringMem * The string free-list to allocate from. + * length size_t The length of the new string (including '\0'). + * Output: + * return char * The new string or NULL on error. + */ +char *_new_StringMemString(StringMem *sm, size_t length) +{ + char *string; /* The string to be returned */ + int was_malloc; /* True if malloc was used to allocate the string */ +/* + * Check arguments. + */ + if(!sm) + return NULL; + if(length < 1) + length = 1; +/* + * Allocate the new node from the free list if possible. + */ + if(length < SM_STRLEN) { + string = (char *)_new_FreeListNode(sm->fl); + if(!string) + return NULL; + was_malloc = 0; + } else { + string = (char *) malloc(length+1); /* Leave room for the flag byte */ + if(!string) + return NULL; +/* + * Count malloc allocations. + */ + was_malloc = 1; + sm->nmalloc++; + }; +/* + * Use the first byte of the string to record whether the string was + * allocated with malloc or from the free-list. Then return the rest + * of the string for use by the user. + */ + string[0] = (char) was_malloc; + return string + 1; +} + +/*....................................................................... + * Free a string that was previously returned by _new_StringMemString(). + * + * Input: + * sm StringMem * The free-list from which the string was originally + * allocated. + * s char * The string to be returned to the free-list, or NULL. + * Output: + * return char * Always NULL. + */ +char *_del_StringMemString(StringMem *sm, char *s) +{ + int was_malloc; /* True if the string originally came from malloc() */ +/* + * Is there anything to be deleted? + */ + if(s && sm) { +/* + * Retrieve the true string pointer. This is one less than the one + * returned by _new_StringMemString() because the first byte of the + * allocated memory is reserved by _new_StringMemString as a flag byte + * to say whether the memory was allocated from the free-list or directly + * from malloc(). + */ + s--; +/* + * Get the origination flag. + */ + was_malloc = s[0]; + if(was_malloc) { + free(s); + s = NULL; + sm->nmalloc--; + } else { + s = (char *) _del_FreeListNode(sm->fl, s); + }; + }; + return NULL; +} diff --git a/libtecla-1.6.1/strngmem.h b/libtecla-1.6.1/strngmem.h new file mode 100644 index 0000000..9efef09 --- /dev/null +++ b/libtecla-1.6.1/strngmem.h @@ -0,0 +1,80 @@ +#ifndef stringmem_h +#define stringmem_h +/* + * Copyright (c) 2000, 2001, 2002, 2003, 2004 by Martin C. Shepherd. + * + * All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, and/or sell copies of the Software, and to permit persons + * to whom the Software is furnished to do so, provided that the above + * copyright notice(s) and this permission notice appear in all copies of + * the Software and that both the above copyright notice(s) and this + * permission notice appear in supporting documentation. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT + * OF THIRD PARTY RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR + * HOLDERS INCLUDED IN THIS NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL + * INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING + * FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, + * NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION + * WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + * Except as contained in this notice, the name of a copyright holder + * shall not be used in advertising or otherwise to promote the sale, use + * or other dealings in this Software without prior written authorization + * of the copyright holder. + */ + +typedef struct StringMem StringMem; + +/* + * Applications that dynamically allocate lots of small strings + * run the risk of significantly fragmenting the heap. This module + * aims to reduce this risk by allocating large arrays of small fixed + * length strings, arranging them as a free-list and allowing + * callers to allocate from the list. Strings that are too long + * to be allocated from the free-list are allocated from the heap. + * Since typical implementations of malloc() eat up a minimum of + * 16 bytes per call to malloc() [because of alignment and space + * management constraints] it makes sense to set the free-list + * string size to 16 bytes. Note that unlike malloc() which typically + * keeps 8 bytes per allocation for its own use, our allocator will + * return all but one of the 16 bytes for use. One hidden byte of overhead + * is reserved for flagging whether the string was allocated directly + * from malloc or from the free-list. + */ + +/* + * Set the length of each free-list string. The longest string that + * will be returned without calling malloc() will be one less than + * this number. + */ +#define SM_STRLEN 16 + +/* + * Create a string free-list container and the first block of its free-list. + */ +StringMem *_new_StringMem(unsigned blocking_factor); + +/* + * Delete a string free-list. + */ +StringMem *_del_StringMem(StringMem *sm, int force); + +/* + * Allocate an array of 'length' chars. + */ +char *_new_StringMemString(StringMem *sm, size_t size); + +/* + * Free a string that was previously returned by _new_StringMemString(). + */ +char *_del_StringMemString(StringMem *sm, char *s); + +#endif diff --git a/libtecla-1.6.1/update_html b/libtecla-1.6.1/update_html new file mode 100755 index 0000000..70f189e --- /dev/null +++ b/libtecla-1.6.1/update_html @@ -0,0 +1,37 @@ +#!/bin/sh + +# Convert man pages to html files. + +for dir in man/prog man/libr man/func man/misc man/file; do + for template in $dir/*.in;do + page=`basename "$template" .in` + if [ `wc -l < $template` -gt 1 ]; then + html="html/$page.html" + man2html $template > $html + for ref in libtecla cpl_complete_word ef_expand_file gl_get_line pca_lookup_file enhance gl_io_mode tecla; do + link="$ref.html" + ed -s $html << EOF + %s|$ref[(][^)][^) ]*[)]|$ref|g + w + q +EOF + done + fi + done +done + +# Convert the change log into a web page. + +cd html +echo 'The tecla library change log' > changes.html +echo '
' >> changes.html
+sed 's/&/&/g; s//\>/g' ../CHANGES >> changes.html
+echo '
' >> changes.html + +# Do the same to the release-notes file. + +cd ../html +echo 'The tecla library release notes' > release.html +echo '
' >> release.html
+sed 's/&/&/g; s/> release.html
+echo '
' >> release.html diff --git a/libtecla-1.6.1/update_version b/libtecla-1.6.1/update_version new file mode 100755 index 0000000..c18f714 --- /dev/null +++ b/libtecla-1.6.1/update_version @@ -0,0 +1,82 @@ +#!/bin/sh +#----------------------------------------------------------------------- +# Change the version number of the library. This changes the number in +# every file that it is known to appear in. +# +# Usage: +# update_version major minor micro +#----------------------------------------------------------------------- + +usage="$0 major minor micro" + +if [ $# -ne 3 ]; then + echo $usage + exit 1 +fi + +# Get the three components of the version number. + +major="$1" +minor="$2" +micro="$3" + +# Everything will need to be reconfigured after this change, so +# discard any existing configuration. + +make distclean 2>/dev/null + +# Check that the version components are all positive integers. + +for c in $major $minor $micro; do + if echo "$c" | awk '{exit $1 ~ /^[0-9]+$/}'; then + echo 'Version number components must all be positive integers.' + exit 1 + fi +done + +# +# Update the version number in the configure.in script. +# +ed -s configure.in << EOF +/^MAJOR_VER=\"[0-9][0-9]*\"/ s/^.*$/MAJOR_VER=\"$major\"/ +/^MINOR_VER=\"[0-9][0-9]*\"/ s/^.*$/MINOR_VER=\"$minor\"/ +/^MICRO_VER=\"[0-9][0-9]*\"/ s/^.*$/MICRO_VER=\"$micro\"/ +w +q +EOF + +if which autoconf 1>/dev/null 2>&1; then + autoconf +else + echo 'Note that autoconf needs to be run.' +fi + +# +# Update the version number in the libtecla header file script. +# +ed -s libtecla.h << EOF +/^#define TECLA_MAJOR_VER [0-9][0-9]*/ s/^.*$/#define TECLA_MAJOR_VER $major/ +/^#define TECLA_MINOR_VER [0-9][0-9]*/ s/^.*$/#define TECLA_MINOR_VER $minor/ +/^#define TECLA_MICRO_VER [0-9][0-9]*/ s/^.*$/#define TECLA_MICRO_VER $micro/ +w +q +EOF + +# +# Update the version number in the README file. +# +ed -s README << EOF +/version [0-9][0-9]*\.[0-9][0-9]*\.[0-9][0-9]* / s/version [0-9][0-9]*\.[0-9][0-9]*\.[0-9][0-9]*/version $major.$minor.$micro/ +w +q +EOF + +# +# Update the version number in the html index file. +# +ed -s html/index.html << EOF +/version [0-9][0-9]*\.[0-9][0-9]*\.[0-9][0-9]*\./ s/version [0-9][0-9]*\.[0-9][0-9]*\.[0-9][0-9]*/version $major.$minor.$micro/g +/libtecla-[0-9][0-9]*\.[0-9][0-9]*\.[0-9][0-9]*\./ s/libtecla-[0-9][0-9]*\.[0-9][0-9]*\.[0-9][0-9]*\./libtecla-$major.$minor.$micro./g +w +q +EOF diff --git a/libtecla-1.6.1/version.c b/libtecla-1.6.1/version.c new file mode 100644 index 0000000..9e1275e --- /dev/null +++ b/libtecla-1.6.1/version.c @@ -0,0 +1,30 @@ +#include "libtecla.h" + +/*....................................................................... + * Return the version number of the tecla library. + * + * Input: + * major int * The major version number of the library + * will be assigned to *major. This number is + * only incremented when a change to the library is + * made that breaks binary (shared library) and/or + * compilation backwards compatibility. + * minor int * The minor version number of the library + * will be assigned to *minor. This number is + * incremented whenever new functions are added to + * the public API. + * micro int * The micro version number of the library will be + * assigned to *micro. This number is incremented + * whenever internal changes are made that don't + * change the public API, such as bug fixes and + * performance enhancements. + */ +void libtecla_version(int *major, int *minor, int *micro) +{ + if(major) + *major = TECLA_MAJOR_VER; + if(minor) + *minor = TECLA_MINOR_VER; + if(micro) + *micro = TECLA_MICRO_VER; +} -- cgit v1.2.3