From e96199c670ce672049d7f009bd03258649352fc5 Mon Sep 17 00:00:00 2001 From: Joel Sherrill Date: Wed, 27 May 2015 13:48:33 -0700 Subject: Rename libtecla to a versioned directory (1.6.3) --- libtecla-1.6.3/CHANGES | 2823 ++++ libtecla-1.6.3/INSTALL | 213 + libtecla-1.6.3/LICENSE.TERMS | 28 + libtecla-1.6.3/Makefile | 12 + libtecla-1.6.3/Makefile.in | 272 + libtecla-1.6.3/Makefile.rules | 169 + libtecla-1.6.3/Makefile.stub | 12 + libtecla-1.6.3/PORTING | 38 + libtecla-1.6.3/README | 53 + libtecla-1.6.3/RELEASE.NOTES | 601 + libtecla-1.6.3/chrqueue.c | 432 + libtecla-1.6.3/chrqueue.h | 106 + libtecla-1.6.3/config.guess | 1410 ++ libtecla-1.6.3/config.sub | 1510 +++ libtecla-1.6.3/configure | 5477 ++++++++ libtecla-1.6.3/configure.in | 614 + libtecla-1.6.3/cplfile.c | 870 ++ libtecla-1.6.3/cplfile.h | 96 + libtecla-1.6.3/cplmatch.c | 1170 ++ libtecla-1.6.3/cplmatch.h | 47 + libtecla-1.6.3/demo.c | 166 + libtecla-1.6.3/demo2.c | 423 + libtecla-1.6.3/demo3.c | 738 ++ libtecla-1.6.3/direader.c | 309 + libtecla-1.6.3/direader.h | 44 + libtecla-1.6.3/enhance.c | 698 + libtecla-1.6.3/errmsg.c | 167 + libtecla-1.6.3/errmsg.h | 85 + libtecla-1.6.3/expand.c | 1448 +++ libtecla-1.6.3/expand.h | 48 + libtecla-1.6.3/freelist.c | 400 + libtecla-1.6.3/freelist.h | 87 + libtecla-1.6.3/getline.c | 12849 +++++++++++++++++++ libtecla-1.6.3/getline.h | 88 + libtecla-1.6.3/hash.c | 737 ++ libtecla-1.6.3/hash.h | 157 + libtecla-1.6.3/history.c | 2842 ++++ libtecla-1.6.3/history.h | 169 + libtecla-1.6.3/homedir.c | 470 + libtecla-1.6.3/homedir.h | 81 + libtecla-1.6.3/html/changes.html | 2826 ++++ libtecla-1.6.3/html/cpl_complete_word.html | 477 + libtecla-1.6.3/html/ef_expand_file.html | 273 + libtecla-1.6.3/html/enhance.html | 105 + libtecla-1.6.3/html/gl_get_line.html | 2407 ++++ libtecla-1.6.3/html/gl_io_mode.html | 634 + libtecla-1.6.3/html/index.html | 73 + libtecla-1.6.3/html/libtecla.html | 199 + libtecla-1.6.3/html/pca_lookup_file.html | 420 + libtecla-1.6.3/html/release.html | 604 + libtecla-1.6.3/html/tecla.html | 1276 ++ libtecla-1.6.3/install-sh | 251 + libtecla-1.6.3/ioutil.c | 330 + libtecla-1.6.3/ioutil.h | 73 + libtecla-1.6.3/keytab.c | 1022 ++ libtecla-1.6.3/keytab.h | 157 + libtecla-1.6.3/libtecla.h | 1834 +++ libtecla-1.6.3/libtecla.map | 155 + libtecla-1.6.3/man/file/teclarc.in | 1 + libtecla-1.6.3/man/func/cfc_file_start.in | 1 + libtecla-1.6.3/man/func/cfc_literal_escapes.in | 1 + libtecla-1.6.3/man/func/cfc_set_check_fn.in | 1 + libtecla-1.6.3/man/func/cpl_add_completion.in | 1 + libtecla-1.6.3/man/func/cpl_complete_word.in | 441 + libtecla-1.6.3/man/func/cpl_file_completions.in | 1 + libtecla-1.6.3/man/func/cpl_last_error.in | 1 + libtecla-1.6.3/man/func/cpl_list_completions.in | 1 + libtecla-1.6.3/man/func/cpl_recall_matches.in | 1 + libtecla-1.6.3/man/func/cpl_record_error.in | 1 + libtecla-1.6.3/man/func/del_CplFileConf.in | 1 + libtecla-1.6.3/man/func/del_ExpandFile.in | 1 + libtecla-1.6.3/man/func/del_GetLine.in | 1 + libtecla-1.6.3/man/func/del_PathCache.in | 1 + libtecla-1.6.3/man/func/del_PcaPathConf.in | 1 + libtecla-1.6.3/man/func/del_WordCompletion.in | 1 + libtecla-1.6.3/man/func/ef_expand_file.in | 248 + libtecla-1.6.3/man/func/ef_last_error.in | 1 + libtecla-1.6.3/man/func/ef_list_expansions.in | 1 + libtecla-1.6.3/man/func/gl_abandon_line.in | 1 + libtecla-1.6.3/man/func/gl_bind_keyseq.in | 1 + libtecla-1.6.3/man/func/gl_catch_blocked.in | 1 + libtecla-1.6.3/man/func/gl_change_terminal.in | 1 + libtecla-1.6.3/man/func/gl_clear_history.in | 1 + libtecla-1.6.3/man/func/gl_completion_action.in | 1 + libtecla-1.6.3/man/func/gl_configure_getline.in | 1 + libtecla-1.6.3/man/func/gl_customize_completion.in | 1 + libtecla-1.6.3/man/func/gl_display_text.in | 1 + libtecla-1.6.3/man/func/gl_echo_mode.in | 1 + libtecla-1.6.3/man/func/gl_erase_terminal.in | 1 + libtecla-1.6.3/man/func/gl_error_message.in | 1 + libtecla-1.6.3/man/func/gl_get_line.in | 2236 ++++ libtecla-1.6.3/man/func/gl_group_history.in | 1 + libtecla-1.6.3/man/func/gl_handle_signal.in | 1 + libtecla-1.6.3/man/func/gl_ignore_signal.in | 1 + libtecla-1.6.3/man/func/gl_inactivity_timeout.in | 1 + libtecla-1.6.3/man/func/gl_io_mode.in | 571 + libtecla-1.6.3/man/func/gl_last_signal.in | 1 + libtecla-1.6.3/man/func/gl_limit_history.in | 1 + libtecla-1.6.3/man/func/gl_list_signals.in | 1 + libtecla-1.6.3/man/func/gl_load_history.in | 1 + libtecla-1.6.3/man/func/gl_lookup_history.in | 1 + libtecla-1.6.3/man/func/gl_normal_io.in | 1 + libtecla-1.6.3/man/func/gl_pending_io.in | 1 + libtecla-1.6.3/man/func/gl_prompt_style.in | 1 + libtecla-1.6.3/man/func/gl_query_char.in | 1 + libtecla-1.6.3/man/func/gl_range_of_history.in | 1 + libtecla-1.6.3/man/func/gl_raw_io.in | 1 + libtecla-1.6.3/man/func/gl_read_char.in | 1 + libtecla-1.6.3/man/func/gl_register_action.in | 1 + libtecla-1.6.3/man/func/gl_resize_history.in | 1 + libtecla-1.6.3/man/func/gl_return_status.in | 1 + libtecla-1.6.3/man/func/gl_save_history.in | 1 + libtecla-1.6.3/man/func/gl_set_term_size.in | 1 + libtecla-1.6.3/man/func/gl_show_history.in | 1 + libtecla-1.6.3/man/func/gl_size_of_history.in | 1 + libtecla-1.6.3/man/func/gl_state_of_history.in | 1 + libtecla-1.6.3/man/func/gl_terminal_size.in | 1 + libtecla-1.6.3/man/func/gl_toggle_history.in | 1 + libtecla-1.6.3/man/func/gl_trap_signal.in | 1 + libtecla-1.6.3/man/func/gl_tty_signals.in | 1 + libtecla-1.6.3/man/func/gl_watch_fd.in | 1 + libtecla-1.6.3/man/func/libtecla_version.in | 1 + libtecla-1.6.3/man/func/new_CplFileConf.in | 1 + libtecla-1.6.3/man/func/new_ExpandFile.in | 1 + libtecla-1.6.3/man/func/new_GetLine.in | 1 + libtecla-1.6.3/man/func/new_PathCache.in | 1 + libtecla-1.6.3/man/func/new_PcaPathConf.in | 1 + libtecla-1.6.3/man/func/new_WordCompletion.in | 1 + libtecla-1.6.3/man/func/pca_last_error.in | 1 + libtecla-1.6.3/man/func/pca_lookup_file.in | 365 + libtecla-1.6.3/man/func/pca_path_completions.in | 1 + libtecla-1.6.3/man/func/pca_scan_path.in | 1 + libtecla-1.6.3/man/func/pca_set_check_fn.in | 1 + libtecla-1.6.3/man/func/ppc_file_start.in | 1 + libtecla-1.6.3/man/func/ppc_literal_escapes.in | 1 + libtecla-1.6.3/man/libr/libtecla.in | 168 + libtecla-1.6.3/man/misc/tecla.in | 1201 ++ libtecla-1.6.3/man/prog/enhance.in | 89 + libtecla-1.6.3/pathutil.c | 539 + libtecla-1.6.3/pathutil.h | 122 + libtecla-1.6.3/pcache.c | 1710 +++ libtecla-1.6.3/stringrp.c | 286 + libtecla-1.6.3/stringrp.h | 84 + libtecla-1.6.3/strngmem.c | 218 + libtecla-1.6.3/strngmem.h | 80 + libtecla-1.6.3/update_html | 29 + libtecla-1.6.3/update_version | 82 + libtecla-1.6.3/version.c | 30 + libtecla/CHANGES | 2823 ---- libtecla/INSTALL | 213 - libtecla/LICENSE.TERMS | 28 - libtecla/Makefile | 12 - libtecla/Makefile.in | 272 - libtecla/Makefile.rules | 169 - libtecla/Makefile.stub | 12 - libtecla/PORTING | 38 - libtecla/README | 53 - libtecla/RELEASE.NOTES | 601 - libtecla/chrqueue.c | 432 - libtecla/chrqueue.h | 106 - libtecla/config.guess | 1410 -- libtecla/config.sub | 1510 --- libtecla/configure | 5477 -------- libtecla/configure.in | 614 - libtecla/cplfile.c | 870 -- libtecla/cplfile.h | 96 - libtecla/cplmatch.c | 1170 -- libtecla/cplmatch.h | 47 - libtecla/demo.c | 166 - libtecla/demo2.c | 423 - libtecla/demo3.c | 738 -- libtecla/direader.c | 309 - libtecla/direader.h | 44 - libtecla/enhance.c | 698 - libtecla/errmsg.c | 167 - libtecla/errmsg.h | 85 - libtecla/expand.c | 1448 --- libtecla/expand.h | 48 - libtecla/freelist.c | 400 - libtecla/freelist.h | 87 - libtecla/getline.c | 12849 ------------------- libtecla/getline.h | 88 - libtecla/hash.c | 737 -- libtecla/hash.h | 157 - libtecla/history.c | 2842 ---- libtecla/history.h | 169 - libtecla/homedir.c | 470 - libtecla/homedir.h | 81 - libtecla/html/changes.html | 2826 ---- libtecla/html/cpl_complete_word.html | 477 - libtecla/html/ef_expand_file.html | 273 - libtecla/html/enhance.html | 105 - libtecla/html/gl_get_line.html | 2407 ---- libtecla/html/gl_io_mode.html | 634 - libtecla/html/index.html | 73 - libtecla/html/libtecla.html | 199 - libtecla/html/pca_lookup_file.html | 420 - libtecla/html/release.html | 604 - libtecla/html/tecla.html | 1276 -- libtecla/install-sh | 251 - libtecla/ioutil.c | 330 - libtecla/ioutil.h | 73 - libtecla/keytab.c | 1022 -- libtecla/keytab.h | 157 - libtecla/libtecla.h | 1834 --- libtecla/libtecla.map | 155 - libtecla/man/file/teclarc.in | 1 - libtecla/man/func/cfc_file_start.in | 1 - libtecla/man/func/cfc_literal_escapes.in | 1 - libtecla/man/func/cfc_set_check_fn.in | 1 - libtecla/man/func/cpl_add_completion.in | 1 - libtecla/man/func/cpl_complete_word.in | 441 - libtecla/man/func/cpl_file_completions.in | 1 - libtecla/man/func/cpl_last_error.in | 1 - libtecla/man/func/cpl_list_completions.in | 1 - libtecla/man/func/cpl_recall_matches.in | 1 - libtecla/man/func/cpl_record_error.in | 1 - libtecla/man/func/del_CplFileConf.in | 1 - libtecla/man/func/del_ExpandFile.in | 1 - libtecla/man/func/del_GetLine.in | 1 - libtecla/man/func/del_PathCache.in | 1 - libtecla/man/func/del_PcaPathConf.in | 1 - libtecla/man/func/del_WordCompletion.in | 1 - libtecla/man/func/ef_expand_file.in | 248 - libtecla/man/func/ef_last_error.in | 1 - libtecla/man/func/ef_list_expansions.in | 1 - libtecla/man/func/gl_abandon_line.in | 1 - libtecla/man/func/gl_bind_keyseq.in | 1 - libtecla/man/func/gl_catch_blocked.in | 1 - libtecla/man/func/gl_change_terminal.in | 1 - libtecla/man/func/gl_clear_history.in | 1 - libtecla/man/func/gl_completion_action.in | 1 - libtecla/man/func/gl_configure_getline.in | 1 - libtecla/man/func/gl_customize_completion.in | 1 - libtecla/man/func/gl_display_text.in | 1 - libtecla/man/func/gl_echo_mode.in | 1 - libtecla/man/func/gl_erase_terminal.in | 1 - libtecla/man/func/gl_error_message.in | 1 - libtecla/man/func/gl_get_line.in | 2236 ---- libtecla/man/func/gl_group_history.in | 1 - libtecla/man/func/gl_handle_signal.in | 1 - libtecla/man/func/gl_ignore_signal.in | 1 - libtecla/man/func/gl_inactivity_timeout.in | 1 - libtecla/man/func/gl_io_mode.in | 571 - libtecla/man/func/gl_last_signal.in | 1 - libtecla/man/func/gl_limit_history.in | 1 - libtecla/man/func/gl_list_signals.in | 1 - libtecla/man/func/gl_load_history.in | 1 - libtecla/man/func/gl_lookup_history.in | 1 - libtecla/man/func/gl_normal_io.in | 1 - libtecla/man/func/gl_pending_io.in | 1 - libtecla/man/func/gl_prompt_style.in | 1 - libtecla/man/func/gl_query_char.in | 1 - libtecla/man/func/gl_range_of_history.in | 1 - libtecla/man/func/gl_raw_io.in | 1 - libtecla/man/func/gl_read_char.in | 1 - libtecla/man/func/gl_register_action.in | 1 - libtecla/man/func/gl_resize_history.in | 1 - libtecla/man/func/gl_return_status.in | 1 - libtecla/man/func/gl_save_history.in | 1 - libtecla/man/func/gl_set_term_size.in | 1 - libtecla/man/func/gl_show_history.in | 1 - libtecla/man/func/gl_size_of_history.in | 1 - libtecla/man/func/gl_state_of_history.in | 1 - libtecla/man/func/gl_terminal_size.in | 1 - libtecla/man/func/gl_toggle_history.in | 1 - libtecla/man/func/gl_trap_signal.in | 1 - libtecla/man/func/gl_tty_signals.in | 1 - libtecla/man/func/gl_watch_fd.in | 1 - libtecla/man/func/libtecla_version.in | 1 - libtecla/man/func/new_CplFileConf.in | 1 - libtecla/man/func/new_ExpandFile.in | 1 - libtecla/man/func/new_GetLine.in | 1 - libtecla/man/func/new_PathCache.in | 1 - libtecla/man/func/new_PcaPathConf.in | 1 - libtecla/man/func/new_WordCompletion.in | 1 - libtecla/man/func/pca_last_error.in | 1 - libtecla/man/func/pca_lookup_file.in | 365 - libtecla/man/func/pca_path_completions.in | 1 - libtecla/man/func/pca_scan_path.in | 1 - libtecla/man/func/pca_set_check_fn.in | 1 - libtecla/man/func/ppc_file_start.in | 1 - libtecla/man/func/ppc_literal_escapes.in | 1 - libtecla/man/libr/libtecla.in | 168 - libtecla/man/misc/tecla.in | 1201 -- libtecla/man/prog/enhance.in | 89 - libtecla/pathutil.c | 539 - libtecla/pathutil.h | 122 - libtecla/pcache.c | 1710 --- libtecla/stringrp.c | 286 - libtecla/stringrp.h | 84 - libtecla/strngmem.c | 218 - libtecla/strngmem.h | 80 - libtecla/update_html | 29 - libtecla/update_version | 82 - libtecla/version.c | 30 - 296 files changed, 59646 insertions(+), 59646 deletions(-) create mode 100644 libtecla-1.6.3/CHANGES create mode 100644 libtecla-1.6.3/INSTALL create mode 100644 libtecla-1.6.3/LICENSE.TERMS create mode 100644 libtecla-1.6.3/Makefile create mode 100644 libtecla-1.6.3/Makefile.in create mode 100644 libtecla-1.6.3/Makefile.rules create mode 100644 libtecla-1.6.3/Makefile.stub create mode 100644 libtecla-1.6.3/PORTING create mode 100644 libtecla-1.6.3/README create mode 100644 libtecla-1.6.3/RELEASE.NOTES create mode 100644 libtecla-1.6.3/chrqueue.c create mode 100644 libtecla-1.6.3/chrqueue.h create mode 100644 libtecla-1.6.3/config.guess create mode 100644 libtecla-1.6.3/config.sub create mode 100755 libtecla-1.6.3/configure create mode 100644 libtecla-1.6.3/configure.in create mode 100644 libtecla-1.6.3/cplfile.c create mode 100644 libtecla-1.6.3/cplfile.h create mode 100644 libtecla-1.6.3/cplmatch.c create mode 100644 libtecla-1.6.3/cplmatch.h create mode 100644 libtecla-1.6.3/demo.c create mode 100644 libtecla-1.6.3/demo2.c create mode 100644 libtecla-1.6.3/demo3.c create mode 100644 libtecla-1.6.3/direader.c create mode 100644 libtecla-1.6.3/direader.h create mode 100644 libtecla-1.6.3/enhance.c create mode 100644 libtecla-1.6.3/errmsg.c create mode 100644 libtecla-1.6.3/errmsg.h create mode 100644 libtecla-1.6.3/expand.c create mode 100644 libtecla-1.6.3/expand.h create mode 100644 libtecla-1.6.3/freelist.c create mode 100644 libtecla-1.6.3/freelist.h create mode 100644 libtecla-1.6.3/getline.c create mode 100644 libtecla-1.6.3/getline.h create mode 100644 libtecla-1.6.3/hash.c create mode 100644 libtecla-1.6.3/hash.h create mode 100644 libtecla-1.6.3/history.c create mode 100644 libtecla-1.6.3/history.h create mode 100644 libtecla-1.6.3/homedir.c create mode 100644 libtecla-1.6.3/homedir.h create mode 100644 libtecla-1.6.3/html/changes.html create mode 100644 libtecla-1.6.3/html/cpl_complete_word.html create mode 100644 libtecla-1.6.3/html/ef_expand_file.html create mode 100644 libtecla-1.6.3/html/enhance.html create mode 100644 libtecla-1.6.3/html/gl_get_line.html create mode 100644 libtecla-1.6.3/html/gl_io_mode.html create mode 100644 libtecla-1.6.3/html/index.html create mode 100644 libtecla-1.6.3/html/libtecla.html create mode 100644 libtecla-1.6.3/html/pca_lookup_file.html create mode 100644 libtecla-1.6.3/html/release.html create mode 100644 libtecla-1.6.3/html/tecla.html create mode 100755 libtecla-1.6.3/install-sh create mode 100644 libtecla-1.6.3/ioutil.c create mode 100644 libtecla-1.6.3/ioutil.h create mode 100644 libtecla-1.6.3/keytab.c create mode 100644 libtecla-1.6.3/keytab.h create mode 100644 libtecla-1.6.3/libtecla.h create mode 100644 libtecla-1.6.3/libtecla.map create mode 100644 libtecla-1.6.3/man/file/teclarc.in create mode 100644 libtecla-1.6.3/man/func/cfc_file_start.in create mode 100644 libtecla-1.6.3/man/func/cfc_literal_escapes.in create mode 100644 libtecla-1.6.3/man/func/cfc_set_check_fn.in create mode 100644 libtecla-1.6.3/man/func/cpl_add_completion.in create mode 100644 libtecla-1.6.3/man/func/cpl_complete_word.in create mode 100644 libtecla-1.6.3/man/func/cpl_file_completions.in create mode 100644 libtecla-1.6.3/man/func/cpl_last_error.in create mode 100644 libtecla-1.6.3/man/func/cpl_list_completions.in create mode 100644 libtecla-1.6.3/man/func/cpl_recall_matches.in create mode 100644 libtecla-1.6.3/man/func/cpl_record_error.in create mode 100644 libtecla-1.6.3/man/func/del_CplFileConf.in create mode 100644 libtecla-1.6.3/man/func/del_ExpandFile.in create mode 100644 libtecla-1.6.3/man/func/del_GetLine.in create mode 100644 libtecla-1.6.3/man/func/del_PathCache.in create mode 100644 libtecla-1.6.3/man/func/del_PcaPathConf.in create mode 100644 libtecla-1.6.3/man/func/del_WordCompletion.in create mode 100644 libtecla-1.6.3/man/func/ef_expand_file.in create mode 100644 libtecla-1.6.3/man/func/ef_last_error.in create mode 100644 libtecla-1.6.3/man/func/ef_list_expansions.in create mode 100644 libtecla-1.6.3/man/func/gl_abandon_line.in create mode 100644 libtecla-1.6.3/man/func/gl_bind_keyseq.in create mode 100644 libtecla-1.6.3/man/func/gl_catch_blocked.in create mode 100644 libtecla-1.6.3/man/func/gl_change_terminal.in create mode 100644 libtecla-1.6.3/man/func/gl_clear_history.in create mode 100644 libtecla-1.6.3/man/func/gl_completion_action.in create mode 100644 libtecla-1.6.3/man/func/gl_configure_getline.in create mode 100644 libtecla-1.6.3/man/func/gl_customize_completion.in create mode 100644 libtecla-1.6.3/man/func/gl_display_text.in create mode 100644 libtecla-1.6.3/man/func/gl_echo_mode.in create mode 100644 libtecla-1.6.3/man/func/gl_erase_terminal.in create mode 100644 libtecla-1.6.3/man/func/gl_error_message.in create mode 100644 libtecla-1.6.3/man/func/gl_get_line.in create mode 100644 libtecla-1.6.3/man/func/gl_group_history.in create mode 100644 libtecla-1.6.3/man/func/gl_handle_signal.in create mode 100644 libtecla-1.6.3/man/func/gl_ignore_signal.in create mode 100644 libtecla-1.6.3/man/func/gl_inactivity_timeout.in create mode 100644 libtecla-1.6.3/man/func/gl_io_mode.in create mode 100644 libtecla-1.6.3/man/func/gl_last_signal.in create mode 100644 libtecla-1.6.3/man/func/gl_limit_history.in create mode 100644 libtecla-1.6.3/man/func/gl_list_signals.in create mode 100644 libtecla-1.6.3/man/func/gl_load_history.in create mode 100644 libtecla-1.6.3/man/func/gl_lookup_history.in create mode 100644 libtecla-1.6.3/man/func/gl_normal_io.in create mode 100644 libtecla-1.6.3/man/func/gl_pending_io.in create mode 100644 libtecla-1.6.3/man/func/gl_prompt_style.in create mode 100644 libtecla-1.6.3/man/func/gl_query_char.in create mode 100644 libtecla-1.6.3/man/func/gl_range_of_history.in create mode 100644 libtecla-1.6.3/man/func/gl_raw_io.in create mode 100644 libtecla-1.6.3/man/func/gl_read_char.in create mode 100644 libtecla-1.6.3/man/func/gl_register_action.in create mode 100644 libtecla-1.6.3/man/func/gl_resize_history.in create mode 100644 libtecla-1.6.3/man/func/gl_return_status.in create mode 100644 libtecla-1.6.3/man/func/gl_save_history.in create mode 100644 libtecla-1.6.3/man/func/gl_set_term_size.in create mode 100644 libtecla-1.6.3/man/func/gl_show_history.in create mode 100644 libtecla-1.6.3/man/func/gl_size_of_history.in create mode 100644 libtecla-1.6.3/man/func/gl_state_of_history.in create mode 100644 libtecla-1.6.3/man/func/gl_terminal_size.in create mode 100644 libtecla-1.6.3/man/func/gl_toggle_history.in create mode 100644 libtecla-1.6.3/man/func/gl_trap_signal.in create mode 100644 libtecla-1.6.3/man/func/gl_tty_signals.in create mode 100644 libtecla-1.6.3/man/func/gl_watch_fd.in create mode 100644 libtecla-1.6.3/man/func/libtecla_version.in create mode 100644 libtecla-1.6.3/man/func/new_CplFileConf.in create mode 100644 libtecla-1.6.3/man/func/new_ExpandFile.in create mode 100644 libtecla-1.6.3/man/func/new_GetLine.in create mode 100644 libtecla-1.6.3/man/func/new_PathCache.in create mode 100644 libtecla-1.6.3/man/func/new_PcaPathConf.in create mode 100644 libtecla-1.6.3/man/func/new_WordCompletion.in create mode 100644 libtecla-1.6.3/man/func/pca_last_error.in create mode 100644 libtecla-1.6.3/man/func/pca_lookup_file.in create mode 100644 libtecla-1.6.3/man/func/pca_path_completions.in create mode 100644 libtecla-1.6.3/man/func/pca_scan_path.in create mode 100644 libtecla-1.6.3/man/func/pca_set_check_fn.in create mode 100644 libtecla-1.6.3/man/func/ppc_file_start.in create mode 100644 libtecla-1.6.3/man/func/ppc_literal_escapes.in create mode 100644 libtecla-1.6.3/man/libr/libtecla.in create mode 100644 libtecla-1.6.3/man/misc/tecla.in create mode 100644 libtecla-1.6.3/man/prog/enhance.in create mode 100644 libtecla-1.6.3/pathutil.c create mode 100644 libtecla-1.6.3/pathutil.h create mode 100644 libtecla-1.6.3/pcache.c create mode 100644 libtecla-1.6.3/stringrp.c create mode 100644 libtecla-1.6.3/stringrp.h create mode 100644 libtecla-1.6.3/strngmem.c create mode 100644 libtecla-1.6.3/strngmem.h create mode 100755 libtecla-1.6.3/update_html create mode 100755 libtecla-1.6.3/update_version create mode 100644 libtecla-1.6.3/version.c delete mode 100644 libtecla/CHANGES delete mode 100644 libtecla/INSTALL delete mode 100644 libtecla/LICENSE.TERMS delete mode 100644 libtecla/Makefile delete mode 100644 libtecla/Makefile.in delete mode 100644 libtecla/Makefile.rules delete mode 100644 libtecla/Makefile.stub delete mode 100644 libtecla/PORTING delete mode 100644 libtecla/README delete mode 100644 libtecla/RELEASE.NOTES delete mode 100644 libtecla/chrqueue.c delete mode 100644 libtecla/chrqueue.h delete mode 100644 libtecla/config.guess delete mode 100644 libtecla/config.sub delete mode 100755 libtecla/configure delete mode 100644 libtecla/configure.in delete mode 100644 libtecla/cplfile.c delete mode 100644 libtecla/cplfile.h delete mode 100644 libtecla/cplmatch.c delete mode 100644 libtecla/cplmatch.h delete mode 100644 libtecla/demo.c delete mode 100644 libtecla/demo2.c delete mode 100644 libtecla/demo3.c delete mode 100644 libtecla/direader.c delete mode 100644 libtecla/direader.h delete mode 100644 libtecla/enhance.c delete mode 100644 libtecla/errmsg.c delete mode 100644 libtecla/errmsg.h delete mode 100644 libtecla/expand.c delete mode 100644 libtecla/expand.h delete mode 100644 libtecla/freelist.c delete mode 100644 libtecla/freelist.h delete mode 100644 libtecla/getline.c delete mode 100644 libtecla/getline.h delete mode 100644 libtecla/hash.c delete mode 100644 libtecla/hash.h delete mode 100644 libtecla/history.c delete mode 100644 libtecla/history.h delete mode 100644 libtecla/homedir.c delete mode 100644 libtecla/homedir.h delete mode 100644 libtecla/html/changes.html delete mode 100644 libtecla/html/cpl_complete_word.html delete mode 100644 libtecla/html/ef_expand_file.html delete mode 100644 libtecla/html/enhance.html delete mode 100644 libtecla/html/gl_get_line.html delete mode 100644 libtecla/html/gl_io_mode.html delete mode 100644 libtecla/html/index.html delete mode 100644 libtecla/html/libtecla.html delete mode 100644 libtecla/html/pca_lookup_file.html delete mode 100644 libtecla/html/release.html delete mode 100644 libtecla/html/tecla.html delete mode 100755 libtecla/install-sh delete mode 100644 libtecla/ioutil.c delete mode 100644 libtecla/ioutil.h delete mode 100644 libtecla/keytab.c delete mode 100644 libtecla/keytab.h delete mode 100644 libtecla/libtecla.h delete mode 100644 libtecla/libtecla.map delete mode 100644 libtecla/man/file/teclarc.in delete mode 100644 libtecla/man/func/cfc_file_start.in delete mode 100644 libtecla/man/func/cfc_literal_escapes.in delete mode 100644 libtecla/man/func/cfc_set_check_fn.in delete mode 100644 libtecla/man/func/cpl_add_completion.in delete mode 100644 libtecla/man/func/cpl_complete_word.in delete mode 100644 libtecla/man/func/cpl_file_completions.in delete mode 100644 libtecla/man/func/cpl_last_error.in delete mode 100644 libtecla/man/func/cpl_list_completions.in delete mode 100644 libtecla/man/func/cpl_recall_matches.in delete mode 100644 libtecla/man/func/cpl_record_error.in delete mode 100644 libtecla/man/func/del_CplFileConf.in delete mode 100644 libtecla/man/func/del_ExpandFile.in delete mode 100644 libtecla/man/func/del_GetLine.in delete mode 100644 libtecla/man/func/del_PathCache.in delete mode 100644 libtecla/man/func/del_PcaPathConf.in delete mode 100644 libtecla/man/func/del_WordCompletion.in delete mode 100644 libtecla/man/func/ef_expand_file.in delete mode 100644 libtecla/man/func/ef_last_error.in delete mode 100644 libtecla/man/func/ef_list_expansions.in delete mode 100644 libtecla/man/func/gl_abandon_line.in delete mode 100644 libtecla/man/func/gl_bind_keyseq.in delete mode 100644 libtecla/man/func/gl_catch_blocked.in delete mode 100644 libtecla/man/func/gl_change_terminal.in delete mode 100644 libtecla/man/func/gl_clear_history.in delete mode 100644 libtecla/man/func/gl_completion_action.in delete mode 100644 libtecla/man/func/gl_configure_getline.in delete mode 100644 libtecla/man/func/gl_customize_completion.in delete mode 100644 libtecla/man/func/gl_display_text.in delete mode 100644 libtecla/man/func/gl_echo_mode.in delete mode 100644 libtecla/man/func/gl_erase_terminal.in delete mode 100644 libtecla/man/func/gl_error_message.in delete mode 100644 libtecla/man/func/gl_get_line.in delete mode 100644 libtecla/man/func/gl_group_history.in delete mode 100644 libtecla/man/func/gl_handle_signal.in delete mode 100644 libtecla/man/func/gl_ignore_signal.in delete mode 100644 libtecla/man/func/gl_inactivity_timeout.in delete mode 100644 libtecla/man/func/gl_io_mode.in delete mode 100644 libtecla/man/func/gl_last_signal.in delete mode 100644 libtecla/man/func/gl_limit_history.in delete mode 100644 libtecla/man/func/gl_list_signals.in delete mode 100644 libtecla/man/func/gl_load_history.in delete mode 100644 libtecla/man/func/gl_lookup_history.in delete mode 100644 libtecla/man/func/gl_normal_io.in delete mode 100644 libtecla/man/func/gl_pending_io.in delete mode 100644 libtecla/man/func/gl_prompt_style.in delete mode 100644 libtecla/man/func/gl_query_char.in delete mode 100644 libtecla/man/func/gl_range_of_history.in delete mode 100644 libtecla/man/func/gl_raw_io.in delete mode 100644 libtecla/man/func/gl_read_char.in delete mode 100644 libtecla/man/func/gl_register_action.in delete mode 100644 libtecla/man/func/gl_resize_history.in delete mode 100644 libtecla/man/func/gl_return_status.in delete mode 100644 libtecla/man/func/gl_save_history.in delete mode 100644 libtecla/man/func/gl_set_term_size.in delete mode 100644 libtecla/man/func/gl_show_history.in delete mode 100644 libtecla/man/func/gl_size_of_history.in delete mode 100644 libtecla/man/func/gl_state_of_history.in delete mode 100644 libtecla/man/func/gl_terminal_size.in delete mode 100644 libtecla/man/func/gl_toggle_history.in delete mode 100644 libtecla/man/func/gl_trap_signal.in delete mode 100644 libtecla/man/func/gl_tty_signals.in delete mode 100644 libtecla/man/func/gl_watch_fd.in delete mode 100644 libtecla/man/func/libtecla_version.in delete mode 100644 libtecla/man/func/new_CplFileConf.in delete mode 100644 libtecla/man/func/new_ExpandFile.in delete mode 100644 libtecla/man/func/new_GetLine.in delete mode 100644 libtecla/man/func/new_PathCache.in delete mode 100644 libtecla/man/func/new_PcaPathConf.in delete mode 100644 libtecla/man/func/new_WordCompletion.in delete mode 100644 libtecla/man/func/pca_last_error.in delete mode 100644 libtecla/man/func/pca_lookup_file.in delete mode 100644 libtecla/man/func/pca_path_completions.in delete mode 100644 libtecla/man/func/pca_scan_path.in delete mode 100644 libtecla/man/func/pca_set_check_fn.in delete mode 100644 libtecla/man/func/ppc_file_start.in delete mode 100644 libtecla/man/func/ppc_literal_escapes.in delete mode 100644 libtecla/man/libr/libtecla.in delete mode 100644 libtecla/man/misc/tecla.in delete mode 100644 libtecla/man/prog/enhance.in delete mode 100644 libtecla/pathutil.c delete mode 100644 libtecla/pathutil.h delete mode 100644 libtecla/pcache.c delete mode 100644 libtecla/stringrp.c delete mode 100644 libtecla/stringrp.h delete mode 100644 libtecla/strngmem.c delete mode 100644 libtecla/strngmem.h delete mode 100755 libtecla/update_html delete mode 100755 libtecla/update_version delete mode 100644 libtecla/version.c diff --git a/libtecla-1.6.3/CHANGES b/libtecla-1.6.3/CHANGES new file mode 100644 index 0000000..3abc035 --- /dev/null +++ b/libtecla-1.6.3/CHANGES @@ -0,0 +1,2823 @@ +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. + +09/11/2014 mcs@astro.caltech.edu + configure.in configure + Dominyk Tiller reported that libtecla didn't compile on + Mac OS X, due to libgcc.a being cited in the makefile + without any path. This turned out to be because Gnu + autoconf claims that the clang compiler is gcc, and clang + has a -print-libgcc-file-name, but this only prints + libgcc.a. I have modified the configure script to check + whether the path returned by -print-libgcc-file-name + actually exists and only use it if it does. + +27/10/2014 Jon Szymaniak (documented here by mcs@astro.caltech.edu) + Makefile.rules + Use $(AR) instead of plain ar, so that cross-compilation + uses the correct ar program when cross-compiling. + + Add $(TARGETS) dependency to building the demo programs + and the enhance program. When using parallel compilation + this is needed to ensure that the library is compiled + before the demos. + +10/04/2013 mcs@astro.caltech.edu + Makefile.in + Jonathan Niehof reported that libtecla wouldn't compile if + there were spaces in LDFLAGS and pointed out that there should + be quotes around $(LDFLAGS) in Makefile.in. This also applied + to a few other variables cited in the same way. + +10/06/2012 mcs@astro.caltech.edu + enhance.c configure.in + I had incorrectly assumed that system-V pseudo-terminal + allocation and system-V streams terminals always went + together. However system-V pseudo terminal allocation is + now part of UNIX98, and this has been adopted into many BSD + style operating systems, without the use of system-V + streams. On such systems the lack of system-V streams IOCTL + opcodes prevented system-V pseudo-terminal allocation being + used. This was hidden under Linux until recently, because + it had a stropts.h file, which made it appear as though + Linux supported system-V streams terminals. + + I have now created separate configuration tests and options + in the configure script for system-V terminal allocation + and system-V streams. On systems that only have the former, + the latter won't be used. + +16/05/2005 mcs@astro.caltech.edu + getline.c + When an initial input line was presented to gl_get_line() + for editing, the new input line was incorrectly appended to + the previous input line, instead of replacing it. + +10/01/2004 Derek Jones (documented here by mcs@astro.caltech.edu) + getline.c + Derek discovered that the function that computes the + width of the prompt, was not correctly skipping over 3 of + the 6 possible prompt-formatting directives. Thus, when + the %f,%p or %v prompt-formatting directives were used, + the width of the prompt was incorrectly calculated. The + fix was to copy the list of directives from + gl_display_prompt(). I have also added a comment to + gl_display_prompt(), to warn anybody who adds or removes + formatting directives there, to also do the same to + gl_displayed_prompt_width(). + +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.3/INSTALL b/libtecla-1.6.3/INSTALL new file mode 100644 index 0000000..14fc62d --- /dev/null +++ b/libtecla-1.6.3/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.3/LICENSE.TERMS b/libtecla-1.6.3/LICENSE.TERMS new file mode 100644 index 0000000..81db521 --- /dev/null +++ b/libtecla-1.6.3/LICENSE.TERMS @@ -0,0 +1,28 @@ +Copyright (c) 2000, 2001, 2002, 2003, 2004, 2012, 2014 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.3/Makefile b/libtecla-1.6.3/Makefile new file mode 100644 index 0000000..cf89638 --- /dev/null +++ b/libtecla-1.6.3/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.3/Makefile.in b/libtecla-1.6.3/Makefile.in new file mode 100644 index 0000000..cb0891f --- /dev/null +++ b/libtecla-1.6.3/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.3/Makefile.rules b/libtecla-1.6.3/Makefile.rules new file mode 100644 index 0000000..0cf2316 --- /dev/null +++ b/libtecla-1.6.3/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 $(TARGETS) + LD_RUN_PATH="$(LIBDIR):$$LD_RUN_PATH:`pwd`" $(CC) $(CFLAGS) -o $@ \ + $(OBJDIR)/demo.o -L. -ltecla$(SUFFIX) $(LIBS) + +demo2$(SUFFIX): $(OBJDIR)/demo2.o $(TARGETS) + LD_RUN_PATH="$(LIBDIR):$$LD_RUN_PATH:`pwd`" $(CC) $(CFLAGS) -o $@ \ + $(OBJDIR)/demo2.o -L. -ltecla$(SUFFIX) $(LIBS) + +demo3$(SUFFIX): $(OBJDIR)/demo3.o $(TARGETS) + LD_RUN_PATH="$(LIBDIR):$$LD_RUN_PATH:`pwd`" $(CC) $(CFLAGS) -o $@ \ + $(OBJDIR)/demo3.o -L. -ltecla$(SUFFIX) $(LIBS) + +enhance$(SUFFIX): $(OBJDIR)/enhance.o $(TARGETS) + 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.3/Makefile.stub b/libtecla-1.6.3/Makefile.stub new file mode 100644 index 0000000..cf89638 --- /dev/null +++ b/libtecla-1.6.3/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.3/PORTING b/libtecla-1.6.3/PORTING new file mode 100644 index 0000000..db39818 --- /dev/null +++ b/libtecla-1.6.3/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.3/README b/libtecla-1.6.3/README new file mode 100644 index 0000000..3a9c40e --- /dev/null +++ b/libtecla-1.6.3/README @@ -0,0 +1,53 @@ +This is version 1.6.3 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, 2012, 2014 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.3/RELEASE.NOTES b/libtecla-1.6.3/RELEASE.NOTES new file mode 100644 index 0000000..68cab0a --- /dev/null +++ b/libtecla-1.6.3/RELEASE.NOTES @@ -0,0 +1,601 @@ +This file lists major changes which accompany each new release. + +Version 1.6.3: + + This release corrects some problems in the build process, + including one that was preventing libtecla from being compiled + on Mac OS X. + +Version 1.6.2: + + This release updates the configuration script to ensure that the + enhance utility program is compiled correctly on systems that have + system V psuedo-terminal allocation but not system V streams. + + There are no new features. + +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.3/chrqueue.c b/libtecla-1.6.3/chrqueue.c new file mode 100644 index 0000000..a1d1d80 --- /dev/null +++ b/libtecla-1.6.3/chrqueue.c @@ -0,0 +1,432 @@ +/* + * Copyright (c) 2000, 2001, 2002, 2003, 2004, 2012 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.3/chrqueue.h b/libtecla-1.6.3/chrqueue.h new file mode 100644 index 0000000..30c9584 --- /dev/null +++ b/libtecla-1.6.3/chrqueue.h @@ -0,0 +1,106 @@ +#ifndef chrqueue_h +#define chrqueue_h + +/* + * Copyright (c) 2000, 2001, 2002, 2003, 2004, 2012 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.3/config.guess b/libtecla-1.6.3/config.guess new file mode 100644 index 0000000..500ee74 --- /dev/null +++ b/libtecla-1.6.3/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.3/config.sub b/libtecla-1.6.3/config.sub new file mode 100644 index 0000000..1f31816 --- /dev/null +++ b/libtecla-1.6.3/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.3/configure b/libtecla-1.6.3/configure new file mode 100755 index 0000000..8122996 --- /dev/null +++ b/libtecla-1.6.3/configure @@ -0,0 +1,5477 @@ +#! /bin/sh +# Guess values for system-dependent variables and create Makefiles. +# Generated by GNU Autoconf 2.68. +# +# +# Copyright (C) 1992, 1993, 1994, 1995, 1996, 1998, 1999, 2000, 2001, +# 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010 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 more Bourne compatible +DUALCASE=1; export DUALCASE # for MKS sh +if test -n "${ZSH_VERSION+set}" && (emulate sh) >/dev/null 2>&1; then : + emulate sh + NULLCMD=: + # Pre-4.2 versions of Zsh do word splitting on ${1+"$@"}, which + # is contrary to our usage. Disable this feature. + alias -g '${1+"$@"}'='"$@"' + setopt NO_GLOB_SUBST +else + case `(set -o) 2>/dev/null` in #( + *posix*) : + set -o posix ;; #( + *) : + ;; +esac +fi + + +as_nl=' +' +export as_nl +# Printing a long string crashes Solaris 7 /usr/bin/printf. +as_echo='\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\' +as_echo=$as_echo$as_echo$as_echo$as_echo$as_echo +as_echo=$as_echo$as_echo$as_echo$as_echo$as_echo$as_echo +# Prefer a ksh shell builtin over an external printf program on Solaris, +# but without wasting forks for bash or zsh. +if test -z "$BASH_VERSION$ZSH_VERSION" \ + && (test "X`print -r -- $as_echo`" = "X$as_echo") 2>/dev/null; then + as_echo='print -r --' + as_echo_n='print -rn --' +elif (test "X`printf %s $as_echo`" = "X$as_echo") 2>/dev/null; then + as_echo='printf %s\n' + as_echo_n='printf %s' +else + if test "X`(/usr/ucb/echo -n -n $as_echo) 2>/dev/null`" = "X-n $as_echo"; then + as_echo_body='eval /usr/ucb/echo -n "$1$as_nl"' + as_echo_n='/usr/ucb/echo -n' + else + as_echo_body='eval expr "X$1" : "X\\(.*\\)"' + as_echo_n_body='eval + arg=$1; + case $arg in #( + *"$as_nl"*) + expr "X$arg" : "X\\(.*\\)$as_nl"; + arg=`expr "X$arg" : ".*$as_nl\\(.*\\)"`;; + esac; + expr "X$arg" : "X\\(.*\\)" | tr -d "$as_nl" + ' + export as_echo_n_body + as_echo_n='sh -c $as_echo_n_body as_echo' + fi + export as_echo_body + as_echo='sh -c $as_echo_body as_echo' +fi + +# The user is always right. +if test "${PATH_SEPARATOR+set}" != set; then + PATH_SEPARATOR=: + (PATH='/bin;/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 && { + (PATH='/bin:/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 || + PATH_SEPARATOR=';' + } +fi + + +# IFS +# We need space, tab and new line, in precisely that order. Quoting is +# there to prevent editors from complaining about space-tab. +# (If _AS_PATH_WALK were called with IFS unset, it would disable word +# splitting by setting IFS to empty value.) +IFS=" "" $as_nl" + +# Find who we are. Look in the path if we contain no directory separator. +as_myself= +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 +IFS=$as_save_IFS + + ;; +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 + $as_echo "$as_myself: error: cannot find myself; rerun with an absolute file name" >&2 + exit 1 +fi + +# Unset variables that we do not need and which cause bugs (e.g. in +# pre-3.0 UWIN ksh). But do not cause bugs in bash 2.01; the "|| exit 1" +# suppresses any "Segmentation fault" message there. '((' could +# trigger a bug in pdksh 5.2.14. +for as_var in BASH_ENV ENV MAIL MAILPATH +do eval test x\${$as_var+set} = xset \ + && ( (unset $as_var) || exit 1) >/dev/null 2>&1 && unset $as_var || : +done +PS1='$ ' +PS2='> ' +PS4='+ ' + +# NLS nuisances. +LC_ALL=C +export LC_ALL +LANGUAGE=C +export LANGUAGE + +# CDPATH. +(unset CDPATH) >/dev/null 2>&1 && unset CDPATH + +if test "x$CONFIG_SHELL" = x; then + as_bourne_compatible="if test -n \"\${ZSH_VERSION+set}\" && (emulate sh) >/dev/null 2>&1; then : + emulate sh + NULLCMD=: + # Pre-4.2 versions of Zsh do word splitting on \${1+\"\$@\"}, which + # is contrary to our usage. Disable this feature. + alias -g '\${1+\"\$@\"}'='\"\$@\"' + setopt NO_GLOB_SUBST +else + case \`(set -o) 2>/dev/null\` in #( + *posix*) : + set -o posix ;; #( + *) : + ;; +esac +fi +" + as_required="as_fn_return () { (exit \$1); } +as_fn_success () { as_fn_return 0; } +as_fn_failure () { as_fn_return 1; } +as_fn_ret_success () { return 0; } +as_fn_ret_failure () { return 1; } + +exitcode=0 +as_fn_success || { exitcode=1; echo as_fn_success failed.; } +as_fn_failure && { exitcode=1; echo as_fn_failure succeeded.; } +as_fn_ret_success || { exitcode=1; echo as_fn_ret_success failed.; } +as_fn_ret_failure && { exitcode=1; echo as_fn_ret_failure succeeded.; } +if ( set x; as_fn_ret_success y && test x = \"\$1\" ); then : + +else + exitcode=1; echo positional parameters were not saved. +fi +test x\$exitcode = x0 || exit 1" + as_suggested=" as_lineno_1=";as_suggested=$as_suggested$LINENO;as_suggested=$as_suggested" as_lineno_1a=\$LINENO + as_lineno_2=";as_suggested=$as_suggested$LINENO;as_suggested=$as_suggested" as_lineno_2a=\$LINENO + eval 'test \"x\$as_lineno_1'\$as_run'\" != \"x\$as_lineno_2'\$as_run'\" && + test \"x\`expr \$as_lineno_1'\$as_run' + 1\`\" = \"x\$as_lineno_2'\$as_run'\"' || exit 1 +test \$(( 1 + 1 )) = 2 || exit 1" + if (eval "$as_required") 2>/dev/null; then : + as_have_required=yes +else + as_have_required=no +fi + if test x$as_have_required = xyes && (eval "$as_suggested") 2>/dev/null; then : + +else + as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +as_found=false +for as_dir in /bin$PATH_SEPARATOR/usr/bin$PATH_SEPARATOR$PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + as_found=: + case $as_dir in #( + /*) + for as_base in sh bash ksh sh5; do + # Try only shells that exist, to save several forks. + as_shell=$as_dir/$as_base + if { test -f "$as_shell" || test -f "$as_shell.exe"; } && + { $as_echo "$as_bourne_compatible""$as_required" | as_run=a "$as_shell"; } 2>/dev/null; then : + CONFIG_SHELL=$as_shell as_have_required=yes + if { $as_echo "$as_bourne_compatible""$as_suggested" | as_run=a "$as_shell"; } 2>/dev/null; then : + break 2 +fi +fi + done;; + esac + as_found=false +done +$as_found || { if { test -f "$SHELL" || test -f "$SHELL.exe"; } && + { $as_echo "$as_bourne_compatible""$as_required" | as_run=a "$SHELL"; } 2>/dev/null; then : + CONFIG_SHELL=$SHELL as_have_required=yes +fi; } +IFS=$as_save_IFS + + + if test "x$CONFIG_SHELL" != x; then : + # We cannot yet assume a decent shell, so we have to provide a + # neutralization value for shells without unset; and this also + # works around shells that cannot unset nonexistent variables. + # Preserve -v and -x to the replacement shell. + BASH_ENV=/dev/null + ENV=/dev/null + (unset BASH_ENV) >/dev/null 2>&1 && unset BASH_ENV ENV + export CONFIG_SHELL + case $- in # (((( + *v*x* | *x*v* ) as_opts=-vx ;; + *v* ) as_opts=-v ;; + *x* ) as_opts=-x ;; + * ) as_opts= ;; + esac + exec "$CONFIG_SHELL" $as_opts "$as_myself" ${1+"$@"} +fi + + if test x$as_have_required = xno; then : + $as_echo "$0: This script requires a shell more modern than all" + $as_echo "$0: the shells that I found on your system." + if test x${ZSH_VERSION+set} = xset ; then + $as_echo "$0: In particular, zsh $ZSH_VERSION has bugs and should" + $as_echo "$0: be upgraded to zsh 4.3.4 or later." + else + $as_echo "$0: Please tell bug-autoconf@gnu.org about your system, +$0: including any error possibly output before this +$0: message. Then install a modern shell, or manually run +$0: the script under such a shell if you do have one." + fi + exit 1 +fi +fi +fi +SHELL=${CONFIG_SHELL-/bin/sh} +export SHELL +# Unset more variables known to interfere with behavior of common tools. +CLICOLOR_FORCE= GREP_OPTIONS= +unset CLICOLOR_FORCE GREP_OPTIONS + +## --------------------- ## +## M4sh Shell Functions. ## +## --------------------- ## +# as_fn_unset VAR +# --------------- +# Portably unset VAR. +as_fn_unset () +{ + { eval $1=; unset $1;} +} +as_unset=as_fn_unset + +# as_fn_set_status STATUS +# ----------------------- +# Set $? to STATUS, without forking. +as_fn_set_status () +{ + return $1 +} # as_fn_set_status + +# as_fn_exit STATUS +# ----------------- +# Exit the shell with STATUS, even in a "trap 0" or "set -e" context. +as_fn_exit () +{ + set +e + as_fn_set_status $1 + exit $1 +} # as_fn_exit + +# as_fn_mkdir_p +# ------------- +# Create "$as_dir" as a directory, including parents if necessary. +as_fn_mkdir_p () +{ + + case $as_dir in #( + -*) as_dir=./$as_dir;; + esac + test -d "$as_dir" || eval $as_mkdir_p || { + as_dirs= + while :; do + case $as_dir in #( + *\'*) as_qdir=`$as_echo "$as_dir" | sed "s/'/'\\\\\\\\''/g"`;; #'( + *) as_qdir=$as_dir;; + esac + as_dirs="'$as_qdir' $as_dirs" + as_dir=`$as_dirname -- "$as_dir" || +$as_expr X"$as_dir" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ + X"$as_dir" : 'X\(//\)[^/]' \| \ + X"$as_dir" : 'X\(//\)$' \| \ + X"$as_dir" : 'X\(/\)' \| . 2>/dev/null || +$as_echo X"$as_dir" | + sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ + s//\1/ + q + } + /^X\(\/\/\)[^/].*/{ + s//\1/ + q + } + /^X\(\/\/\)$/{ + s//\1/ + q + } + /^X\(\/\).*/{ + s//\1/ + q + } + s/.*/./; q'` + test -d "$as_dir" && break + done + test -z "$as_dirs" || eval "mkdir $as_dirs" + } || test -d "$as_dir" || as_fn_error $? "cannot create directory $as_dir" + + +} # as_fn_mkdir_p +# as_fn_append VAR VALUE +# ---------------------- +# Append the text in VALUE to the end of the definition contained in VAR. Take +# advantage of any shell optimizations that allow amortized linear growth over +# repeated appends, instead of the typical quadratic growth present in naive +# implementations. +if (eval "as_var=1; as_var+=2; test x\$as_var = x12") 2>/dev/null; then : + eval 'as_fn_append () + { + eval $1+=\$2 + }' +else + as_fn_append () + { + eval $1=\$$1\$2 + } +fi # as_fn_append + +# as_fn_arith ARG... +# ------------------ +# Perform arithmetic evaluation on the ARGs, and store the result in the +# global $as_val. Take advantage of shells that can avoid forks. The arguments +# must be portable across $(()) and expr. +if (eval "test \$(( 1 + 1 )) = 2") 2>/dev/null; then : + eval 'as_fn_arith () + { + as_val=$(( $* )) + }' +else + as_fn_arith () + { + as_val=`expr "$@" || test $? -eq 1` + } +fi # as_fn_arith + + +# as_fn_error STATUS ERROR [LINENO LOG_FD] +# ---------------------------------------- +# Output "`basename $0`: error: ERROR" to stderr. If LINENO and LOG_FD are +# provided, also output the error to LOG_FD, referencing LINENO. Then exit the +# script with STATUS, using 1 if that was 0. +as_fn_error () +{ + as_status=$1; test $as_status -eq 0 && as_status=1 + if test "$4"; then + as_lineno=${as_lineno-"$3"} as_lineno_stack=as_lineno_stack=$as_lineno_stack + $as_echo "$as_me:${as_lineno-$LINENO}: error: $2" >&$4 + fi + $as_echo "$as_me: error: $2" >&2 + as_fn_exit $as_status +} # as_fn_error + +if expr a : '\(a\)' >/dev/null 2>&1 && + test "X`expr 00001 : '.*\(...\)'`" = X001; 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 + +if (as_dir=`dirname -- /` && test "X$as_dir" = X/) >/dev/null 2>&1; then + as_dirname=dirname +else + as_dirname=false +fi + +as_me=`$as_basename -- "$0" || +$as_expr X/"$0" : '.*/\([^/][^/]*\)/*$' \| \ + X"$0" : 'X\(//\)$' \| \ + X"$0" : 'X\(/\)' \| . 2>/dev/null || +$as_echo X/"$0" | + sed '/^.*\/\([^/][^/]*\)\/*$/{ + s//\1/ + q + } + /^X\/\(\/\/\)$/{ + s//\1/ + q + } + /^X\/\(\/\).*/{ + s//\1/ + q + } + s/.*/./; q'` + +# 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 + + + as_lineno_1=$LINENO as_lineno_1a=$LINENO + as_lineno_2=$LINENO as_lineno_2a=$LINENO + eval 'test "x$as_lineno_1'$as_run'" != "x$as_lineno_2'$as_run'" && + test "x`expr $as_lineno_1'$as_run' + 1`" = "x$as_lineno_2'$as_run'"' || { + # Blame Lee E. McMahon (1931-1989) for sed's syntax. :-) + sed -n ' + p + /[$]LINENO/= + ' <$as_myself | + sed ' + s/[$]LINENO.*/&-/ + t lineno + b + :lineno + N + :loop + s/[$]LINENO\([^'$as_cr_alnum'_].*\n\)\(.*\)/\2\1\2/ + t loop + s/-\n.*// + ' >$as_me.lineno && + chmod +x "$as_me.lineno" || + { $as_echo "$as_me: error: cannot create $as_me.lineno; rerun with a POSIX shell" >&2; as_fn_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 sensitive to this). + . "./$as_me.lineno" + # Exit status is that of the last command. + exit +} + +ECHO_C= ECHO_N= ECHO_T= +case `echo -n x` in #((((( +-n*) + case `echo 'xy\c'` in + *c*) ECHO_T=' ';; # ECHO_T is single tab character. + xy) ECHO_C='\c';; + *) echo `echo ksh88 bug on AIX 6.1` > /dev/null + ECHO_T=' ';; + esac;; +*) + ECHO_N='-n';; +esac + +rm -f conf$$ conf$$.exe conf$$.file +if test -d conf$$.dir; then + rm -f conf$$.dir/conf$$.file +else + rm -f conf$$.dir + mkdir conf$$.dir 2>/dev/null +fi +if (echo >conf$$.file) 2>/dev/null; then + if ln -s conf$$.file conf$$ 2>/dev/null; then + as_ln_s='ln -s' + # ... but there are two gotchas: + # 1) On MSYS, both `ln -s file dir' and `ln file dir' fail. + # 2) DJGPP < 2.04 has no symlinks; `ln -s' creates a wrapper executable. + # In both cases, we have to default to `cp -p'. + ln -s conf$$.file conf$$.dir 2>/dev/null && test ! -f conf$$.exe || + as_ln_s='cp -p' + elif ln conf$$.file conf$$ 2>/dev/null; then + as_ln_s=ln + else + as_ln_s='cp -p' + fi +else + as_ln_s='cp -p' +fi +rm -f conf$$ conf$$.exe conf$$.dir/conf$$.file conf$$.file +rmdir conf$$.dir 2>/dev/null + +if mkdir -p . 2>/dev/null; then + as_mkdir_p='mkdir -p "$as_dir"' +else + test -d ./-p && rmdir ./-p + as_mkdir_p=false +fi + +if test -x / >/dev/null 2>&1; then + as_test_x='test -x' +else + if ls -dL / >/dev/null 2>&1; then + as_ls_L_option=L + else + as_ls_L_option= + fi + as_test_x=' + eval sh -c '\'' + if test -d "$1"; then + test -d "$1/."; + else + case $1 in #( + -*)set "./$1";; + esac; + case `ls -ld'$as_ls_L_option' "$1" 2>/dev/null` in #(( + ???[sx]*):;;*)false;;esac;fi + '\'' sh + ' +fi +as_executable_p=$as_test_x + +# Sed expression to map a string onto a valid CPP name. +as_tr_cpp="eval 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="eval sed 'y%*+%pp%;s%[^_$as_cr_alnum]%_%g'" + + +test -n "$DJDIR" || exec 7<&0 &1 + +# Name of the host. +# hostname on some systems (SVR3.2, old GNU/Linux) returns a bogus exit status, +# so uname gets run too. +ac_hostname=`(hostname || uname -n) 2>/dev/null | sed 1q` + +# +# Initializations. +# +ac_default_prefix=/usr/local +ac_clean_files= +ac_config_libobj_dir=. +LIBOBJS= +cross_compiling=no +subdirs= +MFLAGS= +MAKEFLAGS= + +# Identity of this package. +PACKAGE_NAME= +PACKAGE_TARNAME= +PACKAGE_VERSION= +PACKAGE_STRING= +PACKAGE_BUGREPORT= +PACKAGE_URL= + +ac_unique_file="getline.c" +# Factoring default headers for most tests. +ac_includes_default="\ +#include +#ifdef HAVE_SYS_TYPES_H +# include +#endif +#ifdef HAVE_SYS_STAT_H +# include +#endif +#ifdef STDC_HEADERS +# include +# include +#else +# ifdef HAVE_STDLIB_H +# include +# endif +#endif +#ifdef HAVE_STRING_H +# if !defined STDC_HEADERS && defined HAVE_MEMORY_H +# include +# endif +# include +#endif +#ifdef HAVE_STRINGS_H +# include +#endif +#ifdef HAVE_INTTYPES_H +# include +#endif +#ifdef HAVE_STDINT_H +# include +#endif +#ifdef HAVE_UNISTD_H +# include +#endif" + +ac_subst_vars='LTLIBOBJS +LIBOBJS +MAKE_MAN_PAGES +TARGET_LIBS +FILE_MANEXT +FILE_MANDIR +MISC_MANEXT +MISC_MANDIR +PROG_MANEXT +PROG_MANDIR +FUNC_MANEXT +FUNC_MANDIR +LIBR_MANEXT +LIBR_MANDIR +DEFS_R +LINK_SHARED +SHARED_CFLAGS +SHARED_ALT +SHARED_EXT +TARGETS +EGREP +GREP +CPP +target_os +target_vendor +target_cpu +target +host_os +host_vendor +host_cpu +host +build_os +build_vendor +build_cpu +build +LD +RANLIB +AWK +LN_S +SET_MAKE +OBJEXT +EXEEXT +ac_ct_CC +CPPFLAGS +LDFLAGS +CFLAGS +CC +MICRO_VER +MINOR_VER +MAJOR_VER +target_alias +host_alias +build_alias +LIBS +ECHO_T +ECHO_N +ECHO_C +DEFS +mandir +localedir +libdir +psdir +pdfdir +dvidir +htmldir +infodir +docdir +oldincludedir +includedir +localstatedir +sharedstatedir +sysconfdir +datadir +datarootdir +libexecdir +sbindir +bindir +program_transform_name +prefix +exec_prefix +PACKAGE_URL +PACKAGE_BUGREPORT +PACKAGE_STRING +PACKAGE_VERSION +PACKAGE_TARNAME +PACKAGE_NAME +PATH_SEPARATOR +SHELL' +ac_subst_files='' +ac_user_opts=' +enable_option_checking +with_file_actions +with_file_system +with_man_pages +' + ac_precious_vars='build_alias +host_alias +target_alias +CC +CFLAGS +LDFLAGS +LIBS +CPPFLAGS +CPP' + + +# Initialize some variables set by options. +ac_init_help= +ac_init_version=false +ac_unrecognized_opts= +ac_unrecognized_sep= +# 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. +# (The list follows the same order as the GNU Coding Standards.) +bindir='${exec_prefix}/bin' +sbindir='${exec_prefix}/sbin' +libexecdir='${exec_prefix}/libexec' +datarootdir='${prefix}/share' +datadir='${datarootdir}' +sysconfdir='${prefix}/etc' +sharedstatedir='${prefix}/com' +localstatedir='${prefix}/var' +includedir='${prefix}/include' +oldincludedir='/usr/include' +docdir='${datarootdir}/doc/${PACKAGE}' +infodir='${datarootdir}/info' +htmldir='${docdir}' +dvidir='${docdir}' +pdfdir='${docdir}' +psdir='${docdir}' +libdir='${exec_prefix}/lib' +localedir='${datarootdir}/locale' +mandir='${datarootdir}/man' + +ac_prev= +ac_dashdash= +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=`expr "X$ac_option" : '[^=]*=\(.*\)'` ;; + *=) ac_optarg= ;; + *) ac_optarg=yes ;; + esac + + # Accept the important Cygnus configure options, so we can diagnose typos. + + case $ac_dashdash$ac_option in + --) + ac_dashdash=yes ;; + + -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) + ac_prev=datadir ;; + -datadir=* | --datadir=* | --datadi=* | --datad=*) + datadir=$ac_optarg ;; + + -datarootdir | --datarootdir | --datarootdi | --datarootd | --dataroot \ + | --dataroo | --dataro | --datar) + ac_prev=datarootdir ;; + -datarootdir=* | --datarootdir=* | --datarootdi=* | --datarootd=* \ + | --dataroot=* | --dataroo=* | --dataro=* | --datar=*) + datarootdir=$ac_optarg ;; + + -disable-* | --disable-*) + ac_useropt=`expr "x$ac_option" : 'x-*disable-\(.*\)'` + # Reject names that are not valid shell variable names. + expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null && + as_fn_error $? "invalid feature name: $ac_useropt" + ac_useropt_orig=$ac_useropt + ac_useropt=`$as_echo "$ac_useropt" | sed 's/[-+.]/_/g'` + case $ac_user_opts in + *" +"enable_$ac_useropt" +"*) ;; + *) ac_unrecognized_opts="$ac_unrecognized_opts$ac_unrecognized_sep--disable-$ac_useropt_orig" + ac_unrecognized_sep=', ';; + esac + eval enable_$ac_useropt=no ;; + + -docdir | --docdir | --docdi | --doc | --do) + ac_prev=docdir ;; + -docdir=* | --docdir=* | --docdi=* | --doc=* | --do=*) + docdir=$ac_optarg ;; + + -dvidir | --dvidir | --dvidi | --dvid | --dvi | --dv) + ac_prev=dvidir ;; + -dvidir=* | --dvidir=* | --dvidi=* | --dvid=* | --dvi=* | --dv=*) + dvidir=$ac_optarg ;; + + -enable-* | --enable-*) + ac_useropt=`expr "x$ac_option" : 'x-*enable-\([^=]*\)'` + # Reject names that are not valid shell variable names. + expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null && + as_fn_error $? "invalid feature name: $ac_useropt" + ac_useropt_orig=$ac_useropt + ac_useropt=`$as_echo "$ac_useropt" | sed 's/[-+.]/_/g'` + case $ac_user_opts in + *" +"enable_$ac_useropt" +"*) ;; + *) ac_unrecognized_opts="$ac_unrecognized_opts$ac_unrecognized_sep--enable-$ac_useropt_orig" + ac_unrecognized_sep=', ';; + esac + eval enable_$ac_useropt=\$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 ;; + + -htmldir | --htmldir | --htmldi | --htmld | --html | --htm | --ht) + ac_prev=htmldir ;; + -htmldir=* | --htmldir=* | --htmldi=* | --htmld=* | --html=* | --htm=* \ + | --ht=*) + htmldir=$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 ;; + + -localedir | --localedir | --localedi | --localed | --locale) + ac_prev=localedir ;; + -localedir=* | --localedir=* | --localedi=* | --localed=* | --locale=*) + localedir=$ac_optarg ;; + + -localstatedir | --localstatedir | --localstatedi | --localstated \ + | --localstate | --localstat | --localsta | --localst | --locals) + ac_prev=localstatedir ;; + -localstatedir=* | --localstatedir=* | --localstatedi=* | --localstated=* \ + | --localstate=* | --localstat=* | --localsta=* | --localst=* | --locals=*) + 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 ;; + + -pdfdir | --pdfdir | --pdfdi | --pdfd | --pdf | --pd) + ac_prev=pdfdir ;; + -pdfdir=* | --pdfdir=* | --pdfdi=* | --pdfd=* | --pdf=* | --pd=*) + pdfdir=$ac_optarg ;; + + -psdir | --psdir | --psdi | --psd | --ps) + ac_prev=psdir ;; + -psdir=* | --psdir=* | --psdi=* | --psd=* | --ps=*) + psdir=$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_useropt=`expr "x$ac_option" : 'x-*with-\([^=]*\)'` + # Reject names that are not valid shell variable names. + expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null && + as_fn_error $? "invalid package name: $ac_useropt" + ac_useropt_orig=$ac_useropt + ac_useropt=`$as_echo "$ac_useropt" | sed 's/[-+.]/_/g'` + case $ac_user_opts in + *" +"with_$ac_useropt" +"*) ;; + *) ac_unrecognized_opts="$ac_unrecognized_opts$ac_unrecognized_sep--with-$ac_useropt_orig" + ac_unrecognized_sep=', ';; + esac + eval with_$ac_useropt=\$ac_optarg ;; + + -without-* | --without-*) + ac_useropt=`expr "x$ac_option" : 'x-*without-\(.*\)'` + # Reject names that are not valid shell variable names. + expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null && + as_fn_error $? "invalid package name: $ac_useropt" + ac_useropt_orig=$ac_useropt + ac_useropt=`$as_echo "$ac_useropt" | sed 's/[-+.]/_/g'` + case $ac_user_opts in + *" +"with_$ac_useropt" +"*) ;; + *) ac_unrecognized_opts="$ac_unrecognized_opts$ac_unrecognized_sep--without-$ac_useropt_orig" + ac_unrecognized_sep=', ';; + esac + eval with_$ac_useropt=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 ;; + + -*) as_fn_error $? "unrecognized option: \`$ac_option' +Try \`$0 --help' for more information" + ;; + + *=*) + ac_envvar=`expr "x$ac_option" : 'x\([^=]*\)='` + # Reject names that are not valid shell variable names. + case $ac_envvar in #( + '' | [0-9]* | *[!_$as_cr_alnum]* ) + as_fn_error $? "invalid variable name: \`$ac_envvar'" ;; + esac + eval $ac_envvar=\$ac_optarg + export $ac_envvar ;; + + *) + # FIXME: should be removed in autoconf 3.0. + $as_echo "$as_me: WARNING: you should use --build, --host, --target" >&2 + expr "x$ac_option" : ".*[^-._$as_cr_alnum]" >/dev/null && + $as_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'` + as_fn_error $? "missing argument to $ac_option" +fi + +if test -n "$ac_unrecognized_opts"; then + case $enable_option_checking in + no) ;; + fatal) as_fn_error $? "unrecognized options: $ac_unrecognized_opts" ;; + *) $as_echo "$as_me: WARNING: unrecognized options: $ac_unrecognized_opts" >&2 ;; + esac +fi + +# Check all directory arguments for consistency. +for ac_var in exec_prefix prefix bindir sbindir libexecdir datarootdir \ + datadir sysconfdir sharedstatedir localstatedir includedir \ + oldincludedir docdir infodir htmldir dvidir pdfdir psdir \ + libdir localedir mandir +do + eval ac_val=\$$ac_var + # Remove trailing slashes. + case $ac_val in + */ ) + ac_val=`expr "X$ac_val" : 'X\(.*[^/]\)' \| "X$ac_val" : 'X\(.*\)'` + eval $ac_var=\$ac_val;; + esac + # Be sure to have absolute directory names. + case $ac_val in + [\\/$]* | ?:[\\/]* ) continue;; + NONE | '' ) case $ac_var in *prefix ) continue;; esac;; + esac + as_fn_error $? "expected an absolute directory name for --$ac_var: $ac_val" +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 + $as_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 + + +ac_pwd=`pwd` && test -n "$ac_pwd" && +ac_ls_di=`ls -di .` && +ac_pwd_ls_di=`cd "$ac_pwd" && ls -di .` || + as_fn_error $? "working directory cannot be determined" +test "X$ac_ls_di" = "X$ac_pwd_ls_di" || + as_fn_error $? "pwd does not report name of working directory" + + +# 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 the parent directory. + ac_confdir=`$as_dirname -- "$as_myself" || +$as_expr X"$as_myself" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ + X"$as_myself" : 'X\(//\)[^/]' \| \ + X"$as_myself" : 'X\(//\)$' \| \ + X"$as_myself" : 'X\(/\)' \| . 2>/dev/null || +$as_echo X"$as_myself" | + 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 + test "$ac_srcdir_defaulted" = yes && srcdir="$ac_confdir or .." + as_fn_error $? "cannot find sources ($ac_unique_file) in $srcdir" +fi +ac_msg="sources are in $srcdir, but \`cd $srcdir' does not work" +ac_abs_confdir=`( + cd "$srcdir" && test -r "./$ac_unique_file" || as_fn_error $? "$ac_msg" + pwd)` +# When building in place, set srcdir=. +if test "$ac_abs_confdir" = "$ac_pwd"; then + srcdir=. +fi +# Remove unnecessary trailing slashes from srcdir. +# Double slashes in file names in object file debugging info +# mess up M-x gdb in Emacs. +case $srcdir in +*/) srcdir=`expr "X$srcdir" : 'X\(.*[^/]\)' \| "X$srcdir" : 'X\(.*\)'`;; +esac +for ac_var in $ac_precious_vars; do + eval ac_env_${ac_var}_set=\${${ac_var}+set} + eval ac_env_${ac_var}_value=\$${ac_var} + eval ac_cv_env_${ac_var}_set=\${${ac_var}+set} + eval ac_cv_env_${ac_var}_value=\$${ac_var} +done + +# +# 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 \`..'] + +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] + --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] + --datarootdir=DIR read-only arch.-independent data root [PREFIX/share] + --datadir=DIR read-only architecture-independent data [DATAROOTDIR] + --infodir=DIR info documentation [DATAROOTDIR/info] + --localedir=DIR locale-dependent data [DATAROOTDIR/locale] + --mandir=DIR man documentation [DATAROOTDIR/man] + --docdir=DIR documentation root [DATAROOTDIR/doc/PACKAGE] + --htmldir=DIR html documentation [DOCDIR] + --dvidir=DIR dvi documentation [DOCDIR] + --pdfdir=DIR pdf documentation [DOCDIR] + --psdir=DIR ps documentation [DOCDIR] +_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 + LIBS libraries to pass to the linker, e.g. -l + CPPFLAGS (Objective) 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. + +Report bugs to the package provider. +_ACEOF +ac_status=$? +fi + +if test "$ac_init_help" = "recursive"; then + # If there are subdirs, report their specific --help. + for ac_dir in : $ac_subdirs_all; do test "x$ac_dir" = x: && continue + test -d "$ac_dir" || + { cd "$srcdir" && ac_pwd=`pwd` && srcdir=. && test -d "$ac_dir"; } || + continue + ac_builddir=. + +case "$ac_dir" in +.) ac_dir_suffix= ac_top_builddir_sub=. ac_top_build_prefix= ;; +*) + ac_dir_suffix=/`$as_echo "$ac_dir" | sed 's|^\.[\\/]||'` + # A ".." for each directory in $ac_dir_suffix. + ac_top_builddir_sub=`$as_echo "$ac_dir_suffix" | sed 's|/[^\\/]*|/..|g;s|/||'` + case $ac_top_builddir_sub in + "") ac_top_builddir_sub=. ac_top_build_prefix= ;; + *) ac_top_build_prefix=$ac_top_builddir_sub/ ;; + esac ;; +esac +ac_abs_top_builddir=$ac_pwd +ac_abs_builddir=$ac_pwd$ac_dir_suffix +# for backward compatibility: +ac_top_builddir=$ac_top_build_prefix + +case $srcdir in + .) # We are building in place. + ac_srcdir=. + ac_top_srcdir=$ac_top_builddir_sub + ac_abs_top_srcdir=$ac_pwd ;; + [\\/]* | ?:[\\/]* ) # Absolute name. + ac_srcdir=$srcdir$ac_dir_suffix; + ac_top_srcdir=$srcdir + ac_abs_top_srcdir=$srcdir ;; + *) # Relative name. + ac_srcdir=$ac_top_build_prefix$srcdir$ac_dir_suffix + ac_top_srcdir=$ac_top_build_prefix$srcdir + ac_abs_top_srcdir=$ac_pwd/$srcdir ;; +esac +ac_abs_srcdir=$ac_abs_top_srcdir$ac_dir_suffix + + cd "$ac_dir" || { ac_status=$?; continue; } + # Check for guested 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 + else + $as_echo "$as_me: WARNING: no configuration information is in $ac_dir" >&2 + fi || ac_status=$? + cd "$ac_pwd" || { ac_status=$?; break; } + done +fi + +test -n "$ac_init_help" && exit $ac_status +if $ac_init_version; then + cat <<\_ACEOF +configure +generated by GNU Autoconf 2.68 + +Copyright (C) 2010 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 +fi + +## ------------------------ ## +## Autoconf initialization. ## +## ------------------------ ## + +# ac_fn_c_try_compile LINENO +# -------------------------- +# Try to compile conftest.$ac_ext, and return whether this succeeded. +ac_fn_c_try_compile () +{ + as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack + rm -f conftest.$ac_objext + if { { ac_try="$ac_compile" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" +$as_echo "$ac_try_echo"; } >&5 + (eval "$ac_compile") 2>conftest.err + ac_status=$? + if test -s conftest.err; then + grep -v '^ *+' conftest.err >conftest.er1 + cat conftest.er1 >&5 + mv -f conftest.er1 conftest.err + fi + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; } && { + test -z "$ac_c_werror_flag" || + test ! -s conftest.err + } && test -s conftest.$ac_objext; then : + ac_retval=0 +else + $as_echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + ac_retval=1 +fi + eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno + as_fn_set_status $ac_retval + +} # ac_fn_c_try_compile + +# ac_fn_c_try_link LINENO +# ----------------------- +# Try to link conftest.$ac_ext, and return whether this succeeded. +ac_fn_c_try_link () +{ + as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack + rm -f conftest.$ac_objext conftest$ac_exeext + if { { ac_try="$ac_link" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" +$as_echo "$ac_try_echo"; } >&5 + (eval "$ac_link") 2>conftest.err + ac_status=$? + if test -s conftest.err; then + grep -v '^ *+' conftest.err >conftest.er1 + cat conftest.er1 >&5 + mv -f conftest.er1 conftest.err + fi + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; } && { + test -z "$ac_c_werror_flag" || + test ! -s conftest.err + } && test -s conftest$ac_exeext && { + test "$cross_compiling" = yes || + $as_test_x conftest$ac_exeext + }; then : + ac_retval=0 +else + $as_echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + ac_retval=1 +fi + # Delete the IPA/IPO (Inter Procedural Analysis/Optimization) information + # created by the PGI compiler (conftest_ipa8_conftest.oo), as it would + # interfere with the next link command; also delete a directory that is + # left behind by Apple's compiler. We do this before executing the actions. + rm -rf conftest.dSYM conftest_ipa8_conftest.oo + eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno + as_fn_set_status $ac_retval + +} # ac_fn_c_try_link + +# ac_fn_c_try_cpp LINENO +# ---------------------- +# Try to preprocess conftest.$ac_ext, and return whether this succeeded. +ac_fn_c_try_cpp () +{ + as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack + if { { ac_try="$ac_cpp conftest.$ac_ext" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" +$as_echo "$ac_try_echo"; } >&5 + (eval "$ac_cpp conftest.$ac_ext") 2>conftest.err + ac_status=$? + if test -s conftest.err; then + grep -v '^ *+' conftest.err >conftest.er1 + cat conftest.er1 >&5 + mv -f conftest.er1 conftest.err + fi + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; } > conftest.i && { + test -z "$ac_c_preproc_warn_flag$ac_c_werror_flag" || + test ! -s conftest.err + }; then : + ac_retval=0 +else + $as_echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + ac_retval=1 +fi + eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno + as_fn_set_status $ac_retval + +} # ac_fn_c_try_cpp + +# ac_fn_c_check_header_mongrel LINENO HEADER VAR INCLUDES +# ------------------------------------------------------- +# Tests whether HEADER exists, giving a warning if it cannot be compiled using +# the include files in INCLUDES and setting the cache variable VAR +# accordingly. +ac_fn_c_check_header_mongrel () +{ + as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack + if eval \${$3+:} false; then : + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $2" >&5 +$as_echo_n "checking for $2... " >&6; } +if eval \${$3+:} false; then : + $as_echo_n "(cached) " >&6 +fi +eval ac_res=\$$3 + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 +$as_echo "$ac_res" >&6; } +else + # Is the header compilable? +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking $2 usability" >&5 +$as_echo_n "checking $2 usability... " >&6; } +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +$4 +#include <$2> +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + ac_header_compiler=yes +else + ac_header_compiler=no +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_header_compiler" >&5 +$as_echo "$ac_header_compiler" >&6; } + +# Is the header present? +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking $2 presence" >&5 +$as_echo_n "checking $2 presence... " >&6; } +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include <$2> +_ACEOF +if ac_fn_c_try_cpp "$LINENO"; then : + ac_header_preproc=yes +else + ac_header_preproc=no +fi +rm -f conftest.err conftest.i conftest.$ac_ext +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_header_preproc" >&5 +$as_echo "$ac_header_preproc" >&6; } + +# So? What about this header? +case $ac_header_compiler:$ac_header_preproc:$ac_c_preproc_warn_flag in #(( + yes:no: ) + { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: accepted by the compiler, rejected by the preprocessor!" >&5 +$as_echo "$as_me: WARNING: $2: accepted by the compiler, rejected by the preprocessor!" >&2;} + { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: proceeding with the compiler's result" >&5 +$as_echo "$as_me: WARNING: $2: proceeding with the compiler's result" >&2;} + ;; + no:yes:* ) + { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: present but cannot be compiled" >&5 +$as_echo "$as_me: WARNING: $2: present but cannot be compiled" >&2;} + { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: check for missing prerequisite headers?" >&5 +$as_echo "$as_me: WARNING: $2: check for missing prerequisite headers?" >&2;} + { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: see the Autoconf documentation" >&5 +$as_echo "$as_me: WARNING: $2: see the Autoconf documentation" >&2;} + { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: section \"Present But Cannot Be Compiled\"" >&5 +$as_echo "$as_me: WARNING: $2: section \"Present But Cannot Be Compiled\"" >&2;} + { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: proceeding with the compiler's result" >&5 +$as_echo "$as_me: WARNING: $2: proceeding with the compiler's result" >&2;} + ;; +esac + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $2" >&5 +$as_echo_n "checking for $2... " >&6; } +if eval \${$3+:} false; then : + $as_echo_n "(cached) " >&6 +else + eval "$3=\$ac_header_compiler" +fi +eval ac_res=\$$3 + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 +$as_echo "$ac_res" >&6; } +fi + eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno + +} # ac_fn_c_check_header_mongrel + +# ac_fn_c_try_run LINENO +# ---------------------- +# Try to link conftest.$ac_ext, and return whether this succeeded. Assumes +# that executables *can* be run. +ac_fn_c_try_run () +{ + as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack + if { { ac_try="$ac_link" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" +$as_echo "$ac_try_echo"; } >&5 + (eval "$ac_link") 2>&5 + ac_status=$? + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; } && { ac_try='./conftest$ac_exeext' + { { case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" +$as_echo "$ac_try_echo"; } >&5 + (eval "$ac_try") 2>&5 + ac_status=$? + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; }; }; then : + ac_retval=0 +else + $as_echo "$as_me: program exited with status $ac_status" >&5 + $as_echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + ac_retval=$ac_status +fi + rm -rf conftest.dSYM conftest_ipa8_conftest.oo + eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno + as_fn_set_status $ac_retval + +} # ac_fn_c_try_run + +# ac_fn_c_check_header_compile LINENO HEADER VAR INCLUDES +# ------------------------------------------------------- +# Tests whether HEADER exists and can be compiled using the include files in +# INCLUDES, setting the cache variable VAR accordingly. +ac_fn_c_check_header_compile () +{ + as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $2" >&5 +$as_echo_n "checking for $2... " >&6; } +if eval \${$3+:} false; then : + $as_echo_n "(cached) " >&6 +else + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +$4 +#include <$2> +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + eval "$3=yes" +else + eval "$3=no" +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +fi +eval ac_res=\$$3 + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 +$as_echo "$ac_res" >&6; } + eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno + +} # ac_fn_c_check_header_compile +cat >config.log <<_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.68. Invocation command line was + + $ $0 $@ + +_ACEOF +exec 5>>config.log +{ +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` +/usr/bin/hostinfo = `(/usr/bin/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=. + $as_echo "PATH: $as_dir" + done +IFS=$as_save_IFS + +} >&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_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=`$as_echo "$ac_arg" | sed "s/'/'\\\\\\\\''/g"` ;; + esac + case $ac_pass in + 1) as_fn_append ac_configure_args0 " '$ac_arg'" ;; + 2) + as_fn_append 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 + as_fn_append ac_configure_args " '$ac_arg'" + ;; + esac + done +done +{ ac_configure_args0=; unset ac_configure_args0;} +{ ac_configure_args1=; unset 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: Use '\'' to represent an apostrophe within the trap. +# WARNING: Do not start the trap code with a newline, due to a FreeBSD 4.0 bug. +trap 'exit_status=$? + # Save into config.log some information that might help in debugging. + { + echo + + $as_echo "## ---------------- ## +## Cache variables. ## +## ---------------- ##" + echo + # The following way of writing the cache mishandles newlines in values, +( + for ac_var in `(set) 2>&1 | sed -n '\''s/^\([a-zA-Z_][a-zA-Z0-9_]*\)=.*/\1/p'\''`; do + eval ac_val=\$$ac_var + case $ac_val in #( + *${as_nl}*) + case $ac_var in #( + *_cv_*) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: cache variable $ac_var contains a newline" >&5 +$as_echo "$as_me: WARNING: cache variable $ac_var contains a newline" >&2;} ;; + esac + case $ac_var in #( + _ | IFS | as_nl) ;; #( + BASH_ARGV | BASH_SOURCE) eval $ac_var= ;; #( + *) { eval $ac_var=; unset $ac_var;} ;; + esac ;; + esac + done + (set) 2>&1 | + case $as_nl`(ac_space='\'' '\''; set) 2>&1` in #( + *${as_nl}ac_space=\ *) + sed -n \ + "s/'\''/'\''\\\\'\'''\''/g; + s/^\\([_$as_cr_alnum]*_cv_[_$as_cr_alnum]*\\)=\\(.*\\)/\\1='\''\\2'\''/p" + ;; #( + *) + sed -n "/^[_$as_cr_alnum]*_cv_[_$as_cr_alnum]*=/p" + ;; + esac | + sort +) + echo + + $as_echo "## ----------------- ## +## Output variables. ## +## ----------------- ##" + echo + for ac_var in $ac_subst_vars + do + eval ac_val=\$$ac_var + case $ac_val in + *\'\''*) ac_val=`$as_echo "$ac_val" | sed "s/'\''/'\''\\\\\\\\'\'''\''/g"`;; + esac + $as_echo "$ac_var='\''$ac_val'\''" + done | sort + echo + + if test -n "$ac_subst_files"; then + $as_echo "## ------------------- ## +## File substitutions. ## +## ------------------- ##" + echo + for ac_var in $ac_subst_files + do + eval ac_val=\$$ac_var + case $ac_val in + *\'\''*) ac_val=`$as_echo "$ac_val" | sed "s/'\''/'\''\\\\\\\\'\'''\''/g"`;; + esac + $as_echo "$ac_var='\''$ac_val'\''" + done | sort + echo + fi + + if test -s confdefs.h; then + $as_echo "## ----------- ## +## confdefs.h. ## +## ----------- ##" + echo + cat confdefs.h + echo + fi + test "$ac_signal" != 0 && + $as_echo "$as_me: caught signal $ac_signal" + $as_echo "$as_me: exit $exit_status" + } >&5 + rm -f core *.core core.conftest.* && + rm -f -r conftest* confdefs* conf$$* $ac_clean_files && + exit $exit_status +' 0 +for ac_signal in 1 2 13 15; do + trap 'ac_signal='$ac_signal'; as_fn_exit 1' $ac_signal +done +ac_signal=0 + +# confdefs.h avoids OS command line length limits that DEFS can exceed. +rm -f -r conftest* confdefs.h + +$as_echo "/* confdefs.h */" > 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 + +cat >>confdefs.h <<_ACEOF +#define PACKAGE_URL "$PACKAGE_URL" +_ACEOF + + +# Let the site file select an alternate cache file if it wants to. +# Prefer an explicitly selected file to automatically selected ones. +ac_site_file1=NONE +ac_site_file2=NONE +if test -n "$CONFIG_SITE"; then + # We do not want a PATH search for config.site. + case $CONFIG_SITE in #(( + -*) ac_site_file1=./$CONFIG_SITE;; + */*) ac_site_file1=$CONFIG_SITE;; + *) ac_site_file1=./$CONFIG_SITE;; + esac +elif test "x$prefix" != xNONE; then + ac_site_file1=$prefix/share/config.site + ac_site_file2=$prefix/etc/config.site +else + ac_site_file1=$ac_default_prefix/share/config.site + ac_site_file2=$ac_default_prefix/etc/config.site +fi +for ac_site_file in "$ac_site_file1" "$ac_site_file2" +do + test "x$ac_site_file" = xNONE && continue + if test /dev/null != "$ac_site_file" && test -r "$ac_site_file"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: loading site script $ac_site_file" >&5 +$as_echo "$as_me: loading site script $ac_site_file" >&6;} + sed 's/^/| /' "$ac_site_file" >&5 + . "$ac_site_file" \ + || { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 +$as_echo "$as_me: error: in \`$ac_pwd':" >&2;} +as_fn_error $? "failed to load site script $ac_site_file +See \`config.log' for more details" "$LINENO" 5; } + 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. DJGPP emulates it as a regular file. + if test /dev/null != "$cache_file" && test -f "$cache_file"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: loading cache $cache_file" >&5 +$as_echo "$as_me: loading cache $cache_file" >&6;} + case $cache_file in + [\\/]* | ?:[\\/]* ) . "$cache_file";; + *) . "./$cache_file";; + esac + fi +else + { $as_echo "$as_me:${as_lineno-$LINENO}: creating cache $cache_file" >&5 +$as_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 $ac_precious_vars; 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,) + { $as_echo "$as_me:${as_lineno-$LINENO}: error: \`$ac_var' was set to \`$ac_old_val' in the previous run" >&5 +$as_echo "$as_me: error: \`$ac_var' was set to \`$ac_old_val' in the previous run" >&2;} + ac_cache_corrupted=: ;; + ,set) + { $as_echo "$as_me:${as_lineno-$LINENO}: error: \`$ac_var' was not set in the previous run" >&5 +$as_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 + # differences in whitespace do not lead to failure. + ac_old_val_w=`echo x $ac_old_val` + ac_new_val_w=`echo x $ac_new_val` + if test "$ac_old_val_w" != "$ac_new_val_w"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: error: \`$ac_var' has changed since the previous run:" >&5 +$as_echo "$as_me: error: \`$ac_var' has changed since the previous run:" >&2;} + ac_cache_corrupted=: + else + { $as_echo "$as_me:${as_lineno-$LINENO}: warning: ignoring whitespace changes in \`$ac_var' since the previous run:" >&5 +$as_echo "$as_me: warning: ignoring whitespace changes in \`$ac_var' since the previous run:" >&2;} + eval $ac_var=\$ac_old_val + fi + { $as_echo "$as_me:${as_lineno-$LINENO}: former value: \`$ac_old_val'" >&5 +$as_echo "$as_me: former value: \`$ac_old_val'" >&2;} + { $as_echo "$as_me:${as_lineno-$LINENO}: current value: \`$ac_new_val'" >&5 +$as_echo "$as_me: current value: \`$ac_new_val'" >&2;} + fi;; + esac + # Pass precious variables to config.status. + if test "$ac_new_set" = set; then + case $ac_new_val in + *\'*) ac_arg=$ac_var=`$as_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. + *) as_fn_append ac_configure_args " '$ac_arg'" ;; + esac + fi +done +if $ac_cache_corrupted; then + { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 +$as_echo "$as_me: error: in \`$ac_pwd':" >&2;} + { $as_echo "$as_me:${as_lineno-$LINENO}: error: changes in the environment can compromise the build" >&5 +$as_echo "$as_me: error: changes in the environment can compromise the build" >&2;} + as_fn_error $? "run \`make distclean' and/or \`rm $cache_file' and start over" "$LINENO" 5 +fi +## -------------------- ## +## Main body of script. ## +## -------------------- ## + +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="3" + + +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 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_CC+:} false; then : + $as_echo_n "(cached) " >&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 { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then + ac_cv_prog_CC="${ac_tool_prefix}gcc" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +CC=$ac_cv_prog_CC +if test -n "$CC"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CC" >&5 +$as_echo "$CC" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "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 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_ac_ct_CC+:} false; then : + $as_echo_n "(cached) " >&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 { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then + ac_cv_prog_ac_ct_CC="gcc" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +ac_ct_CC=$ac_cv_prog_ac_ct_CC +if test -n "$ac_ct_CC"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_CC" >&5 +$as_echo "$ac_ct_CC" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + if test "x$ac_ct_CC" = x; then + CC="" + else + case $cross_compiling:$ac_tool_warned in +yes:) +{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 +$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} +ac_tool_warned=yes ;; +esac + CC=$ac_ct_CC + fi +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 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_CC+:} false; then : + $as_echo_n "(cached) " >&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 { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then + ac_cv_prog_CC="${ac_tool_prefix}cc" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +CC=$ac_cv_prog_CC +if test -n "$CC"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CC" >&5 +$as_echo "$CC" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + + 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 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_CC+:} false; then : + $as_echo_n "(cached) " >&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 { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$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" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_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 $# != 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 + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CC" >&5 +$as_echo "$CC" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + +fi +if test -z "$CC"; then + if test -n "$ac_tool_prefix"; then + for ac_prog in cl.exe + 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 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_CC+:} false; then : + $as_echo_n "(cached) " >&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 { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then + ac_cv_prog_CC="$ac_tool_prefix$ac_prog" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +CC=$ac_cv_prog_CC +if test -n "$CC"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CC" >&5 +$as_echo "$CC" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + + test -n "$CC" && break + done +fi +if test -z "$CC"; then + ac_ct_CC=$CC + for ac_prog in cl.exe +do + # Extract the first word of "$ac_prog", so it can be a program name with args. +set dummy $ac_prog; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_ac_ct_CC+:} false; then : + $as_echo_n "(cached) " >&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 { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then + ac_cv_prog_ac_ct_CC="$ac_prog" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +ac_ct_CC=$ac_cv_prog_ac_ct_CC +if test -n "$ac_ct_CC"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_CC" >&5 +$as_echo "$ac_ct_CC" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + + test -n "$ac_ct_CC" && break +done + + if test "x$ac_ct_CC" = x; then + CC="" + else + case $cross_compiling:$ac_tool_warned in +yes:) +{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 +$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} +ac_tool_warned=yes ;; +esac + CC=$ac_ct_CC + fi +fi + +fi + + +test -z "$CC" && { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 +$as_echo "$as_me: error: in \`$ac_pwd':" >&2;} +as_fn_error $? "no acceptable C compiler found in \$PATH +See \`config.log' for more details" "$LINENO" 5; } + +# Provide some information about the compiler. +$as_echo "$as_me:${as_lineno-$LINENO}: checking for C compiler version" >&5 +set X $ac_compile +ac_compiler=$2 +for ac_option in --version -v -V -qversion; do + { { ac_try="$ac_compiler $ac_option >&5" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" +$as_echo "$ac_try_echo"; } >&5 + (eval "$ac_compiler $ac_option >&5") 2>conftest.err + ac_status=$? + if test -s conftest.err; then + sed '10a\ +... rest of stderr output deleted ... + 10q' conftest.err >conftest.er1 + cat conftest.er1 >&5 + fi + rm -f conftest.er1 conftest.err + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; } +done + +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +int +main () +{ + + ; + return 0; +} +_ACEOF +ac_clean_files_save=$ac_clean_files +ac_clean_files="$ac_clean_files a.out a.out.dSYM 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. +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether the C compiler works" >&5 +$as_echo_n "checking whether the C compiler works... " >&6; } +ac_link_default=`$as_echo "$ac_link" | sed 's/ -o *conftest[^ ]*//'` + +# The possible output files: +ac_files="a.out conftest.exe conftest a.exe a_out.exe b.out conftest.*" + +ac_rmfiles= +for ac_file in $ac_files +do + case $ac_file in + *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.dSYM | *.o | *.obj ) ;; + * ) ac_rmfiles="$ac_rmfiles $ac_file";; + esac +done +rm -f $ac_rmfiles + +if { { ac_try="$ac_link_default" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" +$as_echo "$ac_try_echo"; } >&5 + (eval "$ac_link_default") 2>&5 + ac_status=$? + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; }; then : + # Autoconf-2.13 could set the ac_cv_exeext variable to `no'. +# So ignore a value of `no', otherwise this would lead to `EXEEXT = no' +# in a Makefile. We should not override ac_cv_exeext if it was cached, +# so that the user can short-circuit this test for compilers unknown to +# Autoconf. +for ac_file in $ac_files '' +do + test -f "$ac_file" || continue + case $ac_file in + *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.dSYM | *.o | *.obj ) + ;; + [ab].out ) + # We found the default executable, but exeext='' is most + # certainly right. + break;; + *.* ) + if test "${ac_cv_exeext+set}" = set && test "$ac_cv_exeext" != no; + then :; else + ac_cv_exeext=`expr "$ac_file" : '[^.]*\(\..*\)'` + fi + # We set ac_cv_exeext here because the later test for it is not + # safe: cross compilers may not add the suffix if given an `-o' + # argument, so we may need to know it at that point already. + # Even if this section looks crufty: it has the advantage of + # actually working. + break;; + * ) + break;; + esac +done +test "$ac_cv_exeext" = no && ac_cv_exeext= + +else + ac_file='' +fi +if test -z "$ac_file"; then : + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +$as_echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +{ { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 +$as_echo "$as_me: error: in \`$ac_pwd':" >&2;} +as_fn_error 77 "C compiler cannot create executables +See \`config.log' for more details" "$LINENO" 5; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 +$as_echo "yes" >&6; } +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for C compiler default output file name" >&5 +$as_echo_n "checking for C compiler default output file name... " >&6; } +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_file" >&5 +$as_echo "$ac_file" >&6; } +ac_exeext=$ac_cv_exeext + +rm -f -r a.out a.out.dSYM a.exe conftest$ac_cv_exeext b.out +ac_clean_files=$ac_clean_files_save +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for suffix of executables" >&5 +$as_echo_n "checking for suffix of executables... " >&6; } +if { { ac_try="$ac_link" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" +$as_echo "$ac_try_echo"; } >&5 + (eval "$ac_link") 2>&5 + ac_status=$? + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; }; 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 | *.map | *.inf | *.dSYM | *.o | *.obj ) ;; + *.* ) ac_cv_exeext=`expr "$ac_file" : '[^.]*\(\..*\)'` + break;; + * ) break;; + esac +done +else + { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 +$as_echo "$as_me: error: in \`$ac_pwd':" >&2;} +as_fn_error $? "cannot compute suffix of executables: cannot compile and link +See \`config.log' for more details" "$LINENO" 5; } +fi +rm -f conftest conftest$ac_cv_exeext +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_exeext" >&5 +$as_echo "$ac_cv_exeext" >&6; } + +rm -f conftest.$ac_ext +EXEEXT=$ac_cv_exeext +ac_exeext=$EXEEXT +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include +int +main () +{ +FILE *f = fopen ("conftest.out", "w"); + return ferror (f) || fclose (f) != 0; + + ; + return 0; +} +_ACEOF +ac_clean_files="$ac_clean_files conftest.out" +# Check that the compiler produces executables we can run. If not, either +# the compiler is broken, or we cross compile. +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether we are cross compiling" >&5 +$as_echo_n "checking whether we are cross compiling... " >&6; } +if test "$cross_compiling" != yes; then + { { ac_try="$ac_link" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" +$as_echo "$ac_try_echo"; } >&5 + (eval "$ac_link") 2>&5 + ac_status=$? + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; } + if { ac_try='./conftest$ac_cv_exeext' + { { case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" +$as_echo "$ac_try_echo"; } >&5 + (eval "$ac_try") 2>&5 + ac_status=$? + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; }; }; then + cross_compiling=no + else + if test "$cross_compiling" = maybe; then + cross_compiling=yes + else + { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 +$as_echo "$as_me: error: in \`$ac_pwd':" >&2;} +as_fn_error $? "cannot run C compiled programs. +If you meant to cross compile, use \`--host'. +See \`config.log' for more details" "$LINENO" 5; } + fi + fi +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $cross_compiling" >&5 +$as_echo "$cross_compiling" >&6; } + +rm -f conftest.$ac_ext conftest$ac_cv_exeext conftest.out +ac_clean_files=$ac_clean_files_save +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for suffix of object files" >&5 +$as_echo_n "checking for suffix of object files... " >&6; } +if ${ac_cv_objext+:} false; then : + $as_echo_n "(cached) " >&6 +else + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +int +main () +{ + + ; + return 0; +} +_ACEOF +rm -f conftest.o conftest.obj +if { { ac_try="$ac_compile" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" +$as_echo "$ac_try_echo"; } >&5 + (eval "$ac_compile") 2>&5 + ac_status=$? + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; }; then : + for ac_file in conftest.o conftest.obj conftest.*; do + test -f "$ac_file" || continue; + case $ac_file in + *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.dSYM ) ;; + *) ac_cv_objext=`expr "$ac_file" : '.*\.\(.*\)'` + break;; + esac +done +else + $as_echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +{ { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 +$as_echo "$as_me: error: in \`$ac_pwd':" >&2;} +as_fn_error $? "cannot compute suffix of object files: cannot compile +See \`config.log' for more details" "$LINENO" 5; } +fi +rm -f conftest.$ac_cv_objext conftest.$ac_ext +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_objext" >&5 +$as_echo "$ac_cv_objext" >&6; } +OBJEXT=$ac_cv_objext +ac_objext=$OBJEXT +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether we are using the GNU C compiler" >&5 +$as_echo_n "checking whether we are using the GNU C compiler... " >&6; } +if ${ac_cv_c_compiler_gnu+:} false; then : + $as_echo_n "(cached) " >&6 +else + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +int +main () +{ +#ifndef __GNUC__ + choke me +#endif + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + ac_compiler_gnu=yes +else + ac_compiler_gnu=no +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +ac_cv_c_compiler_gnu=$ac_compiler_gnu + +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_c_compiler_gnu" >&5 +$as_echo "$ac_cv_c_compiler_gnu" >&6; } +if test $ac_compiler_gnu = yes; then + GCC=yes +else + GCC= +fi +ac_test_CFLAGS=${CFLAGS+set} +ac_save_CFLAGS=$CFLAGS +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether $CC accepts -g" >&5 +$as_echo_n "checking whether $CC accepts -g... " >&6; } +if ${ac_cv_prog_cc_g+:} false; then : + $as_echo_n "(cached) " >&6 +else + ac_save_c_werror_flag=$ac_c_werror_flag + ac_c_werror_flag=yes + ac_cv_prog_cc_g=no + CFLAGS="-g" + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +int +main () +{ + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + ac_cv_prog_cc_g=yes +else + CFLAGS="" + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +int +main () +{ + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + +else + ac_c_werror_flag=$ac_save_c_werror_flag + CFLAGS="-g" + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +int +main () +{ + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + ac_cv_prog_cc_g=yes +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext + ac_c_werror_flag=$ac_save_c_werror_flag +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_cc_g" >&5 +$as_echo "$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 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $CC option to accept ISO C89" >&5 +$as_echo_n "checking for $CC option to accept ISO C89... " >&6; } +if ${ac_cv_prog_cc_c89+:} false; then : + $as_echo_n "(cached) " >&6 +else + ac_cv_prog_cc_c89=no +ac_save_CC=$CC +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* 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; +} + +/* OSF 4.0 Compaq cc is some sort of almost-ANSI by default. It has + function prototypes and stuff, but not '\xHH' hex character constants. + These don't provoke an error unfortunately, instead are silently treated + as 'x'. The following induces an error, until -std is added to get + proper ANSI mode. Curiously '\x00'!='x' always comes out true, for an + array size at least. It's necessary to write '\x00'==0 to get something + that's true only with -std. */ +int osf4_cc_array ['\x00' == 0 ? 1 : -1]; + +/* IBM C 6 for AIX is almost-ANSI by default, but it replaces macro parameters + inside strings and character constants. */ +#define FOO(x) 'x' +int xlc6_cc_array[FOO(a) == 'x' ? 1 : -1]; + +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 +for ac_arg in '' -qlanglvl=extc89 -qlanglvl=ansi -std \ + -Ae "-Aa -D_HPUX_SOURCE" "-Xc -D__EXTENSIONS__" +do + CC="$ac_save_CC $ac_arg" + if ac_fn_c_try_compile "$LINENO"; then : + ac_cv_prog_cc_c89=$ac_arg +fi +rm -f core conftest.err conftest.$ac_objext + test "x$ac_cv_prog_cc_c89" != "xno" && break +done +rm -f conftest.$ac_ext +CC=$ac_save_CC + +fi +# AC_CACHE_VAL +case "x$ac_cv_prog_cc_c89" in + x) + { $as_echo "$as_me:${as_lineno-$LINENO}: result: none needed" >&5 +$as_echo "none needed" >&6; } ;; + xno) + { $as_echo "$as_me:${as_lineno-$LINENO}: result: unsupported" >&5 +$as_echo "unsupported" >&6; } ;; + *) + CC="$CC $ac_cv_prog_cc_c89" + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_cc_c89" >&5 +$as_echo "$ac_cv_prog_cc_c89" >&6; } ;; +esac +if test "x$ac_cv_prog_cc_c89" != xno; then : + +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 + + + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether ${MAKE-make} sets \$(MAKE)" >&5 +$as_echo_n "checking whether ${MAKE-make} sets \$(MAKE)... " >&6; } +set x ${MAKE-make} +ac_make=`$as_echo "$2" | sed 's/+/p/g; s/[^a-zA-Z0-9_]/_/g'` +if eval \${ac_cv_prog_make_${ac_make}_set+:} false; then : + $as_echo_n "(cached) " >&6 +else + cat >conftest.make <<\_ACEOF +SHELL = /bin/sh +all: + @echo '@@@%%%=$(MAKE)=@@@%%%' +_ACEOF +# GNU make sometimes prints "make[1]: Entering ...", which would confuse us. +case `${MAKE-make} -f conftest.make 2>/dev/null` in + *@@@%%%=?*=@@@%%%*) + eval ac_cv_prog_make_${ac_make}_set=yes;; + *) + eval ac_cv_prog_make_${ac_make}_set=no;; +esac +rm -f conftest.make +fi +if eval test \$ac_cv_prog_make_${ac_make}_set = yes; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 +$as_echo "yes" >&6; } + SET_MAKE= +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } + SET_MAKE="MAKE=${MAKE-make}" +fi + + + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether ln -s works" >&5 +$as_echo_n "checking whether ln -s works... " >&6; } +LN_S=$as_ln_s +if test "$LN_S" = "ln -s"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 +$as_echo "yes" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no, using $LN_S" >&5 +$as_echo "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 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_AWK+:} false; then : + $as_echo_n "(cached) " >&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 { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then + ac_cv_prog_AWK="$ac_prog" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +AWK=$ac_cv_prog_AWK +if test -n "$AWK"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $AWK" >&5 +$as_echo "$AWK" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "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 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_RANLIB+:} false; then : + $as_echo_n "(cached) " >&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 { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then + ac_cv_prog_RANLIB="${ac_tool_prefix}ranlib" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +RANLIB=$ac_cv_prog_RANLIB +if test -n "$RANLIB"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $RANLIB" >&5 +$as_echo "$RANLIB" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "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 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_ac_ct_RANLIB+:} false; then : + $as_echo_n "(cached) " >&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 { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then + ac_cv_prog_ac_ct_RANLIB="ranlib" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +ac_ct_RANLIB=$ac_cv_prog_ac_ct_RANLIB +if test -n "$ac_ct_RANLIB"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_RANLIB" >&5 +$as_echo "$ac_ct_RANLIB" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + if test "x$ac_ct_RANLIB" = x; then + RANLIB="" + else + case $cross_compiling:$ac_tool_warned in +yes:) +{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 +$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} +ac_tool_warned=yes ;; +esac + RANLIB=$ac_ct_RANLIB + fi +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 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_LD+:} false; then : + $as_echo_n "(cached) " >&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 { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then + ac_cv_prog_LD="${ac_tool_prefix}ld" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +LD=$ac_cv_prog_LD +if test -n "$LD"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $LD" >&5 +$as_echo "$LD" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "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 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_ac_ct_LD+:} false; then : + $as_echo_n "(cached) " >&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 { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then + ac_cv_prog_ac_ct_LD="ld" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +ac_ct_LD=$ac_cv_prog_ac_ct_LD +if test -n "$ac_ct_LD"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_LD" >&5 +$as_echo "$ac_ct_LD" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + if test "x$ac_ct_LD" = x; then + LD="" + else + case $cross_compiling:$ac_tool_warned in +yes:) +{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 +$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} +ac_tool_warned=yes ;; +esac + LD=$ac_ct_LD + fi +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 + as_fn_error $? "cannot find install-sh, install.sh, or shtool in \"$srcdir\" \"$srcdir/..\" \"$srcdir/../..\"" "$LINENO" 5 +fi + +# These three variables are undocumented and unsupported, +# and are intended to be withdrawn in a future Autoconf release. +# They can cause serious problems if a builder's source tree is in a directory +# whose full name contains unusual characters. +ac_config_guess="$SHELL $ac_aux_dir/config.guess" # Please don't use this var. +ac_config_sub="$SHELL $ac_aux_dir/config.sub" # Please don't use this var. +ac_configure="$SHELL $ac_aux_dir/configure" # Please don't use this var. + + +# Make sure we can run config.sub. +$SHELL "$ac_aux_dir/config.sub" sun4 >/dev/null 2>&1 || + as_fn_error $? "cannot run $SHELL $ac_aux_dir/config.sub" "$LINENO" 5 + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking build system type" >&5 +$as_echo_n "checking build system type... " >&6; } +if ${ac_cv_build+:} false; then : + $as_echo_n "(cached) " >&6 +else + ac_build_alias=$build_alias +test "x$ac_build_alias" = x && + ac_build_alias=`$SHELL "$ac_aux_dir/config.guess"` +test "x$ac_build_alias" = x && + as_fn_error $? "cannot guess build type; you must specify one" "$LINENO" 5 +ac_cv_build=`$SHELL "$ac_aux_dir/config.sub" $ac_build_alias` || + as_fn_error $? "$SHELL $ac_aux_dir/config.sub $ac_build_alias failed" "$LINENO" 5 + +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_build" >&5 +$as_echo "$ac_cv_build" >&6; } +case $ac_cv_build in +*-*-*) ;; +*) as_fn_error $? "invalid value of canonical build" "$LINENO" 5;; +esac +build=$ac_cv_build +ac_save_IFS=$IFS; IFS='-' +set x $ac_cv_build +shift +build_cpu=$1 +build_vendor=$2 +shift; shift +# Remember, the first character of IFS is used to create $*, +# except with old shells: +build_os=$* +IFS=$ac_save_IFS +case $build_os in *\ *) build_os=`echo "$build_os" | sed 's/ /-/g'`;; esac + + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking host system type" >&5 +$as_echo_n "checking host system type... " >&6; } +if ${ac_cv_host+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test "x$host_alias" = x; then + ac_cv_host=$ac_cv_build +else + ac_cv_host=`$SHELL "$ac_aux_dir/config.sub" $host_alias` || + as_fn_error $? "$SHELL $ac_aux_dir/config.sub $host_alias failed" "$LINENO" 5 +fi + +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_host" >&5 +$as_echo "$ac_cv_host" >&6; } +case $ac_cv_host in +*-*-*) ;; +*) as_fn_error $? "invalid value of canonical host" "$LINENO" 5;; +esac +host=$ac_cv_host +ac_save_IFS=$IFS; IFS='-' +set x $ac_cv_host +shift +host_cpu=$1 +host_vendor=$2 +shift; shift +# Remember, the first character of IFS is used to create $*, +# except with old shells: +host_os=$* +IFS=$ac_save_IFS +case $host_os in *\ *) host_os=`echo "$host_os" | sed 's/ /-/g'`;; esac + + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking target system type" >&5 +$as_echo_n "checking target system type... " >&6; } +if ${ac_cv_target+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test "x$target_alias" = x; then + ac_cv_target=$ac_cv_host +else + ac_cv_target=`$SHELL "$ac_aux_dir/config.sub" $target_alias` || + as_fn_error $? "$SHELL $ac_aux_dir/config.sub $target_alias failed" "$LINENO" 5 +fi + +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_target" >&5 +$as_echo "$ac_cv_target" >&6; } +case $ac_cv_target in +*-*-*) ;; +*) as_fn_error $? "invalid value of canonical target" "$LINENO" 5;; +esac +target=$ac_cv_target +ac_save_IFS=$IFS; IFS='-' +set x $ac_cv_target +shift +target_cpu=$1 +target_vendor=$2 +shift; shift +# Remember, the first character of IFS is used to create $*, +# except with old shells: +target_os=$* +IFS=$ac_save_IFS +case $target_os in *\ *) target_os=`echo "$target_os" | sed 's/ /-/g'`;; esac + + +# 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 +$as_echo_n "checking how to run the C preprocessor... " >&6; } +# On Suns, sometimes $CPP names a directory. +if test -n "$CPP" && test -d "$CPP"; then + CPP= +fi +if test -z "$CPP"; then + if ${ac_cv_prog_CPP+:} false; then : + $as_echo_n "(cached) " >&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 confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#ifdef __STDC__ +# include +#else +# include +#endif + Syntax error +_ACEOF +if ac_fn_c_try_cpp "$LINENO"; then : + +else + # Broken: fails on valid input. +continue +fi +rm -f conftest.err conftest.i conftest.$ac_ext + + # OK, works on sane cases. Now check whether nonexistent headers + # can be detected and how. + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include +_ACEOF +if ac_fn_c_try_cpp "$LINENO"; then : + # Broken: success on invalid input. +continue +else + # Passes both tests. +ac_preproc_ok=: +break +fi +rm -f conftest.err conftest.i conftest.$ac_ext + +done +# Because of `break', _AC_PREPROC_IFELSE's cleaning code was skipped. +rm -f conftest.i 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 +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $CPP" >&5 +$as_echo "$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 confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#ifdef __STDC__ +# include +#else +# include +#endif + Syntax error +_ACEOF +if ac_fn_c_try_cpp "$LINENO"; then : + +else + # Broken: fails on valid input. +continue +fi +rm -f conftest.err conftest.i conftest.$ac_ext + + # OK, works on sane cases. Now check whether nonexistent headers + # can be detected and how. + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include +_ACEOF +if ac_fn_c_try_cpp "$LINENO"; then : + # Broken: success on invalid input. +continue +else + # Passes both tests. +ac_preproc_ok=: +break +fi +rm -f conftest.err conftest.i conftest.$ac_ext + +done +# Because of `break', _AC_PREPROC_IFELSE's cleaning code was skipped. +rm -f conftest.i conftest.err conftest.$ac_ext +if $ac_preproc_ok; then : + +else + { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 +$as_echo "$as_me: error: in \`$ac_pwd':" >&2;} +as_fn_error $? "C preprocessor \"$CPP\" fails sanity check +See \`config.log' for more details" "$LINENO" 5; } +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 + + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for grep that handles long lines and -e" >&5 +$as_echo_n "checking for grep that handles long lines and -e... " >&6; } +if ${ac_cv_path_GREP+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -z "$GREP"; then + ac_path_GREP_found=false + # Loop through the user's path and test for each of PROGNAME-LIST + as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH$PATH_SEPARATOR/usr/xpg4/bin +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_prog in grep ggrep; do + for ac_exec_ext in '' $ac_executable_extensions; do + ac_path_GREP="$as_dir/$ac_prog$ac_exec_ext" + { test -f "$ac_path_GREP" && $as_test_x "$ac_path_GREP"; } || continue +# Check for GNU ac_path_GREP and select it if it is found. + # Check for GNU $ac_path_GREP +case `"$ac_path_GREP" --version 2>&1` in +*GNU*) + ac_cv_path_GREP="$ac_path_GREP" ac_path_GREP_found=:;; +*) + ac_count=0 + $as_echo_n 0123456789 >"conftest.in" + while : + do + cat "conftest.in" "conftest.in" >"conftest.tmp" + mv "conftest.tmp" "conftest.in" + cp "conftest.in" "conftest.nl" + $as_echo 'GREP' >> "conftest.nl" + "$ac_path_GREP" -e 'GREP$' -e '-(cannot match)-' < "conftest.nl" >"conftest.out" 2>/dev/null || break + diff "conftest.out" "conftest.nl" >/dev/null 2>&1 || break + as_fn_arith $ac_count + 1 && ac_count=$as_val + if test $ac_count -gt ${ac_path_GREP_max-0}; then + # Best one so far, save it but keep looking for a better one + ac_cv_path_GREP="$ac_path_GREP" + ac_path_GREP_max=$ac_count + fi + # 10*(2^10) chars as input seems more than enough + test $ac_count -gt 10 && break + done + rm -f conftest.in conftest.tmp conftest.nl conftest.out;; +esac + + $ac_path_GREP_found && break 3 + done + done + done +IFS=$as_save_IFS + if test -z "$ac_cv_path_GREP"; then + as_fn_error $? "no acceptable grep could be found in $PATH$PATH_SEPARATOR/usr/xpg4/bin" "$LINENO" 5 + fi +else + ac_cv_path_GREP=$GREP +fi + +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_path_GREP" >&5 +$as_echo "$ac_cv_path_GREP" >&6; } + GREP="$ac_cv_path_GREP" + + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for egrep" >&5 +$as_echo_n "checking for egrep... " >&6; } +if ${ac_cv_path_EGREP+:} false; then : + $as_echo_n "(cached) " >&6 +else + if echo a | $GREP -E '(a|b)' >/dev/null 2>&1 + then ac_cv_path_EGREP="$GREP -E" + else + if test -z "$EGREP"; then + ac_path_EGREP_found=false + # Loop through the user's path and test for each of PROGNAME-LIST + as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH$PATH_SEPARATOR/usr/xpg4/bin +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_prog in egrep; do + for ac_exec_ext in '' $ac_executable_extensions; do + ac_path_EGREP="$as_dir/$ac_prog$ac_exec_ext" + { test -f "$ac_path_EGREP" && $as_test_x "$ac_path_EGREP"; } || continue +# Check for GNU ac_path_EGREP and select it if it is found. + # Check for GNU $ac_path_EGREP +case `"$ac_path_EGREP" --version 2>&1` in +*GNU*) + ac_cv_path_EGREP="$ac_path_EGREP" ac_path_EGREP_found=:;; +*) + ac_count=0 + $as_echo_n 0123456789 >"conftest.in" + while : + do + cat "conftest.in" "conftest.in" >"conftest.tmp" + mv "conftest.tmp" "conftest.in" + cp "conftest.in" "conftest.nl" + $as_echo 'EGREP' >> "conftest.nl" + "$ac_path_EGREP" 'EGREP$' < "conftest.nl" >"conftest.out" 2>/dev/null || break + diff "conftest.out" "conftest.nl" >/dev/null 2>&1 || break + as_fn_arith $ac_count + 1 && ac_count=$as_val + if test $ac_count -gt ${ac_path_EGREP_max-0}; then + # Best one so far, save it but keep looking for a better one + ac_cv_path_EGREP="$ac_path_EGREP" + ac_path_EGREP_max=$ac_count + fi + # 10*(2^10) chars as input seems more than enough + test $ac_count -gt 10 && break + done + rm -f conftest.in conftest.tmp conftest.nl conftest.out;; +esac + + $ac_path_EGREP_found && break 3 + done + done + done +IFS=$as_save_IFS + if test -z "$ac_cv_path_EGREP"; then + as_fn_error $? "no acceptable egrep could be found in $PATH$PATH_SEPARATOR/usr/xpg4/bin" "$LINENO" 5 + fi +else + ac_cv_path_EGREP=$EGREP +fi + + fi +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_path_EGREP" >&5 +$as_echo "$ac_cv_path_EGREP" >&6; } + EGREP="$ac_cv_path_EGREP" + + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for ANSI C header files" >&5 +$as_echo_n "checking for ANSI C header files... " >&6; } +if ${ac_cv_header_stdc+:} false; then : + $as_echo_n "(cached) " >&6 +else + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include +#include +#include +#include + +int +main () +{ + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + ac_cv_header_stdc=yes +else + ac_cv_header_stdc=no +fi +rm -f core conftest.err 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 confdefs.h - <<_ACEOF >conftest.$ac_ext +/* 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 confdefs.h - <<_ACEOF >conftest.$ac_ext +/* 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 confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include +#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)) + return 2; + return 0; +} +_ACEOF +if ac_fn_c_try_run "$LINENO"; then : + +else + ac_cv_header_stdc=no +fi +rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \ + conftest.$ac_objext conftest.beam conftest.$ac_ext +fi + +fi +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_header_stdc" >&5 +$as_echo "$ac_cv_header_stdc" >&6; } +if test $ac_cv_header_stdc = yes; then + +$as_echo "#define STDC_HEADERS 1" >>confdefs.h + +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=`$as_echo "ac_cv_header_$ac_header" | $as_tr_sh` +ac_fn_c_check_header_compile "$LINENO" "$ac_header" "$as_ac_Header" "$ac_includes_default +" +if eval test \"x\$"$as_ac_Header"\" = x"yes"; then : + cat >>confdefs.h <<_ACEOF +#define `$as_echo "HAVE_$ac_header" | $as_tr_cpp` 1 +_ACEOF + +fi + +done + + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for tigetstr in -lcurses" >&5 +$as_echo_n "checking for tigetstr in -lcurses... " >&6; } +if ${ac_cv_lib_curses_tigetstr+:} false; then : + $as_echo_n "(cached) " >&6 +else + ac_check_lib_save_LIBS=$LIBS +LIBS="-lcurses $LIBS" +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +/* Override any GCC internal prototype to avoid an error. + Use char because int might match the return type of a GCC + builtin and then its argument prototype would still apply. */ +#ifdef __cplusplus +extern "C" +#endif +char tigetstr (); +int +main () +{ +return tigetstr (); + ; + return 0; +} +_ACEOF +if ac_fn_c_try_link "$LINENO"; then : + ac_cv_lib_curses_tigetstr=yes +else + ac_cv_lib_curses_tigetstr=no +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +LIBS=$ac_check_lib_save_LIBS +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_curses_tigetstr" >&5 +$as_echo "$ac_cv_lib_curses_tigetstr" >&6; } +if test "x$ac_cv_lib_curses_tigetstr" = xyes; then : + + $as_echo "#define USE_TERMINFO 1" >>confdefs.h + + LIBS="$LIBS -lcurses" + +else + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for tigetstr in -lncurses" >&5 +$as_echo_n "checking for tigetstr in -lncurses... " >&6; } +if ${ac_cv_lib_ncurses_tigetstr+:} false; then : + $as_echo_n "(cached) " >&6 +else + ac_check_lib_save_LIBS=$LIBS +LIBS="-lncurses $LIBS" +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +/* Override any GCC internal prototype to avoid an error. + Use char because int might match the return type of a GCC + builtin and then its argument prototype would still apply. */ +#ifdef __cplusplus +extern "C" +#endif +char tigetstr (); +int +main () +{ +return tigetstr (); + ; + return 0; +} +_ACEOF +if ac_fn_c_try_link "$LINENO"; then : + ac_cv_lib_ncurses_tigetstr=yes +else + ac_cv_lib_ncurses_tigetstr=no +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +LIBS=$ac_check_lib_save_LIBS +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_ncurses_tigetstr" >&5 +$as_echo "$ac_cv_lib_ncurses_tigetstr" >&6; } +if test "x$ac_cv_lib_ncurses_tigetstr" = xyes; then : + + $as_echo "#define USE_TERMINFO 1" >>confdefs.h + + LIBS="$LIBS -lncurses" + +else + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for tgetstr in -lcurses" >&5 +$as_echo_n "checking for tgetstr in -lcurses... " >&6; } +if ${ac_cv_lib_curses_tgetstr+:} false; then : + $as_echo_n "(cached) " >&6 +else + ac_check_lib_save_LIBS=$LIBS +LIBS="-lcurses $LIBS" +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +/* Override any GCC internal prototype to avoid an error. + Use char because int might match the return type of a GCC + builtin and then its argument prototype would still apply. */ +#ifdef __cplusplus +extern "C" +#endif +char tgetstr (); +int +main () +{ +return tgetstr (); + ; + return 0; +} +_ACEOF +if ac_fn_c_try_link "$LINENO"; then : + ac_cv_lib_curses_tgetstr=yes +else + ac_cv_lib_curses_tgetstr=no +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +LIBS=$ac_check_lib_save_LIBS +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_curses_tgetstr" >&5 +$as_echo "$ac_cv_lib_curses_tgetstr" >&6; } +if test "x$ac_cv_lib_curses_tgetstr" = xyes; then : + + $as_echo "#define USE_TERMCAP 1" >>confdefs.h + + LIBS="$LIBS -lcurses" + ac_fn_c_check_header_mongrel "$LINENO" "termcap.h" "ac_cv_header_termcap_h" "$ac_includes_default" +if test "x$ac_cv_header_termcap_h" = xyes; then : + $as_echo "#define HAVE_TERMCAP_H 1" >>confdefs.h + +fi + + + +fi + +fi + +fi + + + +for ac_header in curses.h +do : + ac_fn_c_check_header_mongrel "$LINENO" "curses.h" "ac_cv_header_curses_h" "$ac_includes_default" +if test "x$ac_cv_header_curses_h" = xyes; then : + cat >>confdefs.h <<_ACEOF +#define HAVE_CURSES_H 1 +_ACEOF + for ac_header in term.h +do : + ac_fn_c_check_header_compile "$LINENO" "term.h" "ac_cv_header_term_h" "#include +" +if test "x$ac_cv_header_term_h" = xyes; then : + cat >>confdefs.h <<_ACEOF +#define HAVE_TERM_H 1 +_ACEOF + +fi + +done + +else + for ac_header in ncurses/curses.h +do : + ac_fn_c_check_header_mongrel "$LINENO" "ncurses/curses.h" "ac_cv_header_ncurses_curses_h" "$ac_includes_default" +if test "x$ac_cv_header_ncurses_curses_h" = xyes; then : + cat >>confdefs.h <<_ACEOF +#define HAVE_NCURSES_CURSES_H 1 +_ACEOF + for ac_header in ncurses/term.h +do : + ac_fn_c_check_header_compile "$LINENO" "ncurses/term.h" "ac_cv_header_ncurses_term_h" "#include +" +if test "x$ac_cv_header_ncurses_term_h" = xyes; then : + cat >>confdefs.h <<_ACEOF +#define HAVE_NCURSES_TERM_H 1 +_ACEOF + +fi + +done + +fi + +done + +fi + +done + + + + +TARGETS="normal reentrant" + + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for reentrant functions" >&5 +$as_echo_n "checking for reentrant functions... " >&6; } +if ${tecla_cv_reentrant+:} false; then : + $as_echo_n "(cached) " >&6 +else + + KEPT_CFLAGS="$CFLAGS" + CFLAGS="$CFLAGS -D_POSIX_C_SOURCE=199506L" + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* 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 +if ac_fn_c_try_link "$LINENO"; then : + tecla_cv_reentrant=yes +else + tecla_cv_reentrant=no +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext + CFLAGS="$KEPT_CFLAGS" + +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $tecla_cv_reentrant" >&5 +$as_echo "$tecla_cv_reentrant" >&6; } + + +if test $tecla_cv_reentrant = no; then + TARGETS="normal" +fi + + +ac_fn_c_check_header_mongrel "$LINENO" "sys/select.h" "ac_cv_header_sys_select_h" "$ac_includes_default" +if test "x$ac_cv_header_sys_select_h" = xyes; then : + $as_echo "#define HAVE_SYS_SELECT_H 1" >>confdefs.h + +fi + + + + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for select system call" >&5 +$as_echo_n "checking for select system call... " >&6; } +if ${tecla_cv_select+:} false; then : + $as_echo_n "(cached) " >&6 +else + + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* 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 +if ac_fn_c_try_link "$LINENO"; then : + tecla_cv_select=yes +else + tecla_cv_select=no +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext + +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $tecla_cv_select" >&5 +$as_echo "$tecla_cv_select" >&6; } + + +if test $tecla_cv_select = yes; then + $as_echo "#define HAVE_SELECT 1" >>confdefs.h + +fi + + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for SysV pseudo-terminals" >&5 +$as_echo_n "checking for SysV pseudo-terminals... " >&6; } +if ${tecla_cv_sysv_pty+:} false; then : + $as_echo_n "(cached) " >&6 +else + + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +#include +#include + +int +main () +{ + + char *name = ptsname(0); + int i1 = grantpt(0); + int i2 = unlockpt(0); + return 0; + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_link "$LINENO"; then : + tecla_cv_sysv_pty=yes +else + tecla_cv_sysv_pty=no +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext + +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $tecla_cv_sysv_pty" >&5 +$as_echo "$tecla_cv_sysv_pty" >&6; } + + +if test $tecla_cv_sysv_pty = yes; then + $as_echo "#define HAVE_SYSV_PTY 1" >>confdefs.h + +fi + + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for SysV streams pseudo-terminals" >&5 +$as_echo_n "checking for SysV streams pseudo-terminals... " >&6; } +if ${tecla_cv_sysv_ptem+:} false; then : + $as_echo_n "(cached) " >&6 +else + + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* 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"); + int i4 = ioctl(fd, I_PUSH, "ldterm"); + return 0; + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_link "$LINENO"; then : + tecla_cv_sysv_ptem=yes +else + tecla_cv_sysv_ptem=no +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext + +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $tecla_cv_sysv_ptem" >&5 +$as_echo "$tecla_cv_sysv_ptem" >&6; } + + +if test $tecla_cv_sysv_ptem = yes; then + $as_echo "#define HAVE_SYSV_PTEM 1" >>confdefs.h + +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 was given. +if test "${with_file_actions+set}" = set; then : + withval=$with_file_actions; $as_echo "#define HIDE_FILE_SYSTEM 1" >>confdefs.h + +fi + + + + +# Check whether --with-file-system was given. +if test "${with_file_system+set}" = set; then : + withval=$with_file_system; $as_echo "#define WITHOUT_FILE_SYSTEM 1" >>confdefs.h + +fi + + + +case $target in +*solaris*) + $as_echo "#define __EXTENSIONS__ 1" >>confdefs.h + + 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}" + + + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for --version-script in GNU ld" >&5 +$as_echo_n "checking for --version-script in GNU ld... " >&6; } +if ${tecla_cv_gnu_ld_script+:} false; then : + $as_echo_n "(cached) " >&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 +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $tecla_cv_gnu_ld_script" >&5 +$as_echo "$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*) + $as_echo "#define _OSF_SOURCE 1" >>confdefs.h + + ;; +*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 + + + LIBGCC="`$CC -print-libgcc-file-name 2>/dev/null`" + if test "$LIBGCC"_ != "_" && test -r "$LIBGCC"; then + LINK_SHARED="$LINK_SHARED $LIBGCC" + fi +fi + + + + + +if test "$LINK_SHARED"_ != "_"; then + TARGET_LIBS="static shared" +else + TARGET_LIBS="static" + LINK_SHARED="@:" +fi + + + + +# Check whether --with-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, we kill variables containing newlines. +# 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. +( + for ac_var in `(set) 2>&1 | sed -n 's/^\([a-zA-Z_][a-zA-Z0-9_]*\)=.*/\1/p'`; do + eval ac_val=\$$ac_var + case $ac_val in #( + *${as_nl}*) + case $ac_var in #( + *_cv_*) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: cache variable $ac_var contains a newline" >&5 +$as_echo "$as_me: WARNING: cache variable $ac_var contains a newline" >&2;} ;; + esac + case $ac_var in #( + _ | IFS | as_nl) ;; #( + BASH_ARGV | BASH_SOURCE) eval $ac_var= ;; #( + *) { eval $ac_var=; unset $ac_var;} ;; + esac ;; + esac + done + + (set) 2>&1 | + case $as_nl`(ac_space=' '; set) 2>&1` in #( + *${as_nl}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 "/^[_$as_cr_alnum]*_cv_[_$as_cr_alnum]*=/p" + ;; + esac | + sort +) | + sed ' + /^ac_cv_env_/b end + t clear + :clear + s/^\([^=]*\)=\(.*[{}].*\)$/test "${\1+set}" = set || &/ + t end + s/^\([^=]*\)=\(.*\)$/\1=${\1=\2}/ + :end' >>confcache +if diff "$cache_file" confcache >/dev/null 2>&1; then :; else + if test -w "$cache_file"; then + if test "x$cache_file" != "x/dev/null"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: updating cache $cache_file" >&5 +$as_echo "$as_me: updating cache $cache_file" >&6;} + if test ! -f "$cache_file" || test -h "$cache_file"; then + cat confcache >"$cache_file" + else + case $cache_file in #( + */* | ?:*) + mv -f confcache "$cache_file"$$ && + mv -f "$cache_file"$$ "$cache_file" ;; #( + *) + mv -f confcache "$cache_file" ;; + esac + fi + fi + else + { $as_echo "$as_me:${as_lineno-$LINENO}: not updating unwritable cache $cache_file" >&5 +$as_echo "$as_me: not updating unwritable cache $cache_file" >&6;} + 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}' + +# 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 branch to the quote section. Otherwise, +# look for a macro that doesn't take arguments. +ac_script=' +:mline +/\\$/{ + N + s,\\\n,, + b mline +} +t clear +:clear +s/^[ ]*#[ ]*define[ ][ ]*\([^ (][^ (]*([^)]*)\)[ ]*\(.*\)/-D\1=\2/g +t quote +s/^[ ]*#[ ]*define[ ][ ]*\([^ ][^ ]*\)[ ]*\(.*\)/-D\1=\2/g +t quote +b any +:quote +s/[ `~#$^&*(){}\\|;'\''"<>?]/\\&/g +s/\[/\\&/g +s/\]/\\&/g +s/\$/$$/g +H +:any +${ + g + s/^\n// + s/\n/ /g + p +} +' +DEFS=`sed -n "$ac_script" confdefs.h` + + +ac_libobjs= +ac_ltlibobjs= +U= +for ac_i in : $LIBOBJS; do test "x$ac_i" = x: && continue + # 1. Remove the extension, and $U if already installed. + ac_script='s/\$U\././;s/\.o$//;s/\.obj$//' + ac_i=`$as_echo "$ac_i" | sed "$ac_script"` + # 2. Prepend LIBOBJDIR. When used with automake>=1.10 LIBOBJDIR + # will be set to the directory where LIBOBJS objects are built. + as_fn_append ac_libobjs " \${LIBOBJDIR}$ac_i\$U.$ac_objext" + as_fn_append ac_ltlibobjs " \${LIBOBJDIR}$ac_i"'$U.lo' +done +LIBOBJS=$ac_libobjs + +LTLIBOBJS=$ac_ltlibobjs + + + +: "${CONFIG_STATUS=./config.status}" +ac_write_fail=0 +ac_clean_files_save=$ac_clean_files +ac_clean_files="$ac_clean_files $CONFIG_STATUS" +{ $as_echo "$as_me:${as_lineno-$LINENO}: creating $CONFIG_STATUS" >&5 +$as_echo "$as_me: creating $CONFIG_STATUS" >&6;} +as_write_fail=0 +cat >$CONFIG_STATUS <<_ASEOF || as_write_fail=1 +#! $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} +export SHELL +_ASEOF +cat >>$CONFIG_STATUS <<\_ASEOF || as_write_fail=1 +## -------------------- ## +## M4sh Initialization. ## +## -------------------- ## + +# Be more Bourne compatible +DUALCASE=1; export DUALCASE # for MKS sh +if test -n "${ZSH_VERSION+set}" && (emulate sh) >/dev/null 2>&1; then : + emulate sh + NULLCMD=: + # Pre-4.2 versions of Zsh do word splitting on ${1+"$@"}, which + # is contrary to our usage. Disable this feature. + alias -g '${1+"$@"}'='"$@"' + setopt NO_GLOB_SUBST +else + case `(set -o) 2>/dev/null` in #( + *posix*) : + set -o posix ;; #( + *) : + ;; +esac +fi + + +as_nl=' +' +export as_nl +# Printing a long string crashes Solaris 7 /usr/bin/printf. +as_echo='\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\' +as_echo=$as_echo$as_echo$as_echo$as_echo$as_echo +as_echo=$as_echo$as_echo$as_echo$as_echo$as_echo$as_echo +# Prefer a ksh shell builtin over an external printf program on Solaris, +# but without wasting forks for bash or zsh. +if test -z "$BASH_VERSION$ZSH_VERSION" \ + && (test "X`print -r -- $as_echo`" = "X$as_echo") 2>/dev/null; then + as_echo='print -r --' + as_echo_n='print -rn --' +elif (test "X`printf %s $as_echo`" = "X$as_echo") 2>/dev/null; then + as_echo='printf %s\n' + as_echo_n='printf %s' +else + if test "X`(/usr/ucb/echo -n -n $as_echo) 2>/dev/null`" = "X-n $as_echo"; then + as_echo_body='eval /usr/ucb/echo -n "$1$as_nl"' + as_echo_n='/usr/ucb/echo -n' + else + as_echo_body='eval expr "X$1" : "X\\(.*\\)"' + as_echo_n_body='eval + arg=$1; + case $arg in #( + *"$as_nl"*) + expr "X$arg" : "X\\(.*\\)$as_nl"; + arg=`expr "X$arg" : ".*$as_nl\\(.*\\)"`;; + esac; + expr "X$arg" : "X\\(.*\\)" | tr -d "$as_nl" + ' + export as_echo_n_body + as_echo_n='sh -c $as_echo_n_body as_echo' + fi + export as_echo_body + as_echo='sh -c $as_echo_body as_echo' +fi + +# The user is always right. +if test "${PATH_SEPARATOR+set}" != set; then + PATH_SEPARATOR=: + (PATH='/bin;/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 && { + (PATH='/bin:/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 || + PATH_SEPARATOR=';' + } +fi + + +# IFS +# We need space, tab and new line, in precisely that order. Quoting is +# there to prevent editors from complaining about space-tab. +# (If _AS_PATH_WALK were called with IFS unset, it would disable word +# splitting by setting IFS to empty value.) +IFS=" "" $as_nl" + +# Find who we are. Look in the path if we contain no directory separator. +as_myself= +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 +IFS=$as_save_IFS + + ;; +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 + $as_echo "$as_myself: error: cannot find myself; rerun with an absolute file name" >&2 + exit 1 +fi + +# Unset variables that we do not need and which cause bugs (e.g. in +# pre-3.0 UWIN ksh). But do not cause bugs in bash 2.01; the "|| exit 1" +# suppresses any "Segmentation fault" message there. '((' could +# trigger a bug in pdksh 5.2.14. +for as_var in BASH_ENV ENV MAIL MAILPATH +do eval test x\${$as_var+set} = xset \ + && ( (unset $as_var) || exit 1) >/dev/null 2>&1 && unset $as_var || : +done +PS1='$ ' +PS2='> ' +PS4='+ ' + +# NLS nuisances. +LC_ALL=C +export LC_ALL +LANGUAGE=C +export LANGUAGE + +# CDPATH. +(unset CDPATH) >/dev/null 2>&1 && unset CDPATH + + +# as_fn_error STATUS ERROR [LINENO LOG_FD] +# ---------------------------------------- +# Output "`basename $0`: error: ERROR" to stderr. If LINENO and LOG_FD are +# provided, also output the error to LOG_FD, referencing LINENO. Then exit the +# script with STATUS, using 1 if that was 0. +as_fn_error () +{ + as_status=$1; test $as_status -eq 0 && as_status=1 + if test "$4"; then + as_lineno=${as_lineno-"$3"} as_lineno_stack=as_lineno_stack=$as_lineno_stack + $as_echo "$as_me:${as_lineno-$LINENO}: error: $2" >&$4 + fi + $as_echo "$as_me: error: $2" >&2 + as_fn_exit $as_status +} # as_fn_error + + +# as_fn_set_status STATUS +# ----------------------- +# Set $? to STATUS, without forking. +as_fn_set_status () +{ + return $1 +} # as_fn_set_status + +# as_fn_exit STATUS +# ----------------- +# Exit the shell with STATUS, even in a "trap 0" or "set -e" context. +as_fn_exit () +{ + set +e + as_fn_set_status $1 + exit $1 +} # as_fn_exit + +# as_fn_unset VAR +# --------------- +# Portably unset VAR. +as_fn_unset () +{ + { eval $1=; unset $1;} +} +as_unset=as_fn_unset +# as_fn_append VAR VALUE +# ---------------------- +# Append the text in VALUE to the end of the definition contained in VAR. Take +# advantage of any shell optimizations that allow amortized linear growth over +# repeated appends, instead of the typical quadratic growth present in naive +# implementations. +if (eval "as_var=1; as_var+=2; test x\$as_var = x12") 2>/dev/null; then : + eval 'as_fn_append () + { + eval $1+=\$2 + }' +else + as_fn_append () + { + eval $1=\$$1\$2 + } +fi # as_fn_append + +# as_fn_arith ARG... +# ------------------ +# Perform arithmetic evaluation on the ARGs, and store the result in the +# global $as_val. Take advantage of shells that can avoid forks. The arguments +# must be portable across $(()) and expr. +if (eval "test \$(( 1 + 1 )) = 2") 2>/dev/null; then : + eval 'as_fn_arith () + { + as_val=$(( $* )) + }' +else + as_fn_arith () + { + as_val=`expr "$@" || test $? -eq 1` + } +fi # as_fn_arith + + +if expr a : '\(a\)' >/dev/null 2>&1 && + test "X`expr 00001 : '.*\(...\)'`" = X001; 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 + +if (as_dir=`dirname -- /` && test "X$as_dir" = X/) >/dev/null 2>&1; then + as_dirname=dirname +else + as_dirname=false +fi + +as_me=`$as_basename -- "$0" || +$as_expr X/"$0" : '.*/\([^/][^/]*\)/*$' \| \ + X"$0" : 'X\(//\)$' \| \ + X"$0" : 'X\(/\)' \| . 2>/dev/null || +$as_echo X/"$0" | + sed '/^.*\/\([^/][^/]*\)\/*$/{ + s//\1/ + q + } + /^X\/\(\/\/\)$/{ + s//\1/ + q + } + /^X\/\(\/\).*/{ + s//\1/ + q + } + s/.*/./; q'` + +# 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 + +ECHO_C= ECHO_N= ECHO_T= +case `echo -n x` in #((((( +-n*) + case `echo 'xy\c'` in + *c*) ECHO_T=' ';; # ECHO_T is single tab character. + xy) ECHO_C='\c';; + *) echo `echo ksh88 bug on AIX 6.1` > /dev/null + ECHO_T=' ';; + esac;; +*) + ECHO_N='-n';; +esac + +rm -f conf$$ conf$$.exe conf$$.file +if test -d conf$$.dir; then + rm -f conf$$.dir/conf$$.file +else + rm -f conf$$.dir + mkdir conf$$.dir 2>/dev/null +fi +if (echo >conf$$.file) 2>/dev/null; then + if ln -s conf$$.file conf$$ 2>/dev/null; then + as_ln_s='ln -s' + # ... but there are two gotchas: + # 1) On MSYS, both `ln -s file dir' and `ln file dir' fail. + # 2) DJGPP < 2.04 has no symlinks; `ln -s' creates a wrapper executable. + # In both cases, we have to default to `cp -p'. + ln -s conf$$.file conf$$.dir 2>/dev/null && test ! -f conf$$.exe || + as_ln_s='cp -p' + elif ln conf$$.file conf$$ 2>/dev/null; then + as_ln_s=ln + else + as_ln_s='cp -p' + fi +else + as_ln_s='cp -p' +fi +rm -f conf$$ conf$$.exe conf$$.dir/conf$$.file conf$$.file +rmdir conf$$.dir 2>/dev/null + + +# as_fn_mkdir_p +# ------------- +# Create "$as_dir" as a directory, including parents if necessary. +as_fn_mkdir_p () +{ + + case $as_dir in #( + -*) as_dir=./$as_dir;; + esac + test -d "$as_dir" || eval $as_mkdir_p || { + as_dirs= + while :; do + case $as_dir in #( + *\'*) as_qdir=`$as_echo "$as_dir" | sed "s/'/'\\\\\\\\''/g"`;; #'( + *) as_qdir=$as_dir;; + esac + as_dirs="'$as_qdir' $as_dirs" + as_dir=`$as_dirname -- "$as_dir" || +$as_expr X"$as_dir" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ + X"$as_dir" : 'X\(//\)[^/]' \| \ + X"$as_dir" : 'X\(//\)$' \| \ + X"$as_dir" : 'X\(/\)' \| . 2>/dev/null || +$as_echo X"$as_dir" | + sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ + s//\1/ + q + } + /^X\(\/\/\)[^/].*/{ + s//\1/ + q + } + /^X\(\/\/\)$/{ + s//\1/ + q + } + /^X\(\/\).*/{ + s//\1/ + q + } + s/.*/./; q'` + test -d "$as_dir" && break + done + test -z "$as_dirs" || eval "mkdir $as_dirs" + } || test -d "$as_dir" || as_fn_error $? "cannot create directory $as_dir" + + +} # as_fn_mkdir_p +if mkdir -p . 2>/dev/null; then + as_mkdir_p='mkdir -p "$as_dir"' +else + test -d ./-p && rmdir ./-p + as_mkdir_p=false +fi + +if test -x / >/dev/null 2>&1; then + as_test_x='test -x' +else + if ls -dL / >/dev/null 2>&1; then + as_ls_L_option=L + else + as_ls_L_option= + fi + as_test_x=' + eval sh -c '\'' + if test -d "$1"; then + test -d "$1/."; + else + case $1 in #( + -*)set "./$1";; + esac; + case `ls -ld'$as_ls_L_option' "$1" 2>/dev/null` in #(( + ???[sx]*):;;*)false;;esac;fi + '\'' sh + ' +fi +as_executable_p=$as_test_x + +# Sed expression to map a string onto a valid CPP name. +as_tr_cpp="eval 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="eval sed 'y%*+%pp%;s%[^_$as_cr_alnum]%_%g'" + + +exec 6>&1 +## ----------------------------------- ## +## Main body of $CONFIG_STATUS script. ## +## ----------------------------------- ## +_ASEOF +test $as_write_fail = 0 && chmod +x $CONFIG_STATUS || ac_write_fail=1 + +cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 +# Save the log message, to keep $0 and so on meaningful, and to +# report actual input values of CONFIG_FILES etc. instead of their +# values after options handling. +ac_log=" +This file was extended by $as_me, which was +generated by GNU Autoconf 2.68. Invocation command line was + + CONFIG_FILES = $CONFIG_FILES + CONFIG_HEADERS = $CONFIG_HEADERS + CONFIG_LINKS = $CONFIG_LINKS + CONFIG_COMMANDS = $CONFIG_COMMANDS + $ $0 $@ + +on `(hostname || uname -n) 2>/dev/null | sed 1q` +" + +_ACEOF + +case $ac_config_files in *" +"*) set x $ac_config_files; shift; ac_config_files=$*;; +esac + + + +cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 +# Files that config.status was made for. +config_files="$ac_config_files" + +_ACEOF + +cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 +ac_cs_usage="\ +\`$as_me' instantiates files and other configuration actions +from templates according to the current configuration. Unless the files +and actions are specified as TAGs, all are instantiated by default. + +Usage: $0 [OPTION]... [TAG]... + + -h, --help print this help, then exit + -V, --version print version number and configuration settings, then exit + --config print configuration, then exit + -q, --quiet, --silent + 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 the package provider." + +_ACEOF +cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 +ac_cs_config="`$as_echo "$ac_configure_args" | sed 's/^ //; s/[\\""\`\$]/\\\\&/g'`" +ac_cs_version="\\ +config.status +configured by $0, generated by GNU Autoconf 2.68, + with options \\"\$ac_cs_config\\" + +Copyright (C) 2010 Free Software Foundation, Inc. +This config.status script is free software; the Free Software Foundation +gives unlimited permission to copy, distribute and modify it." + +ac_pwd='$ac_pwd' +srcdir='$srcdir' +AWK='$AWK' +test -n "\$AWK" || AWK=awk +_ACEOF + +cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 +# The default lists apply if the user does not specify any file. +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=`expr "X$1" : 'X\([^=]*\)='` + ac_optarg= + ac_shift=: + ;; + *) + ac_option=$1 + ac_optarg=$2 + ac_shift=shift + ;; + esac + + case $ac_option in + # Handling of the options. + -recheck | --recheck | --rechec | --reche | --rech | --rec | --re | --r) + ac_cs_recheck=: ;; + --version | --versio | --versi | --vers | --ver | --ve | --v | -V ) + $as_echo "$ac_cs_version"; exit ;; + --config | --confi | --conf | --con | --co | --c ) + $as_echo "$ac_cs_config"; exit ;; + --debug | --debu | --deb | --de | --d | -d ) + debug=: ;; + --file | --fil | --fi | --f ) + $ac_shift + case $ac_optarg in + *\'*) ac_optarg=`$as_echo "$ac_optarg" | sed "s/'/'\\\\\\\\''/g"` ;; + '') as_fn_error $? "missing file argument" ;; + esac + as_fn_append CONFIG_FILES " '$ac_optarg'" + ac_need_defaults=false;; + --he | --h | --help | --hel | -h ) + $as_echo "$ac_cs_usage"; exit ;; + -q | -quiet | --quiet | --quie | --qui | --qu | --q \ + | -silent | --silent | --silen | --sile | --sil | --si | --s) + ac_cs_silent=: ;; + + # This is an error. + -*) as_fn_error $? "unrecognized option: \`$1' +Try \`$0 --help' for more information." ;; + + *) as_fn_append ac_config_targets " $1" + ac_need_defaults=false ;; + + 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 || ac_write_fail=1 +if \$ac_cs_recheck; then + set X '$SHELL' '$0' $ac_configure_args \$ac_configure_extra_args --no-create --no-recursion + shift + \$as_echo "running CONFIG_SHELL=$SHELL \$*" >&6 + CONFIG_SHELL='$SHELL' + export CONFIG_SHELL + exec "\$@" +fi + +_ACEOF +cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 +exec 5>>config.log +{ + echo + sed 'h;s/./-/g;s/^.../## /;s/...$/ ##/;p;x;p;x' <<_ASBOX +## Running $as_me. ## +_ASBOX + $as_echo "$ac_log" +} >&5 + +_ACEOF +cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 +_ACEOF + +cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 + +# Handling of arguments. +for ac_config_target in $ac_config_targets +do + case $ac_config_target in + "$OUTPUT_FILES") CONFIG_FILES="$CONFIG_FILES $OUTPUT_FILES" ;; + + *) as_fn_error $? "invalid argument: \`$ac_config_target'" "$LINENO" 5;; + 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 against having it here, and in addition, +# creating and moving files from /tmp can sometimes cause problems. +# Hook for its removal unless debugging. +# Note that there is a small window in which the directory will not be cleaned: +# after its creation but before its name has been assigned to `$tmp'. +$debug || +{ + tmp= ac_tmp= + trap 'exit_status=$? + : "${ac_tmp:=$tmp}" + { test ! -d "$ac_tmp" || rm -fr "$ac_tmp"; } && exit $exit_status +' 0 + trap 'as_fn_exit 1' 1 2 13 15 +} +# Create a (secure) tmp directory for tmp files. + +{ + tmp=`(umask 077 && mktemp -d "./confXXXXXX") 2>/dev/null` && + test -d "$tmp" +} || +{ + tmp=./conf$$-$RANDOM + (umask 077 && mkdir "$tmp") +} || as_fn_error $? "cannot create a temporary directory in ." "$LINENO" 5 +ac_tmp=$tmp + +# Set up the scripts for CONFIG_FILES section. +# No need to generate them if there are no CONFIG_FILES. +# This happens for instance with `./config.status config.h'. +if test -n "$CONFIG_FILES"; then + + +ac_cr=`echo X | tr X '\015'` +# On cygwin, bash can eat \r inside `` if the user requested igncr. +# But we know of no other shell where ac_cr would be empty at this +# point, so we can use a bashism as a fallback. +if test "x$ac_cr" = x; then + eval ac_cr=\$\'\\r\' +fi +ac_cs_awk_cr=`$AWK 'BEGIN { print "a\rb" }' /dev/null` +if test "$ac_cs_awk_cr" = "a${ac_cr}b"; then + ac_cs_awk_cr='\\r' +else + ac_cs_awk_cr=$ac_cr +fi + +echo 'BEGIN {' >"$ac_tmp/subs1.awk" && +_ACEOF + + +{ + echo "cat >conf$$subs.awk <<_ACEOF" && + echo "$ac_subst_vars" | sed 's/.*/&!$&$ac_delim/' && + echo "_ACEOF" +} >conf$$subs.sh || + as_fn_error $? "could not make $CONFIG_STATUS" "$LINENO" 5 +ac_delim_num=`echo "$ac_subst_vars" | grep -c '^'` +ac_delim='%!_!# ' +for ac_last_try in false false false false false :; do + . ./conf$$subs.sh || + as_fn_error $? "could not make $CONFIG_STATUS" "$LINENO" 5 + + ac_delim_n=`sed -n "s/.*$ac_delim\$/X/p" conf$$subs.awk | grep -c X` + if test $ac_delim_n = $ac_delim_num; then + break + elif $ac_last_try; then + as_fn_error $? "could not make $CONFIG_STATUS" "$LINENO" 5 + else + ac_delim="$ac_delim!$ac_delim _$ac_delim!! " + fi +done +rm -f conf$$subs.sh + +cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 +cat >>"\$ac_tmp/subs1.awk" <<\\_ACAWK && +_ACEOF +sed -n ' +h +s/^/S["/; s/!.*/"]=/ +p +g +s/^[^!]*!// +:repl +t repl +s/'"$ac_delim"'$// +t delim +:nl +h +s/\(.\{148\}\)..*/\1/ +t more1 +s/["\\]/\\&/g; s/^/"/; s/$/\\n"\\/ +p +n +b repl +:more1 +s/["\\]/\\&/g; s/^/"/; s/$/"\\/ +p +g +s/.\{148\}// +t nl +:delim +h +s/\(.\{148\}\)..*/\1/ +t more2 +s/["\\]/\\&/g; s/^/"/; s/$/"/ +p +b +:more2 +s/["\\]/\\&/g; s/^/"/; s/$/"\\/ +p +g +s/.\{148\}// +t delim +' >$CONFIG_STATUS || ac_write_fail=1 +rm -f conf$$subs.awk +cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 +_ACAWK +cat >>"\$ac_tmp/subs1.awk" <<_ACAWK && + for (key in S) S_is_set[key] = 1 + FS = "" + +} +{ + line = $ 0 + nfields = split(line, field, "@") + substed = 0 + len = length(field[1]) + for (i = 2; i < nfields; i++) { + key = field[i] + keylen = length(key) + if (S_is_set[key]) { + value = S[key] + line = substr(line, 1, len) "" value "" substr(line, len + keylen + 3) + len += length(value) + length(field[++i]) + substed = 1 + } else + len += 1 + keylen + } + + print line +} + +_ACAWK +_ACEOF +cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 +if sed "s/$ac_cr//" < /dev/null > /dev/null 2>&1; then + sed "s/$ac_cr\$//; s/$ac_cr/$ac_cs_awk_cr/g" +else + cat +fi < "$ac_tmp/subs1.awk" > "$ac_tmp/subs.awk" \ + || as_fn_error $? "could not setup config files machinery" "$LINENO" 5 +_ACEOF + +# VPATH may cause trouble with some makes, so we remove sole $(srcdir), +# ${srcdir} and @srcdir@ entries 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[ ]*=[ ]*/{ +h +s/// +s/^/:/ +s/[ ]*$/:/ +s/:\$(srcdir):/:/g +s/:\${srcdir}:/:/g +s/:@srcdir@:/:/g +s/^:*// +s/:*$// +x +s/\(=[ ]*\).*/\1/ +G +s/\n// +s/^[^=]*=[ ]*$// +}' +fi + +cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 +fi # test -n "$CONFIG_FILES" + + +eval set X " :F $CONFIG_FILES " +shift +for ac_tag +do + case $ac_tag in + :[FHLC]) ac_mode=$ac_tag; continue;; + esac + case $ac_mode$ac_tag in + :[FHL]*:*);; + :L* | :C*:*) as_fn_error $? "invalid tag \`$ac_tag'" "$LINENO" 5;; + :[FH]-) ac_tag=-:-;; + :[FH]*) ac_tag=$ac_tag:$ac_tag.in;; + esac + ac_save_IFS=$IFS + IFS=: + set x $ac_tag + IFS=$ac_save_IFS + shift + ac_file=$1 + shift + + case $ac_mode in + :L) ac_source=$1;; + :[FH]) + ac_file_inputs= + for ac_f + do + case $ac_f in + -) ac_f="$ac_tmp/stdin";; + *) # Look for the file first in the build tree, then in the source tree + # (if the path is not absolute). The absolute path cannot be DOS-style, + # because $ac_f cannot contain `:'. + test -f "$ac_f" || + case $ac_f in + [\\/$]*) false;; + *) test -f "$srcdir/$ac_f" && ac_f="$srcdir/$ac_f";; + esac || + as_fn_error 1 "cannot find input file: \`$ac_f'" "$LINENO" 5;; + esac + case $ac_f in *\'*) ac_f=`$as_echo "$ac_f" | sed "s/'/'\\\\\\\\''/g"`;; esac + as_fn_append ac_file_inputs " '$ac_f'" + done + + # 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. */ + configure_input='Generated from '` + $as_echo "$*" | sed 's|^[^:]*/||;s|:[^:]*/|, |g' + `' by configure.' + if test x"$ac_file" != x-; then + configure_input="$ac_file. $configure_input" + { $as_echo "$as_me:${as_lineno-$LINENO}: creating $ac_file" >&5 +$as_echo "$as_me: creating $ac_file" >&6;} + fi + # Neutralize special characters interpreted by sed in replacement strings. + case $configure_input in #( + *\&* | *\|* | *\\* ) + ac_sed_conf_input=`$as_echo "$configure_input" | + sed 's/[\\\\&|]/\\\\&/g'`;; #( + *) ac_sed_conf_input=$configure_input;; + esac + + case $ac_tag in + *:-:* | *:-) cat >"$ac_tmp/stdin" \ + || as_fn_error $? "could not create $ac_file" "$LINENO" 5 ;; + esac + ;; + esac + + ac_dir=`$as_dirname -- "$ac_file" || +$as_expr X"$ac_file" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ + X"$ac_file" : 'X\(//\)[^/]' \| \ + X"$ac_file" : 'X\(//\)$' \| \ + X"$ac_file" : 'X\(/\)' \| . 2>/dev/null || +$as_echo X"$ac_file" | + sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ + s//\1/ + q + } + /^X\(\/\/\)[^/].*/{ + s//\1/ + q + } + /^X\(\/\/\)$/{ + s//\1/ + q + } + /^X\(\/\).*/{ + s//\1/ + q + } + s/.*/./; q'` + as_dir="$ac_dir"; as_fn_mkdir_p + ac_builddir=. + +case "$ac_dir" in +.) ac_dir_suffix= ac_top_builddir_sub=. ac_top_build_prefix= ;; +*) + ac_dir_suffix=/`$as_echo "$ac_dir" | sed 's|^\.[\\/]||'` + # A ".." for each directory in $ac_dir_suffix. + ac_top_builddir_sub=`$as_echo "$ac_dir_suffix" | sed 's|/[^\\/]*|/..|g;s|/||'` + case $ac_top_builddir_sub in + "") ac_top_builddir_sub=. ac_top_build_prefix= ;; + *) ac_top_build_prefix=$ac_top_builddir_sub/ ;; + esac ;; +esac +ac_abs_top_builddir=$ac_pwd +ac_abs_builddir=$ac_pwd$ac_dir_suffix +# for backward compatibility: +ac_top_builddir=$ac_top_build_prefix + +case $srcdir in + .) # We are building in place. + ac_srcdir=. + ac_top_srcdir=$ac_top_builddir_sub + ac_abs_top_srcdir=$ac_pwd ;; + [\\/]* | ?:[\\/]* ) # Absolute name. + ac_srcdir=$srcdir$ac_dir_suffix; + ac_top_srcdir=$srcdir + ac_abs_top_srcdir=$srcdir ;; + *) # Relative name. + ac_srcdir=$ac_top_build_prefix$srcdir$ac_dir_suffix + ac_top_srcdir=$ac_top_build_prefix$srcdir + ac_abs_top_srcdir=$ac_pwd/$srcdir ;; +esac +ac_abs_srcdir=$ac_abs_top_srcdir$ac_dir_suffix + + + case $ac_mode in + :F) + # + # CONFIG_FILE + # + +_ACEOF + +cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 +# If the template does not know about datarootdir, expand it. +# FIXME: This hack should be removed a few years after 2.60. +ac_datarootdir_hack=; ac_datarootdir_seen= +ac_sed_dataroot=' +/datarootdir/ { + p + q +} +/@datadir@/p +/@docdir@/p +/@infodir@/p +/@localedir@/p +/@mandir@/p' +case `eval "sed -n \"\$ac_sed_dataroot\" $ac_file_inputs"` in +*datarootdir*) ac_datarootdir_seen=yes;; +*@datadir@*|*@docdir@*|*@infodir@*|*@localedir@*|*@mandir@*) + { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $ac_file_inputs seems to ignore the --datarootdir setting" >&5 +$as_echo "$as_me: WARNING: $ac_file_inputs seems to ignore the --datarootdir setting" >&2;} +_ACEOF +cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 + ac_datarootdir_hack=' + s&@datadir@&$datadir&g + s&@docdir@&$docdir&g + s&@infodir@&$infodir&g + s&@localedir@&$localedir&g + s&@mandir@&$mandir&g + s&\\\${datarootdir}&$datarootdir&g' ;; +esac +_ACEOF + +# Neutralize VPATH when `$srcdir' = `.'. +# Shell code in configure.ac might set extrasub. +# FIXME: do we really want to maintain this feature? +cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 +ac_sed_extra="$ac_vpsub +$extrasub +_ACEOF +cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 +:t +/@[a-zA-Z_][a-zA-Z_0-9]*@/!b +s|@configure_input@|$ac_sed_conf_input|;t t +s&@top_builddir@&$ac_top_builddir_sub&;t t +s&@top_build_prefix@&$ac_top_build_prefix&;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&@abs_top_builddir@&$ac_abs_top_builddir&;t t +$ac_datarootdir_hack +" +eval sed \"\$ac_sed_extra\" "$ac_file_inputs" | $AWK -f "$ac_tmp/subs.awk" \ + >$ac_tmp/out || as_fn_error $? "could not create $ac_file" "$LINENO" 5 + +test -z "$ac_datarootdir_hack$ac_datarootdir_seen" && + { ac_out=`sed -n '/\${datarootdir}/p' "$ac_tmp/out"`; test -n "$ac_out"; } && + { ac_out=`sed -n '/^[ ]*datarootdir[ ]*:*=/p' \ + "$ac_tmp/out"`; test -z "$ac_out"; } && + { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $ac_file contains a reference to the variable \`datarootdir' +which seems to be undefined. Please make sure it is defined" >&5 +$as_echo "$as_me: WARNING: $ac_file contains a reference to the variable \`datarootdir' +which seems to be undefined. Please make sure it is defined" >&2;} + + rm -f "$ac_tmp/stdin" + case $ac_file in + -) cat "$ac_tmp/out" && rm -f "$ac_tmp/out";; + *) rm -f "$ac_file" && mv "$ac_tmp/out" "$ac_file";; + esac \ + || as_fn_error $? "could not create $ac_file" "$LINENO" 5 + ;; + + + + esac + +done # for ac_tag + + +as_fn_exit 0 +_ACEOF +ac_clean_files=$ac_clean_files_save + +test $ac_write_fail = 0 || + as_fn_error $? "write failure creating $CONFIG_STATUS" "$LINENO" 5 + + +# 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 || as_fn_exit 1 +fi +if test -n "$ac_unrecognized_opts" && test "$enable_option_checking" != no; then + { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: unrecognized options: $ac_unrecognized_opts" >&5 +$as_echo "$as_me: WARNING: unrecognized options: $ac_unrecognized_opts" >&2;} +fi + diff --git a/libtecla-1.6.3/configure.in b/libtecla-1.6.3/configure.in new file mode 100644 index 0000000..2d668d5 --- /dev/null +++ b/libtecla-1.6.3/configure.in @@ -0,0 +1,614 @@ +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="3" + +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 + ], [ + char *name = ptsname(0); + int i1 = grantpt(0); + int i2 = unlockpt(0); + 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 Check if this system uses the system-V streams pseudo terminal interface. + +AC_CACHE_CHECK(for SysV streams pseudo-terminals, tecla_cv_sysv_ptem, [ + 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"); + int i4 = ioctl(fd, I_PUSH, "ldterm"); + return 0; + ], tecla_cv_sysv_ptem=yes, tecla_cv_sysv_ptem=no) +]) + +dnl If the system-V pseudo-terminal interface is available, arrange +dnl for HAVE_SYSV_PTEM to be defined by CFLAGS. + +if test $tecla_cv_sysv_ptem = yes; then + AC_DEFINE(HAVE_SYSV_PTEM) +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 + +dnl Try to find libgcc.a. Beware that the clang compiler pretends to +dnl be gcc and even understands -print-libgcc-file-name, but just +dnl replies to it with "libgcc.a" without a path. This makes +dnl compilations fail, so we have to check here whether the returned +dnl filename actually exists, and only use it if it does. + + LIBGCC="`$CC -print-libgcc-file-name 2>/dev/null`" + if test "$LIBGCC"_ != "_" && test -r "$LIBGCC"; then + LINK_SHARED="$LINK_SHARED $LIBGCC" + fi +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.3/cplfile.c b/libtecla-1.6.3/cplfile.c new file mode 100644 index 0000000..35d15eb --- /dev/null +++ b/libtecla-1.6.3/cplfile.c @@ -0,0 +1,870 @@ +/* + * Copyright (c) 2000, 2001, 2002, 2003, 2004, 2012 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.3/cplmatch.h b/libtecla-1.6.3/cplmatch.h new file mode 100644 index 0000000..0976265 --- /dev/null +++ b/libtecla-1.6.3/cplmatch.h @@ -0,0 +1,47 @@ +#ifndef cplmatch_h +#define cplmatch_h + +/* + * Copyright (c) 2000, 2001, 2002, 2003, 2004, 2012 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.3/demo.c b/libtecla-1.6.3/demo.c new file mode 100644 index 0000000..e115472 --- /dev/null +++ b/libtecla-1.6.3/demo.c @@ -0,0 +1,166 @@ +/* + * Copyright (c) 2000, 2001, 2002, 2003, 2004, 2012 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.3/demo2.c b/libtecla-1.6.3/demo2.c new file mode 100644 index 0000000..8bfd739 --- /dev/null +++ b/libtecla-1.6.3/demo2.c @@ -0,0 +1,423 @@ +/* + * Copyright (c) 2000, 2001, 2002, 2003, 2004, 2012 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.3/demo3.c b/libtecla-1.6.3/demo3.c new file mode 100644 index 0000000..7dff98a --- /dev/null +++ b/libtecla-1.6.3/demo3.c @@ -0,0 +1,738 @@ +/* + * Copyright (c) 2002, 2003, 2004, 2012 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.3/direader.c b/libtecla-1.6.3/direader.c new file mode 100644 index 0000000..ec993db --- /dev/null +++ b/libtecla-1.6.3/direader.c @@ -0,0 +1,309 @@ +/* + * Copyright (c) 2000, 2001, 2002, 2003, 2004, 2012 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.3/direader.h b/libtecla-1.6.3/direader.h new file mode 100644 index 0000000..b7dbcbb --- /dev/null +++ b/libtecla-1.6.3/direader.h @@ -0,0 +1,44 @@ +#ifndef dirreader_h +#define dirreader_h + +/* + * Copyright (c) 2000, 2001, 2002, 2003, 2004, 2012 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.3/enhance.c b/libtecla-1.6.3/enhance.c new file mode 100644 index 0000000..c6dccbb --- /dev/null +++ b/libtecla-1.6.3/enhance.c @@ -0,0 +1,698 @@ +#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 +char *ptsname(int fd); +int grantpt(int fd); +int unlockpt(int fd); +#endif + +#if HAVE_SYSV_PTEM +#include /* System-V stream I/O */ +#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 20 + +/* + * 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_PTEM + (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.3/errmsg.c b/libtecla-1.6.3/errmsg.c new file mode 100644 index 0000000..95748f5 --- /dev/null +++ b/libtecla-1.6.3/errmsg.c @@ -0,0 +1,167 @@ +/* + * Copyright (c) 2000, 2001, 2002, 2003, 2004, 2012 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.3/errmsg.h b/libtecla-1.6.3/errmsg.h new file mode 100644 index 0000000..091bfdd --- /dev/null +++ b/libtecla-1.6.3/errmsg.h @@ -0,0 +1,85 @@ +#ifndef errmsg_h +#define errmsg_h + +/* + * Copyright (c) 2000, 2001, 2002, 2003, 2004, 2012 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.3/expand.c b/libtecla-1.6.3/expand.c new file mode 100644 index 0000000..d22e069 --- /dev/null +++ b/libtecla-1.6.3/expand.c @@ -0,0 +1,1448 @@ +/* + * Copyright (c) 2000, 2001, 2002, 2003, 2004, 2012 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.3/expand.h b/libtecla-1.6.3/expand.h new file mode 100644 index 0000000..ca05dc7 --- /dev/null +++ b/libtecla-1.6.3/expand.h @@ -0,0 +1,48 @@ +#ifndef expand_h +#define expand_h + +/* + * Copyright (c) 2000, 2001, 2002, 2003, 2004, 2012 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.3/freelist.c b/libtecla-1.6.3/freelist.c new file mode 100644 index 0000000..d539639 --- /dev/null +++ b/libtecla-1.6.3/freelist.c @@ -0,0 +1,400 @@ +/* + * Copyright (c) 2000, 2001, 2002, 2003, 2004, 2012 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.3/freelist.h b/libtecla-1.6.3/freelist.h new file mode 100644 index 0000000..6edd9fa --- /dev/null +++ b/libtecla-1.6.3/freelist.h @@ -0,0 +1,87 @@ +#ifndef freelist_h +#define freelist_h + +/* + * Copyright (c) 2000, 2001, 2002, 2003, 2004, 2012 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.3/getline.c b/libtecla-1.6.3/getline.c new file mode 100644 index 0000000..a58a1b4 --- /dev/null +++ b/libtecla-1.6.3/getline.c @@ -0,0 +1,12849 @@ +/* + * Copyright (c) 2000, 2001, 2002, 2003, 2004, 2012 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. + * If you add or remove a directive from this list, be sure to update + * the equivalent list of directives in gl_displayed_prompt_width(). + */ + 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 'U': case 'S': case 'P': case 'F': case 'V': + case 'b': case 'u': case 's': case 'p': case 'f': case 'v': + 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_truncate_buffer(gl, 0); + 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.3/getline.h b/libtecla-1.6.3/getline.h new file mode 100644 index 0000000..9dba35f --- /dev/null +++ b/libtecla-1.6.3/getline.h @@ -0,0 +1,88 @@ +#ifndef getline_h +#define getline_h + +/* + * Copyright (c) 2000, 2001, 2002, 2003, 2004, 2012 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.3/hash.c b/libtecla-1.6.3/hash.c new file mode 100644 index 0000000..e61c564 --- /dev/null +++ b/libtecla-1.6.3/hash.c @@ -0,0 +1,737 @@ +/* + * Copyright (c) 2000, 2001, 2002, 2003, 2004, 2012 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.3/hash.h b/libtecla-1.6.3/hash.h new file mode 100644 index 0000000..4cad68a --- /dev/null +++ b/libtecla-1.6.3/hash.h @@ -0,0 +1,157 @@ +#ifndef hash_h +#define hash_h + +/* + * Copyright (c) 2000, 2001, 2002, 2003, 2004, 2012 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.3/history.c b/libtecla-1.6.3/history.c new file mode 100644 index 0000000..f798ff8 --- /dev/null +++ b/libtecla-1.6.3/history.c @@ -0,0 +1,2842 @@ +/* + * Copyright (c) 2000, 2001, 2002, 2003, 2004, 2012 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.3/history.h b/libtecla-1.6.3/history.h new file mode 100644 index 0000000..e0278cd --- /dev/null +++ b/libtecla-1.6.3/history.h @@ -0,0 +1,169 @@ +#ifndef history_h +#define history_h + +/* + * Copyright (c) 2000, 2001, 2002, 2003, 2004, 2012 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.3/homedir.c b/libtecla-1.6.3/homedir.c new file mode 100644 index 0000000..29257f8 --- /dev/null +++ b/libtecla-1.6.3/homedir.c @@ -0,0 +1,470 @@ +/* + * Copyright (c) 2000, 2001, 2002, 2003, 2004, 2012 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.3/homedir.h b/libtecla-1.6.3/homedir.h new file mode 100644 index 0000000..096708d --- /dev/null +++ b/libtecla-1.6.3/homedir.h @@ -0,0 +1,81 @@ +#ifndef homedir_h +#define homedir_h + +/* + * Copyright (c) 2000, 2001, 2002, 2003, 2004, 2012 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.3/html/changes.html b/libtecla-1.6.3/html/changes.html new file mode 100644 index 0000000..ed34d3b --- /dev/null +++ b/libtecla-1.6.3/html/changes.html @@ -0,0 +1,2826 @@ +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.
+
+09/11/2014 mcs@astro.caltech.edu
+           configure.in configure
+             Dominyk Tiller reported that libtecla didn't compile on
+             Mac OS X, due to libgcc.a being cited in the makefile
+             without any path. This turned out to be because Gnu
+             autoconf claims that the clang compiler is gcc, and clang
+             has a -print-libgcc-file-name, but this only prints
+             libgcc.a. I have modified the configure script to check
+             whether the path returned by -print-libgcc-file-name
+             actually exists and only use it if it does.
+
+27/10/2014 Jon Szymaniak (documented here by mcs@astro.caltech.edu)
+           Makefile.rules
+             Use $(AR) instead of plain ar, so that cross-compilation
+             uses the correct ar program when cross-compiling.
+
+             Add $(TARGETS) dependency to building the demo programs
+             and the enhance program. When using parallel compilation
+             this is needed to ensure that the library is compiled
+             before the demos.
+
+10/04/2013 mcs@astro.caltech.edu
+           Makefile.in
+             Jonathan Niehof reported that libtecla wouldn't compile if
+             there were spaces in LDFLAGS and pointed out that there should
+             be quotes around $(LDFLAGS) in Makefile.in. This also applied
+             to a few other variables cited in the same way.
+
+10/06/2012 mcs@astro.caltech.edu
+           enhance.c configure.in
+	     I had incorrectly assumed that system-V pseudo-terminal
+	     allocation and system-V streams terminals always went
+	     together.  However system-V pseudo terminal allocation is
+	     now part of UNIX98, and this has been adopted into many BSD
+	     style operating systems, without the use of system-V
+	     streams. On such systems the lack of system-V streams IOCTL
+             opcodes prevented system-V pseudo-terminal allocation being
+             used. This was hidden under Linux until recently, because
+             it had a stropts.h file, which made it appear as though
+             Linux supported system-V streams terminals.
+
+	     I have now created separate configuration tests and options
+	     in the configure script for system-V terminal allocation
+	     and system-V streams. On systems that only have the former,
+             the latter won't be used.
+
+16/05/2005 mcs@astro.caltech.edu
+           getline.c
+	     When an initial input line was presented to gl_get_line()
+	     for editing, the new input line was incorrectly appended to
+	     the previous input line, instead of replacing it.
+
+10/01/2004 Derek Jones (documented here by mcs@astro.caltech.edu)
+           getline.c
+             Derek discovered that the function that computes the
+             width of the prompt, was not correctly skipping over 3 of
+             the 6 possible prompt-formatting directives. Thus, when
+             the %f,%p or %v prompt-formatting directives were used,
+             the width of the prompt was incorrectly calculated. The
+             fix was to copy the list of directives from
+             gl_display_prompt(). I have also added a comment to
+             gl_display_prompt(), to warn anybody who adds or removes
+             formatting directives there, to also do the same to
+             gl_displayed_prompt_width().
+
+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.3/html/cpl_complete_word.html b/libtecla-1.6.3/html/cpl_complete_word.html new file mode 100644 index 0000000..e69c8c0 --- /dev/null +++ b/libtecla-1.6.3/html/cpl_complete_word.html @@ -0,0 +1,477 @@ +Content-type: text/html + + +Man page of cpl_complete_word + +

cpl_complete_word

+Section: C Library Functions (3)
Index +Return to Main Contents
+ +  +

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(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 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 cpl_add_completion(). +

+

+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 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 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_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 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_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 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. +

+

+  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 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 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 function. +

+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(). +

+

+  CplMatches *cpl_recall_matches(WordCompletion *cpl);
+
+ +

+As a convenience, the return value of the last call to +cpl_complete_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 +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. Otherwise it should be a +pointer to a CplFileConf 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 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 +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 filename that it is given represents a normal file that the +user has execute 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_completions(). +

+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(3), gl_get_line(3), ef_expand_file(3),
+pca_lookup_file(3)
+
+ +

+  +

AUTHOR

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

+ +


+ 

Index

+
+
NAME
+
SYNOPSIS
+
DESCRIPTION
+
THE BUILT-IN FILENAME-COMPLETION CALLBACK
+
THREAD SAFETY
+
FILES
+
SEE ALSO
+
AUTHOR
+
+
+This document was created by +man2html, +using the manual pages.
+Time: 22:21:57 GMT, November 09, 2014 + + diff --git a/libtecla-1.6.3/html/ef_expand_file.html b/libtecla-1.6.3/html/ef_expand_file.html new file mode 100644 index 0000000..13db3b5 --- /dev/null +++ b/libtecla-1.6.3/html/ef_expand_file.html @@ -0,0 +1,273 @@ +Content-type: text/html + + +Man page of ef_expand_file + +

ef_expand_file

+Section: C Library Functions (3)
Index +Return to Main Contents
+ +  +

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(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 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 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 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(3), gl_get_line(3), cpl_complete_word(3),
+pca_lookup_file(3)
+
+ +

+  +

AUTHOR

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

+ +


+ 

Index

+
+
NAME
+
SYNOPSIS
+
DESCRIPTION
+
THREAD SAFETY
+
FILES
+
SEE ALSO
+
AUTHOR
+
+
+This document was created by +man2html, +using the manual pages.
+Time: 22:21:57 GMT, November 09, 2014 + + diff --git a/libtecla-1.6.3/html/enhance.html b/libtecla-1.6.3/html/enhance.html new file mode 100644 index 0000000..f048a81 --- /dev/null +++ b/libtecla-1.6.3/html/enhance.html @@ -0,0 +1,105 @@ +Content-type: text/html + + +Man page of enhance + +

enhance

+Section: User Commands (1)
Index +Return to Main Contents
+ +  +

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 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. +

+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. +

+Note that the user-level command-line editing facilities provided by +the Tecla library are documented in the tecla(7) 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 +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 knowing 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 +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(7), libtecla(3) +
   +  +

AUTHOR

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

+ +


+ 

Index

+
+
NAME
+
SYNOPSIS
+
DESCRIPTION
+
DEFICIENCIES
+
FILES
+
SEE ALSO
+
AUTHOR
+
+
+This document was created by +man2html, +using the manual pages.
+Time: 22:21:57 GMT, November 09, 2014 + + diff --git a/libtecla-1.6.3/html/gl_get_line.html b/libtecla-1.6.3/html/gl_get_line.html new file mode 100644 index 0000000..7f922fe --- /dev/null +++ b/libtecla-1.6.3/html/gl_get_line.html @@ -0,0 +1,2407 @@ +Content-type: text/html + + +Man page of gl_get_line + +

gl_get_line

+Section: C Library Functions (3)
Index +Return to Main Contents
+ +  +

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 +  +

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 +libtecla(3) 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 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(7) 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() 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 allocated by new_GetLine(), 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 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, 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 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. 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 terminal,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 information 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(3) 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 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.
+
+ +

+  +

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 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 characters. 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 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 +configuration commands from an optional .teclarc file in the +user's home directory. Note that the arguments are listed in ascending +order of priority, with the contents of app_string being +potentially overriden 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 specifies the filenames that you want re-read when the user +requests that the configuration 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_configure_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 +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 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_completion_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_complete_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 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 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 additional action +won't be known, and any reference to it in the configuration +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 +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 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 entering 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. 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 count argument, or use its value for a +different purpose. +

+A copy of the current input line is passed in the read-only line +argument. 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 +restoring the normal mapping of \n to \r\n, 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 +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 +application, and subsequently restore them when you next start the +application, 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 +prefixes 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 +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 entering +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. +Initially 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 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. +

+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 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 %H 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 successively 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. Therefore 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 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 +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 terminated 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 +calling 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 little 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 +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 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_history()), 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-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 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 +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 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(3) 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 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 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 +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. +

+

+

+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_normal_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 +function which occasionally and unpredictably writes to the terminal, +the automatic conversion of " to " 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 +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 following 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(3)), 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 +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. +

+

+

+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_normal_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 +function which occasionally and unpredictably writes to the terminal, +the automatic conversion of " to " 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 +GLTO_REFRESH return value, to tell gl_get_line() to +redisplay the input line. +

+

+

+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 +sysconf(_SC_CLK_TCK)). +

+

+

+To turn off timeouts, simply call gl_inactivity_timeout() 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. Changing 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 signal 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 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 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 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-control shells usually reset the +terminal settings when a process relinquishes 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 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 overridden it, never +either writes to the terminal, 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 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 +\ +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 +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 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 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 +conditions. In gl_get_line() a lot of care has been taken to +allow applications 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 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 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 functions, such as sigsetjmp() and +sigsuspend(), and in particular, the former of these +two functions can be used in conjunction with +siglongjmp() 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 executing. 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 signals 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 +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 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 support 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 +dimensions, 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. +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 provided 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 +completion will invisibly complete your prefix as far as +possible, ambiguous completions 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 character, 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_status(), 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 ' 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(3)), 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() 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 +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_terminal(). 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 introductions. In those examples the advanced use of +optional prefixes, suffixes and filled lines to draw a box around the +text is also illustrated. +

+

+

+  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 output of a program that uses gl_get_line() is being piped +to another program 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 character 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 paragraph. 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(3) 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 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. Further documentation on how the user can make use +of this to enter international characters can be found in the +tecla(7) 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 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. +

+  +

FILES

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

+  +

SEE ALSO

+ +
+libtecla(3), gl_io_mode(3), tecla(7), ef_expand_file(3),
+cpl_complete_word(3), pca_lookup_file(3)
+
+ +

+  +

AUTHOR

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

+ +


+ 

Index

+
+
NAME
+
SYNOPSIS
+
DESCRIPTION
+
AN EXAMPLE
+
THE FUNCTIONS USED IN THE EXAMPLE
+
THE RETURN STATUS OF GL_GET_LINE
+
OPTIONAL PROMPT FORMATTING
+
ALTERNATE CONFIGURATION SOURCES
+
CUSTOMIZED WORD COMPLETION
+
ADDING COMPLETION ACTIONS
+
DEFINING CUSTOM ACTIONS
+
HISTORY FILES
+
MULTIPLE HISTORY LISTS
+
DISPLAYING HISTORY
+
LOOKING UP HISTORY
+
MANUAL HISTORY ARCHIVAL
+
MISCELLANEOUS HISTORY CONFIGURATION
+
QUERYING HISTORY INFORMATION
+
CHANGING TERMINALS
+
EXTERNAL EVENT HANDLING
+
SETTING AN INACTIVITY TIMEOUT
+
SIGNAL HANDLING DEFAULTS
+
CUSTOMIZED SIGNAL HANDLING
+
RELIABLE SIGNAL HANDLING
+
THE TERMINAL SIZE
+
HIDING WHAT YOU TYPE
+
SINGLE CHARACTER QUERIES
+
READING RAW CHARACTERS
+
CLEARING THE TERMINAL
+
DISPLAYING TEXT DYNAMICALLY
+
CALLBACK FUNCTION FACILITIES
+
INTERNATIONAL CHARACTER SETS
+
THREAD SAFETY
+
FILES
+
SEE ALSO
+
AUTHOR
+
+
+This document was created by +man2html, +using the manual pages.
+Time: 22:21:57 GMT, November 09, 2014 + + diff --git a/libtecla-1.6.3/html/gl_io_mode.html b/libtecla-1.6.3/html/gl_io_mode.html new file mode 100644 index 0000000..42d27dc --- /dev/null +++ b/libtecla-1.6.3/html/gl_io_mode.html @@ -0,0 +1,634 @@ +Content-type: text/html + + +Man page of gl_io_mode + +

gl_io_mode

+Section: C Library Functions (3)
Index +Return to Main Contents
+ +  +

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(3) 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(3) 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 +application calls the gl_pending_io() function. +

+

+

+  GlPendingIO gl_pending_io(GetLine *gl);
+
+ +

+

+The return value of this function is one of the following two +enumerated 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(3) 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(3) 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 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 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 +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 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 handlers 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 particular, it is rarely useful to trap SIGCONT, +so the cont_handler argument 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 +different 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 +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. +

+  +

Process termination signals.

+ +

+If the signal that was caught is one of those that by default +terminates 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 +configure the arguments of select() or poll() 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 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 +handler will be triggered and cause control to return to the +sigsetjmp() statement, where this time, sigsetjmp() 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 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 arguments 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 signal 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 signal, gl_get_line() returns NULL, and +a following call to gl_return_status() 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 +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 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 application 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_timeout() function (see +gl_get_line(3)), 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(3), gl_get_line(3), tecla(7), ef_expand_file(3),
+cpl_complete_word(3), pca_lookup_file(3)
+
+ +

+  +

AUTHOR

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

+ +


+ 

Index

+
+
NAME
+
SYNOPSIS
+
DESCRIPTION
+
SERVER I/O MODE
+
GIVING UP THE TERMINAL
+
SIGNAL HANDLING
+
+
Terminal resize signals (SIGWINCH)
+
+
Process termination signals.
+
Process suspension signals.
+
INTERRUPTING THE EVENT LOOP
+
SIGNALS CAUGHT BY GL_GET_LINE
+
ABORTING LINE INPUT
+
SIGNAL SAFE FUNCTIONS
+
USING TIMEOUTS TO POLL
+
THE SERVER DEMO PROGRAM
+
FILES
+
SEE ALSO
+
AUTHOR
+
+
+This document was created by +man2html, +using the manual pages.
+Time: 22:21:57 GMT, November 09, 2014 + + diff --git a/libtecla-1.6.3/html/index.html b/libtecla-1.6.3/html/index.html new file mode 100644 index 0000000..fb34147 --- /dev/null +++ b/libtecla-1.6.3/html/index.html @@ -0,0 +1,73 @@ +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.3. This may be obtained from: +

+ http://www.astro.caltech.edu/~mcs/tecla/libtecla-1.6.3.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. +
+ +
+Martin Shepherd (09-Nov-2014) + diff --git a/libtecla-1.6.3/html/libtecla.html b/libtecla-1.6.3/html/libtecla.html new file mode 100644 index 0000000..b155918 --- /dev/null +++ b/libtecla-1.6.3/html/libtecla.html @@ -0,0 +1,199 @@ +Content-type: text/html + + +Man page of libtecla + +

libtecla

+Section: C Library Functions (3)
Index +Return to Main Contents
+ +  +

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 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(7)              -  Use level documentation of the
+                        command-line editing facilities
+                        provided by gl_get_line().
+  gl_get_line(3)        -  The interactive line-input module.
+  gl_io_mode(3)         -  How to use gl_get_line() in an
+                        incremental, non-blocking fashion.
+  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(1)            -  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 ~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 +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), tecla(7), gl_io_mode(3), ef_expand_file(3),
+cpl_complete_word(3), pca_lookup_file(3), enhance(1)
+
+ +

+  +

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.
+
+ +

+ +


+ 

Index

+
+
NAME
+
SYNOPSIS
+
DESCRIPTION
+
THREAD SAFETY
+
LIBRARY VERSION NUMBER
+
TRIVIA
+
FILES
+
SEE ALSO
+
AUTHOR
+
ACKNOWLEDGMENTS
+
+
+This document was created by +man2html, +using the manual pages.
+Time: 22:21:57 GMT, November 09, 2014 + + diff --git a/libtecla-1.6.3/html/pca_lookup_file.html b/libtecla-1.6.3/html/pca_lookup_file.html new file mode 100644 index 0000000..44b0cb2 --- /dev/null +++ b/libtecla-1.6.3/html/pca_lookup_file.html @@ -0,0 +1,420 @@ +Content-type: text/html + + +Man page of pca_lookup_file + +

pca_lookup_file

+Section: C Library Functions (3)
Index +Return to Main Contents
+ +  +

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 relative 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 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. +

+  +

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 specified 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 +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 functions which allow you to +individually change these parameters are discussed below. +

+By default, the pca_path_completions() 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 +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 +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 +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 variable. 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 +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 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 callback 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_completions(). +

+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_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) +

+ +


+ 

Index

+
+
NAME
+
SYNOPSIS
+
DESCRIPTION
+
AN EXAMPLE
+
FUNCTION DESCRIPTIONS
+
POPULATING THE CACHE
+
LOOKING UP FILES
+
FILENAME COMPLETION
+
BEING SELECTIVE
+
ERROR HANDLING
+
CLEANING UP
+
THREAD SAFETY
+
FILES
+
SEE ALSO
+
AUTHOR
+
+
+This document was created by +man2html, +using the manual pages.
+Time: 22:21:57 GMT, November 09, 2014 + + diff --git a/libtecla-1.6.3/html/release.html b/libtecla-1.6.3/html/release.html new file mode 100644 index 0000000..22729cf --- /dev/null +++ b/libtecla-1.6.3/html/release.html @@ -0,0 +1,604 @@ +The tecla library release notes +
+This file lists major changes which accompany each new release.
+
+Version 1.6.3:
+
+  This release corrects some problems in the build process,
+  including one that was preventing libtecla from being compiled
+  on Mac OS X.
+
+Version 1.6.2:
+
+  This release updates the configuration script to ensure that the
+  enhance utility program is compiled correctly on systems that have
+  system V psuedo-terminal allocation but not system V streams.
+
+  There are no new features.
+
+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.3/html/tecla.html b/libtecla-1.6.3/html/tecla.html new file mode 100644 index 0000000..3031a48 --- /dev/null +++ b/libtecla-1.6.3/html/tecla.html @@ -0,0 +1,1276 @@ +Content-type: text/html + + +Man page of tecla + +

tecla

+Section: Environments, Tables, and Troff Macros (7)
Index +Return to Main Contents
+ +  +

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 +familiar, but with a few minor differences, most notably in how +forward and backward searches through the list of historical commands +are performed. 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 completion 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 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 +following 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 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 +example, 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_complete_word(3) 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 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. 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 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", Tecla +would recall the "ls -l getline.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, Similarly, hitting M-p again, would recall the "ls +~/tecla/" line, and hitting it once more would recall the "ls +~/tecla/" line. The pattern syntax is the same as that described for +filename expansion, in the ef_expand_file(3 man +page. +

+  +

HISTORY FILES

+ +

+Authors of programs that use the Tecla library have the option of +saving 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 different 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 +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 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 +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 usually 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 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 laptop 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. +

+  +

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 +combination 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 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 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 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 +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 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 +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 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 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 +interpreted 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 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 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(3), gl_get_line(3), gl_io_mode(3), ef_expand_file(3),
+cpl_complete_word(3), pca_lookup_file(3)
+
+ +
   +  +

AUTHOR

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

+ +


+ 

Index

+
+
NAME
+
DESCRIPTION
+
KEY SEQUENCE NOTATION
+
THE TECLA CONFIGURATION FILE
+
FILENAME AND TILDE COMPLETION
+
FILENAME EXPANSION
+
RECALLING PREVIOUSLY TYPED LINES
+
HISTORY FILES
+
INTERNATIONAL CHARACTER SETS
+
+
Meta keys and locales
+
Entering international characters
+
+
THE AVAILABLE KEY BINDING FUNCTIONS
+
DEFAULT KEY BINDINGS IN EMACS MODE
+
DEFAULT KEY BINDINGS IN VI MODE
+
ENTERING REPEAT COUNTS
+
FILES
+
SEE ALSO
+
AUTHOR
+
+
+This document was created by +man2html, +using the manual pages.
+Time: 22:21:57 GMT, November 09, 2014 + + diff --git a/libtecla-1.6.3/install-sh b/libtecla-1.6.3/install-sh new file mode 100755 index 0000000..e9de238 --- /dev/null +++ b/libtecla-1.6.3/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.3/ioutil.c b/libtecla-1.6.3/ioutil.c new file mode 100644 index 0000000..3d002c7 --- /dev/null +++ b/libtecla-1.6.3/ioutil.c @@ -0,0 +1,330 @@ +/* + * Copyright (c) 2000, 2001, 2002, 2003, 2004, 2012 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.3/keytab.h b/libtecla-1.6.3/keytab.h new file mode 100644 index 0000000..25556e0 --- /dev/null +++ b/libtecla-1.6.3/keytab.h @@ -0,0 +1,157 @@ +#ifndef keytab_h +#define keytab_h + +/* + * Copyright (c) 2000, 2001, 2002, 2003, 2004, 2012 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.3/libtecla.h b/libtecla-1.6.3/libtecla.h new file mode 100644 index 0000000..eb76e2f --- /dev/null +++ b/libtecla-1.6.3/libtecla.h @@ -0,0 +1,1834 @@ +#ifndef libtecla_h +#define libtecla_h + +/* + * Copyright (c) 2000, 2001, 2002, 2003, 2004, 2012 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 3 + +/*....................................................................... + * 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.3/libtecla.map b/libtecla-1.6.3/libtecla.map new file mode 100644 index 0000000..a63378e --- /dev/null +++ b/libtecla-1.6.3/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.3/man/file/teclarc.in b/libtecla-1.6.3/man/file/teclarc.in new file mode 100644 index 0000000..b5ee705 --- /dev/null +++ b/libtecla-1.6.3/man/file/teclarc.in @@ -0,0 +1 @@ +.so @MISC_MANDIR@/tecla.@MISC_MANEXT@ diff --git a/libtecla-1.6.3/man/func/cfc_file_start.in b/libtecla-1.6.3/man/func/cfc_file_start.in new file mode 100644 index 0000000..734f281 --- /dev/null +++ b/libtecla-1.6.3/man/func/cfc_file_start.in @@ -0,0 +1 @@ +.so @FUNC_MANDIR@/cpl_complete_word.@FUNC_MANEXT@ diff --git a/libtecla-1.6.3/man/func/cfc_literal_escapes.in b/libtecla-1.6.3/man/func/cfc_literal_escapes.in new file mode 100644 index 0000000..734f281 --- /dev/null +++ b/libtecla-1.6.3/man/func/cfc_literal_escapes.in @@ -0,0 +1 @@ +.so @FUNC_MANDIR@/cpl_complete_word.@FUNC_MANEXT@ diff --git a/libtecla-1.6.3/man/func/cfc_set_check_fn.in b/libtecla-1.6.3/man/func/cfc_set_check_fn.in new file mode 100644 index 0000000..734f281 --- /dev/null +++ b/libtecla-1.6.3/man/func/cfc_set_check_fn.in @@ -0,0 +1 @@ +.so @FUNC_MANDIR@/cpl_complete_word.@FUNC_MANEXT@ diff --git a/libtecla-1.6.3/man/func/cpl_add_completion.in b/libtecla-1.6.3/man/func/cpl_add_completion.in new file mode 100644 index 0000000..734f281 --- /dev/null +++ b/libtecla-1.6.3/man/func/cpl_add_completion.in @@ -0,0 +1 @@ +.so @FUNC_MANDIR@/cpl_complete_word.@FUNC_MANEXT@ diff --git a/libtecla-1.6.3/man/func/cpl_complete_word.in b/libtecla-1.6.3/man/func/cpl_complete_word.in new file mode 100644 index 0000000..2479657 --- /dev/null +++ b/libtecla-1.6.3/man/func/cpl_complete_word.in @@ -0,0 +1,441 @@ +.\" Copyright (c) 2000, 2001, 2002, 2003, 2004, 2012 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.3/man/func/cpl_file_completions.in b/libtecla-1.6.3/man/func/cpl_file_completions.in new file mode 100644 index 0000000..734f281 --- /dev/null +++ b/libtecla-1.6.3/man/func/cpl_file_completions.in @@ -0,0 +1 @@ +.so @FUNC_MANDIR@/cpl_complete_word.@FUNC_MANEXT@ diff --git a/libtecla-1.6.3/man/func/cpl_last_error.in b/libtecla-1.6.3/man/func/cpl_last_error.in new file mode 100644 index 0000000..734f281 --- /dev/null +++ b/libtecla-1.6.3/man/func/cpl_last_error.in @@ -0,0 +1 @@ +.so @FUNC_MANDIR@/cpl_complete_word.@FUNC_MANEXT@ diff --git a/libtecla-1.6.3/man/func/cpl_list_completions.in b/libtecla-1.6.3/man/func/cpl_list_completions.in new file mode 100644 index 0000000..734f281 --- /dev/null +++ b/libtecla-1.6.3/man/func/cpl_list_completions.in @@ -0,0 +1 @@ +.so @FUNC_MANDIR@/cpl_complete_word.@FUNC_MANEXT@ diff --git a/libtecla-1.6.3/man/func/cpl_recall_matches.in b/libtecla-1.6.3/man/func/cpl_recall_matches.in new file mode 100644 index 0000000..734f281 --- /dev/null +++ b/libtecla-1.6.3/man/func/cpl_recall_matches.in @@ -0,0 +1 @@ +.so @FUNC_MANDIR@/cpl_complete_word.@FUNC_MANEXT@ diff --git a/libtecla-1.6.3/man/func/cpl_record_error.in b/libtecla-1.6.3/man/func/cpl_record_error.in new file mode 100644 index 0000000..734f281 --- /dev/null +++ b/libtecla-1.6.3/man/func/cpl_record_error.in @@ -0,0 +1 @@ +.so @FUNC_MANDIR@/cpl_complete_word.@FUNC_MANEXT@ diff --git a/libtecla-1.6.3/man/func/del_CplFileConf.in b/libtecla-1.6.3/man/func/del_CplFileConf.in new file mode 100644 index 0000000..734f281 --- /dev/null +++ b/libtecla-1.6.3/man/func/del_CplFileConf.in @@ -0,0 +1 @@ +.so @FUNC_MANDIR@/cpl_complete_word.@FUNC_MANEXT@ diff --git a/libtecla-1.6.3/man/func/del_ExpandFile.in b/libtecla-1.6.3/man/func/del_ExpandFile.in new file mode 100644 index 0000000..3d0a884 --- /dev/null +++ b/libtecla-1.6.3/man/func/del_ExpandFile.in @@ -0,0 +1 @@ +.so @FUNC_MANDIR@/ef_expand_file.@FUNC_MANEXT@ diff --git a/libtecla-1.6.3/man/func/del_GetLine.in b/libtecla-1.6.3/man/func/del_GetLine.in new file mode 100644 index 0000000..6e46fc6 --- /dev/null +++ b/libtecla-1.6.3/man/func/del_GetLine.in @@ -0,0 +1 @@ +.so @FUNC_MANDIR@/gl_get_line.@FUNC_MANEXT@ diff --git a/libtecla-1.6.3/man/func/del_PathCache.in b/libtecla-1.6.3/man/func/del_PathCache.in new file mode 100644 index 0000000..dbc4da7 --- /dev/null +++ b/libtecla-1.6.3/man/func/del_PathCache.in @@ -0,0 +1 @@ +.so @FUNC_MANDIR@/pca_lookup_file.@FUNC_MANEXT@ diff --git a/libtecla-1.6.3/man/func/del_PcaPathConf.in b/libtecla-1.6.3/man/func/del_PcaPathConf.in new file mode 100644 index 0000000..dbc4da7 --- /dev/null +++ b/libtecla-1.6.3/man/func/del_PcaPathConf.in @@ -0,0 +1 @@ +.so @FUNC_MANDIR@/pca_lookup_file.@FUNC_MANEXT@ diff --git a/libtecla-1.6.3/man/func/del_WordCompletion.in b/libtecla-1.6.3/man/func/del_WordCompletion.in new file mode 100644 index 0000000..734f281 --- /dev/null +++ b/libtecla-1.6.3/man/func/del_WordCompletion.in @@ -0,0 +1 @@ +.so @FUNC_MANDIR@/cpl_complete_word.@FUNC_MANEXT@ diff --git a/libtecla-1.6.3/man/func/ef_expand_file.in b/libtecla-1.6.3/man/func/ef_expand_file.in new file mode 100644 index 0000000..6637f9f --- /dev/null +++ b/libtecla-1.6.3/man/func/ef_expand_file.in @@ -0,0 +1,248 @@ +.\" Copyright (c) 2000, 2001, 2002, 2003, 2004, 2012 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.3/man/func/ef_last_error.in b/libtecla-1.6.3/man/func/ef_last_error.in new file mode 100644 index 0000000..3d0a884 --- /dev/null +++ b/libtecla-1.6.3/man/func/ef_last_error.in @@ -0,0 +1 @@ +.so @FUNC_MANDIR@/ef_expand_file.@FUNC_MANEXT@ diff --git a/libtecla-1.6.3/man/func/ef_list_expansions.in b/libtecla-1.6.3/man/func/ef_list_expansions.in new file mode 100644 index 0000000..3d0a884 --- /dev/null +++ b/libtecla-1.6.3/man/func/ef_list_expansions.in @@ -0,0 +1 @@ +.so @FUNC_MANDIR@/ef_expand_file.@FUNC_MANEXT@ diff --git a/libtecla-1.6.3/man/func/gl_abandon_line.in b/libtecla-1.6.3/man/func/gl_abandon_line.in new file mode 100644 index 0000000..24798bc --- /dev/null +++ b/libtecla-1.6.3/man/func/gl_abandon_line.in @@ -0,0 +1 @@ +.so @FUNC_MANDIR@/gl_io_mode.@FUNC_MANEXT@ diff --git a/libtecla-1.6.3/man/func/gl_bind_keyseq.in b/libtecla-1.6.3/man/func/gl_bind_keyseq.in new file mode 100644 index 0000000..6e46fc6 --- /dev/null +++ b/libtecla-1.6.3/man/func/gl_bind_keyseq.in @@ -0,0 +1 @@ +.so @FUNC_MANDIR@/gl_get_line.@FUNC_MANEXT@ diff --git a/libtecla-1.6.3/man/func/gl_catch_blocked.in b/libtecla-1.6.3/man/func/gl_catch_blocked.in new file mode 100644 index 0000000..6e46fc6 --- /dev/null +++ b/libtecla-1.6.3/man/func/gl_catch_blocked.in @@ -0,0 +1 @@ +.so @FUNC_MANDIR@/gl_get_line.@FUNC_MANEXT@ diff --git a/libtecla-1.6.3/man/func/gl_change_terminal.in b/libtecla-1.6.3/man/func/gl_change_terminal.in new file mode 100644 index 0000000..6e46fc6 --- /dev/null +++ b/libtecla-1.6.3/man/func/gl_change_terminal.in @@ -0,0 +1 @@ +.so @FUNC_MANDIR@/gl_get_line.@FUNC_MANEXT@ diff --git a/libtecla-1.6.3/man/func/gl_clear_history.in b/libtecla-1.6.3/man/func/gl_clear_history.in new file mode 100644 index 0000000..6e46fc6 --- /dev/null +++ b/libtecla-1.6.3/man/func/gl_clear_history.in @@ -0,0 +1 @@ +.so @FUNC_MANDIR@/gl_get_line.@FUNC_MANEXT@ diff --git a/libtecla-1.6.3/man/func/gl_completion_action.in b/libtecla-1.6.3/man/func/gl_completion_action.in new file mode 100644 index 0000000..6e46fc6 --- /dev/null +++ b/libtecla-1.6.3/man/func/gl_completion_action.in @@ -0,0 +1 @@ +.so @FUNC_MANDIR@/gl_get_line.@FUNC_MANEXT@ diff --git a/libtecla-1.6.3/man/func/gl_configure_getline.in b/libtecla-1.6.3/man/func/gl_configure_getline.in new file mode 100644 index 0000000..6e46fc6 --- /dev/null +++ b/libtecla-1.6.3/man/func/gl_configure_getline.in @@ -0,0 +1 @@ +.so @FUNC_MANDIR@/gl_get_line.@FUNC_MANEXT@ diff --git a/libtecla-1.6.3/man/func/gl_customize_completion.in b/libtecla-1.6.3/man/func/gl_customize_completion.in new file mode 100644 index 0000000..6e46fc6 --- /dev/null +++ b/libtecla-1.6.3/man/func/gl_customize_completion.in @@ -0,0 +1 @@ +.so @FUNC_MANDIR@/gl_get_line.@FUNC_MANEXT@ diff --git a/libtecla-1.6.3/man/func/gl_display_text.in b/libtecla-1.6.3/man/func/gl_display_text.in new file mode 100644 index 0000000..6e46fc6 --- /dev/null +++ b/libtecla-1.6.3/man/func/gl_display_text.in @@ -0,0 +1 @@ +.so @FUNC_MANDIR@/gl_get_line.@FUNC_MANEXT@ diff --git a/libtecla-1.6.3/man/func/gl_echo_mode.in b/libtecla-1.6.3/man/func/gl_echo_mode.in new file mode 100644 index 0000000..6e46fc6 --- /dev/null +++ b/libtecla-1.6.3/man/func/gl_echo_mode.in @@ -0,0 +1 @@ +.so @FUNC_MANDIR@/gl_get_line.@FUNC_MANEXT@ diff --git a/libtecla-1.6.3/man/func/gl_erase_terminal.in b/libtecla-1.6.3/man/func/gl_erase_terminal.in new file mode 100644 index 0000000..6e46fc6 --- /dev/null +++ b/libtecla-1.6.3/man/func/gl_erase_terminal.in @@ -0,0 +1 @@ +.so @FUNC_MANDIR@/gl_get_line.@FUNC_MANEXT@ diff --git a/libtecla-1.6.3/man/func/gl_error_message.in b/libtecla-1.6.3/man/func/gl_error_message.in new file mode 100644 index 0000000..6e46fc6 --- /dev/null +++ b/libtecla-1.6.3/man/func/gl_error_message.in @@ -0,0 +1 @@ +.so @FUNC_MANDIR@/gl_get_line.@FUNC_MANEXT@ diff --git a/libtecla-1.6.3/man/func/gl_get_line.in b/libtecla-1.6.3/man/func/gl_get_line.in new file mode 100644 index 0000000..ba0df8d --- /dev/null +++ b/libtecla-1.6.3/man/func/gl_get_line.in @@ -0,0 +1,2236 @@ +.\" Copyright (c) 2000, 2001, 2002, 2003, 2004, 2012 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.3/man/func/gl_group_history.in b/libtecla-1.6.3/man/func/gl_group_history.in new file mode 100644 index 0000000..6e46fc6 --- /dev/null +++ b/libtecla-1.6.3/man/func/gl_group_history.in @@ -0,0 +1 @@ +.so @FUNC_MANDIR@/gl_get_line.@FUNC_MANEXT@ diff --git a/libtecla-1.6.3/man/func/gl_handle_signal.in b/libtecla-1.6.3/man/func/gl_handle_signal.in new file mode 100644 index 0000000..24798bc --- /dev/null +++ b/libtecla-1.6.3/man/func/gl_handle_signal.in @@ -0,0 +1 @@ +.so @FUNC_MANDIR@/gl_io_mode.@FUNC_MANEXT@ diff --git a/libtecla-1.6.3/man/func/gl_ignore_signal.in b/libtecla-1.6.3/man/func/gl_ignore_signal.in new file mode 100644 index 0000000..6e46fc6 --- /dev/null +++ b/libtecla-1.6.3/man/func/gl_ignore_signal.in @@ -0,0 +1 @@ +.so @FUNC_MANDIR@/gl_get_line.@FUNC_MANEXT@ diff --git a/libtecla-1.6.3/man/func/gl_inactivity_timeout.in b/libtecla-1.6.3/man/func/gl_inactivity_timeout.in new file mode 100644 index 0000000..6e46fc6 --- /dev/null +++ b/libtecla-1.6.3/man/func/gl_inactivity_timeout.in @@ -0,0 +1 @@ +.so @FUNC_MANDIR@/gl_get_line.@FUNC_MANEXT@ diff --git a/libtecla-1.6.3/man/func/gl_io_mode.in b/libtecla-1.6.3/man/func/gl_io_mode.in new file mode 100644 index 0000000..0bcca8b --- /dev/null +++ b/libtecla-1.6.3/man/func/gl_io_mode.in @@ -0,0 +1,571 @@ +.\" Copyright (c) 2002, 2003, 2004, 2012 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.3/man/func/gl_last_signal.in b/libtecla-1.6.3/man/func/gl_last_signal.in new file mode 100644 index 0000000..6e46fc6 --- /dev/null +++ b/libtecla-1.6.3/man/func/gl_last_signal.in @@ -0,0 +1 @@ +.so @FUNC_MANDIR@/gl_get_line.@FUNC_MANEXT@ diff --git a/libtecla-1.6.3/man/func/gl_limit_history.in b/libtecla-1.6.3/man/func/gl_limit_history.in new file mode 100644 index 0000000..6e46fc6 --- /dev/null +++ b/libtecla-1.6.3/man/func/gl_limit_history.in @@ -0,0 +1 @@ +.so @FUNC_MANDIR@/gl_get_line.@FUNC_MANEXT@ diff --git a/libtecla-1.6.3/man/func/gl_list_signals.in b/libtecla-1.6.3/man/func/gl_list_signals.in new file mode 100644 index 0000000..6e46fc6 --- /dev/null +++ b/libtecla-1.6.3/man/func/gl_list_signals.in @@ -0,0 +1 @@ +.so @FUNC_MANDIR@/gl_get_line.@FUNC_MANEXT@ diff --git a/libtecla-1.6.3/man/func/gl_load_history.in b/libtecla-1.6.3/man/func/gl_load_history.in new file mode 100644 index 0000000..6e46fc6 --- /dev/null +++ b/libtecla-1.6.3/man/func/gl_load_history.in @@ -0,0 +1 @@ +.so @FUNC_MANDIR@/gl_get_line.@FUNC_MANEXT@ diff --git a/libtecla-1.6.3/man/func/gl_lookup_history.in b/libtecla-1.6.3/man/func/gl_lookup_history.in new file mode 100644 index 0000000..6e46fc6 --- /dev/null +++ b/libtecla-1.6.3/man/func/gl_lookup_history.in @@ -0,0 +1 @@ +.so @FUNC_MANDIR@/gl_get_line.@FUNC_MANEXT@ diff --git a/libtecla-1.6.3/man/func/gl_normal_io.in b/libtecla-1.6.3/man/func/gl_normal_io.in new file mode 100644 index 0000000..24798bc --- /dev/null +++ b/libtecla-1.6.3/man/func/gl_normal_io.in @@ -0,0 +1 @@ +.so @FUNC_MANDIR@/gl_io_mode.@FUNC_MANEXT@ diff --git a/libtecla-1.6.3/man/func/gl_pending_io.in b/libtecla-1.6.3/man/func/gl_pending_io.in new file mode 100644 index 0000000..24798bc --- /dev/null +++ b/libtecla-1.6.3/man/func/gl_pending_io.in @@ -0,0 +1 @@ +.so @FUNC_MANDIR@/gl_io_mode.@FUNC_MANEXT@ diff --git a/libtecla-1.6.3/man/func/gl_prompt_style.in b/libtecla-1.6.3/man/func/gl_prompt_style.in new file mode 100644 index 0000000..6e46fc6 --- /dev/null +++ b/libtecla-1.6.3/man/func/gl_prompt_style.in @@ -0,0 +1 @@ +.so @FUNC_MANDIR@/gl_get_line.@FUNC_MANEXT@ diff --git a/libtecla-1.6.3/man/func/gl_query_char.in b/libtecla-1.6.3/man/func/gl_query_char.in new file mode 100644 index 0000000..6e46fc6 --- /dev/null +++ b/libtecla-1.6.3/man/func/gl_query_char.in @@ -0,0 +1 @@ +.so @FUNC_MANDIR@/gl_get_line.@FUNC_MANEXT@ diff --git a/libtecla-1.6.3/man/func/gl_range_of_history.in b/libtecla-1.6.3/man/func/gl_range_of_history.in new file mode 100644 index 0000000..6e46fc6 --- /dev/null +++ b/libtecla-1.6.3/man/func/gl_range_of_history.in @@ -0,0 +1 @@ +.so @FUNC_MANDIR@/gl_get_line.@FUNC_MANEXT@ diff --git a/libtecla-1.6.3/man/func/gl_raw_io.in b/libtecla-1.6.3/man/func/gl_raw_io.in new file mode 100644 index 0000000..24798bc --- /dev/null +++ b/libtecla-1.6.3/man/func/gl_raw_io.in @@ -0,0 +1 @@ +.so @FUNC_MANDIR@/gl_io_mode.@FUNC_MANEXT@ diff --git a/libtecla-1.6.3/man/func/gl_read_char.in b/libtecla-1.6.3/man/func/gl_read_char.in new file mode 100644 index 0000000..6e46fc6 --- /dev/null +++ b/libtecla-1.6.3/man/func/gl_read_char.in @@ -0,0 +1 @@ +.so @FUNC_MANDIR@/gl_get_line.@FUNC_MANEXT@ diff --git a/libtecla-1.6.3/man/func/gl_register_action.in b/libtecla-1.6.3/man/func/gl_register_action.in new file mode 100644 index 0000000..6e46fc6 --- /dev/null +++ b/libtecla-1.6.3/man/func/gl_register_action.in @@ -0,0 +1 @@ +.so @FUNC_MANDIR@/gl_get_line.@FUNC_MANEXT@ diff --git a/libtecla-1.6.3/man/func/gl_resize_history.in b/libtecla-1.6.3/man/func/gl_resize_history.in new file mode 100644 index 0000000..6e46fc6 --- /dev/null +++ b/libtecla-1.6.3/man/func/gl_resize_history.in @@ -0,0 +1 @@ +.so @FUNC_MANDIR@/gl_get_line.@FUNC_MANEXT@ diff --git a/libtecla-1.6.3/man/func/gl_return_status.in b/libtecla-1.6.3/man/func/gl_return_status.in new file mode 100644 index 0000000..6e46fc6 --- /dev/null +++ b/libtecla-1.6.3/man/func/gl_return_status.in @@ -0,0 +1 @@ +.so @FUNC_MANDIR@/gl_get_line.@FUNC_MANEXT@ diff --git a/libtecla-1.6.3/man/func/gl_save_history.in b/libtecla-1.6.3/man/func/gl_save_history.in new file mode 100644 index 0000000..6e46fc6 --- /dev/null +++ b/libtecla-1.6.3/man/func/gl_save_history.in @@ -0,0 +1 @@ +.so @FUNC_MANDIR@/gl_get_line.@FUNC_MANEXT@ diff --git a/libtecla-1.6.3/man/func/gl_set_term_size.in b/libtecla-1.6.3/man/func/gl_set_term_size.in new file mode 100644 index 0000000..6e46fc6 --- /dev/null +++ b/libtecla-1.6.3/man/func/gl_set_term_size.in @@ -0,0 +1 @@ +.so @FUNC_MANDIR@/gl_get_line.@FUNC_MANEXT@ diff --git a/libtecla-1.6.3/man/func/gl_show_history.in b/libtecla-1.6.3/man/func/gl_show_history.in new file mode 100644 index 0000000..6e46fc6 --- /dev/null +++ b/libtecla-1.6.3/man/func/gl_show_history.in @@ -0,0 +1 @@ +.so @FUNC_MANDIR@/gl_get_line.@FUNC_MANEXT@ diff --git a/libtecla-1.6.3/man/func/gl_size_of_history.in b/libtecla-1.6.3/man/func/gl_size_of_history.in new file mode 100644 index 0000000..6e46fc6 --- /dev/null +++ b/libtecla-1.6.3/man/func/gl_size_of_history.in @@ -0,0 +1 @@ +.so @FUNC_MANDIR@/gl_get_line.@FUNC_MANEXT@ diff --git a/libtecla-1.6.3/man/func/gl_state_of_history.in b/libtecla-1.6.3/man/func/gl_state_of_history.in new file mode 100644 index 0000000..6e46fc6 --- /dev/null +++ b/libtecla-1.6.3/man/func/gl_state_of_history.in @@ -0,0 +1 @@ +.so @FUNC_MANDIR@/gl_get_line.@FUNC_MANEXT@ diff --git a/libtecla-1.6.3/man/func/gl_terminal_size.in b/libtecla-1.6.3/man/func/gl_terminal_size.in new file mode 100644 index 0000000..6e46fc6 --- /dev/null +++ b/libtecla-1.6.3/man/func/gl_terminal_size.in @@ -0,0 +1 @@ +.so @FUNC_MANDIR@/gl_get_line.@FUNC_MANEXT@ diff --git a/libtecla-1.6.3/man/func/gl_toggle_history.in b/libtecla-1.6.3/man/func/gl_toggle_history.in new file mode 100644 index 0000000..6e46fc6 --- /dev/null +++ b/libtecla-1.6.3/man/func/gl_toggle_history.in @@ -0,0 +1 @@ +.so @FUNC_MANDIR@/gl_get_line.@FUNC_MANEXT@ diff --git a/libtecla-1.6.3/man/func/gl_trap_signal.in b/libtecla-1.6.3/man/func/gl_trap_signal.in new file mode 100644 index 0000000..6e46fc6 --- /dev/null +++ b/libtecla-1.6.3/man/func/gl_trap_signal.in @@ -0,0 +1 @@ +.so @FUNC_MANDIR@/gl_get_line.@FUNC_MANEXT@ diff --git a/libtecla-1.6.3/man/func/gl_tty_signals.in b/libtecla-1.6.3/man/func/gl_tty_signals.in new file mode 100644 index 0000000..24798bc --- /dev/null +++ b/libtecla-1.6.3/man/func/gl_tty_signals.in @@ -0,0 +1 @@ +.so @FUNC_MANDIR@/gl_io_mode.@FUNC_MANEXT@ diff --git a/libtecla-1.6.3/man/func/gl_watch_fd.in b/libtecla-1.6.3/man/func/gl_watch_fd.in new file mode 100644 index 0000000..6e46fc6 --- /dev/null +++ b/libtecla-1.6.3/man/func/gl_watch_fd.in @@ -0,0 +1 @@ +.so @FUNC_MANDIR@/gl_get_line.@FUNC_MANEXT@ diff --git a/libtecla-1.6.3/man/func/libtecla_version.in b/libtecla-1.6.3/man/func/libtecla_version.in new file mode 100644 index 0000000..31867c4 --- /dev/null +++ b/libtecla-1.6.3/man/func/libtecla_version.in @@ -0,0 +1 @@ +.so @LIBR_MANDIR@/libtecla.@LIBR_MANEXT@ diff --git a/libtecla-1.6.3/man/func/new_CplFileConf.in b/libtecla-1.6.3/man/func/new_CplFileConf.in new file mode 100644 index 0000000..734f281 --- /dev/null +++ b/libtecla-1.6.3/man/func/new_CplFileConf.in @@ -0,0 +1 @@ +.so @FUNC_MANDIR@/cpl_complete_word.@FUNC_MANEXT@ diff --git a/libtecla-1.6.3/man/func/new_ExpandFile.in b/libtecla-1.6.3/man/func/new_ExpandFile.in new file mode 100644 index 0000000..3d0a884 --- /dev/null +++ b/libtecla-1.6.3/man/func/new_ExpandFile.in @@ -0,0 +1 @@ +.so @FUNC_MANDIR@/ef_expand_file.@FUNC_MANEXT@ diff --git a/libtecla-1.6.3/man/func/new_GetLine.in b/libtecla-1.6.3/man/func/new_GetLine.in new file mode 100644 index 0000000..6e46fc6 --- /dev/null +++ b/libtecla-1.6.3/man/func/new_GetLine.in @@ -0,0 +1 @@ +.so @FUNC_MANDIR@/gl_get_line.@FUNC_MANEXT@ diff --git a/libtecla-1.6.3/man/func/new_PathCache.in b/libtecla-1.6.3/man/func/new_PathCache.in new file mode 100644 index 0000000..dbc4da7 --- /dev/null +++ b/libtecla-1.6.3/man/func/new_PathCache.in @@ -0,0 +1 @@ +.so @FUNC_MANDIR@/pca_lookup_file.@FUNC_MANEXT@ diff --git a/libtecla-1.6.3/man/func/new_PcaPathConf.in b/libtecla-1.6.3/man/func/new_PcaPathConf.in new file mode 100644 index 0000000..dbc4da7 --- /dev/null +++ b/libtecla-1.6.3/man/func/new_PcaPathConf.in @@ -0,0 +1 @@ +.so @FUNC_MANDIR@/pca_lookup_file.@FUNC_MANEXT@ diff --git a/libtecla-1.6.3/man/func/new_WordCompletion.in b/libtecla-1.6.3/man/func/new_WordCompletion.in new file mode 100644 index 0000000..734f281 --- /dev/null +++ b/libtecla-1.6.3/man/func/new_WordCompletion.in @@ -0,0 +1 @@ +.so @FUNC_MANDIR@/cpl_complete_word.@FUNC_MANEXT@ diff --git a/libtecla-1.6.3/man/func/pca_last_error.in b/libtecla-1.6.3/man/func/pca_last_error.in new file mode 100644 index 0000000..dbc4da7 --- /dev/null +++ b/libtecla-1.6.3/man/func/pca_last_error.in @@ -0,0 +1 @@ +.so @FUNC_MANDIR@/pca_lookup_file.@FUNC_MANEXT@ diff --git a/libtecla-1.6.3/man/func/pca_lookup_file.in b/libtecla-1.6.3/man/func/pca_lookup_file.in new file mode 100644 index 0000000..e74114a --- /dev/null +++ b/libtecla-1.6.3/man/func/pca_lookup_file.in @@ -0,0 +1,365 @@ +.\" Copyright (c) 2001, 2002, 2003, 2004, 2012 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.3/man/prog/enhance.in b/libtecla-1.6.3/man/prog/enhance.in new file mode 100644 index 0000000..aacd8a0 --- /dev/null +++ b/libtecla-1.6.3/man/prog/enhance.in @@ -0,0 +1,89 @@ +.\" Copyright (c) 2000, 2001, 2002, 2003, 2004, 2012 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.3/pathutil.c b/libtecla-1.6.3/pathutil.c new file mode 100644 index 0000000..be14484 --- /dev/null +++ b/libtecla-1.6.3/pathutil.c @@ -0,0 +1,539 @@ +/* + * Copyright (c) 2000, 2001, 2002, 2003, 2004, 2012 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.3/pathutil.h b/libtecla-1.6.3/pathutil.h new file mode 100644 index 0000000..55c9b32 --- /dev/null +++ b/libtecla-1.6.3/pathutil.h @@ -0,0 +1,122 @@ +#ifndef pathutil_h +#define pathutil_h + +/* + * Copyright (c) 2000, 2001, 2002, 2003, 2004, 2012 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.3/pcache.c b/libtecla-1.6.3/pcache.c new file mode 100644 index 0000000..5a489b3 --- /dev/null +++ b/libtecla-1.6.3/pcache.c @@ -0,0 +1,1710 @@ +/* + * Copyright (c) 2000, 2001, 2002, 2003, 2004, 2012 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.3/stringrp.c b/libtecla-1.6.3/stringrp.c new file mode 100644 index 0000000..de7369a --- /dev/null +++ b/libtecla-1.6.3/stringrp.c @@ -0,0 +1,286 @@ +/* + * Copyright (c) 2000, 2001, 2002, 2003, 2004, 2012 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.3/stringrp.h b/libtecla-1.6.3/stringrp.h new file mode 100644 index 0000000..94a4922 --- /dev/null +++ b/libtecla-1.6.3/stringrp.h @@ -0,0 +1,84 @@ +#ifndef stringrp_h +#define stringrp_h +/* + * Copyright (c) 2000, 2001, 2002, 2003, 2004, 2012 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.3/strngmem.c b/libtecla-1.6.3/strngmem.c new file mode 100644 index 0000000..5390475 --- /dev/null +++ b/libtecla-1.6.3/strngmem.c @@ -0,0 +1,218 @@ +/* + * Copyright (c) 2000, 2001, 2002, 2003, 2004, 2012 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.3/strngmem.h b/libtecla-1.6.3/strngmem.h new file mode 100644 index 0000000..2138cfb --- /dev/null +++ b/libtecla-1.6.3/strngmem.h @@ -0,0 +1,80 @@ +#ifndef stringmem_h +#define stringmem_h +/* + * Copyright (c) 2000, 2001, 2002, 2003, 2004, 2012 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.3/update_html b/libtecla-1.6.3/update_html new file mode 100755 index 0000000..b6a5613 --- /dev/null +++ b/libtecla-1.6.3/update_html @@ -0,0 +1,29 @@ +#!/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 -r $dir/$page | sed -s 's/\.\.\/man[0-9]*\///g' | sed -s 's/HREF="\.\.\//HREF="/g' | sed -s 's/\.[0-9][0-9]*\.html/.html/g' > $html + 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.3/update_version b/libtecla-1.6.3/update_version new file mode 100755 index 0000000..c18f714 --- /dev/null +++ b/libtecla-1.6.3/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.3/version.c b/libtecla-1.6.3/version.c new file mode 100644 index 0000000..9e1275e --- /dev/null +++ b/libtecla-1.6.3/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; +} diff --git a/libtecla/CHANGES b/libtecla/CHANGES deleted file mode 100644 index 3abc035..0000000 --- a/libtecla/CHANGES +++ /dev/null @@ -1,2823 +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. - -09/11/2014 mcs@astro.caltech.edu - configure.in configure - Dominyk Tiller reported that libtecla didn't compile on - Mac OS X, due to libgcc.a being cited in the makefile - without any path. This turned out to be because Gnu - autoconf claims that the clang compiler is gcc, and clang - has a -print-libgcc-file-name, but this only prints - libgcc.a. I have modified the configure script to check - whether the path returned by -print-libgcc-file-name - actually exists and only use it if it does. - -27/10/2014 Jon Szymaniak (documented here by mcs@astro.caltech.edu) - Makefile.rules - Use $(AR) instead of plain ar, so that cross-compilation - uses the correct ar program when cross-compiling. - - Add $(TARGETS) dependency to building the demo programs - and the enhance program. When using parallel compilation - this is needed to ensure that the library is compiled - before the demos. - -10/04/2013 mcs@astro.caltech.edu - Makefile.in - Jonathan Niehof reported that libtecla wouldn't compile if - there were spaces in LDFLAGS and pointed out that there should - be quotes around $(LDFLAGS) in Makefile.in. This also applied - to a few other variables cited in the same way. - -10/06/2012 mcs@astro.caltech.edu - enhance.c configure.in - I had incorrectly assumed that system-V pseudo-terminal - allocation and system-V streams terminals always went - together. However system-V pseudo terminal allocation is - now part of UNIX98, and this has been adopted into many BSD - style operating systems, without the use of system-V - streams. On such systems the lack of system-V streams IOCTL - opcodes prevented system-V pseudo-terminal allocation being - used. This was hidden under Linux until recently, because - it had a stropts.h file, which made it appear as though - Linux supported system-V streams terminals. - - I have now created separate configuration tests and options - in the configure script for system-V terminal allocation - and system-V streams. On systems that only have the former, - the latter won't be used. - -16/05/2005 mcs@astro.caltech.edu - getline.c - When an initial input line was presented to gl_get_line() - for editing, the new input line was incorrectly appended to - the previous input line, instead of replacing it. - -10/01/2004 Derek Jones (documented here by mcs@astro.caltech.edu) - getline.c - Derek discovered that the function that computes the - width of the prompt, was not correctly skipping over 3 of - the 6 possible prompt-formatting directives. Thus, when - the %f,%p or %v prompt-formatting directives were used, - the width of the prompt was incorrectly calculated. The - fix was to copy the list of directives from - gl_display_prompt(). I have also added a comment to - gl_display_prompt(), to warn anybody who adds or removes - formatting directives there, to also do the same to - gl_displayed_prompt_width(). - -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/INSTALL b/libtecla/INSTALL deleted file mode 100644 index 14fc62d..0000000 --- a/libtecla/INSTALL +++ /dev/null @@ -1,213 +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 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/LICENSE.TERMS b/libtecla/LICENSE.TERMS deleted file mode 100644 index 81db521..0000000 --- a/libtecla/LICENSE.TERMS +++ /dev/null @@ -1,28 +0,0 @@ -Copyright (c) 2000, 2001, 2002, 2003, 2004, 2012, 2014 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/Makefile b/libtecla/Makefile deleted file mode 100644 index cf89638..0000000 --- a/libtecla/Makefile +++ /dev/null @@ -1,12 +0,0 @@ -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/Makefile.in b/libtecla/Makefile.in deleted file mode 100644 index cb0891f..0000000 --- a/libtecla/Makefile.in +++ /dev/null @@ -1,272 +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@ - -# 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/Makefile.rules b/libtecla/Makefile.rules deleted file mode 100644 index 0cf2316..0000000 --- a/libtecla/Makefile.rules +++ /dev/null @@ -1,169 +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 \ - $(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 $(TARGETS) - LD_RUN_PATH="$(LIBDIR):$$LD_RUN_PATH:`pwd`" $(CC) $(CFLAGS) -o $@ \ - $(OBJDIR)/demo.o -L. -ltecla$(SUFFIX) $(LIBS) - -demo2$(SUFFIX): $(OBJDIR)/demo2.o $(TARGETS) - LD_RUN_PATH="$(LIBDIR):$$LD_RUN_PATH:`pwd`" $(CC) $(CFLAGS) -o $@ \ - $(OBJDIR)/demo2.o -L. -ltecla$(SUFFIX) $(LIBS) - -demo3$(SUFFIX): $(OBJDIR)/demo3.o $(TARGETS) - LD_RUN_PATH="$(LIBDIR):$$LD_RUN_PATH:`pwd`" $(CC) $(CFLAGS) -o $@ \ - $(OBJDIR)/demo3.o -L. -ltecla$(SUFFIX) $(LIBS) - -enhance$(SUFFIX): $(OBJDIR)/enhance.o $(TARGETS) - 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/Makefile.stub b/libtecla/Makefile.stub deleted file mode 100644 index cf89638..0000000 --- a/libtecla/Makefile.stub +++ /dev/null @@ -1,12 +0,0 @@ -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/PORTING b/libtecla/PORTING deleted file mode 100644 index db39818..0000000 --- a/libtecla/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/README b/libtecla/README deleted file mode 100644 index 3a9c40e..0000000 --- a/libtecla/README +++ /dev/null @@ -1,53 +0,0 @@ -This is version 1.6.3 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, 2012, 2014 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/RELEASE.NOTES b/libtecla/RELEASE.NOTES deleted file mode 100644 index 68cab0a..0000000 --- a/libtecla/RELEASE.NOTES +++ /dev/null @@ -1,601 +0,0 @@ -This file lists major changes which accompany each new release. - -Version 1.6.3: - - This release corrects some problems in the build process, - including one that was preventing libtecla from being compiled - on Mac OS X. - -Version 1.6.2: - - This release updates the configuration script to ensure that the - enhance utility program is compiled correctly on systems that have - system V psuedo-terminal allocation but not system V streams. - - There are no new features. - -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/chrqueue.c b/libtecla/chrqueue.c deleted file mode 100644 index a1d1d80..0000000 --- a/libtecla/chrqueue.c +++ /dev/null @@ -1,432 +0,0 @@ -/* - * Copyright (c) 2000, 2001, 2002, 2003, 2004, 2012 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/chrqueue.h b/libtecla/chrqueue.h deleted file mode 100644 index 30c9584..0000000 --- a/libtecla/chrqueue.h +++ /dev/null @@ -1,106 +0,0 @@ -#ifndef chrqueue_h -#define chrqueue_h - -/* - * Copyright (c) 2000, 2001, 2002, 2003, 2004, 2012 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/config.guess b/libtecla/config.guess deleted file mode 100644 index 500ee74..0000000 --- a/libtecla/config.guess +++ /dev/null @@ -1,1410 +0,0 @@ -#! /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/config.sub b/libtecla/config.sub deleted file mode 100644 index 1f31816..0000000 --- a/libtecla/config.sub +++ /dev/null @@ -1,1510 +0,0 @@ -#! /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/configure b/libtecla/configure deleted file mode 100755 index 8122996..0000000 --- a/libtecla/configure +++ /dev/null @@ -1,5477 +0,0 @@ -#! /bin/sh -# Guess values for system-dependent variables and create Makefiles. -# Generated by GNU Autoconf 2.68. -# -# -# Copyright (C) 1992, 1993, 1994, 1995, 1996, 1998, 1999, 2000, 2001, -# 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010 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 more Bourne compatible -DUALCASE=1; export DUALCASE # for MKS sh -if test -n "${ZSH_VERSION+set}" && (emulate sh) >/dev/null 2>&1; then : - emulate sh - NULLCMD=: - # Pre-4.2 versions of Zsh do word splitting on ${1+"$@"}, which - # is contrary to our usage. Disable this feature. - alias -g '${1+"$@"}'='"$@"' - setopt NO_GLOB_SUBST -else - case `(set -o) 2>/dev/null` in #( - *posix*) : - set -o posix ;; #( - *) : - ;; -esac -fi - - -as_nl=' -' -export as_nl -# Printing a long string crashes Solaris 7 /usr/bin/printf. -as_echo='\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\' -as_echo=$as_echo$as_echo$as_echo$as_echo$as_echo -as_echo=$as_echo$as_echo$as_echo$as_echo$as_echo$as_echo -# Prefer a ksh shell builtin over an external printf program on Solaris, -# but without wasting forks for bash or zsh. -if test -z "$BASH_VERSION$ZSH_VERSION" \ - && (test "X`print -r -- $as_echo`" = "X$as_echo") 2>/dev/null; then - as_echo='print -r --' - as_echo_n='print -rn --' -elif (test "X`printf %s $as_echo`" = "X$as_echo") 2>/dev/null; then - as_echo='printf %s\n' - as_echo_n='printf %s' -else - if test "X`(/usr/ucb/echo -n -n $as_echo) 2>/dev/null`" = "X-n $as_echo"; then - as_echo_body='eval /usr/ucb/echo -n "$1$as_nl"' - as_echo_n='/usr/ucb/echo -n' - else - as_echo_body='eval expr "X$1" : "X\\(.*\\)"' - as_echo_n_body='eval - arg=$1; - case $arg in #( - *"$as_nl"*) - expr "X$arg" : "X\\(.*\\)$as_nl"; - arg=`expr "X$arg" : ".*$as_nl\\(.*\\)"`;; - esac; - expr "X$arg" : "X\\(.*\\)" | tr -d "$as_nl" - ' - export as_echo_n_body - as_echo_n='sh -c $as_echo_n_body as_echo' - fi - export as_echo_body - as_echo='sh -c $as_echo_body as_echo' -fi - -# The user is always right. -if test "${PATH_SEPARATOR+set}" != set; then - PATH_SEPARATOR=: - (PATH='/bin;/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 && { - (PATH='/bin:/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 || - PATH_SEPARATOR=';' - } -fi - - -# IFS -# We need space, tab and new line, in precisely that order. Quoting is -# there to prevent editors from complaining about space-tab. -# (If _AS_PATH_WALK were called with IFS unset, it would disable word -# splitting by setting IFS to empty value.) -IFS=" "" $as_nl" - -# Find who we are. Look in the path if we contain no directory separator. -as_myself= -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 -IFS=$as_save_IFS - - ;; -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 - $as_echo "$as_myself: error: cannot find myself; rerun with an absolute file name" >&2 - exit 1 -fi - -# Unset variables that we do not need and which cause bugs (e.g. in -# pre-3.0 UWIN ksh). But do not cause bugs in bash 2.01; the "|| exit 1" -# suppresses any "Segmentation fault" message there. '((' could -# trigger a bug in pdksh 5.2.14. -for as_var in BASH_ENV ENV MAIL MAILPATH -do eval test x\${$as_var+set} = xset \ - && ( (unset $as_var) || exit 1) >/dev/null 2>&1 && unset $as_var || : -done -PS1='$ ' -PS2='> ' -PS4='+ ' - -# NLS nuisances. -LC_ALL=C -export LC_ALL -LANGUAGE=C -export LANGUAGE - -# CDPATH. -(unset CDPATH) >/dev/null 2>&1 && unset CDPATH - -if test "x$CONFIG_SHELL" = x; then - as_bourne_compatible="if test -n \"\${ZSH_VERSION+set}\" && (emulate sh) >/dev/null 2>&1; then : - emulate sh - NULLCMD=: - # Pre-4.2 versions of Zsh do word splitting on \${1+\"\$@\"}, which - # is contrary to our usage. Disable this feature. - alias -g '\${1+\"\$@\"}'='\"\$@\"' - setopt NO_GLOB_SUBST -else - case \`(set -o) 2>/dev/null\` in #( - *posix*) : - set -o posix ;; #( - *) : - ;; -esac -fi -" - as_required="as_fn_return () { (exit \$1); } -as_fn_success () { as_fn_return 0; } -as_fn_failure () { as_fn_return 1; } -as_fn_ret_success () { return 0; } -as_fn_ret_failure () { return 1; } - -exitcode=0 -as_fn_success || { exitcode=1; echo as_fn_success failed.; } -as_fn_failure && { exitcode=1; echo as_fn_failure succeeded.; } -as_fn_ret_success || { exitcode=1; echo as_fn_ret_success failed.; } -as_fn_ret_failure && { exitcode=1; echo as_fn_ret_failure succeeded.; } -if ( set x; as_fn_ret_success y && test x = \"\$1\" ); then : - -else - exitcode=1; echo positional parameters were not saved. -fi -test x\$exitcode = x0 || exit 1" - as_suggested=" as_lineno_1=";as_suggested=$as_suggested$LINENO;as_suggested=$as_suggested" as_lineno_1a=\$LINENO - as_lineno_2=";as_suggested=$as_suggested$LINENO;as_suggested=$as_suggested" as_lineno_2a=\$LINENO - eval 'test \"x\$as_lineno_1'\$as_run'\" != \"x\$as_lineno_2'\$as_run'\" && - test \"x\`expr \$as_lineno_1'\$as_run' + 1\`\" = \"x\$as_lineno_2'\$as_run'\"' || exit 1 -test \$(( 1 + 1 )) = 2 || exit 1" - if (eval "$as_required") 2>/dev/null; then : - as_have_required=yes -else - as_have_required=no -fi - if test x$as_have_required = xyes && (eval "$as_suggested") 2>/dev/null; then : - -else - as_save_IFS=$IFS; IFS=$PATH_SEPARATOR -as_found=false -for as_dir in /bin$PATH_SEPARATOR/usr/bin$PATH_SEPARATOR$PATH -do - IFS=$as_save_IFS - test -z "$as_dir" && as_dir=. - as_found=: - case $as_dir in #( - /*) - for as_base in sh bash ksh sh5; do - # Try only shells that exist, to save several forks. - as_shell=$as_dir/$as_base - if { test -f "$as_shell" || test -f "$as_shell.exe"; } && - { $as_echo "$as_bourne_compatible""$as_required" | as_run=a "$as_shell"; } 2>/dev/null; then : - CONFIG_SHELL=$as_shell as_have_required=yes - if { $as_echo "$as_bourne_compatible""$as_suggested" | as_run=a "$as_shell"; } 2>/dev/null; then : - break 2 -fi -fi - done;; - esac - as_found=false -done -$as_found || { if { test -f "$SHELL" || test -f "$SHELL.exe"; } && - { $as_echo "$as_bourne_compatible""$as_required" | as_run=a "$SHELL"; } 2>/dev/null; then : - CONFIG_SHELL=$SHELL as_have_required=yes -fi; } -IFS=$as_save_IFS - - - if test "x$CONFIG_SHELL" != x; then : - # We cannot yet assume a decent shell, so we have to provide a - # neutralization value for shells without unset; and this also - # works around shells that cannot unset nonexistent variables. - # Preserve -v and -x to the replacement shell. - BASH_ENV=/dev/null - ENV=/dev/null - (unset BASH_ENV) >/dev/null 2>&1 && unset BASH_ENV ENV - export CONFIG_SHELL - case $- in # (((( - *v*x* | *x*v* ) as_opts=-vx ;; - *v* ) as_opts=-v ;; - *x* ) as_opts=-x ;; - * ) as_opts= ;; - esac - exec "$CONFIG_SHELL" $as_opts "$as_myself" ${1+"$@"} -fi - - if test x$as_have_required = xno; then : - $as_echo "$0: This script requires a shell more modern than all" - $as_echo "$0: the shells that I found on your system." - if test x${ZSH_VERSION+set} = xset ; then - $as_echo "$0: In particular, zsh $ZSH_VERSION has bugs and should" - $as_echo "$0: be upgraded to zsh 4.3.4 or later." - else - $as_echo "$0: Please tell bug-autoconf@gnu.org about your system, -$0: including any error possibly output before this -$0: message. Then install a modern shell, or manually run -$0: the script under such a shell if you do have one." - fi - exit 1 -fi -fi -fi -SHELL=${CONFIG_SHELL-/bin/sh} -export SHELL -# Unset more variables known to interfere with behavior of common tools. -CLICOLOR_FORCE= GREP_OPTIONS= -unset CLICOLOR_FORCE GREP_OPTIONS - -## --------------------- ## -## M4sh Shell Functions. ## -## --------------------- ## -# as_fn_unset VAR -# --------------- -# Portably unset VAR. -as_fn_unset () -{ - { eval $1=; unset $1;} -} -as_unset=as_fn_unset - -# as_fn_set_status STATUS -# ----------------------- -# Set $? to STATUS, without forking. -as_fn_set_status () -{ - return $1 -} # as_fn_set_status - -# as_fn_exit STATUS -# ----------------- -# Exit the shell with STATUS, even in a "trap 0" or "set -e" context. -as_fn_exit () -{ - set +e - as_fn_set_status $1 - exit $1 -} # as_fn_exit - -# as_fn_mkdir_p -# ------------- -# Create "$as_dir" as a directory, including parents if necessary. -as_fn_mkdir_p () -{ - - case $as_dir in #( - -*) as_dir=./$as_dir;; - esac - test -d "$as_dir" || eval $as_mkdir_p || { - as_dirs= - while :; do - case $as_dir in #( - *\'*) as_qdir=`$as_echo "$as_dir" | sed "s/'/'\\\\\\\\''/g"`;; #'( - *) as_qdir=$as_dir;; - esac - as_dirs="'$as_qdir' $as_dirs" - as_dir=`$as_dirname -- "$as_dir" || -$as_expr X"$as_dir" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ - X"$as_dir" : 'X\(//\)[^/]' \| \ - X"$as_dir" : 'X\(//\)$' \| \ - X"$as_dir" : 'X\(/\)' \| . 2>/dev/null || -$as_echo X"$as_dir" | - sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ - s//\1/ - q - } - /^X\(\/\/\)[^/].*/{ - s//\1/ - q - } - /^X\(\/\/\)$/{ - s//\1/ - q - } - /^X\(\/\).*/{ - s//\1/ - q - } - s/.*/./; q'` - test -d "$as_dir" && break - done - test -z "$as_dirs" || eval "mkdir $as_dirs" - } || test -d "$as_dir" || as_fn_error $? "cannot create directory $as_dir" - - -} # as_fn_mkdir_p -# as_fn_append VAR VALUE -# ---------------------- -# Append the text in VALUE to the end of the definition contained in VAR. Take -# advantage of any shell optimizations that allow amortized linear growth over -# repeated appends, instead of the typical quadratic growth present in naive -# implementations. -if (eval "as_var=1; as_var+=2; test x\$as_var = x12") 2>/dev/null; then : - eval 'as_fn_append () - { - eval $1+=\$2 - }' -else - as_fn_append () - { - eval $1=\$$1\$2 - } -fi # as_fn_append - -# as_fn_arith ARG... -# ------------------ -# Perform arithmetic evaluation on the ARGs, and store the result in the -# global $as_val. Take advantage of shells that can avoid forks. The arguments -# must be portable across $(()) and expr. -if (eval "test \$(( 1 + 1 )) = 2") 2>/dev/null; then : - eval 'as_fn_arith () - { - as_val=$(( $* )) - }' -else - as_fn_arith () - { - as_val=`expr "$@" || test $? -eq 1` - } -fi # as_fn_arith - - -# as_fn_error STATUS ERROR [LINENO LOG_FD] -# ---------------------------------------- -# Output "`basename $0`: error: ERROR" to stderr. If LINENO and LOG_FD are -# provided, also output the error to LOG_FD, referencing LINENO. Then exit the -# script with STATUS, using 1 if that was 0. -as_fn_error () -{ - as_status=$1; test $as_status -eq 0 && as_status=1 - if test "$4"; then - as_lineno=${as_lineno-"$3"} as_lineno_stack=as_lineno_stack=$as_lineno_stack - $as_echo "$as_me:${as_lineno-$LINENO}: error: $2" >&$4 - fi - $as_echo "$as_me: error: $2" >&2 - as_fn_exit $as_status -} # as_fn_error - -if expr a : '\(a\)' >/dev/null 2>&1 && - test "X`expr 00001 : '.*\(...\)'`" = X001; 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 - -if (as_dir=`dirname -- /` && test "X$as_dir" = X/) >/dev/null 2>&1; then - as_dirname=dirname -else - as_dirname=false -fi - -as_me=`$as_basename -- "$0" || -$as_expr X/"$0" : '.*/\([^/][^/]*\)/*$' \| \ - X"$0" : 'X\(//\)$' \| \ - X"$0" : 'X\(/\)' \| . 2>/dev/null || -$as_echo X/"$0" | - sed '/^.*\/\([^/][^/]*\)\/*$/{ - s//\1/ - q - } - /^X\/\(\/\/\)$/{ - s//\1/ - q - } - /^X\/\(\/\).*/{ - s//\1/ - q - } - s/.*/./; q'` - -# 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 - - - as_lineno_1=$LINENO as_lineno_1a=$LINENO - as_lineno_2=$LINENO as_lineno_2a=$LINENO - eval 'test "x$as_lineno_1'$as_run'" != "x$as_lineno_2'$as_run'" && - test "x`expr $as_lineno_1'$as_run' + 1`" = "x$as_lineno_2'$as_run'"' || { - # Blame Lee E. McMahon (1931-1989) for sed's syntax. :-) - sed -n ' - p - /[$]LINENO/= - ' <$as_myself | - sed ' - s/[$]LINENO.*/&-/ - t lineno - b - :lineno - N - :loop - s/[$]LINENO\([^'$as_cr_alnum'_].*\n\)\(.*\)/\2\1\2/ - t loop - s/-\n.*// - ' >$as_me.lineno && - chmod +x "$as_me.lineno" || - { $as_echo "$as_me: error: cannot create $as_me.lineno; rerun with a POSIX shell" >&2; as_fn_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 sensitive to this). - . "./$as_me.lineno" - # Exit status is that of the last command. - exit -} - -ECHO_C= ECHO_N= ECHO_T= -case `echo -n x` in #((((( --n*) - case `echo 'xy\c'` in - *c*) ECHO_T=' ';; # ECHO_T is single tab character. - xy) ECHO_C='\c';; - *) echo `echo ksh88 bug on AIX 6.1` > /dev/null - ECHO_T=' ';; - esac;; -*) - ECHO_N='-n';; -esac - -rm -f conf$$ conf$$.exe conf$$.file -if test -d conf$$.dir; then - rm -f conf$$.dir/conf$$.file -else - rm -f conf$$.dir - mkdir conf$$.dir 2>/dev/null -fi -if (echo >conf$$.file) 2>/dev/null; then - if ln -s conf$$.file conf$$ 2>/dev/null; then - as_ln_s='ln -s' - # ... but there are two gotchas: - # 1) On MSYS, both `ln -s file dir' and `ln file dir' fail. - # 2) DJGPP < 2.04 has no symlinks; `ln -s' creates a wrapper executable. - # In both cases, we have to default to `cp -p'. - ln -s conf$$.file conf$$.dir 2>/dev/null && test ! -f conf$$.exe || - as_ln_s='cp -p' - elif ln conf$$.file conf$$ 2>/dev/null; then - as_ln_s=ln - else - as_ln_s='cp -p' - fi -else - as_ln_s='cp -p' -fi -rm -f conf$$ conf$$.exe conf$$.dir/conf$$.file conf$$.file -rmdir conf$$.dir 2>/dev/null - -if mkdir -p . 2>/dev/null; then - as_mkdir_p='mkdir -p "$as_dir"' -else - test -d ./-p && rmdir ./-p - as_mkdir_p=false -fi - -if test -x / >/dev/null 2>&1; then - as_test_x='test -x' -else - if ls -dL / >/dev/null 2>&1; then - as_ls_L_option=L - else - as_ls_L_option= - fi - as_test_x=' - eval sh -c '\'' - if test -d "$1"; then - test -d "$1/."; - else - case $1 in #( - -*)set "./$1";; - esac; - case `ls -ld'$as_ls_L_option' "$1" 2>/dev/null` in #(( - ???[sx]*):;;*)false;;esac;fi - '\'' sh - ' -fi -as_executable_p=$as_test_x - -# Sed expression to map a string onto a valid CPP name. -as_tr_cpp="eval 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="eval sed 'y%*+%pp%;s%[^_$as_cr_alnum]%_%g'" - - -test -n "$DJDIR" || exec 7<&0 &1 - -# Name of the host. -# hostname on some systems (SVR3.2, old GNU/Linux) returns a bogus exit status, -# so uname gets run too. -ac_hostname=`(hostname || uname -n) 2>/dev/null | sed 1q` - -# -# Initializations. -# -ac_default_prefix=/usr/local -ac_clean_files= -ac_config_libobj_dir=. -LIBOBJS= -cross_compiling=no -subdirs= -MFLAGS= -MAKEFLAGS= - -# Identity of this package. -PACKAGE_NAME= -PACKAGE_TARNAME= -PACKAGE_VERSION= -PACKAGE_STRING= -PACKAGE_BUGREPORT= -PACKAGE_URL= - -ac_unique_file="getline.c" -# Factoring default headers for most tests. -ac_includes_default="\ -#include -#ifdef HAVE_SYS_TYPES_H -# include -#endif -#ifdef HAVE_SYS_STAT_H -# include -#endif -#ifdef STDC_HEADERS -# include -# include -#else -# ifdef HAVE_STDLIB_H -# include -# endif -#endif -#ifdef HAVE_STRING_H -# if !defined STDC_HEADERS && defined HAVE_MEMORY_H -# include -# endif -# include -#endif -#ifdef HAVE_STRINGS_H -# include -#endif -#ifdef HAVE_INTTYPES_H -# include -#endif -#ifdef HAVE_STDINT_H -# include -#endif -#ifdef HAVE_UNISTD_H -# include -#endif" - -ac_subst_vars='LTLIBOBJS -LIBOBJS -MAKE_MAN_PAGES -TARGET_LIBS -FILE_MANEXT -FILE_MANDIR -MISC_MANEXT -MISC_MANDIR -PROG_MANEXT -PROG_MANDIR -FUNC_MANEXT -FUNC_MANDIR -LIBR_MANEXT -LIBR_MANDIR -DEFS_R -LINK_SHARED -SHARED_CFLAGS -SHARED_ALT -SHARED_EXT -TARGETS -EGREP -GREP -CPP -target_os -target_vendor -target_cpu -target -host_os -host_vendor -host_cpu -host -build_os -build_vendor -build_cpu -build -LD -RANLIB -AWK -LN_S -SET_MAKE -OBJEXT -EXEEXT -ac_ct_CC -CPPFLAGS -LDFLAGS -CFLAGS -CC -MICRO_VER -MINOR_VER -MAJOR_VER -target_alias -host_alias -build_alias -LIBS -ECHO_T -ECHO_N -ECHO_C -DEFS -mandir -localedir -libdir -psdir -pdfdir -dvidir -htmldir -infodir -docdir -oldincludedir -includedir -localstatedir -sharedstatedir -sysconfdir -datadir -datarootdir -libexecdir -sbindir -bindir -program_transform_name -prefix -exec_prefix -PACKAGE_URL -PACKAGE_BUGREPORT -PACKAGE_STRING -PACKAGE_VERSION -PACKAGE_TARNAME -PACKAGE_NAME -PATH_SEPARATOR -SHELL' -ac_subst_files='' -ac_user_opts=' -enable_option_checking -with_file_actions -with_file_system -with_man_pages -' - ac_precious_vars='build_alias -host_alias -target_alias -CC -CFLAGS -LDFLAGS -LIBS -CPPFLAGS -CPP' - - -# Initialize some variables set by options. -ac_init_help= -ac_init_version=false -ac_unrecognized_opts= -ac_unrecognized_sep= -# 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. -# (The list follows the same order as the GNU Coding Standards.) -bindir='${exec_prefix}/bin' -sbindir='${exec_prefix}/sbin' -libexecdir='${exec_prefix}/libexec' -datarootdir='${prefix}/share' -datadir='${datarootdir}' -sysconfdir='${prefix}/etc' -sharedstatedir='${prefix}/com' -localstatedir='${prefix}/var' -includedir='${prefix}/include' -oldincludedir='/usr/include' -docdir='${datarootdir}/doc/${PACKAGE}' -infodir='${datarootdir}/info' -htmldir='${docdir}' -dvidir='${docdir}' -pdfdir='${docdir}' -psdir='${docdir}' -libdir='${exec_prefix}/lib' -localedir='${datarootdir}/locale' -mandir='${datarootdir}/man' - -ac_prev= -ac_dashdash= -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=`expr "X$ac_option" : '[^=]*=\(.*\)'` ;; - *=) ac_optarg= ;; - *) ac_optarg=yes ;; - esac - - # Accept the important Cygnus configure options, so we can diagnose typos. - - case $ac_dashdash$ac_option in - --) - ac_dashdash=yes ;; - - -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) - ac_prev=datadir ;; - -datadir=* | --datadir=* | --datadi=* | --datad=*) - datadir=$ac_optarg ;; - - -datarootdir | --datarootdir | --datarootdi | --datarootd | --dataroot \ - | --dataroo | --dataro | --datar) - ac_prev=datarootdir ;; - -datarootdir=* | --datarootdir=* | --datarootdi=* | --datarootd=* \ - | --dataroot=* | --dataroo=* | --dataro=* | --datar=*) - datarootdir=$ac_optarg ;; - - -disable-* | --disable-*) - ac_useropt=`expr "x$ac_option" : 'x-*disable-\(.*\)'` - # Reject names that are not valid shell variable names. - expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null && - as_fn_error $? "invalid feature name: $ac_useropt" - ac_useropt_orig=$ac_useropt - ac_useropt=`$as_echo "$ac_useropt" | sed 's/[-+.]/_/g'` - case $ac_user_opts in - *" -"enable_$ac_useropt" -"*) ;; - *) ac_unrecognized_opts="$ac_unrecognized_opts$ac_unrecognized_sep--disable-$ac_useropt_orig" - ac_unrecognized_sep=', ';; - esac - eval enable_$ac_useropt=no ;; - - -docdir | --docdir | --docdi | --doc | --do) - ac_prev=docdir ;; - -docdir=* | --docdir=* | --docdi=* | --doc=* | --do=*) - docdir=$ac_optarg ;; - - -dvidir | --dvidir | --dvidi | --dvid | --dvi | --dv) - ac_prev=dvidir ;; - -dvidir=* | --dvidir=* | --dvidi=* | --dvid=* | --dvi=* | --dv=*) - dvidir=$ac_optarg ;; - - -enable-* | --enable-*) - ac_useropt=`expr "x$ac_option" : 'x-*enable-\([^=]*\)'` - # Reject names that are not valid shell variable names. - expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null && - as_fn_error $? "invalid feature name: $ac_useropt" - ac_useropt_orig=$ac_useropt - ac_useropt=`$as_echo "$ac_useropt" | sed 's/[-+.]/_/g'` - case $ac_user_opts in - *" -"enable_$ac_useropt" -"*) ;; - *) ac_unrecognized_opts="$ac_unrecognized_opts$ac_unrecognized_sep--enable-$ac_useropt_orig" - ac_unrecognized_sep=', ';; - esac - eval enable_$ac_useropt=\$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 ;; - - -htmldir | --htmldir | --htmldi | --htmld | --html | --htm | --ht) - ac_prev=htmldir ;; - -htmldir=* | --htmldir=* | --htmldi=* | --htmld=* | --html=* | --htm=* \ - | --ht=*) - htmldir=$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 ;; - - -localedir | --localedir | --localedi | --localed | --locale) - ac_prev=localedir ;; - -localedir=* | --localedir=* | --localedi=* | --localed=* | --locale=*) - localedir=$ac_optarg ;; - - -localstatedir | --localstatedir | --localstatedi | --localstated \ - | --localstate | --localstat | --localsta | --localst | --locals) - ac_prev=localstatedir ;; - -localstatedir=* | --localstatedir=* | --localstatedi=* | --localstated=* \ - | --localstate=* | --localstat=* | --localsta=* | --localst=* | --locals=*) - 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 ;; - - -pdfdir | --pdfdir | --pdfdi | --pdfd | --pdf | --pd) - ac_prev=pdfdir ;; - -pdfdir=* | --pdfdir=* | --pdfdi=* | --pdfd=* | --pdf=* | --pd=*) - pdfdir=$ac_optarg ;; - - -psdir | --psdir | --psdi | --psd | --ps) - ac_prev=psdir ;; - -psdir=* | --psdir=* | --psdi=* | --psd=* | --ps=*) - psdir=$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_useropt=`expr "x$ac_option" : 'x-*with-\([^=]*\)'` - # Reject names that are not valid shell variable names. - expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null && - as_fn_error $? "invalid package name: $ac_useropt" - ac_useropt_orig=$ac_useropt - ac_useropt=`$as_echo "$ac_useropt" | sed 's/[-+.]/_/g'` - case $ac_user_opts in - *" -"with_$ac_useropt" -"*) ;; - *) ac_unrecognized_opts="$ac_unrecognized_opts$ac_unrecognized_sep--with-$ac_useropt_orig" - ac_unrecognized_sep=', ';; - esac - eval with_$ac_useropt=\$ac_optarg ;; - - -without-* | --without-*) - ac_useropt=`expr "x$ac_option" : 'x-*without-\(.*\)'` - # Reject names that are not valid shell variable names. - expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null && - as_fn_error $? "invalid package name: $ac_useropt" - ac_useropt_orig=$ac_useropt - ac_useropt=`$as_echo "$ac_useropt" | sed 's/[-+.]/_/g'` - case $ac_user_opts in - *" -"with_$ac_useropt" -"*) ;; - *) ac_unrecognized_opts="$ac_unrecognized_opts$ac_unrecognized_sep--without-$ac_useropt_orig" - ac_unrecognized_sep=', ';; - esac - eval with_$ac_useropt=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 ;; - - -*) as_fn_error $? "unrecognized option: \`$ac_option' -Try \`$0 --help' for more information" - ;; - - *=*) - ac_envvar=`expr "x$ac_option" : 'x\([^=]*\)='` - # Reject names that are not valid shell variable names. - case $ac_envvar in #( - '' | [0-9]* | *[!_$as_cr_alnum]* ) - as_fn_error $? "invalid variable name: \`$ac_envvar'" ;; - esac - eval $ac_envvar=\$ac_optarg - export $ac_envvar ;; - - *) - # FIXME: should be removed in autoconf 3.0. - $as_echo "$as_me: WARNING: you should use --build, --host, --target" >&2 - expr "x$ac_option" : ".*[^-._$as_cr_alnum]" >/dev/null && - $as_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'` - as_fn_error $? "missing argument to $ac_option" -fi - -if test -n "$ac_unrecognized_opts"; then - case $enable_option_checking in - no) ;; - fatal) as_fn_error $? "unrecognized options: $ac_unrecognized_opts" ;; - *) $as_echo "$as_me: WARNING: unrecognized options: $ac_unrecognized_opts" >&2 ;; - esac -fi - -# Check all directory arguments for consistency. -for ac_var in exec_prefix prefix bindir sbindir libexecdir datarootdir \ - datadir sysconfdir sharedstatedir localstatedir includedir \ - oldincludedir docdir infodir htmldir dvidir pdfdir psdir \ - libdir localedir mandir -do - eval ac_val=\$$ac_var - # Remove trailing slashes. - case $ac_val in - */ ) - ac_val=`expr "X$ac_val" : 'X\(.*[^/]\)' \| "X$ac_val" : 'X\(.*\)'` - eval $ac_var=\$ac_val;; - esac - # Be sure to have absolute directory names. - case $ac_val in - [\\/$]* | ?:[\\/]* ) continue;; - NONE | '' ) case $ac_var in *prefix ) continue;; esac;; - esac - as_fn_error $? "expected an absolute directory name for --$ac_var: $ac_val" -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 - $as_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 - - -ac_pwd=`pwd` && test -n "$ac_pwd" && -ac_ls_di=`ls -di .` && -ac_pwd_ls_di=`cd "$ac_pwd" && ls -di .` || - as_fn_error $? "working directory cannot be determined" -test "X$ac_ls_di" = "X$ac_pwd_ls_di" || - as_fn_error $? "pwd does not report name of working directory" - - -# 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 the parent directory. - ac_confdir=`$as_dirname -- "$as_myself" || -$as_expr X"$as_myself" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ - X"$as_myself" : 'X\(//\)[^/]' \| \ - X"$as_myself" : 'X\(//\)$' \| \ - X"$as_myself" : 'X\(/\)' \| . 2>/dev/null || -$as_echo X"$as_myself" | - 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 - test "$ac_srcdir_defaulted" = yes && srcdir="$ac_confdir or .." - as_fn_error $? "cannot find sources ($ac_unique_file) in $srcdir" -fi -ac_msg="sources are in $srcdir, but \`cd $srcdir' does not work" -ac_abs_confdir=`( - cd "$srcdir" && test -r "./$ac_unique_file" || as_fn_error $? "$ac_msg" - pwd)` -# When building in place, set srcdir=. -if test "$ac_abs_confdir" = "$ac_pwd"; then - srcdir=. -fi -# Remove unnecessary trailing slashes from srcdir. -# Double slashes in file names in object file debugging info -# mess up M-x gdb in Emacs. -case $srcdir in -*/) srcdir=`expr "X$srcdir" : 'X\(.*[^/]\)' \| "X$srcdir" : 'X\(.*\)'`;; -esac -for ac_var in $ac_precious_vars; do - eval ac_env_${ac_var}_set=\${${ac_var}+set} - eval ac_env_${ac_var}_value=\$${ac_var} - eval ac_cv_env_${ac_var}_set=\${${ac_var}+set} - eval ac_cv_env_${ac_var}_value=\$${ac_var} -done - -# -# 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 \`..'] - -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] - --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] - --datarootdir=DIR read-only arch.-independent data root [PREFIX/share] - --datadir=DIR read-only architecture-independent data [DATAROOTDIR] - --infodir=DIR info documentation [DATAROOTDIR/info] - --localedir=DIR locale-dependent data [DATAROOTDIR/locale] - --mandir=DIR man documentation [DATAROOTDIR/man] - --docdir=DIR documentation root [DATAROOTDIR/doc/PACKAGE] - --htmldir=DIR html documentation [DOCDIR] - --dvidir=DIR dvi documentation [DOCDIR] - --pdfdir=DIR pdf documentation [DOCDIR] - --psdir=DIR ps documentation [DOCDIR] -_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 - LIBS libraries to pass to the linker, e.g. -l - CPPFLAGS (Objective) 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. - -Report bugs to the package provider. -_ACEOF -ac_status=$? -fi - -if test "$ac_init_help" = "recursive"; then - # If there are subdirs, report their specific --help. - for ac_dir in : $ac_subdirs_all; do test "x$ac_dir" = x: && continue - test -d "$ac_dir" || - { cd "$srcdir" && ac_pwd=`pwd` && srcdir=. && test -d "$ac_dir"; } || - continue - ac_builddir=. - -case "$ac_dir" in -.) ac_dir_suffix= ac_top_builddir_sub=. ac_top_build_prefix= ;; -*) - ac_dir_suffix=/`$as_echo "$ac_dir" | sed 's|^\.[\\/]||'` - # A ".." for each directory in $ac_dir_suffix. - ac_top_builddir_sub=`$as_echo "$ac_dir_suffix" | sed 's|/[^\\/]*|/..|g;s|/||'` - case $ac_top_builddir_sub in - "") ac_top_builddir_sub=. ac_top_build_prefix= ;; - *) ac_top_build_prefix=$ac_top_builddir_sub/ ;; - esac ;; -esac -ac_abs_top_builddir=$ac_pwd -ac_abs_builddir=$ac_pwd$ac_dir_suffix -# for backward compatibility: -ac_top_builddir=$ac_top_build_prefix - -case $srcdir in - .) # We are building in place. - ac_srcdir=. - ac_top_srcdir=$ac_top_builddir_sub - ac_abs_top_srcdir=$ac_pwd ;; - [\\/]* | ?:[\\/]* ) # Absolute name. - ac_srcdir=$srcdir$ac_dir_suffix; - ac_top_srcdir=$srcdir - ac_abs_top_srcdir=$srcdir ;; - *) # Relative name. - ac_srcdir=$ac_top_build_prefix$srcdir$ac_dir_suffix - ac_top_srcdir=$ac_top_build_prefix$srcdir - ac_abs_top_srcdir=$ac_pwd/$srcdir ;; -esac -ac_abs_srcdir=$ac_abs_top_srcdir$ac_dir_suffix - - cd "$ac_dir" || { ac_status=$?; continue; } - # Check for guested 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 - else - $as_echo "$as_me: WARNING: no configuration information is in $ac_dir" >&2 - fi || ac_status=$? - cd "$ac_pwd" || { ac_status=$?; break; } - done -fi - -test -n "$ac_init_help" && exit $ac_status -if $ac_init_version; then - cat <<\_ACEOF -configure -generated by GNU Autoconf 2.68 - -Copyright (C) 2010 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 -fi - -## ------------------------ ## -## Autoconf initialization. ## -## ------------------------ ## - -# ac_fn_c_try_compile LINENO -# -------------------------- -# Try to compile conftest.$ac_ext, and return whether this succeeded. -ac_fn_c_try_compile () -{ - as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack - rm -f conftest.$ac_objext - if { { ac_try="$ac_compile" -case "(($ac_try" in - *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; - *) ac_try_echo=$ac_try;; -esac -eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" -$as_echo "$ac_try_echo"; } >&5 - (eval "$ac_compile") 2>conftest.err - ac_status=$? - if test -s conftest.err; then - grep -v '^ *+' conftest.err >conftest.er1 - cat conftest.er1 >&5 - mv -f conftest.er1 conftest.err - fi - $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 - test $ac_status = 0; } && { - test -z "$ac_c_werror_flag" || - test ! -s conftest.err - } && test -s conftest.$ac_objext; then : - ac_retval=0 -else - $as_echo "$as_me: failed program was:" >&5 -sed 's/^/| /' conftest.$ac_ext >&5 - - ac_retval=1 -fi - eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno - as_fn_set_status $ac_retval - -} # ac_fn_c_try_compile - -# ac_fn_c_try_link LINENO -# ----------------------- -# Try to link conftest.$ac_ext, and return whether this succeeded. -ac_fn_c_try_link () -{ - as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack - rm -f conftest.$ac_objext conftest$ac_exeext - if { { ac_try="$ac_link" -case "(($ac_try" in - *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; - *) ac_try_echo=$ac_try;; -esac -eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" -$as_echo "$ac_try_echo"; } >&5 - (eval "$ac_link") 2>conftest.err - ac_status=$? - if test -s conftest.err; then - grep -v '^ *+' conftest.err >conftest.er1 - cat conftest.er1 >&5 - mv -f conftest.er1 conftest.err - fi - $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 - test $ac_status = 0; } && { - test -z "$ac_c_werror_flag" || - test ! -s conftest.err - } && test -s conftest$ac_exeext && { - test "$cross_compiling" = yes || - $as_test_x conftest$ac_exeext - }; then : - ac_retval=0 -else - $as_echo "$as_me: failed program was:" >&5 -sed 's/^/| /' conftest.$ac_ext >&5 - - ac_retval=1 -fi - # Delete the IPA/IPO (Inter Procedural Analysis/Optimization) information - # created by the PGI compiler (conftest_ipa8_conftest.oo), as it would - # interfere with the next link command; also delete a directory that is - # left behind by Apple's compiler. We do this before executing the actions. - rm -rf conftest.dSYM conftest_ipa8_conftest.oo - eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno - as_fn_set_status $ac_retval - -} # ac_fn_c_try_link - -# ac_fn_c_try_cpp LINENO -# ---------------------- -# Try to preprocess conftest.$ac_ext, and return whether this succeeded. -ac_fn_c_try_cpp () -{ - as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack - if { { ac_try="$ac_cpp conftest.$ac_ext" -case "(($ac_try" in - *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; - *) ac_try_echo=$ac_try;; -esac -eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" -$as_echo "$ac_try_echo"; } >&5 - (eval "$ac_cpp conftest.$ac_ext") 2>conftest.err - ac_status=$? - if test -s conftest.err; then - grep -v '^ *+' conftest.err >conftest.er1 - cat conftest.er1 >&5 - mv -f conftest.er1 conftest.err - fi - $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 - test $ac_status = 0; } > conftest.i && { - test -z "$ac_c_preproc_warn_flag$ac_c_werror_flag" || - test ! -s conftest.err - }; then : - ac_retval=0 -else - $as_echo "$as_me: failed program was:" >&5 -sed 's/^/| /' conftest.$ac_ext >&5 - - ac_retval=1 -fi - eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno - as_fn_set_status $ac_retval - -} # ac_fn_c_try_cpp - -# ac_fn_c_check_header_mongrel LINENO HEADER VAR INCLUDES -# ------------------------------------------------------- -# Tests whether HEADER exists, giving a warning if it cannot be compiled using -# the include files in INCLUDES and setting the cache variable VAR -# accordingly. -ac_fn_c_check_header_mongrel () -{ - as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack - if eval \${$3+:} false; then : - { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $2" >&5 -$as_echo_n "checking for $2... " >&6; } -if eval \${$3+:} false; then : - $as_echo_n "(cached) " >&6 -fi -eval ac_res=\$$3 - { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 -$as_echo "$ac_res" >&6; } -else - # Is the header compilable? -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking $2 usability" >&5 -$as_echo_n "checking $2 usability... " >&6; } -cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ -$4 -#include <$2> -_ACEOF -if ac_fn_c_try_compile "$LINENO"; then : - ac_header_compiler=yes -else - ac_header_compiler=no -fi -rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext -{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_header_compiler" >&5 -$as_echo "$ac_header_compiler" >&6; } - -# Is the header present? -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking $2 presence" >&5 -$as_echo_n "checking $2 presence... " >&6; } -cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ -#include <$2> -_ACEOF -if ac_fn_c_try_cpp "$LINENO"; then : - ac_header_preproc=yes -else - ac_header_preproc=no -fi -rm -f conftest.err conftest.i conftest.$ac_ext -{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_header_preproc" >&5 -$as_echo "$ac_header_preproc" >&6; } - -# So? What about this header? -case $ac_header_compiler:$ac_header_preproc:$ac_c_preproc_warn_flag in #(( - yes:no: ) - { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: accepted by the compiler, rejected by the preprocessor!" >&5 -$as_echo "$as_me: WARNING: $2: accepted by the compiler, rejected by the preprocessor!" >&2;} - { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: proceeding with the compiler's result" >&5 -$as_echo "$as_me: WARNING: $2: proceeding with the compiler's result" >&2;} - ;; - no:yes:* ) - { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: present but cannot be compiled" >&5 -$as_echo "$as_me: WARNING: $2: present but cannot be compiled" >&2;} - { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: check for missing prerequisite headers?" >&5 -$as_echo "$as_me: WARNING: $2: check for missing prerequisite headers?" >&2;} - { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: see the Autoconf documentation" >&5 -$as_echo "$as_me: WARNING: $2: see the Autoconf documentation" >&2;} - { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: section \"Present But Cannot Be Compiled\"" >&5 -$as_echo "$as_me: WARNING: $2: section \"Present But Cannot Be Compiled\"" >&2;} - { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: proceeding with the compiler's result" >&5 -$as_echo "$as_me: WARNING: $2: proceeding with the compiler's result" >&2;} - ;; -esac - { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $2" >&5 -$as_echo_n "checking for $2... " >&6; } -if eval \${$3+:} false; then : - $as_echo_n "(cached) " >&6 -else - eval "$3=\$ac_header_compiler" -fi -eval ac_res=\$$3 - { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 -$as_echo "$ac_res" >&6; } -fi - eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno - -} # ac_fn_c_check_header_mongrel - -# ac_fn_c_try_run LINENO -# ---------------------- -# Try to link conftest.$ac_ext, and return whether this succeeded. Assumes -# that executables *can* be run. -ac_fn_c_try_run () -{ - as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack - if { { ac_try="$ac_link" -case "(($ac_try" in - *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; - *) ac_try_echo=$ac_try;; -esac -eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" -$as_echo "$ac_try_echo"; } >&5 - (eval "$ac_link") 2>&5 - ac_status=$? - $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 - test $ac_status = 0; } && { ac_try='./conftest$ac_exeext' - { { case "(($ac_try" in - *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; - *) ac_try_echo=$ac_try;; -esac -eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" -$as_echo "$ac_try_echo"; } >&5 - (eval "$ac_try") 2>&5 - ac_status=$? - $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 - test $ac_status = 0; }; }; then : - ac_retval=0 -else - $as_echo "$as_me: program exited with status $ac_status" >&5 - $as_echo "$as_me: failed program was:" >&5 -sed 's/^/| /' conftest.$ac_ext >&5 - - ac_retval=$ac_status -fi - rm -rf conftest.dSYM conftest_ipa8_conftest.oo - eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno - as_fn_set_status $ac_retval - -} # ac_fn_c_try_run - -# ac_fn_c_check_header_compile LINENO HEADER VAR INCLUDES -# ------------------------------------------------------- -# Tests whether HEADER exists and can be compiled using the include files in -# INCLUDES, setting the cache variable VAR accordingly. -ac_fn_c_check_header_compile () -{ - as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack - { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $2" >&5 -$as_echo_n "checking for $2... " >&6; } -if eval \${$3+:} false; then : - $as_echo_n "(cached) " >&6 -else - cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ -$4 -#include <$2> -_ACEOF -if ac_fn_c_try_compile "$LINENO"; then : - eval "$3=yes" -else - eval "$3=no" -fi -rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext -fi -eval ac_res=\$$3 - { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 -$as_echo "$ac_res" >&6; } - eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno - -} # ac_fn_c_check_header_compile -cat >config.log <<_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.68. Invocation command line was - - $ $0 $@ - -_ACEOF -exec 5>>config.log -{ -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` -/usr/bin/hostinfo = `(/usr/bin/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=. - $as_echo "PATH: $as_dir" - done -IFS=$as_save_IFS - -} >&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_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=`$as_echo "$ac_arg" | sed "s/'/'\\\\\\\\''/g"` ;; - esac - case $ac_pass in - 1) as_fn_append ac_configure_args0 " '$ac_arg'" ;; - 2) - as_fn_append 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 - as_fn_append ac_configure_args " '$ac_arg'" - ;; - esac - done -done -{ ac_configure_args0=; unset ac_configure_args0;} -{ ac_configure_args1=; unset 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: Use '\'' to represent an apostrophe within the trap. -# WARNING: Do not start the trap code with a newline, due to a FreeBSD 4.0 bug. -trap 'exit_status=$? - # Save into config.log some information that might help in debugging. - { - echo - - $as_echo "## ---------------- ## -## Cache variables. ## -## ---------------- ##" - echo - # The following way of writing the cache mishandles newlines in values, -( - for ac_var in `(set) 2>&1 | sed -n '\''s/^\([a-zA-Z_][a-zA-Z0-9_]*\)=.*/\1/p'\''`; do - eval ac_val=\$$ac_var - case $ac_val in #( - *${as_nl}*) - case $ac_var in #( - *_cv_*) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: cache variable $ac_var contains a newline" >&5 -$as_echo "$as_me: WARNING: cache variable $ac_var contains a newline" >&2;} ;; - esac - case $ac_var in #( - _ | IFS | as_nl) ;; #( - BASH_ARGV | BASH_SOURCE) eval $ac_var= ;; #( - *) { eval $ac_var=; unset $ac_var;} ;; - esac ;; - esac - done - (set) 2>&1 | - case $as_nl`(ac_space='\'' '\''; set) 2>&1` in #( - *${as_nl}ac_space=\ *) - sed -n \ - "s/'\''/'\''\\\\'\'''\''/g; - s/^\\([_$as_cr_alnum]*_cv_[_$as_cr_alnum]*\\)=\\(.*\\)/\\1='\''\\2'\''/p" - ;; #( - *) - sed -n "/^[_$as_cr_alnum]*_cv_[_$as_cr_alnum]*=/p" - ;; - esac | - sort -) - echo - - $as_echo "## ----------------- ## -## Output variables. ## -## ----------------- ##" - echo - for ac_var in $ac_subst_vars - do - eval ac_val=\$$ac_var - case $ac_val in - *\'\''*) ac_val=`$as_echo "$ac_val" | sed "s/'\''/'\''\\\\\\\\'\'''\''/g"`;; - esac - $as_echo "$ac_var='\''$ac_val'\''" - done | sort - echo - - if test -n "$ac_subst_files"; then - $as_echo "## ------------------- ## -## File substitutions. ## -## ------------------- ##" - echo - for ac_var in $ac_subst_files - do - eval ac_val=\$$ac_var - case $ac_val in - *\'\''*) ac_val=`$as_echo "$ac_val" | sed "s/'\''/'\''\\\\\\\\'\'''\''/g"`;; - esac - $as_echo "$ac_var='\''$ac_val'\''" - done | sort - echo - fi - - if test -s confdefs.h; then - $as_echo "## ----------- ## -## confdefs.h. ## -## ----------- ##" - echo - cat confdefs.h - echo - fi - test "$ac_signal" != 0 && - $as_echo "$as_me: caught signal $ac_signal" - $as_echo "$as_me: exit $exit_status" - } >&5 - rm -f core *.core core.conftest.* && - rm -f -r conftest* confdefs* conf$$* $ac_clean_files && - exit $exit_status -' 0 -for ac_signal in 1 2 13 15; do - trap 'ac_signal='$ac_signal'; as_fn_exit 1' $ac_signal -done -ac_signal=0 - -# confdefs.h avoids OS command line length limits that DEFS can exceed. -rm -f -r conftest* confdefs.h - -$as_echo "/* confdefs.h */" > 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 - -cat >>confdefs.h <<_ACEOF -#define PACKAGE_URL "$PACKAGE_URL" -_ACEOF - - -# Let the site file select an alternate cache file if it wants to. -# Prefer an explicitly selected file to automatically selected ones. -ac_site_file1=NONE -ac_site_file2=NONE -if test -n "$CONFIG_SITE"; then - # We do not want a PATH search for config.site. - case $CONFIG_SITE in #(( - -*) ac_site_file1=./$CONFIG_SITE;; - */*) ac_site_file1=$CONFIG_SITE;; - *) ac_site_file1=./$CONFIG_SITE;; - esac -elif test "x$prefix" != xNONE; then - ac_site_file1=$prefix/share/config.site - ac_site_file2=$prefix/etc/config.site -else - ac_site_file1=$ac_default_prefix/share/config.site - ac_site_file2=$ac_default_prefix/etc/config.site -fi -for ac_site_file in "$ac_site_file1" "$ac_site_file2" -do - test "x$ac_site_file" = xNONE && continue - if test /dev/null != "$ac_site_file" && test -r "$ac_site_file"; then - { $as_echo "$as_me:${as_lineno-$LINENO}: loading site script $ac_site_file" >&5 -$as_echo "$as_me: loading site script $ac_site_file" >&6;} - sed 's/^/| /' "$ac_site_file" >&5 - . "$ac_site_file" \ - || { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 -$as_echo "$as_me: error: in \`$ac_pwd':" >&2;} -as_fn_error $? "failed to load site script $ac_site_file -See \`config.log' for more details" "$LINENO" 5; } - 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. DJGPP emulates it as a regular file. - if test /dev/null != "$cache_file" && test -f "$cache_file"; then - { $as_echo "$as_me:${as_lineno-$LINENO}: loading cache $cache_file" >&5 -$as_echo "$as_me: loading cache $cache_file" >&6;} - case $cache_file in - [\\/]* | ?:[\\/]* ) . "$cache_file";; - *) . "./$cache_file";; - esac - fi -else - { $as_echo "$as_me:${as_lineno-$LINENO}: creating cache $cache_file" >&5 -$as_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 $ac_precious_vars; 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,) - { $as_echo "$as_me:${as_lineno-$LINENO}: error: \`$ac_var' was set to \`$ac_old_val' in the previous run" >&5 -$as_echo "$as_me: error: \`$ac_var' was set to \`$ac_old_val' in the previous run" >&2;} - ac_cache_corrupted=: ;; - ,set) - { $as_echo "$as_me:${as_lineno-$LINENO}: error: \`$ac_var' was not set in the previous run" >&5 -$as_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 - # differences in whitespace do not lead to failure. - ac_old_val_w=`echo x $ac_old_val` - ac_new_val_w=`echo x $ac_new_val` - if test "$ac_old_val_w" != "$ac_new_val_w"; then - { $as_echo "$as_me:${as_lineno-$LINENO}: error: \`$ac_var' has changed since the previous run:" >&5 -$as_echo "$as_me: error: \`$ac_var' has changed since the previous run:" >&2;} - ac_cache_corrupted=: - else - { $as_echo "$as_me:${as_lineno-$LINENO}: warning: ignoring whitespace changes in \`$ac_var' since the previous run:" >&5 -$as_echo "$as_me: warning: ignoring whitespace changes in \`$ac_var' since the previous run:" >&2;} - eval $ac_var=\$ac_old_val - fi - { $as_echo "$as_me:${as_lineno-$LINENO}: former value: \`$ac_old_val'" >&5 -$as_echo "$as_me: former value: \`$ac_old_val'" >&2;} - { $as_echo "$as_me:${as_lineno-$LINENO}: current value: \`$ac_new_val'" >&5 -$as_echo "$as_me: current value: \`$ac_new_val'" >&2;} - fi;; - esac - # Pass precious variables to config.status. - if test "$ac_new_set" = set; then - case $ac_new_val in - *\'*) ac_arg=$ac_var=`$as_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. - *) as_fn_append ac_configure_args " '$ac_arg'" ;; - esac - fi -done -if $ac_cache_corrupted; then - { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 -$as_echo "$as_me: error: in \`$ac_pwd':" >&2;} - { $as_echo "$as_me:${as_lineno-$LINENO}: error: changes in the environment can compromise the build" >&5 -$as_echo "$as_me: error: changes in the environment can compromise the build" >&2;} - as_fn_error $? "run \`make distclean' and/or \`rm $cache_file' and start over" "$LINENO" 5 -fi -## -------------------- ## -## Main body of script. ## -## -------------------- ## - -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="3" - - -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 -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 -$as_echo_n "checking for $ac_word... " >&6; } -if ${ac_cv_prog_CC+:} false; then : - $as_echo_n "(cached) " >&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 { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then - ac_cv_prog_CC="${ac_tool_prefix}gcc" - $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 - break 2 - fi -done - done -IFS=$as_save_IFS - -fi -fi -CC=$ac_cv_prog_CC -if test -n "$CC"; then - { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CC" >&5 -$as_echo "$CC" >&6; } -else - { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 -$as_echo "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 -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 -$as_echo_n "checking for $ac_word... " >&6; } -if ${ac_cv_prog_ac_ct_CC+:} false; then : - $as_echo_n "(cached) " >&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 { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then - ac_cv_prog_ac_ct_CC="gcc" - $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 - break 2 - fi -done - done -IFS=$as_save_IFS - -fi -fi -ac_ct_CC=$ac_cv_prog_ac_ct_CC -if test -n "$ac_ct_CC"; then - { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_CC" >&5 -$as_echo "$ac_ct_CC" >&6; } -else - { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 -$as_echo "no" >&6; } -fi - - if test "x$ac_ct_CC" = x; then - CC="" - else - case $cross_compiling:$ac_tool_warned in -yes:) -{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 -$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} -ac_tool_warned=yes ;; -esac - CC=$ac_ct_CC - fi -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 -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 -$as_echo_n "checking for $ac_word... " >&6; } -if ${ac_cv_prog_CC+:} false; then : - $as_echo_n "(cached) " >&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 { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then - ac_cv_prog_CC="${ac_tool_prefix}cc" - $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 - break 2 - fi -done - done -IFS=$as_save_IFS - -fi -fi -CC=$ac_cv_prog_CC -if test -n "$CC"; then - { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CC" >&5 -$as_echo "$CC" >&6; } -else - { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 -$as_echo "no" >&6; } -fi - - - 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 -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 -$as_echo_n "checking for $ac_word... " >&6; } -if ${ac_cv_prog_CC+:} false; then : - $as_echo_n "(cached) " >&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 { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$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" - $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 - break 2 - fi -done - done -IFS=$as_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 $# != 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 - { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CC" >&5 -$as_echo "$CC" >&6; } -else - { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 -$as_echo "no" >&6; } -fi - - -fi -if test -z "$CC"; then - if test -n "$ac_tool_prefix"; then - for ac_prog in cl.exe - 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 -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 -$as_echo_n "checking for $ac_word... " >&6; } -if ${ac_cv_prog_CC+:} false; then : - $as_echo_n "(cached) " >&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 { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then - ac_cv_prog_CC="$ac_tool_prefix$ac_prog" - $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 - break 2 - fi -done - done -IFS=$as_save_IFS - -fi -fi -CC=$ac_cv_prog_CC -if test -n "$CC"; then - { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CC" >&5 -$as_echo "$CC" >&6; } -else - { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 -$as_echo "no" >&6; } -fi - - - test -n "$CC" && break - done -fi -if test -z "$CC"; then - ac_ct_CC=$CC - for ac_prog in cl.exe -do - # Extract the first word of "$ac_prog", so it can be a program name with args. -set dummy $ac_prog; ac_word=$2 -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 -$as_echo_n "checking for $ac_word... " >&6; } -if ${ac_cv_prog_ac_ct_CC+:} false; then : - $as_echo_n "(cached) " >&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 { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then - ac_cv_prog_ac_ct_CC="$ac_prog" - $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 - break 2 - fi -done - done -IFS=$as_save_IFS - -fi -fi -ac_ct_CC=$ac_cv_prog_ac_ct_CC -if test -n "$ac_ct_CC"; then - { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_CC" >&5 -$as_echo "$ac_ct_CC" >&6; } -else - { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 -$as_echo "no" >&6; } -fi - - - test -n "$ac_ct_CC" && break -done - - if test "x$ac_ct_CC" = x; then - CC="" - else - case $cross_compiling:$ac_tool_warned in -yes:) -{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 -$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} -ac_tool_warned=yes ;; -esac - CC=$ac_ct_CC - fi -fi - -fi - - -test -z "$CC" && { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 -$as_echo "$as_me: error: in \`$ac_pwd':" >&2;} -as_fn_error $? "no acceptable C compiler found in \$PATH -See \`config.log' for more details" "$LINENO" 5; } - -# Provide some information about the compiler. -$as_echo "$as_me:${as_lineno-$LINENO}: checking for C compiler version" >&5 -set X $ac_compile -ac_compiler=$2 -for ac_option in --version -v -V -qversion; do - { { ac_try="$ac_compiler $ac_option >&5" -case "(($ac_try" in - *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; - *) ac_try_echo=$ac_try;; -esac -eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" -$as_echo "$ac_try_echo"; } >&5 - (eval "$ac_compiler $ac_option >&5") 2>conftest.err - ac_status=$? - if test -s conftest.err; then - sed '10a\ -... rest of stderr output deleted ... - 10q' conftest.err >conftest.er1 - cat conftest.er1 >&5 - fi - rm -f conftest.er1 conftest.err - $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 - test $ac_status = 0; } -done - -cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ - -int -main () -{ - - ; - return 0; -} -_ACEOF -ac_clean_files_save=$ac_clean_files -ac_clean_files="$ac_clean_files a.out a.out.dSYM 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. -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether the C compiler works" >&5 -$as_echo_n "checking whether the C compiler works... " >&6; } -ac_link_default=`$as_echo "$ac_link" | sed 's/ -o *conftest[^ ]*//'` - -# The possible output files: -ac_files="a.out conftest.exe conftest a.exe a_out.exe b.out conftest.*" - -ac_rmfiles= -for ac_file in $ac_files -do - case $ac_file in - *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.dSYM | *.o | *.obj ) ;; - * ) ac_rmfiles="$ac_rmfiles $ac_file";; - esac -done -rm -f $ac_rmfiles - -if { { ac_try="$ac_link_default" -case "(($ac_try" in - *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; - *) ac_try_echo=$ac_try;; -esac -eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" -$as_echo "$ac_try_echo"; } >&5 - (eval "$ac_link_default") 2>&5 - ac_status=$? - $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 - test $ac_status = 0; }; then : - # Autoconf-2.13 could set the ac_cv_exeext variable to `no'. -# So ignore a value of `no', otherwise this would lead to `EXEEXT = no' -# in a Makefile. We should not override ac_cv_exeext if it was cached, -# so that the user can short-circuit this test for compilers unknown to -# Autoconf. -for ac_file in $ac_files '' -do - test -f "$ac_file" || continue - case $ac_file in - *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.dSYM | *.o | *.obj ) - ;; - [ab].out ) - # We found the default executable, but exeext='' is most - # certainly right. - break;; - *.* ) - if test "${ac_cv_exeext+set}" = set && test "$ac_cv_exeext" != no; - then :; else - ac_cv_exeext=`expr "$ac_file" : '[^.]*\(\..*\)'` - fi - # We set ac_cv_exeext here because the later test for it is not - # safe: cross compilers may not add the suffix if given an `-o' - # argument, so we may need to know it at that point already. - # Even if this section looks crufty: it has the advantage of - # actually working. - break;; - * ) - break;; - esac -done -test "$ac_cv_exeext" = no && ac_cv_exeext= - -else - ac_file='' -fi -if test -z "$ac_file"; then : - { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 -$as_echo "no" >&6; } -$as_echo "$as_me: failed program was:" >&5 -sed 's/^/| /' conftest.$ac_ext >&5 - -{ { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 -$as_echo "$as_me: error: in \`$ac_pwd':" >&2;} -as_fn_error 77 "C compiler cannot create executables -See \`config.log' for more details" "$LINENO" 5; } -else - { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 -$as_echo "yes" >&6; } -fi -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for C compiler default output file name" >&5 -$as_echo_n "checking for C compiler default output file name... " >&6; } -{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_file" >&5 -$as_echo "$ac_file" >&6; } -ac_exeext=$ac_cv_exeext - -rm -f -r a.out a.out.dSYM a.exe conftest$ac_cv_exeext b.out -ac_clean_files=$ac_clean_files_save -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for suffix of executables" >&5 -$as_echo_n "checking for suffix of executables... " >&6; } -if { { ac_try="$ac_link" -case "(($ac_try" in - *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; - *) ac_try_echo=$ac_try;; -esac -eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" -$as_echo "$ac_try_echo"; } >&5 - (eval "$ac_link") 2>&5 - ac_status=$? - $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 - test $ac_status = 0; }; 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 | *.map | *.inf | *.dSYM | *.o | *.obj ) ;; - *.* ) ac_cv_exeext=`expr "$ac_file" : '[^.]*\(\..*\)'` - break;; - * ) break;; - esac -done -else - { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 -$as_echo "$as_me: error: in \`$ac_pwd':" >&2;} -as_fn_error $? "cannot compute suffix of executables: cannot compile and link -See \`config.log' for more details" "$LINENO" 5; } -fi -rm -f conftest conftest$ac_cv_exeext -{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_exeext" >&5 -$as_echo "$ac_cv_exeext" >&6; } - -rm -f conftest.$ac_ext -EXEEXT=$ac_cv_exeext -ac_exeext=$EXEEXT -cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ -#include -int -main () -{ -FILE *f = fopen ("conftest.out", "w"); - return ferror (f) || fclose (f) != 0; - - ; - return 0; -} -_ACEOF -ac_clean_files="$ac_clean_files conftest.out" -# Check that the compiler produces executables we can run. If not, either -# the compiler is broken, or we cross compile. -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether we are cross compiling" >&5 -$as_echo_n "checking whether we are cross compiling... " >&6; } -if test "$cross_compiling" != yes; then - { { ac_try="$ac_link" -case "(($ac_try" in - *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; - *) ac_try_echo=$ac_try;; -esac -eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" -$as_echo "$ac_try_echo"; } >&5 - (eval "$ac_link") 2>&5 - ac_status=$? - $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 - test $ac_status = 0; } - if { ac_try='./conftest$ac_cv_exeext' - { { case "(($ac_try" in - *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; - *) ac_try_echo=$ac_try;; -esac -eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" -$as_echo "$ac_try_echo"; } >&5 - (eval "$ac_try") 2>&5 - ac_status=$? - $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 - test $ac_status = 0; }; }; then - cross_compiling=no - else - if test "$cross_compiling" = maybe; then - cross_compiling=yes - else - { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 -$as_echo "$as_me: error: in \`$ac_pwd':" >&2;} -as_fn_error $? "cannot run C compiled programs. -If you meant to cross compile, use \`--host'. -See \`config.log' for more details" "$LINENO" 5; } - fi - fi -fi -{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $cross_compiling" >&5 -$as_echo "$cross_compiling" >&6; } - -rm -f conftest.$ac_ext conftest$ac_cv_exeext conftest.out -ac_clean_files=$ac_clean_files_save -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for suffix of object files" >&5 -$as_echo_n "checking for suffix of object files... " >&6; } -if ${ac_cv_objext+:} false; then : - $as_echo_n "(cached) " >&6 -else - cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ - -int -main () -{ - - ; - return 0; -} -_ACEOF -rm -f conftest.o conftest.obj -if { { ac_try="$ac_compile" -case "(($ac_try" in - *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; - *) ac_try_echo=$ac_try;; -esac -eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" -$as_echo "$ac_try_echo"; } >&5 - (eval "$ac_compile") 2>&5 - ac_status=$? - $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 - test $ac_status = 0; }; then : - for ac_file in conftest.o conftest.obj conftest.*; do - test -f "$ac_file" || continue; - case $ac_file in - *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.dSYM ) ;; - *) ac_cv_objext=`expr "$ac_file" : '.*\.\(.*\)'` - break;; - esac -done -else - $as_echo "$as_me: failed program was:" >&5 -sed 's/^/| /' conftest.$ac_ext >&5 - -{ { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 -$as_echo "$as_me: error: in \`$ac_pwd':" >&2;} -as_fn_error $? "cannot compute suffix of object files: cannot compile -See \`config.log' for more details" "$LINENO" 5; } -fi -rm -f conftest.$ac_cv_objext conftest.$ac_ext -fi -{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_objext" >&5 -$as_echo "$ac_cv_objext" >&6; } -OBJEXT=$ac_cv_objext -ac_objext=$OBJEXT -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether we are using the GNU C compiler" >&5 -$as_echo_n "checking whether we are using the GNU C compiler... " >&6; } -if ${ac_cv_c_compiler_gnu+:} false; then : - $as_echo_n "(cached) " >&6 -else - cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ - -int -main () -{ -#ifndef __GNUC__ - choke me -#endif - - ; - return 0; -} -_ACEOF -if ac_fn_c_try_compile "$LINENO"; then : - ac_compiler_gnu=yes -else - ac_compiler_gnu=no -fi -rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext -ac_cv_c_compiler_gnu=$ac_compiler_gnu - -fi -{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_c_compiler_gnu" >&5 -$as_echo "$ac_cv_c_compiler_gnu" >&6; } -if test $ac_compiler_gnu = yes; then - GCC=yes -else - GCC= -fi -ac_test_CFLAGS=${CFLAGS+set} -ac_save_CFLAGS=$CFLAGS -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether $CC accepts -g" >&5 -$as_echo_n "checking whether $CC accepts -g... " >&6; } -if ${ac_cv_prog_cc_g+:} false; then : - $as_echo_n "(cached) " >&6 -else - ac_save_c_werror_flag=$ac_c_werror_flag - ac_c_werror_flag=yes - ac_cv_prog_cc_g=no - CFLAGS="-g" - cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ - -int -main () -{ - - ; - return 0; -} -_ACEOF -if ac_fn_c_try_compile "$LINENO"; then : - ac_cv_prog_cc_g=yes -else - CFLAGS="" - cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ - -int -main () -{ - - ; - return 0; -} -_ACEOF -if ac_fn_c_try_compile "$LINENO"; then : - -else - ac_c_werror_flag=$ac_save_c_werror_flag - CFLAGS="-g" - cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ - -int -main () -{ - - ; - return 0; -} -_ACEOF -if ac_fn_c_try_compile "$LINENO"; then : - ac_cv_prog_cc_g=yes -fi -rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext -fi -rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext -fi -rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext - ac_c_werror_flag=$ac_save_c_werror_flag -fi -{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_cc_g" >&5 -$as_echo "$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 -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $CC option to accept ISO C89" >&5 -$as_echo_n "checking for $CC option to accept ISO C89... " >&6; } -if ${ac_cv_prog_cc_c89+:} false; then : - $as_echo_n "(cached) " >&6 -else - ac_cv_prog_cc_c89=no -ac_save_CC=$CC -cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* 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; -} - -/* OSF 4.0 Compaq cc is some sort of almost-ANSI by default. It has - function prototypes and stuff, but not '\xHH' hex character constants. - These don't provoke an error unfortunately, instead are silently treated - as 'x'. The following induces an error, until -std is added to get - proper ANSI mode. Curiously '\x00'!='x' always comes out true, for an - array size at least. It's necessary to write '\x00'==0 to get something - that's true only with -std. */ -int osf4_cc_array ['\x00' == 0 ? 1 : -1]; - -/* IBM C 6 for AIX is almost-ANSI by default, but it replaces macro parameters - inside strings and character constants. */ -#define FOO(x) 'x' -int xlc6_cc_array[FOO(a) == 'x' ? 1 : -1]; - -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 -for ac_arg in '' -qlanglvl=extc89 -qlanglvl=ansi -std \ - -Ae "-Aa -D_HPUX_SOURCE" "-Xc -D__EXTENSIONS__" -do - CC="$ac_save_CC $ac_arg" - if ac_fn_c_try_compile "$LINENO"; then : - ac_cv_prog_cc_c89=$ac_arg -fi -rm -f core conftest.err conftest.$ac_objext - test "x$ac_cv_prog_cc_c89" != "xno" && break -done -rm -f conftest.$ac_ext -CC=$ac_save_CC - -fi -# AC_CACHE_VAL -case "x$ac_cv_prog_cc_c89" in - x) - { $as_echo "$as_me:${as_lineno-$LINENO}: result: none needed" >&5 -$as_echo "none needed" >&6; } ;; - xno) - { $as_echo "$as_me:${as_lineno-$LINENO}: result: unsupported" >&5 -$as_echo "unsupported" >&6; } ;; - *) - CC="$CC $ac_cv_prog_cc_c89" - { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_cc_c89" >&5 -$as_echo "$ac_cv_prog_cc_c89" >&6; } ;; -esac -if test "x$ac_cv_prog_cc_c89" != xno; then : - -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 - - - -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether ${MAKE-make} sets \$(MAKE)" >&5 -$as_echo_n "checking whether ${MAKE-make} sets \$(MAKE)... " >&6; } -set x ${MAKE-make} -ac_make=`$as_echo "$2" | sed 's/+/p/g; s/[^a-zA-Z0-9_]/_/g'` -if eval \${ac_cv_prog_make_${ac_make}_set+:} false; then : - $as_echo_n "(cached) " >&6 -else - cat >conftest.make <<\_ACEOF -SHELL = /bin/sh -all: - @echo '@@@%%%=$(MAKE)=@@@%%%' -_ACEOF -# GNU make sometimes prints "make[1]: Entering ...", which would confuse us. -case `${MAKE-make} -f conftest.make 2>/dev/null` in - *@@@%%%=?*=@@@%%%*) - eval ac_cv_prog_make_${ac_make}_set=yes;; - *) - eval ac_cv_prog_make_${ac_make}_set=no;; -esac -rm -f conftest.make -fi -if eval test \$ac_cv_prog_make_${ac_make}_set = yes; then - { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 -$as_echo "yes" >&6; } - SET_MAKE= -else - { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 -$as_echo "no" >&6; } - SET_MAKE="MAKE=${MAKE-make}" -fi - - - -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether ln -s works" >&5 -$as_echo_n "checking whether ln -s works... " >&6; } -LN_S=$as_ln_s -if test "$LN_S" = "ln -s"; then - { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 -$as_echo "yes" >&6; } -else - { $as_echo "$as_me:${as_lineno-$LINENO}: result: no, using $LN_S" >&5 -$as_echo "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 -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 -$as_echo_n "checking for $ac_word... " >&6; } -if ${ac_cv_prog_AWK+:} false; then : - $as_echo_n "(cached) " >&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 { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then - ac_cv_prog_AWK="$ac_prog" - $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 - break 2 - fi -done - done -IFS=$as_save_IFS - -fi -fi -AWK=$ac_cv_prog_AWK -if test -n "$AWK"; then - { $as_echo "$as_me:${as_lineno-$LINENO}: result: $AWK" >&5 -$as_echo "$AWK" >&6; } -else - { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 -$as_echo "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 -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 -$as_echo_n "checking for $ac_word... " >&6; } -if ${ac_cv_prog_RANLIB+:} false; then : - $as_echo_n "(cached) " >&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 { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then - ac_cv_prog_RANLIB="${ac_tool_prefix}ranlib" - $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 - break 2 - fi -done - done -IFS=$as_save_IFS - -fi -fi -RANLIB=$ac_cv_prog_RANLIB -if test -n "$RANLIB"; then - { $as_echo "$as_me:${as_lineno-$LINENO}: result: $RANLIB" >&5 -$as_echo "$RANLIB" >&6; } -else - { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 -$as_echo "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 -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 -$as_echo_n "checking for $ac_word... " >&6; } -if ${ac_cv_prog_ac_ct_RANLIB+:} false; then : - $as_echo_n "(cached) " >&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 { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then - ac_cv_prog_ac_ct_RANLIB="ranlib" - $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 - break 2 - fi -done - done -IFS=$as_save_IFS - -fi -fi -ac_ct_RANLIB=$ac_cv_prog_ac_ct_RANLIB -if test -n "$ac_ct_RANLIB"; then - { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_RANLIB" >&5 -$as_echo "$ac_ct_RANLIB" >&6; } -else - { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 -$as_echo "no" >&6; } -fi - - if test "x$ac_ct_RANLIB" = x; then - RANLIB="" - else - case $cross_compiling:$ac_tool_warned in -yes:) -{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 -$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} -ac_tool_warned=yes ;; -esac - RANLIB=$ac_ct_RANLIB - fi -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 -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 -$as_echo_n "checking for $ac_word... " >&6; } -if ${ac_cv_prog_LD+:} false; then : - $as_echo_n "(cached) " >&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 { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then - ac_cv_prog_LD="${ac_tool_prefix}ld" - $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 - break 2 - fi -done - done -IFS=$as_save_IFS - -fi -fi -LD=$ac_cv_prog_LD -if test -n "$LD"; then - { $as_echo "$as_me:${as_lineno-$LINENO}: result: $LD" >&5 -$as_echo "$LD" >&6; } -else - { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 -$as_echo "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 -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 -$as_echo_n "checking for $ac_word... " >&6; } -if ${ac_cv_prog_ac_ct_LD+:} false; then : - $as_echo_n "(cached) " >&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 { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then - ac_cv_prog_ac_ct_LD="ld" - $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 - break 2 - fi -done - done -IFS=$as_save_IFS - -fi -fi -ac_ct_LD=$ac_cv_prog_ac_ct_LD -if test -n "$ac_ct_LD"; then - { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_LD" >&5 -$as_echo "$ac_ct_LD" >&6; } -else - { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 -$as_echo "no" >&6; } -fi - - if test "x$ac_ct_LD" = x; then - LD="" - else - case $cross_compiling:$ac_tool_warned in -yes:) -{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 -$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} -ac_tool_warned=yes ;; -esac - LD=$ac_ct_LD - fi -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 - as_fn_error $? "cannot find install-sh, install.sh, or shtool in \"$srcdir\" \"$srcdir/..\" \"$srcdir/../..\"" "$LINENO" 5 -fi - -# These three variables are undocumented and unsupported, -# and are intended to be withdrawn in a future Autoconf release. -# They can cause serious problems if a builder's source tree is in a directory -# whose full name contains unusual characters. -ac_config_guess="$SHELL $ac_aux_dir/config.guess" # Please don't use this var. -ac_config_sub="$SHELL $ac_aux_dir/config.sub" # Please don't use this var. -ac_configure="$SHELL $ac_aux_dir/configure" # Please don't use this var. - - -# Make sure we can run config.sub. -$SHELL "$ac_aux_dir/config.sub" sun4 >/dev/null 2>&1 || - as_fn_error $? "cannot run $SHELL $ac_aux_dir/config.sub" "$LINENO" 5 - -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking build system type" >&5 -$as_echo_n "checking build system type... " >&6; } -if ${ac_cv_build+:} false; then : - $as_echo_n "(cached) " >&6 -else - ac_build_alias=$build_alias -test "x$ac_build_alias" = x && - ac_build_alias=`$SHELL "$ac_aux_dir/config.guess"` -test "x$ac_build_alias" = x && - as_fn_error $? "cannot guess build type; you must specify one" "$LINENO" 5 -ac_cv_build=`$SHELL "$ac_aux_dir/config.sub" $ac_build_alias` || - as_fn_error $? "$SHELL $ac_aux_dir/config.sub $ac_build_alias failed" "$LINENO" 5 - -fi -{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_build" >&5 -$as_echo "$ac_cv_build" >&6; } -case $ac_cv_build in -*-*-*) ;; -*) as_fn_error $? "invalid value of canonical build" "$LINENO" 5;; -esac -build=$ac_cv_build -ac_save_IFS=$IFS; IFS='-' -set x $ac_cv_build -shift -build_cpu=$1 -build_vendor=$2 -shift; shift -# Remember, the first character of IFS is used to create $*, -# except with old shells: -build_os=$* -IFS=$ac_save_IFS -case $build_os in *\ *) build_os=`echo "$build_os" | sed 's/ /-/g'`;; esac - - -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking host system type" >&5 -$as_echo_n "checking host system type... " >&6; } -if ${ac_cv_host+:} false; then : - $as_echo_n "(cached) " >&6 -else - if test "x$host_alias" = x; then - ac_cv_host=$ac_cv_build -else - ac_cv_host=`$SHELL "$ac_aux_dir/config.sub" $host_alias` || - as_fn_error $? "$SHELL $ac_aux_dir/config.sub $host_alias failed" "$LINENO" 5 -fi - -fi -{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_host" >&5 -$as_echo "$ac_cv_host" >&6; } -case $ac_cv_host in -*-*-*) ;; -*) as_fn_error $? "invalid value of canonical host" "$LINENO" 5;; -esac -host=$ac_cv_host -ac_save_IFS=$IFS; IFS='-' -set x $ac_cv_host -shift -host_cpu=$1 -host_vendor=$2 -shift; shift -# Remember, the first character of IFS is used to create $*, -# except with old shells: -host_os=$* -IFS=$ac_save_IFS -case $host_os in *\ *) host_os=`echo "$host_os" | sed 's/ /-/g'`;; esac - - -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking target system type" >&5 -$as_echo_n "checking target system type... " >&6; } -if ${ac_cv_target+:} false; then : - $as_echo_n "(cached) " >&6 -else - if test "x$target_alias" = x; then - ac_cv_target=$ac_cv_host -else - ac_cv_target=`$SHELL "$ac_aux_dir/config.sub" $target_alias` || - as_fn_error $? "$SHELL $ac_aux_dir/config.sub $target_alias failed" "$LINENO" 5 -fi - -fi -{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_target" >&5 -$as_echo "$ac_cv_target" >&6; } -case $ac_cv_target in -*-*-*) ;; -*) as_fn_error $? "invalid value of canonical target" "$LINENO" 5;; -esac -target=$ac_cv_target -ac_save_IFS=$IFS; IFS='-' -set x $ac_cv_target -shift -target_cpu=$1 -target_vendor=$2 -shift; shift -# Remember, the first character of IFS is used to create $*, -# except with old shells: -target_os=$* -IFS=$ac_save_IFS -case $target_os in *\ *) target_os=`echo "$target_os" | sed 's/ /-/g'`;; esac - - -# 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 -$as_echo_n "checking how to run the C preprocessor... " >&6; } -# On Suns, sometimes $CPP names a directory. -if test -n "$CPP" && test -d "$CPP"; then - CPP= -fi -if test -z "$CPP"; then - if ${ac_cv_prog_CPP+:} false; then : - $as_echo_n "(cached) " >&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 confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ -#ifdef __STDC__ -# include -#else -# include -#endif - Syntax error -_ACEOF -if ac_fn_c_try_cpp "$LINENO"; then : - -else - # Broken: fails on valid input. -continue -fi -rm -f conftest.err conftest.i conftest.$ac_ext - - # OK, works on sane cases. Now check whether nonexistent headers - # can be detected and how. - cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ -#include -_ACEOF -if ac_fn_c_try_cpp "$LINENO"; then : - # Broken: success on invalid input. -continue -else - # Passes both tests. -ac_preproc_ok=: -break -fi -rm -f conftest.err conftest.i conftest.$ac_ext - -done -# Because of `break', _AC_PREPROC_IFELSE's cleaning code was skipped. -rm -f conftest.i 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 -{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $CPP" >&5 -$as_echo "$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 confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ -#ifdef __STDC__ -# include -#else -# include -#endif - Syntax error -_ACEOF -if ac_fn_c_try_cpp "$LINENO"; then : - -else - # Broken: fails on valid input. -continue -fi -rm -f conftest.err conftest.i conftest.$ac_ext - - # OK, works on sane cases. Now check whether nonexistent headers - # can be detected and how. - cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ -#include -_ACEOF -if ac_fn_c_try_cpp "$LINENO"; then : - # Broken: success on invalid input. -continue -else - # Passes both tests. -ac_preproc_ok=: -break -fi -rm -f conftest.err conftest.i conftest.$ac_ext - -done -# Because of `break', _AC_PREPROC_IFELSE's cleaning code was skipped. -rm -f conftest.i conftest.err conftest.$ac_ext -if $ac_preproc_ok; then : - -else - { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 -$as_echo "$as_me: error: in \`$ac_pwd':" >&2;} -as_fn_error $? "C preprocessor \"$CPP\" fails sanity check -See \`config.log' for more details" "$LINENO" 5; } -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 - - -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for grep that handles long lines and -e" >&5 -$as_echo_n "checking for grep that handles long lines and -e... " >&6; } -if ${ac_cv_path_GREP+:} false; then : - $as_echo_n "(cached) " >&6 -else - if test -z "$GREP"; then - ac_path_GREP_found=false - # Loop through the user's path and test for each of PROGNAME-LIST - as_save_IFS=$IFS; IFS=$PATH_SEPARATOR -for as_dir in $PATH$PATH_SEPARATOR/usr/xpg4/bin -do - IFS=$as_save_IFS - test -z "$as_dir" && as_dir=. - for ac_prog in grep ggrep; do - for ac_exec_ext in '' $ac_executable_extensions; do - ac_path_GREP="$as_dir/$ac_prog$ac_exec_ext" - { test -f "$ac_path_GREP" && $as_test_x "$ac_path_GREP"; } || continue -# Check for GNU ac_path_GREP and select it if it is found. - # Check for GNU $ac_path_GREP -case `"$ac_path_GREP" --version 2>&1` in -*GNU*) - ac_cv_path_GREP="$ac_path_GREP" ac_path_GREP_found=:;; -*) - ac_count=0 - $as_echo_n 0123456789 >"conftest.in" - while : - do - cat "conftest.in" "conftest.in" >"conftest.tmp" - mv "conftest.tmp" "conftest.in" - cp "conftest.in" "conftest.nl" - $as_echo 'GREP' >> "conftest.nl" - "$ac_path_GREP" -e 'GREP$' -e '-(cannot match)-' < "conftest.nl" >"conftest.out" 2>/dev/null || break - diff "conftest.out" "conftest.nl" >/dev/null 2>&1 || break - as_fn_arith $ac_count + 1 && ac_count=$as_val - if test $ac_count -gt ${ac_path_GREP_max-0}; then - # Best one so far, save it but keep looking for a better one - ac_cv_path_GREP="$ac_path_GREP" - ac_path_GREP_max=$ac_count - fi - # 10*(2^10) chars as input seems more than enough - test $ac_count -gt 10 && break - done - rm -f conftest.in conftest.tmp conftest.nl conftest.out;; -esac - - $ac_path_GREP_found && break 3 - done - done - done -IFS=$as_save_IFS - if test -z "$ac_cv_path_GREP"; then - as_fn_error $? "no acceptable grep could be found in $PATH$PATH_SEPARATOR/usr/xpg4/bin" "$LINENO" 5 - fi -else - ac_cv_path_GREP=$GREP -fi - -fi -{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_path_GREP" >&5 -$as_echo "$ac_cv_path_GREP" >&6; } - GREP="$ac_cv_path_GREP" - - -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for egrep" >&5 -$as_echo_n "checking for egrep... " >&6; } -if ${ac_cv_path_EGREP+:} false; then : - $as_echo_n "(cached) " >&6 -else - if echo a | $GREP -E '(a|b)' >/dev/null 2>&1 - then ac_cv_path_EGREP="$GREP -E" - else - if test -z "$EGREP"; then - ac_path_EGREP_found=false - # Loop through the user's path and test for each of PROGNAME-LIST - as_save_IFS=$IFS; IFS=$PATH_SEPARATOR -for as_dir in $PATH$PATH_SEPARATOR/usr/xpg4/bin -do - IFS=$as_save_IFS - test -z "$as_dir" && as_dir=. - for ac_prog in egrep; do - for ac_exec_ext in '' $ac_executable_extensions; do - ac_path_EGREP="$as_dir/$ac_prog$ac_exec_ext" - { test -f "$ac_path_EGREP" && $as_test_x "$ac_path_EGREP"; } || continue -# Check for GNU ac_path_EGREP and select it if it is found. - # Check for GNU $ac_path_EGREP -case `"$ac_path_EGREP" --version 2>&1` in -*GNU*) - ac_cv_path_EGREP="$ac_path_EGREP" ac_path_EGREP_found=:;; -*) - ac_count=0 - $as_echo_n 0123456789 >"conftest.in" - while : - do - cat "conftest.in" "conftest.in" >"conftest.tmp" - mv "conftest.tmp" "conftest.in" - cp "conftest.in" "conftest.nl" - $as_echo 'EGREP' >> "conftest.nl" - "$ac_path_EGREP" 'EGREP$' < "conftest.nl" >"conftest.out" 2>/dev/null || break - diff "conftest.out" "conftest.nl" >/dev/null 2>&1 || break - as_fn_arith $ac_count + 1 && ac_count=$as_val - if test $ac_count -gt ${ac_path_EGREP_max-0}; then - # Best one so far, save it but keep looking for a better one - ac_cv_path_EGREP="$ac_path_EGREP" - ac_path_EGREP_max=$ac_count - fi - # 10*(2^10) chars as input seems more than enough - test $ac_count -gt 10 && break - done - rm -f conftest.in conftest.tmp conftest.nl conftest.out;; -esac - - $ac_path_EGREP_found && break 3 - done - done - done -IFS=$as_save_IFS - if test -z "$ac_cv_path_EGREP"; then - as_fn_error $? "no acceptable egrep could be found in $PATH$PATH_SEPARATOR/usr/xpg4/bin" "$LINENO" 5 - fi -else - ac_cv_path_EGREP=$EGREP -fi - - fi -fi -{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_path_EGREP" >&5 -$as_echo "$ac_cv_path_EGREP" >&6; } - EGREP="$ac_cv_path_EGREP" - - -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for ANSI C header files" >&5 -$as_echo_n "checking for ANSI C header files... " >&6; } -if ${ac_cv_header_stdc+:} false; then : - $as_echo_n "(cached) " >&6 -else - cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ -#include -#include -#include -#include - -int -main () -{ - - ; - return 0; -} -_ACEOF -if ac_fn_c_try_compile "$LINENO"; then : - ac_cv_header_stdc=yes -else - ac_cv_header_stdc=no -fi -rm -f core conftest.err 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 confdefs.h - <<_ACEOF >conftest.$ac_ext -/* 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 confdefs.h - <<_ACEOF >conftest.$ac_ext -/* 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 confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ -#include -#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)) - return 2; - return 0; -} -_ACEOF -if ac_fn_c_try_run "$LINENO"; then : - -else - ac_cv_header_stdc=no -fi -rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \ - conftest.$ac_objext conftest.beam conftest.$ac_ext -fi - -fi -fi -{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_header_stdc" >&5 -$as_echo "$ac_cv_header_stdc" >&6; } -if test $ac_cv_header_stdc = yes; then - -$as_echo "#define STDC_HEADERS 1" >>confdefs.h - -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=`$as_echo "ac_cv_header_$ac_header" | $as_tr_sh` -ac_fn_c_check_header_compile "$LINENO" "$ac_header" "$as_ac_Header" "$ac_includes_default -" -if eval test \"x\$"$as_ac_Header"\" = x"yes"; then : - cat >>confdefs.h <<_ACEOF -#define `$as_echo "HAVE_$ac_header" | $as_tr_cpp` 1 -_ACEOF - -fi - -done - - -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for tigetstr in -lcurses" >&5 -$as_echo_n "checking for tigetstr in -lcurses... " >&6; } -if ${ac_cv_lib_curses_tigetstr+:} false; then : - $as_echo_n "(cached) " >&6 -else - ac_check_lib_save_LIBS=$LIBS -LIBS="-lcurses $LIBS" -cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ - -/* Override any GCC internal prototype to avoid an error. - Use char because int might match the return type of a GCC - builtin and then its argument prototype would still apply. */ -#ifdef __cplusplus -extern "C" -#endif -char tigetstr (); -int -main () -{ -return tigetstr (); - ; - return 0; -} -_ACEOF -if ac_fn_c_try_link "$LINENO"; then : - ac_cv_lib_curses_tigetstr=yes -else - ac_cv_lib_curses_tigetstr=no -fi -rm -f core conftest.err conftest.$ac_objext \ - conftest$ac_exeext conftest.$ac_ext -LIBS=$ac_check_lib_save_LIBS -fi -{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_curses_tigetstr" >&5 -$as_echo "$ac_cv_lib_curses_tigetstr" >&6; } -if test "x$ac_cv_lib_curses_tigetstr" = xyes; then : - - $as_echo "#define USE_TERMINFO 1" >>confdefs.h - - LIBS="$LIBS -lcurses" - -else - { $as_echo "$as_me:${as_lineno-$LINENO}: checking for tigetstr in -lncurses" >&5 -$as_echo_n "checking for tigetstr in -lncurses... " >&6; } -if ${ac_cv_lib_ncurses_tigetstr+:} false; then : - $as_echo_n "(cached) " >&6 -else - ac_check_lib_save_LIBS=$LIBS -LIBS="-lncurses $LIBS" -cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ - -/* Override any GCC internal prototype to avoid an error. - Use char because int might match the return type of a GCC - builtin and then its argument prototype would still apply. */ -#ifdef __cplusplus -extern "C" -#endif -char tigetstr (); -int -main () -{ -return tigetstr (); - ; - return 0; -} -_ACEOF -if ac_fn_c_try_link "$LINENO"; then : - ac_cv_lib_ncurses_tigetstr=yes -else - ac_cv_lib_ncurses_tigetstr=no -fi -rm -f core conftest.err conftest.$ac_objext \ - conftest$ac_exeext conftest.$ac_ext -LIBS=$ac_check_lib_save_LIBS -fi -{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_ncurses_tigetstr" >&5 -$as_echo "$ac_cv_lib_ncurses_tigetstr" >&6; } -if test "x$ac_cv_lib_ncurses_tigetstr" = xyes; then : - - $as_echo "#define USE_TERMINFO 1" >>confdefs.h - - LIBS="$LIBS -lncurses" - -else - { $as_echo "$as_me:${as_lineno-$LINENO}: checking for tgetstr in -lcurses" >&5 -$as_echo_n "checking for tgetstr in -lcurses... " >&6; } -if ${ac_cv_lib_curses_tgetstr+:} false; then : - $as_echo_n "(cached) " >&6 -else - ac_check_lib_save_LIBS=$LIBS -LIBS="-lcurses $LIBS" -cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ - -/* Override any GCC internal prototype to avoid an error. - Use char because int might match the return type of a GCC - builtin and then its argument prototype would still apply. */ -#ifdef __cplusplus -extern "C" -#endif -char tgetstr (); -int -main () -{ -return tgetstr (); - ; - return 0; -} -_ACEOF -if ac_fn_c_try_link "$LINENO"; then : - ac_cv_lib_curses_tgetstr=yes -else - ac_cv_lib_curses_tgetstr=no -fi -rm -f core conftest.err conftest.$ac_objext \ - conftest$ac_exeext conftest.$ac_ext -LIBS=$ac_check_lib_save_LIBS -fi -{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_curses_tgetstr" >&5 -$as_echo "$ac_cv_lib_curses_tgetstr" >&6; } -if test "x$ac_cv_lib_curses_tgetstr" = xyes; then : - - $as_echo "#define USE_TERMCAP 1" >>confdefs.h - - LIBS="$LIBS -lcurses" - ac_fn_c_check_header_mongrel "$LINENO" "termcap.h" "ac_cv_header_termcap_h" "$ac_includes_default" -if test "x$ac_cv_header_termcap_h" = xyes; then : - $as_echo "#define HAVE_TERMCAP_H 1" >>confdefs.h - -fi - - - -fi - -fi - -fi - - - -for ac_header in curses.h -do : - ac_fn_c_check_header_mongrel "$LINENO" "curses.h" "ac_cv_header_curses_h" "$ac_includes_default" -if test "x$ac_cv_header_curses_h" = xyes; then : - cat >>confdefs.h <<_ACEOF -#define HAVE_CURSES_H 1 -_ACEOF - for ac_header in term.h -do : - ac_fn_c_check_header_compile "$LINENO" "term.h" "ac_cv_header_term_h" "#include -" -if test "x$ac_cv_header_term_h" = xyes; then : - cat >>confdefs.h <<_ACEOF -#define HAVE_TERM_H 1 -_ACEOF - -fi - -done - -else - for ac_header in ncurses/curses.h -do : - ac_fn_c_check_header_mongrel "$LINENO" "ncurses/curses.h" "ac_cv_header_ncurses_curses_h" "$ac_includes_default" -if test "x$ac_cv_header_ncurses_curses_h" = xyes; then : - cat >>confdefs.h <<_ACEOF -#define HAVE_NCURSES_CURSES_H 1 -_ACEOF - for ac_header in ncurses/term.h -do : - ac_fn_c_check_header_compile "$LINENO" "ncurses/term.h" "ac_cv_header_ncurses_term_h" "#include -" -if test "x$ac_cv_header_ncurses_term_h" = xyes; then : - cat >>confdefs.h <<_ACEOF -#define HAVE_NCURSES_TERM_H 1 -_ACEOF - -fi - -done - -fi - -done - -fi - -done - - - - -TARGETS="normal reentrant" - - -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for reentrant functions" >&5 -$as_echo_n "checking for reentrant functions... " >&6; } -if ${tecla_cv_reentrant+:} false; then : - $as_echo_n "(cached) " >&6 -else - - KEPT_CFLAGS="$CFLAGS" - CFLAGS="$CFLAGS -D_POSIX_C_SOURCE=199506L" - cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* 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 -if ac_fn_c_try_link "$LINENO"; then : - tecla_cv_reentrant=yes -else - tecla_cv_reentrant=no -fi -rm -f core conftest.err conftest.$ac_objext \ - conftest$ac_exeext conftest.$ac_ext - CFLAGS="$KEPT_CFLAGS" - -fi -{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $tecla_cv_reentrant" >&5 -$as_echo "$tecla_cv_reentrant" >&6; } - - -if test $tecla_cv_reentrant = no; then - TARGETS="normal" -fi - - -ac_fn_c_check_header_mongrel "$LINENO" "sys/select.h" "ac_cv_header_sys_select_h" "$ac_includes_default" -if test "x$ac_cv_header_sys_select_h" = xyes; then : - $as_echo "#define HAVE_SYS_SELECT_H 1" >>confdefs.h - -fi - - - - -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for select system call" >&5 -$as_echo_n "checking for select system call... " >&6; } -if ${tecla_cv_select+:} false; then : - $as_echo_n "(cached) " >&6 -else - - cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* 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 -if ac_fn_c_try_link "$LINENO"; then : - tecla_cv_select=yes -else - tecla_cv_select=no -fi -rm -f core conftest.err conftest.$ac_objext \ - conftest$ac_exeext conftest.$ac_ext - -fi -{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $tecla_cv_select" >&5 -$as_echo "$tecla_cv_select" >&6; } - - -if test $tecla_cv_select = yes; then - $as_echo "#define HAVE_SELECT 1" >>confdefs.h - -fi - - -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for SysV pseudo-terminals" >&5 -$as_echo_n "checking for SysV pseudo-terminals... " >&6; } -if ${tecla_cv_sysv_pty+:} false; then : - $as_echo_n "(cached) " >&6 -else - - cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ - -#include -#include - -int -main () -{ - - char *name = ptsname(0); - int i1 = grantpt(0); - int i2 = unlockpt(0); - return 0; - - ; - return 0; -} -_ACEOF -if ac_fn_c_try_link "$LINENO"; then : - tecla_cv_sysv_pty=yes -else - tecla_cv_sysv_pty=no -fi -rm -f core conftest.err conftest.$ac_objext \ - conftest$ac_exeext conftest.$ac_ext - -fi -{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $tecla_cv_sysv_pty" >&5 -$as_echo "$tecla_cv_sysv_pty" >&6; } - - -if test $tecla_cv_sysv_pty = yes; then - $as_echo "#define HAVE_SYSV_PTY 1" >>confdefs.h - -fi - - -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for SysV streams pseudo-terminals" >&5 -$as_echo_n "checking for SysV streams pseudo-terminals... " >&6; } -if ${tecla_cv_sysv_ptem+:} false; then : - $as_echo_n "(cached) " >&6 -else - - cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* 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"); - int i4 = ioctl(fd, I_PUSH, "ldterm"); - return 0; - - ; - return 0; -} -_ACEOF -if ac_fn_c_try_link "$LINENO"; then : - tecla_cv_sysv_ptem=yes -else - tecla_cv_sysv_ptem=no -fi -rm -f core conftest.err conftest.$ac_objext \ - conftest$ac_exeext conftest.$ac_ext - -fi -{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $tecla_cv_sysv_ptem" >&5 -$as_echo "$tecla_cv_sysv_ptem" >&6; } - - -if test $tecla_cv_sysv_ptem = yes; then - $as_echo "#define HAVE_SYSV_PTEM 1" >>confdefs.h - -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 was given. -if test "${with_file_actions+set}" = set; then : - withval=$with_file_actions; $as_echo "#define HIDE_FILE_SYSTEM 1" >>confdefs.h - -fi - - - - -# Check whether --with-file-system was given. -if test "${with_file_system+set}" = set; then : - withval=$with_file_system; $as_echo "#define WITHOUT_FILE_SYSTEM 1" >>confdefs.h - -fi - - - -case $target in -*solaris*) - $as_echo "#define __EXTENSIONS__ 1" >>confdefs.h - - 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}" - - - { $as_echo "$as_me:${as_lineno-$LINENO}: checking for --version-script in GNU ld" >&5 -$as_echo_n "checking for --version-script in GNU ld... " >&6; } -if ${tecla_cv_gnu_ld_script+:} false; then : - $as_echo_n "(cached) " >&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 -{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $tecla_cv_gnu_ld_script" >&5 -$as_echo "$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*) - $as_echo "#define _OSF_SOURCE 1" >>confdefs.h - - ;; -*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 - - - LIBGCC="`$CC -print-libgcc-file-name 2>/dev/null`" - if test "$LIBGCC"_ != "_" && test -r "$LIBGCC"; then - LINK_SHARED="$LINK_SHARED $LIBGCC" - fi -fi - - - - - -if test "$LINK_SHARED"_ != "_"; then - TARGET_LIBS="static shared" -else - TARGET_LIBS="static" - LINK_SHARED="@:" -fi - - - - -# Check whether --with-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, we kill variables containing newlines. -# 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. -( - for ac_var in `(set) 2>&1 | sed -n 's/^\([a-zA-Z_][a-zA-Z0-9_]*\)=.*/\1/p'`; do - eval ac_val=\$$ac_var - case $ac_val in #( - *${as_nl}*) - case $ac_var in #( - *_cv_*) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: cache variable $ac_var contains a newline" >&5 -$as_echo "$as_me: WARNING: cache variable $ac_var contains a newline" >&2;} ;; - esac - case $ac_var in #( - _ | IFS | as_nl) ;; #( - BASH_ARGV | BASH_SOURCE) eval $ac_var= ;; #( - *) { eval $ac_var=; unset $ac_var;} ;; - esac ;; - esac - done - - (set) 2>&1 | - case $as_nl`(ac_space=' '; set) 2>&1` in #( - *${as_nl}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 "/^[_$as_cr_alnum]*_cv_[_$as_cr_alnum]*=/p" - ;; - esac | - sort -) | - sed ' - /^ac_cv_env_/b end - t clear - :clear - s/^\([^=]*\)=\(.*[{}].*\)$/test "${\1+set}" = set || &/ - t end - s/^\([^=]*\)=\(.*\)$/\1=${\1=\2}/ - :end' >>confcache -if diff "$cache_file" confcache >/dev/null 2>&1; then :; else - if test -w "$cache_file"; then - if test "x$cache_file" != "x/dev/null"; then - { $as_echo "$as_me:${as_lineno-$LINENO}: updating cache $cache_file" >&5 -$as_echo "$as_me: updating cache $cache_file" >&6;} - if test ! -f "$cache_file" || test -h "$cache_file"; then - cat confcache >"$cache_file" - else - case $cache_file in #( - */* | ?:*) - mv -f confcache "$cache_file"$$ && - mv -f "$cache_file"$$ "$cache_file" ;; #( - *) - mv -f confcache "$cache_file" ;; - esac - fi - fi - else - { $as_echo "$as_me:${as_lineno-$LINENO}: not updating unwritable cache $cache_file" >&5 -$as_echo "$as_me: not updating unwritable cache $cache_file" >&6;} - 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}' - -# 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 branch to the quote section. Otherwise, -# look for a macro that doesn't take arguments. -ac_script=' -:mline -/\\$/{ - N - s,\\\n,, - b mline -} -t clear -:clear -s/^[ ]*#[ ]*define[ ][ ]*\([^ (][^ (]*([^)]*)\)[ ]*\(.*\)/-D\1=\2/g -t quote -s/^[ ]*#[ ]*define[ ][ ]*\([^ ][^ ]*\)[ ]*\(.*\)/-D\1=\2/g -t quote -b any -:quote -s/[ `~#$^&*(){}\\|;'\''"<>?]/\\&/g -s/\[/\\&/g -s/\]/\\&/g -s/\$/$$/g -H -:any -${ - g - s/^\n// - s/\n/ /g - p -} -' -DEFS=`sed -n "$ac_script" confdefs.h` - - -ac_libobjs= -ac_ltlibobjs= -U= -for ac_i in : $LIBOBJS; do test "x$ac_i" = x: && continue - # 1. Remove the extension, and $U if already installed. - ac_script='s/\$U\././;s/\.o$//;s/\.obj$//' - ac_i=`$as_echo "$ac_i" | sed "$ac_script"` - # 2. Prepend LIBOBJDIR. When used with automake>=1.10 LIBOBJDIR - # will be set to the directory where LIBOBJS objects are built. - as_fn_append ac_libobjs " \${LIBOBJDIR}$ac_i\$U.$ac_objext" - as_fn_append ac_ltlibobjs " \${LIBOBJDIR}$ac_i"'$U.lo' -done -LIBOBJS=$ac_libobjs - -LTLIBOBJS=$ac_ltlibobjs - - - -: "${CONFIG_STATUS=./config.status}" -ac_write_fail=0 -ac_clean_files_save=$ac_clean_files -ac_clean_files="$ac_clean_files $CONFIG_STATUS" -{ $as_echo "$as_me:${as_lineno-$LINENO}: creating $CONFIG_STATUS" >&5 -$as_echo "$as_me: creating $CONFIG_STATUS" >&6;} -as_write_fail=0 -cat >$CONFIG_STATUS <<_ASEOF || as_write_fail=1 -#! $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} -export SHELL -_ASEOF -cat >>$CONFIG_STATUS <<\_ASEOF || as_write_fail=1 -## -------------------- ## -## M4sh Initialization. ## -## -------------------- ## - -# Be more Bourne compatible -DUALCASE=1; export DUALCASE # for MKS sh -if test -n "${ZSH_VERSION+set}" && (emulate sh) >/dev/null 2>&1; then : - emulate sh - NULLCMD=: - # Pre-4.2 versions of Zsh do word splitting on ${1+"$@"}, which - # is contrary to our usage. Disable this feature. - alias -g '${1+"$@"}'='"$@"' - setopt NO_GLOB_SUBST -else - case `(set -o) 2>/dev/null` in #( - *posix*) : - set -o posix ;; #( - *) : - ;; -esac -fi - - -as_nl=' -' -export as_nl -# Printing a long string crashes Solaris 7 /usr/bin/printf. -as_echo='\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\' -as_echo=$as_echo$as_echo$as_echo$as_echo$as_echo -as_echo=$as_echo$as_echo$as_echo$as_echo$as_echo$as_echo -# Prefer a ksh shell builtin over an external printf program on Solaris, -# but without wasting forks for bash or zsh. -if test -z "$BASH_VERSION$ZSH_VERSION" \ - && (test "X`print -r -- $as_echo`" = "X$as_echo") 2>/dev/null; then - as_echo='print -r --' - as_echo_n='print -rn --' -elif (test "X`printf %s $as_echo`" = "X$as_echo") 2>/dev/null; then - as_echo='printf %s\n' - as_echo_n='printf %s' -else - if test "X`(/usr/ucb/echo -n -n $as_echo) 2>/dev/null`" = "X-n $as_echo"; then - as_echo_body='eval /usr/ucb/echo -n "$1$as_nl"' - as_echo_n='/usr/ucb/echo -n' - else - as_echo_body='eval expr "X$1" : "X\\(.*\\)"' - as_echo_n_body='eval - arg=$1; - case $arg in #( - *"$as_nl"*) - expr "X$arg" : "X\\(.*\\)$as_nl"; - arg=`expr "X$arg" : ".*$as_nl\\(.*\\)"`;; - esac; - expr "X$arg" : "X\\(.*\\)" | tr -d "$as_nl" - ' - export as_echo_n_body - as_echo_n='sh -c $as_echo_n_body as_echo' - fi - export as_echo_body - as_echo='sh -c $as_echo_body as_echo' -fi - -# The user is always right. -if test "${PATH_SEPARATOR+set}" != set; then - PATH_SEPARATOR=: - (PATH='/bin;/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 && { - (PATH='/bin:/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 || - PATH_SEPARATOR=';' - } -fi - - -# IFS -# We need space, tab and new line, in precisely that order. Quoting is -# there to prevent editors from complaining about space-tab. -# (If _AS_PATH_WALK were called with IFS unset, it would disable word -# splitting by setting IFS to empty value.) -IFS=" "" $as_nl" - -# Find who we are. Look in the path if we contain no directory separator. -as_myself= -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 -IFS=$as_save_IFS - - ;; -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 - $as_echo "$as_myself: error: cannot find myself; rerun with an absolute file name" >&2 - exit 1 -fi - -# Unset variables that we do not need and which cause bugs (e.g. in -# pre-3.0 UWIN ksh). But do not cause bugs in bash 2.01; the "|| exit 1" -# suppresses any "Segmentation fault" message there. '((' could -# trigger a bug in pdksh 5.2.14. -for as_var in BASH_ENV ENV MAIL MAILPATH -do eval test x\${$as_var+set} = xset \ - && ( (unset $as_var) || exit 1) >/dev/null 2>&1 && unset $as_var || : -done -PS1='$ ' -PS2='> ' -PS4='+ ' - -# NLS nuisances. -LC_ALL=C -export LC_ALL -LANGUAGE=C -export LANGUAGE - -# CDPATH. -(unset CDPATH) >/dev/null 2>&1 && unset CDPATH - - -# as_fn_error STATUS ERROR [LINENO LOG_FD] -# ---------------------------------------- -# Output "`basename $0`: error: ERROR" to stderr. If LINENO and LOG_FD are -# provided, also output the error to LOG_FD, referencing LINENO. Then exit the -# script with STATUS, using 1 if that was 0. -as_fn_error () -{ - as_status=$1; test $as_status -eq 0 && as_status=1 - if test "$4"; then - as_lineno=${as_lineno-"$3"} as_lineno_stack=as_lineno_stack=$as_lineno_stack - $as_echo "$as_me:${as_lineno-$LINENO}: error: $2" >&$4 - fi - $as_echo "$as_me: error: $2" >&2 - as_fn_exit $as_status -} # as_fn_error - - -# as_fn_set_status STATUS -# ----------------------- -# Set $? to STATUS, without forking. -as_fn_set_status () -{ - return $1 -} # as_fn_set_status - -# as_fn_exit STATUS -# ----------------- -# Exit the shell with STATUS, even in a "trap 0" or "set -e" context. -as_fn_exit () -{ - set +e - as_fn_set_status $1 - exit $1 -} # as_fn_exit - -# as_fn_unset VAR -# --------------- -# Portably unset VAR. -as_fn_unset () -{ - { eval $1=; unset $1;} -} -as_unset=as_fn_unset -# as_fn_append VAR VALUE -# ---------------------- -# Append the text in VALUE to the end of the definition contained in VAR. Take -# advantage of any shell optimizations that allow amortized linear growth over -# repeated appends, instead of the typical quadratic growth present in naive -# implementations. -if (eval "as_var=1; as_var+=2; test x\$as_var = x12") 2>/dev/null; then : - eval 'as_fn_append () - { - eval $1+=\$2 - }' -else - as_fn_append () - { - eval $1=\$$1\$2 - } -fi # as_fn_append - -# as_fn_arith ARG... -# ------------------ -# Perform arithmetic evaluation on the ARGs, and store the result in the -# global $as_val. Take advantage of shells that can avoid forks. The arguments -# must be portable across $(()) and expr. -if (eval "test \$(( 1 + 1 )) = 2") 2>/dev/null; then : - eval 'as_fn_arith () - { - as_val=$(( $* )) - }' -else - as_fn_arith () - { - as_val=`expr "$@" || test $? -eq 1` - } -fi # as_fn_arith - - -if expr a : '\(a\)' >/dev/null 2>&1 && - test "X`expr 00001 : '.*\(...\)'`" = X001; 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 - -if (as_dir=`dirname -- /` && test "X$as_dir" = X/) >/dev/null 2>&1; then - as_dirname=dirname -else - as_dirname=false -fi - -as_me=`$as_basename -- "$0" || -$as_expr X/"$0" : '.*/\([^/][^/]*\)/*$' \| \ - X"$0" : 'X\(//\)$' \| \ - X"$0" : 'X\(/\)' \| . 2>/dev/null || -$as_echo X/"$0" | - sed '/^.*\/\([^/][^/]*\)\/*$/{ - s//\1/ - q - } - /^X\/\(\/\/\)$/{ - s//\1/ - q - } - /^X\/\(\/\).*/{ - s//\1/ - q - } - s/.*/./; q'` - -# 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 - -ECHO_C= ECHO_N= ECHO_T= -case `echo -n x` in #((((( --n*) - case `echo 'xy\c'` in - *c*) ECHO_T=' ';; # ECHO_T is single tab character. - xy) ECHO_C='\c';; - *) echo `echo ksh88 bug on AIX 6.1` > /dev/null - ECHO_T=' ';; - esac;; -*) - ECHO_N='-n';; -esac - -rm -f conf$$ conf$$.exe conf$$.file -if test -d conf$$.dir; then - rm -f conf$$.dir/conf$$.file -else - rm -f conf$$.dir - mkdir conf$$.dir 2>/dev/null -fi -if (echo >conf$$.file) 2>/dev/null; then - if ln -s conf$$.file conf$$ 2>/dev/null; then - as_ln_s='ln -s' - # ... but there are two gotchas: - # 1) On MSYS, both `ln -s file dir' and `ln file dir' fail. - # 2) DJGPP < 2.04 has no symlinks; `ln -s' creates a wrapper executable. - # In both cases, we have to default to `cp -p'. - ln -s conf$$.file conf$$.dir 2>/dev/null && test ! -f conf$$.exe || - as_ln_s='cp -p' - elif ln conf$$.file conf$$ 2>/dev/null; then - as_ln_s=ln - else - as_ln_s='cp -p' - fi -else - as_ln_s='cp -p' -fi -rm -f conf$$ conf$$.exe conf$$.dir/conf$$.file conf$$.file -rmdir conf$$.dir 2>/dev/null - - -# as_fn_mkdir_p -# ------------- -# Create "$as_dir" as a directory, including parents if necessary. -as_fn_mkdir_p () -{ - - case $as_dir in #( - -*) as_dir=./$as_dir;; - esac - test -d "$as_dir" || eval $as_mkdir_p || { - as_dirs= - while :; do - case $as_dir in #( - *\'*) as_qdir=`$as_echo "$as_dir" | sed "s/'/'\\\\\\\\''/g"`;; #'( - *) as_qdir=$as_dir;; - esac - as_dirs="'$as_qdir' $as_dirs" - as_dir=`$as_dirname -- "$as_dir" || -$as_expr X"$as_dir" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ - X"$as_dir" : 'X\(//\)[^/]' \| \ - X"$as_dir" : 'X\(//\)$' \| \ - X"$as_dir" : 'X\(/\)' \| . 2>/dev/null || -$as_echo X"$as_dir" | - sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ - s//\1/ - q - } - /^X\(\/\/\)[^/].*/{ - s//\1/ - q - } - /^X\(\/\/\)$/{ - s//\1/ - q - } - /^X\(\/\).*/{ - s//\1/ - q - } - s/.*/./; q'` - test -d "$as_dir" && break - done - test -z "$as_dirs" || eval "mkdir $as_dirs" - } || test -d "$as_dir" || as_fn_error $? "cannot create directory $as_dir" - - -} # as_fn_mkdir_p -if mkdir -p . 2>/dev/null; then - as_mkdir_p='mkdir -p "$as_dir"' -else - test -d ./-p && rmdir ./-p - as_mkdir_p=false -fi - -if test -x / >/dev/null 2>&1; then - as_test_x='test -x' -else - if ls -dL / >/dev/null 2>&1; then - as_ls_L_option=L - else - as_ls_L_option= - fi - as_test_x=' - eval sh -c '\'' - if test -d "$1"; then - test -d "$1/."; - else - case $1 in #( - -*)set "./$1";; - esac; - case `ls -ld'$as_ls_L_option' "$1" 2>/dev/null` in #(( - ???[sx]*):;;*)false;;esac;fi - '\'' sh - ' -fi -as_executable_p=$as_test_x - -# Sed expression to map a string onto a valid CPP name. -as_tr_cpp="eval 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="eval sed 'y%*+%pp%;s%[^_$as_cr_alnum]%_%g'" - - -exec 6>&1 -## ----------------------------------- ## -## Main body of $CONFIG_STATUS script. ## -## ----------------------------------- ## -_ASEOF -test $as_write_fail = 0 && chmod +x $CONFIG_STATUS || ac_write_fail=1 - -cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 -# Save the log message, to keep $0 and so on meaningful, and to -# report actual input values of CONFIG_FILES etc. instead of their -# values after options handling. -ac_log=" -This file was extended by $as_me, which was -generated by GNU Autoconf 2.68. Invocation command line was - - CONFIG_FILES = $CONFIG_FILES - CONFIG_HEADERS = $CONFIG_HEADERS - CONFIG_LINKS = $CONFIG_LINKS - CONFIG_COMMANDS = $CONFIG_COMMANDS - $ $0 $@ - -on `(hostname || uname -n) 2>/dev/null | sed 1q` -" - -_ACEOF - -case $ac_config_files in *" -"*) set x $ac_config_files; shift; ac_config_files=$*;; -esac - - - -cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 -# Files that config.status was made for. -config_files="$ac_config_files" - -_ACEOF - -cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 -ac_cs_usage="\ -\`$as_me' instantiates files and other configuration actions -from templates according to the current configuration. Unless the files -and actions are specified as TAGs, all are instantiated by default. - -Usage: $0 [OPTION]... [TAG]... - - -h, --help print this help, then exit - -V, --version print version number and configuration settings, then exit - --config print configuration, then exit - -q, --quiet, --silent - 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 the package provider." - -_ACEOF -cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 -ac_cs_config="`$as_echo "$ac_configure_args" | sed 's/^ //; s/[\\""\`\$]/\\\\&/g'`" -ac_cs_version="\\ -config.status -configured by $0, generated by GNU Autoconf 2.68, - with options \\"\$ac_cs_config\\" - -Copyright (C) 2010 Free Software Foundation, Inc. -This config.status script is free software; the Free Software Foundation -gives unlimited permission to copy, distribute and modify it." - -ac_pwd='$ac_pwd' -srcdir='$srcdir' -AWK='$AWK' -test -n "\$AWK" || AWK=awk -_ACEOF - -cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 -# The default lists apply if the user does not specify any file. -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=`expr "X$1" : 'X\([^=]*\)='` - ac_optarg= - ac_shift=: - ;; - *) - ac_option=$1 - ac_optarg=$2 - ac_shift=shift - ;; - esac - - case $ac_option in - # Handling of the options. - -recheck | --recheck | --rechec | --reche | --rech | --rec | --re | --r) - ac_cs_recheck=: ;; - --version | --versio | --versi | --vers | --ver | --ve | --v | -V ) - $as_echo "$ac_cs_version"; exit ;; - --config | --confi | --conf | --con | --co | --c ) - $as_echo "$ac_cs_config"; exit ;; - --debug | --debu | --deb | --de | --d | -d ) - debug=: ;; - --file | --fil | --fi | --f ) - $ac_shift - case $ac_optarg in - *\'*) ac_optarg=`$as_echo "$ac_optarg" | sed "s/'/'\\\\\\\\''/g"` ;; - '') as_fn_error $? "missing file argument" ;; - esac - as_fn_append CONFIG_FILES " '$ac_optarg'" - ac_need_defaults=false;; - --he | --h | --help | --hel | -h ) - $as_echo "$ac_cs_usage"; exit ;; - -q | -quiet | --quiet | --quie | --qui | --qu | --q \ - | -silent | --silent | --silen | --sile | --sil | --si | --s) - ac_cs_silent=: ;; - - # This is an error. - -*) as_fn_error $? "unrecognized option: \`$1' -Try \`$0 --help' for more information." ;; - - *) as_fn_append ac_config_targets " $1" - ac_need_defaults=false ;; - - 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 || ac_write_fail=1 -if \$ac_cs_recheck; then - set X '$SHELL' '$0' $ac_configure_args \$ac_configure_extra_args --no-create --no-recursion - shift - \$as_echo "running CONFIG_SHELL=$SHELL \$*" >&6 - CONFIG_SHELL='$SHELL' - export CONFIG_SHELL - exec "\$@" -fi - -_ACEOF -cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 -exec 5>>config.log -{ - echo - sed 'h;s/./-/g;s/^.../## /;s/...$/ ##/;p;x;p;x' <<_ASBOX -## Running $as_me. ## -_ASBOX - $as_echo "$ac_log" -} >&5 - -_ACEOF -cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 -_ACEOF - -cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 - -# Handling of arguments. -for ac_config_target in $ac_config_targets -do - case $ac_config_target in - "$OUTPUT_FILES") CONFIG_FILES="$CONFIG_FILES $OUTPUT_FILES" ;; - - *) as_fn_error $? "invalid argument: \`$ac_config_target'" "$LINENO" 5;; - 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 against having it here, and in addition, -# creating and moving files from /tmp can sometimes cause problems. -# Hook for its removal unless debugging. -# Note that there is a small window in which the directory will not be cleaned: -# after its creation but before its name has been assigned to `$tmp'. -$debug || -{ - tmp= ac_tmp= - trap 'exit_status=$? - : "${ac_tmp:=$tmp}" - { test ! -d "$ac_tmp" || rm -fr "$ac_tmp"; } && exit $exit_status -' 0 - trap 'as_fn_exit 1' 1 2 13 15 -} -# Create a (secure) tmp directory for tmp files. - -{ - tmp=`(umask 077 && mktemp -d "./confXXXXXX") 2>/dev/null` && - test -d "$tmp" -} || -{ - tmp=./conf$$-$RANDOM - (umask 077 && mkdir "$tmp") -} || as_fn_error $? "cannot create a temporary directory in ." "$LINENO" 5 -ac_tmp=$tmp - -# Set up the scripts for CONFIG_FILES section. -# No need to generate them if there are no CONFIG_FILES. -# This happens for instance with `./config.status config.h'. -if test -n "$CONFIG_FILES"; then - - -ac_cr=`echo X | tr X '\015'` -# On cygwin, bash can eat \r inside `` if the user requested igncr. -# But we know of no other shell where ac_cr would be empty at this -# point, so we can use a bashism as a fallback. -if test "x$ac_cr" = x; then - eval ac_cr=\$\'\\r\' -fi -ac_cs_awk_cr=`$AWK 'BEGIN { print "a\rb" }' /dev/null` -if test "$ac_cs_awk_cr" = "a${ac_cr}b"; then - ac_cs_awk_cr='\\r' -else - ac_cs_awk_cr=$ac_cr -fi - -echo 'BEGIN {' >"$ac_tmp/subs1.awk" && -_ACEOF - - -{ - echo "cat >conf$$subs.awk <<_ACEOF" && - echo "$ac_subst_vars" | sed 's/.*/&!$&$ac_delim/' && - echo "_ACEOF" -} >conf$$subs.sh || - as_fn_error $? "could not make $CONFIG_STATUS" "$LINENO" 5 -ac_delim_num=`echo "$ac_subst_vars" | grep -c '^'` -ac_delim='%!_!# ' -for ac_last_try in false false false false false :; do - . ./conf$$subs.sh || - as_fn_error $? "could not make $CONFIG_STATUS" "$LINENO" 5 - - ac_delim_n=`sed -n "s/.*$ac_delim\$/X/p" conf$$subs.awk | grep -c X` - if test $ac_delim_n = $ac_delim_num; then - break - elif $ac_last_try; then - as_fn_error $? "could not make $CONFIG_STATUS" "$LINENO" 5 - else - ac_delim="$ac_delim!$ac_delim _$ac_delim!! " - fi -done -rm -f conf$$subs.sh - -cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 -cat >>"\$ac_tmp/subs1.awk" <<\\_ACAWK && -_ACEOF -sed -n ' -h -s/^/S["/; s/!.*/"]=/ -p -g -s/^[^!]*!// -:repl -t repl -s/'"$ac_delim"'$// -t delim -:nl -h -s/\(.\{148\}\)..*/\1/ -t more1 -s/["\\]/\\&/g; s/^/"/; s/$/\\n"\\/ -p -n -b repl -:more1 -s/["\\]/\\&/g; s/^/"/; s/$/"\\/ -p -g -s/.\{148\}// -t nl -:delim -h -s/\(.\{148\}\)..*/\1/ -t more2 -s/["\\]/\\&/g; s/^/"/; s/$/"/ -p -b -:more2 -s/["\\]/\\&/g; s/^/"/; s/$/"\\/ -p -g -s/.\{148\}// -t delim -' >$CONFIG_STATUS || ac_write_fail=1 -rm -f conf$$subs.awk -cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 -_ACAWK -cat >>"\$ac_tmp/subs1.awk" <<_ACAWK && - for (key in S) S_is_set[key] = 1 - FS = "" - -} -{ - line = $ 0 - nfields = split(line, field, "@") - substed = 0 - len = length(field[1]) - for (i = 2; i < nfields; i++) { - key = field[i] - keylen = length(key) - if (S_is_set[key]) { - value = S[key] - line = substr(line, 1, len) "" value "" substr(line, len + keylen + 3) - len += length(value) + length(field[++i]) - substed = 1 - } else - len += 1 + keylen - } - - print line -} - -_ACAWK -_ACEOF -cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 -if sed "s/$ac_cr//" < /dev/null > /dev/null 2>&1; then - sed "s/$ac_cr\$//; s/$ac_cr/$ac_cs_awk_cr/g" -else - cat -fi < "$ac_tmp/subs1.awk" > "$ac_tmp/subs.awk" \ - || as_fn_error $? "could not setup config files machinery" "$LINENO" 5 -_ACEOF - -# VPATH may cause trouble with some makes, so we remove sole $(srcdir), -# ${srcdir} and @srcdir@ entries 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[ ]*=[ ]*/{ -h -s/// -s/^/:/ -s/[ ]*$/:/ -s/:\$(srcdir):/:/g -s/:\${srcdir}:/:/g -s/:@srcdir@:/:/g -s/^:*// -s/:*$// -x -s/\(=[ ]*\).*/\1/ -G -s/\n// -s/^[^=]*=[ ]*$// -}' -fi - -cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 -fi # test -n "$CONFIG_FILES" - - -eval set X " :F $CONFIG_FILES " -shift -for ac_tag -do - case $ac_tag in - :[FHLC]) ac_mode=$ac_tag; continue;; - esac - case $ac_mode$ac_tag in - :[FHL]*:*);; - :L* | :C*:*) as_fn_error $? "invalid tag \`$ac_tag'" "$LINENO" 5;; - :[FH]-) ac_tag=-:-;; - :[FH]*) ac_tag=$ac_tag:$ac_tag.in;; - esac - ac_save_IFS=$IFS - IFS=: - set x $ac_tag - IFS=$ac_save_IFS - shift - ac_file=$1 - shift - - case $ac_mode in - :L) ac_source=$1;; - :[FH]) - ac_file_inputs= - for ac_f - do - case $ac_f in - -) ac_f="$ac_tmp/stdin";; - *) # Look for the file first in the build tree, then in the source tree - # (if the path is not absolute). The absolute path cannot be DOS-style, - # because $ac_f cannot contain `:'. - test -f "$ac_f" || - case $ac_f in - [\\/$]*) false;; - *) test -f "$srcdir/$ac_f" && ac_f="$srcdir/$ac_f";; - esac || - as_fn_error 1 "cannot find input file: \`$ac_f'" "$LINENO" 5;; - esac - case $ac_f in *\'*) ac_f=`$as_echo "$ac_f" | sed "s/'/'\\\\\\\\''/g"`;; esac - as_fn_append ac_file_inputs " '$ac_f'" - done - - # 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. */ - configure_input='Generated from '` - $as_echo "$*" | sed 's|^[^:]*/||;s|:[^:]*/|, |g' - `' by configure.' - if test x"$ac_file" != x-; then - configure_input="$ac_file. $configure_input" - { $as_echo "$as_me:${as_lineno-$LINENO}: creating $ac_file" >&5 -$as_echo "$as_me: creating $ac_file" >&6;} - fi - # Neutralize special characters interpreted by sed in replacement strings. - case $configure_input in #( - *\&* | *\|* | *\\* ) - ac_sed_conf_input=`$as_echo "$configure_input" | - sed 's/[\\\\&|]/\\\\&/g'`;; #( - *) ac_sed_conf_input=$configure_input;; - esac - - case $ac_tag in - *:-:* | *:-) cat >"$ac_tmp/stdin" \ - || as_fn_error $? "could not create $ac_file" "$LINENO" 5 ;; - esac - ;; - esac - - ac_dir=`$as_dirname -- "$ac_file" || -$as_expr X"$ac_file" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ - X"$ac_file" : 'X\(//\)[^/]' \| \ - X"$ac_file" : 'X\(//\)$' \| \ - X"$ac_file" : 'X\(/\)' \| . 2>/dev/null || -$as_echo X"$ac_file" | - sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ - s//\1/ - q - } - /^X\(\/\/\)[^/].*/{ - s//\1/ - q - } - /^X\(\/\/\)$/{ - s//\1/ - q - } - /^X\(\/\).*/{ - s//\1/ - q - } - s/.*/./; q'` - as_dir="$ac_dir"; as_fn_mkdir_p - ac_builddir=. - -case "$ac_dir" in -.) ac_dir_suffix= ac_top_builddir_sub=. ac_top_build_prefix= ;; -*) - ac_dir_suffix=/`$as_echo "$ac_dir" | sed 's|^\.[\\/]||'` - # A ".." for each directory in $ac_dir_suffix. - ac_top_builddir_sub=`$as_echo "$ac_dir_suffix" | sed 's|/[^\\/]*|/..|g;s|/||'` - case $ac_top_builddir_sub in - "") ac_top_builddir_sub=. ac_top_build_prefix= ;; - *) ac_top_build_prefix=$ac_top_builddir_sub/ ;; - esac ;; -esac -ac_abs_top_builddir=$ac_pwd -ac_abs_builddir=$ac_pwd$ac_dir_suffix -# for backward compatibility: -ac_top_builddir=$ac_top_build_prefix - -case $srcdir in - .) # We are building in place. - ac_srcdir=. - ac_top_srcdir=$ac_top_builddir_sub - ac_abs_top_srcdir=$ac_pwd ;; - [\\/]* | ?:[\\/]* ) # Absolute name. - ac_srcdir=$srcdir$ac_dir_suffix; - ac_top_srcdir=$srcdir - ac_abs_top_srcdir=$srcdir ;; - *) # Relative name. - ac_srcdir=$ac_top_build_prefix$srcdir$ac_dir_suffix - ac_top_srcdir=$ac_top_build_prefix$srcdir - ac_abs_top_srcdir=$ac_pwd/$srcdir ;; -esac -ac_abs_srcdir=$ac_abs_top_srcdir$ac_dir_suffix - - - case $ac_mode in - :F) - # - # CONFIG_FILE - # - -_ACEOF - -cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 -# If the template does not know about datarootdir, expand it. -# FIXME: This hack should be removed a few years after 2.60. -ac_datarootdir_hack=; ac_datarootdir_seen= -ac_sed_dataroot=' -/datarootdir/ { - p - q -} -/@datadir@/p -/@docdir@/p -/@infodir@/p -/@localedir@/p -/@mandir@/p' -case `eval "sed -n \"\$ac_sed_dataroot\" $ac_file_inputs"` in -*datarootdir*) ac_datarootdir_seen=yes;; -*@datadir@*|*@docdir@*|*@infodir@*|*@localedir@*|*@mandir@*) - { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $ac_file_inputs seems to ignore the --datarootdir setting" >&5 -$as_echo "$as_me: WARNING: $ac_file_inputs seems to ignore the --datarootdir setting" >&2;} -_ACEOF -cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 - ac_datarootdir_hack=' - s&@datadir@&$datadir&g - s&@docdir@&$docdir&g - s&@infodir@&$infodir&g - s&@localedir@&$localedir&g - s&@mandir@&$mandir&g - s&\\\${datarootdir}&$datarootdir&g' ;; -esac -_ACEOF - -# Neutralize VPATH when `$srcdir' = `.'. -# Shell code in configure.ac might set extrasub. -# FIXME: do we really want to maintain this feature? -cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 -ac_sed_extra="$ac_vpsub -$extrasub -_ACEOF -cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 -:t -/@[a-zA-Z_][a-zA-Z_0-9]*@/!b -s|@configure_input@|$ac_sed_conf_input|;t t -s&@top_builddir@&$ac_top_builddir_sub&;t t -s&@top_build_prefix@&$ac_top_build_prefix&;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&@abs_top_builddir@&$ac_abs_top_builddir&;t t -$ac_datarootdir_hack -" -eval sed \"\$ac_sed_extra\" "$ac_file_inputs" | $AWK -f "$ac_tmp/subs.awk" \ - >$ac_tmp/out || as_fn_error $? "could not create $ac_file" "$LINENO" 5 - -test -z "$ac_datarootdir_hack$ac_datarootdir_seen" && - { ac_out=`sed -n '/\${datarootdir}/p' "$ac_tmp/out"`; test -n "$ac_out"; } && - { ac_out=`sed -n '/^[ ]*datarootdir[ ]*:*=/p' \ - "$ac_tmp/out"`; test -z "$ac_out"; } && - { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $ac_file contains a reference to the variable \`datarootdir' -which seems to be undefined. Please make sure it is defined" >&5 -$as_echo "$as_me: WARNING: $ac_file contains a reference to the variable \`datarootdir' -which seems to be undefined. Please make sure it is defined" >&2;} - - rm -f "$ac_tmp/stdin" - case $ac_file in - -) cat "$ac_tmp/out" && rm -f "$ac_tmp/out";; - *) rm -f "$ac_file" && mv "$ac_tmp/out" "$ac_file";; - esac \ - || as_fn_error $? "could not create $ac_file" "$LINENO" 5 - ;; - - - - esac - -done # for ac_tag - - -as_fn_exit 0 -_ACEOF -ac_clean_files=$ac_clean_files_save - -test $ac_write_fail = 0 || - as_fn_error $? "write failure creating $CONFIG_STATUS" "$LINENO" 5 - - -# 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 || as_fn_exit 1 -fi -if test -n "$ac_unrecognized_opts" && test "$enable_option_checking" != no; then - { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: unrecognized options: $ac_unrecognized_opts" >&5 -$as_echo "$as_me: WARNING: unrecognized options: $ac_unrecognized_opts" >&2;} -fi - diff --git a/libtecla/configure.in b/libtecla/configure.in deleted file mode 100644 index 2d668d5..0000000 --- a/libtecla/configure.in +++ /dev/null @@ -1,614 +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="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="3" - -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 - ], [ - char *name = ptsname(0); - int i1 = grantpt(0); - int i2 = unlockpt(0); - 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 Check if this system uses the system-V streams pseudo terminal interface. - -AC_CACHE_CHECK(for SysV streams pseudo-terminals, tecla_cv_sysv_ptem, [ - 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"); - int i4 = ioctl(fd, I_PUSH, "ldterm"); - return 0; - ], tecla_cv_sysv_ptem=yes, tecla_cv_sysv_ptem=no) -]) - -dnl If the system-V pseudo-terminal interface is available, arrange -dnl for HAVE_SYSV_PTEM to be defined by CFLAGS. - -if test $tecla_cv_sysv_ptem = yes; then - AC_DEFINE(HAVE_SYSV_PTEM) -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 - -dnl Try to find libgcc.a. Beware that the clang compiler pretends to -dnl be gcc and even understands -print-libgcc-file-name, but just -dnl replies to it with "libgcc.a" without a path. This makes -dnl compilations fail, so we have to check here whether the returned -dnl filename actually exists, and only use it if it does. - - LIBGCC="`$CC -print-libgcc-file-name 2>/dev/null`" - if test "$LIBGCC"_ != "_" && test -r "$LIBGCC"; then - LINK_SHARED="$LINK_SHARED $LIBGCC" - fi -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/cplfile.c b/libtecla/cplfile.c deleted file mode 100644 index 35d15eb..0000000 --- a/libtecla/cplfile.c +++ /dev/null @@ -1,870 +0,0 @@ -/* - * Copyright (c) 2000, 2001, 2002, 2003, 2004, 2012 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/cplmatch.h b/libtecla/cplmatch.h deleted file mode 100644 index 0976265..0000000 --- a/libtecla/cplmatch.h +++ /dev/null @@ -1,47 +0,0 @@ -#ifndef cplmatch_h -#define cplmatch_h - -/* - * Copyright (c) 2000, 2001, 2002, 2003, 2004, 2012 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/demo.c b/libtecla/demo.c deleted file mode 100644 index e115472..0000000 --- a/libtecla/demo.c +++ /dev/null @@ -1,166 +0,0 @@ -/* - * Copyright (c) 2000, 2001, 2002, 2003, 2004, 2012 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/demo2.c b/libtecla/demo2.c deleted file mode 100644 index 8bfd739..0000000 --- a/libtecla/demo2.c +++ /dev/null @@ -1,423 +0,0 @@ -/* - * Copyright (c) 2000, 2001, 2002, 2003, 2004, 2012 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/demo3.c b/libtecla/demo3.c deleted file mode 100644 index 7dff98a..0000000 --- a/libtecla/demo3.c +++ /dev/null @@ -1,738 +0,0 @@ -/* - * Copyright (c) 2002, 2003, 2004, 2012 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/direader.c b/libtecla/direader.c deleted file mode 100644 index ec993db..0000000 --- a/libtecla/direader.c +++ /dev/null @@ -1,309 +0,0 @@ -/* - * Copyright (c) 2000, 2001, 2002, 2003, 2004, 2012 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/direader.h b/libtecla/direader.h deleted file mode 100644 index b7dbcbb..0000000 --- a/libtecla/direader.h +++ /dev/null @@ -1,44 +0,0 @@ -#ifndef dirreader_h -#define dirreader_h - -/* - * Copyright (c) 2000, 2001, 2002, 2003, 2004, 2012 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/enhance.c b/libtecla/enhance.c deleted file mode 100644 index c6dccbb..0000000 --- a/libtecla/enhance.c +++ /dev/null @@ -1,698 +0,0 @@ -#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 -char *ptsname(int fd); -int grantpt(int fd); -int unlockpt(int fd); -#endif - -#if HAVE_SYSV_PTEM -#include /* System-V stream I/O */ -#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 20 - -/* - * 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_PTEM - (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/errmsg.c b/libtecla/errmsg.c deleted file mode 100644 index 95748f5..0000000 --- a/libtecla/errmsg.c +++ /dev/null @@ -1,167 +0,0 @@ -/* - * Copyright (c) 2000, 2001, 2002, 2003, 2004, 2012 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/errmsg.h b/libtecla/errmsg.h deleted file mode 100644 index 091bfdd..0000000 --- a/libtecla/errmsg.h +++ /dev/null @@ -1,85 +0,0 @@ -#ifndef errmsg_h -#define errmsg_h - -/* - * Copyright (c) 2000, 2001, 2002, 2003, 2004, 2012 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/expand.c b/libtecla/expand.c deleted file mode 100644 index d22e069..0000000 --- a/libtecla/expand.c +++ /dev/null @@ -1,1448 +0,0 @@ -/* - * Copyright (c) 2000, 2001, 2002, 2003, 2004, 2012 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/expand.h b/libtecla/expand.h deleted file mode 100644 index ca05dc7..0000000 --- a/libtecla/expand.h +++ /dev/null @@ -1,48 +0,0 @@ -#ifndef expand_h -#define expand_h - -/* - * Copyright (c) 2000, 2001, 2002, 2003, 2004, 2012 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/freelist.c b/libtecla/freelist.c deleted file mode 100644 index d539639..0000000 --- a/libtecla/freelist.c +++ /dev/null @@ -1,400 +0,0 @@ -/* - * Copyright (c) 2000, 2001, 2002, 2003, 2004, 2012 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/freelist.h b/libtecla/freelist.h deleted file mode 100644 index 6edd9fa..0000000 --- a/libtecla/freelist.h +++ /dev/null @@ -1,87 +0,0 @@ -#ifndef freelist_h -#define freelist_h - -/* - * Copyright (c) 2000, 2001, 2002, 2003, 2004, 2012 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/getline.c b/libtecla/getline.c deleted file mode 100644 index a58a1b4..0000000 --- a/libtecla/getline.c +++ /dev/null @@ -1,12849 +0,0 @@ -/* - * Copyright (c) 2000, 2001, 2002, 2003, 2004, 2012 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. - * If you add or remove a directive from this list, be sure to update - * the equivalent list of directives in gl_displayed_prompt_width(). - */ - 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 'U': case 'S': case 'P': case 'F': case 'V': - case 'b': case 'u': case 's': case 'p': case 'f': case 'v': - 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_truncate_buffer(gl, 0); - 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/getline.h b/libtecla/getline.h deleted file mode 100644 index 9dba35f..0000000 --- a/libtecla/getline.h +++ /dev/null @@ -1,88 +0,0 @@ -#ifndef getline_h -#define getline_h - -/* - * Copyright (c) 2000, 2001, 2002, 2003, 2004, 2012 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/hash.c b/libtecla/hash.c deleted file mode 100644 index e61c564..0000000 --- a/libtecla/hash.c +++ /dev/null @@ -1,737 +0,0 @@ -/* - * Copyright (c) 2000, 2001, 2002, 2003, 2004, 2012 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/hash.h b/libtecla/hash.h deleted file mode 100644 index 4cad68a..0000000 --- a/libtecla/hash.h +++ /dev/null @@ -1,157 +0,0 @@ -#ifndef hash_h -#define hash_h - -/* - * Copyright (c) 2000, 2001, 2002, 2003, 2004, 2012 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/history.c b/libtecla/history.c deleted file mode 100644 index f798ff8..0000000 --- a/libtecla/history.c +++ /dev/null @@ -1,2842 +0,0 @@ -/* - * Copyright (c) 2000, 2001, 2002, 2003, 2004, 2012 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/history.h b/libtecla/history.h deleted file mode 100644 index e0278cd..0000000 --- a/libtecla/history.h +++ /dev/null @@ -1,169 +0,0 @@ -#ifndef history_h -#define history_h - -/* - * Copyright (c) 2000, 2001, 2002, 2003, 2004, 2012 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/homedir.c b/libtecla/homedir.c deleted file mode 100644 index 29257f8..0000000 --- a/libtecla/homedir.c +++ /dev/null @@ -1,470 +0,0 @@ -/* - * Copyright (c) 2000, 2001, 2002, 2003, 2004, 2012 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/homedir.h b/libtecla/homedir.h deleted file mode 100644 index 096708d..0000000 --- a/libtecla/homedir.h +++ /dev/null @@ -1,81 +0,0 @@ -#ifndef homedir_h -#define homedir_h - -/* - * Copyright (c) 2000, 2001, 2002, 2003, 2004, 2012 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/html/changes.html b/libtecla/html/changes.html deleted file mode 100644 index ed34d3b..0000000 --- a/libtecla/html/changes.html +++ /dev/null @@ -1,2826 +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.
-
-09/11/2014 mcs@astro.caltech.edu
-           configure.in configure
-             Dominyk Tiller reported that libtecla didn't compile on
-             Mac OS X, due to libgcc.a being cited in the makefile
-             without any path. This turned out to be because Gnu
-             autoconf claims that the clang compiler is gcc, and clang
-             has a -print-libgcc-file-name, but this only prints
-             libgcc.a. I have modified the configure script to check
-             whether the path returned by -print-libgcc-file-name
-             actually exists and only use it if it does.
-
-27/10/2014 Jon Szymaniak (documented here by mcs@astro.caltech.edu)
-           Makefile.rules
-             Use $(AR) instead of plain ar, so that cross-compilation
-             uses the correct ar program when cross-compiling.
-
-             Add $(TARGETS) dependency to building the demo programs
-             and the enhance program. When using parallel compilation
-             this is needed to ensure that the library is compiled
-             before the demos.
-
-10/04/2013 mcs@astro.caltech.edu
-           Makefile.in
-             Jonathan Niehof reported that libtecla wouldn't compile if
-             there were spaces in LDFLAGS and pointed out that there should
-             be quotes around $(LDFLAGS) in Makefile.in. This also applied
-             to a few other variables cited in the same way.
-
-10/06/2012 mcs@astro.caltech.edu
-           enhance.c configure.in
-	     I had incorrectly assumed that system-V pseudo-terminal
-	     allocation and system-V streams terminals always went
-	     together.  However system-V pseudo terminal allocation is
-	     now part of UNIX98, and this has been adopted into many BSD
-	     style operating systems, without the use of system-V
-	     streams. On such systems the lack of system-V streams IOCTL
-             opcodes prevented system-V pseudo-terminal allocation being
-             used. This was hidden under Linux until recently, because
-             it had a stropts.h file, which made it appear as though
-             Linux supported system-V streams terminals.
-
-	     I have now created separate configuration tests and options
-	     in the configure script for system-V terminal allocation
-	     and system-V streams. On systems that only have the former,
-             the latter won't be used.
-
-16/05/2005 mcs@astro.caltech.edu
-           getline.c
-	     When an initial input line was presented to gl_get_line()
-	     for editing, the new input line was incorrectly appended to
-	     the previous input line, instead of replacing it.
-
-10/01/2004 Derek Jones (documented here by mcs@astro.caltech.edu)
-           getline.c
-             Derek discovered that the function that computes the
-             width of the prompt, was not correctly skipping over 3 of
-             the 6 possible prompt-formatting directives. Thus, when
-             the %f,%p or %v prompt-formatting directives were used,
-             the width of the prompt was incorrectly calculated. The
-             fix was to copy the list of directives from
-             gl_display_prompt(). I have also added a comment to
-             gl_display_prompt(), to warn anybody who adds or removes
-             formatting directives there, to also do the same to
-             gl_displayed_prompt_width().
-
-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/html/cpl_complete_word.html b/libtecla/html/cpl_complete_word.html deleted file mode 100644 index e69c8c0..0000000 --- a/libtecla/html/cpl_complete_word.html +++ /dev/null @@ -1,477 +0,0 @@ -Content-type: text/html - - -Man page of cpl_complete_word - -

cpl_complete_word

-Section: C Library Functions (3)
Index -Return to Main Contents
- -  -

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(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 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 cpl_add_completion(). -

-

-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 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 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_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 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_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 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. -

-

-  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 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 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 function. -

-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(). -

-

-  CplMatches *cpl_recall_matches(WordCompletion *cpl);
-
- -

-As a convenience, the return value of the last call to -cpl_complete_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 -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. Otherwise it should be a -pointer to a CplFileConf 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 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 -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 filename that it is given represents a normal file that the -user has execute 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_completions(). -

-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(3), gl_get_line(3), ef_expand_file(3),
-pca_lookup_file(3)
-
- -

-  -

AUTHOR

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

- -


- 

Index

-
-
NAME
-
SYNOPSIS
-
DESCRIPTION
-
THE BUILT-IN FILENAME-COMPLETION CALLBACK
-
THREAD SAFETY
-
FILES
-
SEE ALSO
-
AUTHOR
-
-
-This document was created by -man2html, -using the manual pages.
-Time: 22:21:57 GMT, November 09, 2014 - - diff --git a/libtecla/html/ef_expand_file.html b/libtecla/html/ef_expand_file.html deleted file mode 100644 index 13db3b5..0000000 --- a/libtecla/html/ef_expand_file.html +++ /dev/null @@ -1,273 +0,0 @@ -Content-type: text/html - - -Man page of ef_expand_file - -

ef_expand_file

-Section: C Library Functions (3)
Index -Return to Main Contents
- -  -

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(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 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 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 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(3), gl_get_line(3), cpl_complete_word(3),
-pca_lookup_file(3)
-
- -

-  -

AUTHOR

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

- -


- 

Index

-
-
NAME
-
SYNOPSIS
-
DESCRIPTION
-
THREAD SAFETY
-
FILES
-
SEE ALSO
-
AUTHOR
-
-
-This document was created by -man2html, -using the manual pages.
-Time: 22:21:57 GMT, November 09, 2014 - - diff --git a/libtecla/html/enhance.html b/libtecla/html/enhance.html deleted file mode 100644 index f048a81..0000000 --- a/libtecla/html/enhance.html +++ /dev/null @@ -1,105 +0,0 @@ -Content-type: text/html - - -Man page of enhance - -

enhance

-Section: User Commands (1)
Index -Return to Main Contents
- -  -

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 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. -

-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. -

-Note that the user-level command-line editing facilities provided by -the Tecla library are documented in the tecla(7) 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 -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 knowing 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 -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(7), libtecla(3) -
   -  -

AUTHOR

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

- -


- 

Index

-
-
NAME
-
SYNOPSIS
-
DESCRIPTION
-
DEFICIENCIES
-
FILES
-
SEE ALSO
-
AUTHOR
-
-
-This document was created by -man2html, -using the manual pages.
-Time: 22:21:57 GMT, November 09, 2014 - - diff --git a/libtecla/html/gl_get_line.html b/libtecla/html/gl_get_line.html deleted file mode 100644 index 7f922fe..0000000 --- a/libtecla/html/gl_get_line.html +++ /dev/null @@ -1,2407 +0,0 @@ -Content-type: text/html - - -Man page of gl_get_line - -

gl_get_line

-Section: C Library Functions (3)
Index -Return to Main Contents
- -  -

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

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 -libtecla(3) 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 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(7) 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() 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 allocated by new_GetLine(), 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 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, 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 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. 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 terminal,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 information 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(3) 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 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.
-
- -

-  -

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 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 characters. 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 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 -configuration commands from an optional .teclarc file in the -user's home directory. Note that the arguments are listed in ascending -order of priority, with the contents of app_string being -potentially overriden 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 specifies the filenames that you want re-read when the user -requests that the configuration 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_configure_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 -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 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_completion_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_complete_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 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 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 additional action -won't be known, and any reference to it in the configuration -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 -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 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 entering 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. 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 count argument, or use its value for a -different purpose. -

-A copy of the current input line is passed in the read-only line -argument. 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 -restoring the normal mapping of \n to \r\n, 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 -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 -application, and subsequently restore them when you next start the -application, 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 -prefixes 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 -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 entering -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. -Initially 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 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. -

-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 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 %H 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 successively 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. Therefore 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 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 -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 terminated 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 -calling 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 little 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 -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 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_history()), 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-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 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 -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 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(3) 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 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 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 -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. -

-

-

-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_normal_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 -function which occasionally and unpredictably writes to the terminal, -the automatic conversion of " to " 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 -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 following 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(3)), 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 -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. -

-

-

-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_normal_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 -function which occasionally and unpredictably writes to the terminal, -the automatic conversion of " to " 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 -GLTO_REFRESH return value, to tell gl_get_line() to -redisplay the input line. -

-

-

-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 -sysconf(_SC_CLK_TCK)). -

-

-

-To turn off timeouts, simply call gl_inactivity_timeout() 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. Changing 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 signal 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 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 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 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-control shells usually reset the -terminal settings when a process relinquishes 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 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 overridden it, never -either writes to the terminal, 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 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 -\ -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 -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 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 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 -conditions. In gl_get_line() a lot of care has been taken to -allow applications 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 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 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 functions, such as sigsetjmp() and -sigsuspend(), and in particular, the former of these -two functions can be used in conjunction with -siglongjmp() 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 executing. 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 signals 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 -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 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 support 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 -dimensions, 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. -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 provided 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 -completion will invisibly complete your prefix as far as -possible, ambiguous completions 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 character, 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_status(), 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 ' 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(3)), 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() 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 -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_terminal(). 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 introductions. In those examples the advanced use of -optional prefixes, suffixes and filled lines to draw a box around the -text is also illustrated. -

-

-

-  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 output of a program that uses gl_get_line() is being piped -to another program 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 character 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 paragraph. 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(3) 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 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. Further documentation on how the user can make use -of this to enter international characters can be found in the -tecla(7) 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 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. -

-  -

FILES

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

-  -

SEE ALSO

- -
-libtecla(3), gl_io_mode(3), tecla(7), ef_expand_file(3),
-cpl_complete_word(3), pca_lookup_file(3)
-
- -

-  -

AUTHOR

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

- -


- 

Index

-
-
NAME
-
SYNOPSIS
-
DESCRIPTION
-
AN EXAMPLE
-
THE FUNCTIONS USED IN THE EXAMPLE
-
THE RETURN STATUS OF GL_GET_LINE
-
OPTIONAL PROMPT FORMATTING
-
ALTERNATE CONFIGURATION SOURCES
-
CUSTOMIZED WORD COMPLETION
-
ADDING COMPLETION ACTIONS
-
DEFINING CUSTOM ACTIONS
-
HISTORY FILES
-
MULTIPLE HISTORY LISTS
-
DISPLAYING HISTORY
-
LOOKING UP HISTORY
-
MANUAL HISTORY ARCHIVAL
-
MISCELLANEOUS HISTORY CONFIGURATION
-
QUERYING HISTORY INFORMATION
-
CHANGING TERMINALS
-
EXTERNAL EVENT HANDLING
-
SETTING AN INACTIVITY TIMEOUT
-
SIGNAL HANDLING DEFAULTS
-
CUSTOMIZED SIGNAL HANDLING
-
RELIABLE SIGNAL HANDLING
-
THE TERMINAL SIZE
-
HIDING WHAT YOU TYPE
-
SINGLE CHARACTER QUERIES
-
READING RAW CHARACTERS
-
CLEARING THE TERMINAL
-
DISPLAYING TEXT DYNAMICALLY
-
CALLBACK FUNCTION FACILITIES
-
INTERNATIONAL CHARACTER SETS
-
THREAD SAFETY
-
FILES
-
SEE ALSO
-
AUTHOR
-
-
-This document was created by -man2html, -using the manual pages.
-Time: 22:21:57 GMT, November 09, 2014 - - diff --git a/libtecla/html/gl_io_mode.html b/libtecla/html/gl_io_mode.html deleted file mode 100644 index 42d27dc..0000000 --- a/libtecla/html/gl_io_mode.html +++ /dev/null @@ -1,634 +0,0 @@ -Content-type: text/html - - -Man page of gl_io_mode - -

gl_io_mode

-Section: C Library Functions (3)
Index -Return to Main Contents
- -  -

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(3) 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(3) 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 -application calls the gl_pending_io() function. -

-

-

-  GlPendingIO gl_pending_io(GetLine *gl);
-
- -

-

-The return value of this function is one of the following two -enumerated 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(3) 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(3) 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 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 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 -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 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 handlers 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 particular, it is rarely useful to trap SIGCONT, -so the cont_handler argument 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 -different 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 -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. -

-  -

Process termination signals.

- -

-If the signal that was caught is one of those that by default -terminates 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 -configure the arguments of select() or poll() 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 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 -handler will be triggered and cause control to return to the -sigsetjmp() statement, where this time, sigsetjmp() 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 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 arguments 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 signal 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 signal, gl_get_line() returns NULL, and -a following call to gl_return_status() 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 -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 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 application 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_timeout() function (see -gl_get_line(3)), 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(3), gl_get_line(3), tecla(7), ef_expand_file(3),
-cpl_complete_word(3), pca_lookup_file(3)
-
- -

-  -

AUTHOR

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

- -


- 

Index

-
-
NAME
-
SYNOPSIS
-
DESCRIPTION
-
SERVER I/O MODE
-
GIVING UP THE TERMINAL
-
SIGNAL HANDLING
-
-
Terminal resize signals (SIGWINCH)
-
-
Process termination signals.
-
Process suspension signals.
-
INTERRUPTING THE EVENT LOOP
-
SIGNALS CAUGHT BY GL_GET_LINE
-
ABORTING LINE INPUT
-
SIGNAL SAFE FUNCTIONS
-
USING TIMEOUTS TO POLL
-
THE SERVER DEMO PROGRAM
-
FILES
-
SEE ALSO
-
AUTHOR
-
-
-This document was created by -man2html, -using the manual pages.
-Time: 22:21:57 GMT, November 09, 2014 - - diff --git a/libtecla/html/index.html b/libtecla/html/index.html deleted file mode 100644 index fb34147..0000000 --- a/libtecla/html/index.html +++ /dev/null @@ -1,73 +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.6.3. This may be obtained from: -

- http://www.astro.caltech.edu/~mcs/tecla/libtecla-1.6.3.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. -
- -
-Martin Shepherd (09-Nov-2014) - diff --git a/libtecla/html/libtecla.html b/libtecla/html/libtecla.html deleted file mode 100644 index b155918..0000000 --- a/libtecla/html/libtecla.html +++ /dev/null @@ -1,199 +0,0 @@ -Content-type: text/html - - -Man page of libtecla - -

libtecla

-Section: C Library Functions (3)
Index -Return to Main Contents
- -  -

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 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(7)              -  Use level documentation of the
-                        command-line editing facilities
-                        provided by gl_get_line().
-  gl_get_line(3)        -  The interactive line-input module.
-  gl_io_mode(3)         -  How to use gl_get_line() in an
-                        incremental, non-blocking fashion.
-  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(1)            -  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 ~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 -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), tecla(7), gl_io_mode(3), ef_expand_file(3),
-cpl_complete_word(3), pca_lookup_file(3), enhance(1)
-
- -

-  -

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.
-
- -

- -


- 

Index

-
-
NAME
-
SYNOPSIS
-
DESCRIPTION
-
THREAD SAFETY
-
LIBRARY VERSION NUMBER
-
TRIVIA
-
FILES
-
SEE ALSO
-
AUTHOR
-
ACKNOWLEDGMENTS
-
-
-This document was created by -man2html, -using the manual pages.
-Time: 22:21:57 GMT, November 09, 2014 - - diff --git a/libtecla/html/pca_lookup_file.html b/libtecla/html/pca_lookup_file.html deleted file mode 100644 index 44b0cb2..0000000 --- a/libtecla/html/pca_lookup_file.html +++ /dev/null @@ -1,420 +0,0 @@ -Content-type: text/html - - -Man page of pca_lookup_file - -

pca_lookup_file

-Section: C Library Functions (3)
Index -Return to Main Contents
- -  -

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 relative 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 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. -

-  -

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 specified 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 -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 functions which allow you to -individually change these parameters are discussed below. -

-By default, the pca_path_completions() 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 -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 -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 -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 variable. 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 -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 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 callback 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_completions(). -

-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_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) -

- -


- 

Index

-
-
NAME
-
SYNOPSIS
-
DESCRIPTION
-
AN EXAMPLE
-
FUNCTION DESCRIPTIONS
-
POPULATING THE CACHE
-
LOOKING UP FILES
-
FILENAME COMPLETION
-
BEING SELECTIVE
-
ERROR HANDLING
-
CLEANING UP
-
THREAD SAFETY
-
FILES
-
SEE ALSO
-
AUTHOR
-
-
-This document was created by -man2html, -using the manual pages.
-Time: 22:21:57 GMT, November 09, 2014 - - diff --git a/libtecla/html/release.html b/libtecla/html/release.html deleted file mode 100644 index 22729cf..0000000 --- a/libtecla/html/release.html +++ /dev/null @@ -1,604 +0,0 @@ -The tecla library release notes -
-This file lists major changes which accompany each new release.
-
-Version 1.6.3:
-
-  This release corrects some problems in the build process,
-  including one that was preventing libtecla from being compiled
-  on Mac OS X.
-
-Version 1.6.2:
-
-  This release updates the configuration script to ensure that the
-  enhance utility program is compiled correctly on systems that have
-  system V psuedo-terminal allocation but not system V streams.
-
-  There are no new features.
-
-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/html/tecla.html b/libtecla/html/tecla.html deleted file mode 100644 index 3031a48..0000000 --- a/libtecla/html/tecla.html +++ /dev/null @@ -1,1276 +0,0 @@ -Content-type: text/html - - -Man page of tecla - -

tecla

-Section: Environments, Tables, and Troff Macros (7)
Index -Return to Main Contents
- -  -

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 -familiar, but with a few minor differences, most notably in how -forward and backward searches through the list of historical commands -are performed. 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 completion 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 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 -following 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 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 -example, 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_complete_word(3) 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 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. 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 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", Tecla -would recall the "ls -l getline.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, Similarly, hitting M-p again, would recall the "ls -~/tecla/" line, and hitting it once more would recall the "ls -~/tecla/" line. The pattern syntax is the same as that described for -filename expansion, in the ef_expand_file(3 man -page. -

-  -

HISTORY FILES

- -

-Authors of programs that use the Tecla library have the option of -saving 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 different 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 -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 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 -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 usually 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 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 laptop 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. -

-  -

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 -combination 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 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 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 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 -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 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 -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 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 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 -interpreted 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 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 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(3), gl_get_line(3), gl_io_mode(3), ef_expand_file(3),
-cpl_complete_word(3), pca_lookup_file(3)
-
- -
   -  -

AUTHOR

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

- -


- 

Index

-
-
NAME
-
DESCRIPTION
-
KEY SEQUENCE NOTATION
-
THE TECLA CONFIGURATION FILE
-
FILENAME AND TILDE COMPLETION
-
FILENAME EXPANSION
-
RECALLING PREVIOUSLY TYPED LINES
-
HISTORY FILES
-
INTERNATIONAL CHARACTER SETS
-
-
Meta keys and locales
-
Entering international characters
-
-
THE AVAILABLE KEY BINDING FUNCTIONS
-
DEFAULT KEY BINDINGS IN EMACS MODE
-
DEFAULT KEY BINDINGS IN VI MODE
-
ENTERING REPEAT COUNTS
-
FILES
-
SEE ALSO
-
AUTHOR
-
-
-This document was created by -man2html, -using the manual pages.
-Time: 22:21:57 GMT, November 09, 2014 - - diff --git a/libtecla/install-sh b/libtecla/install-sh deleted file mode 100755 index e9de238..0000000 --- a/libtecla/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/ioutil.c b/libtecla/ioutil.c deleted file mode 100644 index 3d002c7..0000000 --- a/libtecla/ioutil.c +++ /dev/null @@ -1,330 +0,0 @@ -/* - * Copyright (c) 2000, 2001, 2002, 2003, 2004, 2012 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/keytab.h b/libtecla/keytab.h deleted file mode 100644 index 25556e0..0000000 --- a/libtecla/keytab.h +++ /dev/null @@ -1,157 +0,0 @@ -#ifndef keytab_h -#define keytab_h - -/* - * Copyright (c) 2000, 2001, 2002, 2003, 2004, 2012 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/libtecla.h b/libtecla/libtecla.h deleted file mode 100644 index eb76e2f..0000000 --- a/libtecla/libtecla.h +++ /dev/null @@ -1,1834 +0,0 @@ -#ifndef libtecla_h -#define libtecla_h - -/* - * Copyright (c) 2000, 2001, 2002, 2003, 2004, 2012 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 3 - -/*....................................................................... - * 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/libtecla.map b/libtecla/libtecla.map deleted file mode 100644 index a63378e..0000000 --- a/libtecla/libtecla.map +++ /dev/null @@ -1,155 +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; - -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/man/file/teclarc.in b/libtecla/man/file/teclarc.in deleted file mode 100644 index b5ee705..0000000 --- a/libtecla/man/file/teclarc.in +++ /dev/null @@ -1 +0,0 @@ -.so @MISC_MANDIR@/tecla.@MISC_MANEXT@ diff --git a/libtecla/man/func/cfc_file_start.in b/libtecla/man/func/cfc_file_start.in deleted file mode 100644 index 734f281..0000000 --- a/libtecla/man/func/cfc_file_start.in +++ /dev/null @@ -1 +0,0 @@ -.so @FUNC_MANDIR@/cpl_complete_word.@FUNC_MANEXT@ diff --git a/libtecla/man/func/cfc_literal_escapes.in b/libtecla/man/func/cfc_literal_escapes.in deleted file mode 100644 index 734f281..0000000 --- a/libtecla/man/func/cfc_literal_escapes.in +++ /dev/null @@ -1 +0,0 @@ -.so @FUNC_MANDIR@/cpl_complete_word.@FUNC_MANEXT@ diff --git a/libtecla/man/func/cfc_set_check_fn.in b/libtecla/man/func/cfc_set_check_fn.in deleted file mode 100644 index 734f281..0000000 --- a/libtecla/man/func/cfc_set_check_fn.in +++ /dev/null @@ -1 +0,0 @@ -.so @FUNC_MANDIR@/cpl_complete_word.@FUNC_MANEXT@ diff --git a/libtecla/man/func/cpl_add_completion.in b/libtecla/man/func/cpl_add_completion.in deleted file mode 100644 index 734f281..0000000 --- a/libtecla/man/func/cpl_add_completion.in +++ /dev/null @@ -1 +0,0 @@ -.so @FUNC_MANDIR@/cpl_complete_word.@FUNC_MANEXT@ diff --git a/libtecla/man/func/cpl_complete_word.in b/libtecla/man/func/cpl_complete_word.in deleted file mode 100644 index 2479657..0000000 --- a/libtecla/man/func/cpl_complete_word.in +++ /dev/null @@ -1,441 +0,0 @@ -.\" Copyright (c) 2000, 2001, 2002, 2003, 2004, 2012 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/man/func/cpl_file_completions.in b/libtecla/man/func/cpl_file_completions.in deleted file mode 100644 index 734f281..0000000 --- a/libtecla/man/func/cpl_file_completions.in +++ /dev/null @@ -1 +0,0 @@ -.so @FUNC_MANDIR@/cpl_complete_word.@FUNC_MANEXT@ diff --git a/libtecla/man/func/cpl_last_error.in b/libtecla/man/func/cpl_last_error.in deleted file mode 100644 index 734f281..0000000 --- a/libtecla/man/func/cpl_last_error.in +++ /dev/null @@ -1 +0,0 @@ -.so @FUNC_MANDIR@/cpl_complete_word.@FUNC_MANEXT@ diff --git a/libtecla/man/func/cpl_list_completions.in b/libtecla/man/func/cpl_list_completions.in deleted file mode 100644 index 734f281..0000000 --- a/libtecla/man/func/cpl_list_completions.in +++ /dev/null @@ -1 +0,0 @@ -.so @FUNC_MANDIR@/cpl_complete_word.@FUNC_MANEXT@ diff --git a/libtecla/man/func/cpl_recall_matches.in b/libtecla/man/func/cpl_recall_matches.in deleted file mode 100644 index 734f281..0000000 --- a/libtecla/man/func/cpl_recall_matches.in +++ /dev/null @@ -1 +0,0 @@ -.so @FUNC_MANDIR@/cpl_complete_word.@FUNC_MANEXT@ diff --git a/libtecla/man/func/cpl_record_error.in b/libtecla/man/func/cpl_record_error.in deleted file mode 100644 index 734f281..0000000 --- a/libtecla/man/func/cpl_record_error.in +++ /dev/null @@ -1 +0,0 @@ -.so @FUNC_MANDIR@/cpl_complete_word.@FUNC_MANEXT@ diff --git a/libtecla/man/func/del_CplFileConf.in b/libtecla/man/func/del_CplFileConf.in deleted file mode 100644 index 734f281..0000000 --- a/libtecla/man/func/del_CplFileConf.in +++ /dev/null @@ -1 +0,0 @@ -.so @FUNC_MANDIR@/cpl_complete_word.@FUNC_MANEXT@ diff --git a/libtecla/man/func/del_ExpandFile.in b/libtecla/man/func/del_ExpandFile.in deleted file mode 100644 index 3d0a884..0000000 --- a/libtecla/man/func/del_ExpandFile.in +++ /dev/null @@ -1 +0,0 @@ -.so @FUNC_MANDIR@/ef_expand_file.@FUNC_MANEXT@ diff --git a/libtecla/man/func/del_GetLine.in b/libtecla/man/func/del_GetLine.in deleted file mode 100644 index 6e46fc6..0000000 --- a/libtecla/man/func/del_GetLine.in +++ /dev/null @@ -1 +0,0 @@ -.so @FUNC_MANDIR@/gl_get_line.@FUNC_MANEXT@ diff --git a/libtecla/man/func/del_PathCache.in b/libtecla/man/func/del_PathCache.in deleted file mode 100644 index dbc4da7..0000000 --- a/libtecla/man/func/del_PathCache.in +++ /dev/null @@ -1 +0,0 @@ -.so @FUNC_MANDIR@/pca_lookup_file.@FUNC_MANEXT@ diff --git a/libtecla/man/func/del_PcaPathConf.in b/libtecla/man/func/del_PcaPathConf.in deleted file mode 100644 index dbc4da7..0000000 --- a/libtecla/man/func/del_PcaPathConf.in +++ /dev/null @@ -1 +0,0 @@ -.so @FUNC_MANDIR@/pca_lookup_file.@FUNC_MANEXT@ diff --git a/libtecla/man/func/del_WordCompletion.in b/libtecla/man/func/del_WordCompletion.in deleted file mode 100644 index 734f281..0000000 --- a/libtecla/man/func/del_WordCompletion.in +++ /dev/null @@ -1 +0,0 @@ -.so @FUNC_MANDIR@/cpl_complete_word.@FUNC_MANEXT@ diff --git a/libtecla/man/func/ef_expand_file.in b/libtecla/man/func/ef_expand_file.in deleted file mode 100644 index 6637f9f..0000000 --- a/libtecla/man/func/ef_expand_file.in +++ /dev/null @@ -1,248 +0,0 @@ -.\" Copyright (c) 2000, 2001, 2002, 2003, 2004, 2012 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/man/func/ef_last_error.in b/libtecla/man/func/ef_last_error.in deleted file mode 100644 index 3d0a884..0000000 --- a/libtecla/man/func/ef_last_error.in +++ /dev/null @@ -1 +0,0 @@ -.so @FUNC_MANDIR@/ef_expand_file.@FUNC_MANEXT@ diff --git a/libtecla/man/func/ef_list_expansions.in b/libtecla/man/func/ef_list_expansions.in deleted file mode 100644 index 3d0a884..0000000 --- a/libtecla/man/func/ef_list_expansions.in +++ /dev/null @@ -1 +0,0 @@ -.so @FUNC_MANDIR@/ef_expand_file.@FUNC_MANEXT@ diff --git a/libtecla/man/func/gl_abandon_line.in b/libtecla/man/func/gl_abandon_line.in deleted file mode 100644 index 24798bc..0000000 --- a/libtecla/man/func/gl_abandon_line.in +++ /dev/null @@ -1 +0,0 @@ -.so @FUNC_MANDIR@/gl_io_mode.@FUNC_MANEXT@ diff --git a/libtecla/man/func/gl_bind_keyseq.in b/libtecla/man/func/gl_bind_keyseq.in deleted file mode 100644 index 6e46fc6..0000000 --- a/libtecla/man/func/gl_bind_keyseq.in +++ /dev/null @@ -1 +0,0 @@ -.so @FUNC_MANDIR@/gl_get_line.@FUNC_MANEXT@ diff --git a/libtecla/man/func/gl_catch_blocked.in b/libtecla/man/func/gl_catch_blocked.in deleted file mode 100644 index 6e46fc6..0000000 --- a/libtecla/man/func/gl_catch_blocked.in +++ /dev/null @@ -1 +0,0 @@ -.so @FUNC_MANDIR@/gl_get_line.@FUNC_MANEXT@ diff --git a/libtecla/man/func/gl_change_terminal.in b/libtecla/man/func/gl_change_terminal.in deleted file mode 100644 index 6e46fc6..0000000 --- a/libtecla/man/func/gl_change_terminal.in +++ /dev/null @@ -1 +0,0 @@ -.so @FUNC_MANDIR@/gl_get_line.@FUNC_MANEXT@ diff --git a/libtecla/man/func/gl_clear_history.in b/libtecla/man/func/gl_clear_history.in deleted file mode 100644 index 6e46fc6..0000000 --- a/libtecla/man/func/gl_clear_history.in +++ /dev/null @@ -1 +0,0 @@ -.so @FUNC_MANDIR@/gl_get_line.@FUNC_MANEXT@ diff --git a/libtecla/man/func/gl_completion_action.in b/libtecla/man/func/gl_completion_action.in deleted file mode 100644 index 6e46fc6..0000000 --- a/libtecla/man/func/gl_completion_action.in +++ /dev/null @@ -1 +0,0 @@ -.so @FUNC_MANDIR@/gl_get_line.@FUNC_MANEXT@ diff --git a/libtecla/man/func/gl_configure_getline.in b/libtecla/man/func/gl_configure_getline.in deleted file mode 100644 index 6e46fc6..0000000 --- a/libtecla/man/func/gl_configure_getline.in +++ /dev/null @@ -1 +0,0 @@ -.so @FUNC_MANDIR@/gl_get_line.@FUNC_MANEXT@ diff --git a/libtecla/man/func/gl_customize_completion.in b/libtecla/man/func/gl_customize_completion.in deleted file mode 100644 index 6e46fc6..0000000 --- a/libtecla/man/func/gl_customize_completion.in +++ /dev/null @@ -1 +0,0 @@ -.so @FUNC_MANDIR@/gl_get_line.@FUNC_MANEXT@ diff --git a/libtecla/man/func/gl_display_text.in b/libtecla/man/func/gl_display_text.in deleted file mode 100644 index 6e46fc6..0000000 --- a/libtecla/man/func/gl_display_text.in +++ /dev/null @@ -1 +0,0 @@ -.so @FUNC_MANDIR@/gl_get_line.@FUNC_MANEXT@ diff --git a/libtecla/man/func/gl_echo_mode.in b/libtecla/man/func/gl_echo_mode.in deleted file mode 100644 index 6e46fc6..0000000 --- a/libtecla/man/func/gl_echo_mode.in +++ /dev/null @@ -1 +0,0 @@ -.so @FUNC_MANDIR@/gl_get_line.@FUNC_MANEXT@ diff --git a/libtecla/man/func/gl_erase_terminal.in b/libtecla/man/func/gl_erase_terminal.in deleted file mode 100644 index 6e46fc6..0000000 --- a/libtecla/man/func/gl_erase_terminal.in +++ /dev/null @@ -1 +0,0 @@ -.so @FUNC_MANDIR@/gl_get_line.@FUNC_MANEXT@ diff --git a/libtecla/man/func/gl_error_message.in b/libtecla/man/func/gl_error_message.in deleted file mode 100644 index 6e46fc6..0000000 --- a/libtecla/man/func/gl_error_message.in +++ /dev/null @@ -1 +0,0 @@ -.so @FUNC_MANDIR@/gl_get_line.@FUNC_MANEXT@ diff --git a/libtecla/man/func/gl_get_line.in b/libtecla/man/func/gl_get_line.in deleted file mode 100644 index ba0df8d..0000000 --- a/libtecla/man/func/gl_get_line.in +++ /dev/null @@ -1,2236 +0,0 @@ -.\" Copyright (c) 2000, 2001, 2002, 2003, 2004, 2012 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/man/func/gl_group_history.in b/libtecla/man/func/gl_group_history.in deleted file mode 100644 index 6e46fc6..0000000 --- a/libtecla/man/func/gl_group_history.in +++ /dev/null @@ -1 +0,0 @@ -.so @FUNC_MANDIR@/gl_get_line.@FUNC_MANEXT@ diff --git a/libtecla/man/func/gl_handle_signal.in b/libtecla/man/func/gl_handle_signal.in deleted file mode 100644 index 24798bc..0000000 --- a/libtecla/man/func/gl_handle_signal.in +++ /dev/null @@ -1 +0,0 @@ -.so @FUNC_MANDIR@/gl_io_mode.@FUNC_MANEXT@ diff --git a/libtecla/man/func/gl_ignore_signal.in b/libtecla/man/func/gl_ignore_signal.in deleted file mode 100644 index 6e46fc6..0000000 --- a/libtecla/man/func/gl_ignore_signal.in +++ /dev/null @@ -1 +0,0 @@ -.so @FUNC_MANDIR@/gl_get_line.@FUNC_MANEXT@ diff --git a/libtecla/man/func/gl_inactivity_timeout.in b/libtecla/man/func/gl_inactivity_timeout.in deleted file mode 100644 index 6e46fc6..0000000 --- a/libtecla/man/func/gl_inactivity_timeout.in +++ /dev/null @@ -1 +0,0 @@ -.so @FUNC_MANDIR@/gl_get_line.@FUNC_MANEXT@ diff --git a/libtecla/man/func/gl_io_mode.in b/libtecla/man/func/gl_io_mode.in deleted file mode 100644 index 0bcca8b..0000000 --- a/libtecla/man/func/gl_io_mode.in +++ /dev/null @@ -1,571 +0,0 @@ -.\" Copyright (c) 2002, 2003, 2004, 2012 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/man/func/gl_last_signal.in b/libtecla/man/func/gl_last_signal.in deleted file mode 100644 index 6e46fc6..0000000 --- a/libtecla/man/func/gl_last_signal.in +++ /dev/null @@ -1 +0,0 @@ -.so @FUNC_MANDIR@/gl_get_line.@FUNC_MANEXT@ diff --git a/libtecla/man/func/gl_limit_history.in b/libtecla/man/func/gl_limit_history.in deleted file mode 100644 index 6e46fc6..0000000 --- a/libtecla/man/func/gl_limit_history.in +++ /dev/null @@ -1 +0,0 @@ -.so @FUNC_MANDIR@/gl_get_line.@FUNC_MANEXT@ diff --git a/libtecla/man/func/gl_list_signals.in b/libtecla/man/func/gl_list_signals.in deleted file mode 100644 index 6e46fc6..0000000 --- a/libtecla/man/func/gl_list_signals.in +++ /dev/null @@ -1 +0,0 @@ -.so @FUNC_MANDIR@/gl_get_line.@FUNC_MANEXT@ diff --git a/libtecla/man/func/gl_load_history.in b/libtecla/man/func/gl_load_history.in deleted file mode 100644 index 6e46fc6..0000000 --- a/libtecla/man/func/gl_load_history.in +++ /dev/null @@ -1 +0,0 @@ -.so @FUNC_MANDIR@/gl_get_line.@FUNC_MANEXT@ diff --git a/libtecla/man/func/gl_lookup_history.in b/libtecla/man/func/gl_lookup_history.in deleted file mode 100644 index 6e46fc6..0000000 --- a/libtecla/man/func/gl_lookup_history.in +++ /dev/null @@ -1 +0,0 @@ -.so @FUNC_MANDIR@/gl_get_line.@FUNC_MANEXT@ diff --git a/libtecla/man/func/gl_normal_io.in b/libtecla/man/func/gl_normal_io.in deleted file mode 100644 index 24798bc..0000000 --- a/libtecla/man/func/gl_normal_io.in +++ /dev/null @@ -1 +0,0 @@ -.so @FUNC_MANDIR@/gl_io_mode.@FUNC_MANEXT@ diff --git a/libtecla/man/func/gl_pending_io.in b/libtecla/man/func/gl_pending_io.in deleted file mode 100644 index 24798bc..0000000 --- a/libtecla/man/func/gl_pending_io.in +++ /dev/null @@ -1 +0,0 @@ -.so @FUNC_MANDIR@/gl_io_mode.@FUNC_MANEXT@ diff --git a/libtecla/man/func/gl_prompt_style.in b/libtecla/man/func/gl_prompt_style.in deleted file mode 100644 index 6e46fc6..0000000 --- a/libtecla/man/func/gl_prompt_style.in +++ /dev/null @@ -1 +0,0 @@ -.so @FUNC_MANDIR@/gl_get_line.@FUNC_MANEXT@ diff --git a/libtecla/man/func/gl_query_char.in b/libtecla/man/func/gl_query_char.in deleted file mode 100644 index 6e46fc6..0000000 --- a/libtecla/man/func/gl_query_char.in +++ /dev/null @@ -1 +0,0 @@ -.so @FUNC_MANDIR@/gl_get_line.@FUNC_MANEXT@ diff --git a/libtecla/man/func/gl_range_of_history.in b/libtecla/man/func/gl_range_of_history.in deleted file mode 100644 index 6e46fc6..0000000 --- a/libtecla/man/func/gl_range_of_history.in +++ /dev/null @@ -1 +0,0 @@ -.so @FUNC_MANDIR@/gl_get_line.@FUNC_MANEXT@ diff --git a/libtecla/man/func/gl_raw_io.in b/libtecla/man/func/gl_raw_io.in deleted file mode 100644 index 24798bc..0000000 --- a/libtecla/man/func/gl_raw_io.in +++ /dev/null @@ -1 +0,0 @@ -.so @FUNC_MANDIR@/gl_io_mode.@FUNC_MANEXT@ diff --git a/libtecla/man/func/gl_read_char.in b/libtecla/man/func/gl_read_char.in deleted file mode 100644 index 6e46fc6..0000000 --- a/libtecla/man/func/gl_read_char.in +++ /dev/null @@ -1 +0,0 @@ -.so @FUNC_MANDIR@/gl_get_line.@FUNC_MANEXT@ diff --git a/libtecla/man/func/gl_register_action.in b/libtecla/man/func/gl_register_action.in deleted file mode 100644 index 6e46fc6..0000000 --- a/libtecla/man/func/gl_register_action.in +++ /dev/null @@ -1 +0,0 @@ -.so @FUNC_MANDIR@/gl_get_line.@FUNC_MANEXT@ diff --git a/libtecla/man/func/gl_resize_history.in b/libtecla/man/func/gl_resize_history.in deleted file mode 100644 index 6e46fc6..0000000 --- a/libtecla/man/func/gl_resize_history.in +++ /dev/null @@ -1 +0,0 @@ -.so @FUNC_MANDIR@/gl_get_line.@FUNC_MANEXT@ diff --git a/libtecla/man/func/gl_return_status.in b/libtecla/man/func/gl_return_status.in deleted file mode 100644 index 6e46fc6..0000000 --- a/libtecla/man/func/gl_return_status.in +++ /dev/null @@ -1 +0,0 @@ -.so @FUNC_MANDIR@/gl_get_line.@FUNC_MANEXT@ diff --git a/libtecla/man/func/gl_save_history.in b/libtecla/man/func/gl_save_history.in deleted file mode 100644 index 6e46fc6..0000000 --- a/libtecla/man/func/gl_save_history.in +++ /dev/null @@ -1 +0,0 @@ -.so @FUNC_MANDIR@/gl_get_line.@FUNC_MANEXT@ diff --git a/libtecla/man/func/gl_set_term_size.in b/libtecla/man/func/gl_set_term_size.in deleted file mode 100644 index 6e46fc6..0000000 --- a/libtecla/man/func/gl_set_term_size.in +++ /dev/null @@ -1 +0,0 @@ -.so @FUNC_MANDIR@/gl_get_line.@FUNC_MANEXT@ diff --git a/libtecla/man/func/gl_show_history.in b/libtecla/man/func/gl_show_history.in deleted file mode 100644 index 6e46fc6..0000000 --- a/libtecla/man/func/gl_show_history.in +++ /dev/null @@ -1 +0,0 @@ -.so @FUNC_MANDIR@/gl_get_line.@FUNC_MANEXT@ diff --git a/libtecla/man/func/gl_size_of_history.in b/libtecla/man/func/gl_size_of_history.in deleted file mode 100644 index 6e46fc6..0000000 --- a/libtecla/man/func/gl_size_of_history.in +++ /dev/null @@ -1 +0,0 @@ -.so @FUNC_MANDIR@/gl_get_line.@FUNC_MANEXT@ diff --git a/libtecla/man/func/gl_state_of_history.in b/libtecla/man/func/gl_state_of_history.in deleted file mode 100644 index 6e46fc6..0000000 --- a/libtecla/man/func/gl_state_of_history.in +++ /dev/null @@ -1 +0,0 @@ -.so @FUNC_MANDIR@/gl_get_line.@FUNC_MANEXT@ diff --git a/libtecla/man/func/gl_terminal_size.in b/libtecla/man/func/gl_terminal_size.in deleted file mode 100644 index 6e46fc6..0000000 --- a/libtecla/man/func/gl_terminal_size.in +++ /dev/null @@ -1 +0,0 @@ -.so @FUNC_MANDIR@/gl_get_line.@FUNC_MANEXT@ diff --git a/libtecla/man/func/gl_toggle_history.in b/libtecla/man/func/gl_toggle_history.in deleted file mode 100644 index 6e46fc6..0000000 --- a/libtecla/man/func/gl_toggle_history.in +++ /dev/null @@ -1 +0,0 @@ -.so @FUNC_MANDIR@/gl_get_line.@FUNC_MANEXT@ diff --git a/libtecla/man/func/gl_trap_signal.in b/libtecla/man/func/gl_trap_signal.in deleted file mode 100644 index 6e46fc6..0000000 --- a/libtecla/man/func/gl_trap_signal.in +++ /dev/null @@ -1 +0,0 @@ -.so @FUNC_MANDIR@/gl_get_line.@FUNC_MANEXT@ diff --git a/libtecla/man/func/gl_tty_signals.in b/libtecla/man/func/gl_tty_signals.in deleted file mode 100644 index 24798bc..0000000 --- a/libtecla/man/func/gl_tty_signals.in +++ /dev/null @@ -1 +0,0 @@ -.so @FUNC_MANDIR@/gl_io_mode.@FUNC_MANEXT@ diff --git a/libtecla/man/func/gl_watch_fd.in b/libtecla/man/func/gl_watch_fd.in deleted file mode 100644 index 6e46fc6..0000000 --- a/libtecla/man/func/gl_watch_fd.in +++ /dev/null @@ -1 +0,0 @@ -.so @FUNC_MANDIR@/gl_get_line.@FUNC_MANEXT@ diff --git a/libtecla/man/func/libtecla_version.in b/libtecla/man/func/libtecla_version.in deleted file mode 100644 index 31867c4..0000000 --- a/libtecla/man/func/libtecla_version.in +++ /dev/null @@ -1 +0,0 @@ -.so @LIBR_MANDIR@/libtecla.@LIBR_MANEXT@ diff --git a/libtecla/man/func/new_CplFileConf.in b/libtecla/man/func/new_CplFileConf.in deleted file mode 100644 index 734f281..0000000 --- a/libtecla/man/func/new_CplFileConf.in +++ /dev/null @@ -1 +0,0 @@ -.so @FUNC_MANDIR@/cpl_complete_word.@FUNC_MANEXT@ diff --git a/libtecla/man/func/new_ExpandFile.in b/libtecla/man/func/new_ExpandFile.in deleted file mode 100644 index 3d0a884..0000000 --- a/libtecla/man/func/new_ExpandFile.in +++ /dev/null @@ -1 +0,0 @@ -.so @FUNC_MANDIR@/ef_expand_file.@FUNC_MANEXT@ diff --git a/libtecla/man/func/new_GetLine.in b/libtecla/man/func/new_GetLine.in deleted file mode 100644 index 6e46fc6..0000000 --- a/libtecla/man/func/new_GetLine.in +++ /dev/null @@ -1 +0,0 @@ -.so @FUNC_MANDIR@/gl_get_line.@FUNC_MANEXT@ diff --git a/libtecla/man/func/new_PathCache.in b/libtecla/man/func/new_PathCache.in deleted file mode 100644 index dbc4da7..0000000 --- a/libtecla/man/func/new_PathCache.in +++ /dev/null @@ -1 +0,0 @@ -.so @FUNC_MANDIR@/pca_lookup_file.@FUNC_MANEXT@ diff --git a/libtecla/man/func/new_PcaPathConf.in b/libtecla/man/func/new_PcaPathConf.in deleted file mode 100644 index dbc4da7..0000000 --- a/libtecla/man/func/new_PcaPathConf.in +++ /dev/null @@ -1 +0,0 @@ -.so @FUNC_MANDIR@/pca_lookup_file.@FUNC_MANEXT@ diff --git a/libtecla/man/func/new_WordCompletion.in b/libtecla/man/func/new_WordCompletion.in deleted file mode 100644 index 734f281..0000000 --- a/libtecla/man/func/new_WordCompletion.in +++ /dev/null @@ -1 +0,0 @@ -.so @FUNC_MANDIR@/cpl_complete_word.@FUNC_MANEXT@ diff --git a/libtecla/man/func/pca_last_error.in b/libtecla/man/func/pca_last_error.in deleted file mode 100644 index dbc4da7..0000000 --- a/libtecla/man/func/pca_last_error.in +++ /dev/null @@ -1 +0,0 @@ -.so @FUNC_MANDIR@/pca_lookup_file.@FUNC_MANEXT@ diff --git a/libtecla/man/func/pca_lookup_file.in b/libtecla/man/func/pca_lookup_file.in deleted file mode 100644 index e74114a..0000000 --- a/libtecla/man/func/pca_lookup_file.in +++ /dev/null @@ -1,365 +0,0 @@ -.\" Copyright (c) 2001, 2002, 2003, 2004, 2012 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/man/prog/enhance.in b/libtecla/man/prog/enhance.in deleted file mode 100644 index aacd8a0..0000000 --- a/libtecla/man/prog/enhance.in +++ /dev/null @@ -1,89 +0,0 @@ -.\" Copyright (c) 2000, 2001, 2002, 2003, 2004, 2012 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/pathutil.c b/libtecla/pathutil.c deleted file mode 100644 index be14484..0000000 --- a/libtecla/pathutil.c +++ /dev/null @@ -1,539 +0,0 @@ -/* - * Copyright (c) 2000, 2001, 2002, 2003, 2004, 2012 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/pathutil.h b/libtecla/pathutil.h deleted file mode 100644 index 55c9b32..0000000 --- a/libtecla/pathutil.h +++ /dev/null @@ -1,122 +0,0 @@ -#ifndef pathutil_h -#define pathutil_h - -/* - * Copyright (c) 2000, 2001, 2002, 2003, 2004, 2012 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/pcache.c b/libtecla/pcache.c deleted file mode 100644 index 5a489b3..0000000 --- a/libtecla/pcache.c +++ /dev/null @@ -1,1710 +0,0 @@ -/* - * Copyright (c) 2000, 2001, 2002, 2003, 2004, 2012 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/stringrp.c b/libtecla/stringrp.c deleted file mode 100644 index de7369a..0000000 --- a/libtecla/stringrp.c +++ /dev/null @@ -1,286 +0,0 @@ -/* - * Copyright (c) 2000, 2001, 2002, 2003, 2004, 2012 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/stringrp.h b/libtecla/stringrp.h deleted file mode 100644 index 94a4922..0000000 --- a/libtecla/stringrp.h +++ /dev/null @@ -1,84 +0,0 @@ -#ifndef stringrp_h -#define stringrp_h -/* - * Copyright (c) 2000, 2001, 2002, 2003, 2004, 2012 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/strngmem.c b/libtecla/strngmem.c deleted file mode 100644 index 5390475..0000000 --- a/libtecla/strngmem.c +++ /dev/null @@ -1,218 +0,0 @@ -/* - * Copyright (c) 2000, 2001, 2002, 2003, 2004, 2012 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/strngmem.h b/libtecla/strngmem.h deleted file mode 100644 index 2138cfb..0000000 --- a/libtecla/strngmem.h +++ /dev/null @@ -1,80 +0,0 @@ -#ifndef stringmem_h -#define stringmem_h -/* - * Copyright (c) 2000, 2001, 2002, 2003, 2004, 2012 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/update_html b/libtecla/update_html deleted file mode 100755 index b6a5613..0000000 --- a/libtecla/update_html +++ /dev/null @@ -1,29 +0,0 @@ -#!/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 -r $dir/$page | sed -s 's/\.\.\/man[0-9]*\///g' | sed -s 's/HREF="\.\.\//HREF="/g' | sed -s 's/\.[0-9][0-9]*\.html/.html/g' > $html - 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/update_version b/libtecla/update_version deleted file mode 100755 index c18f714..0000000 --- a/libtecla/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/version.c b/libtecla/version.c deleted file mode 100644 index 9e1275e..0000000 --- a/libtecla/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; -} -- cgit v1.2.3