From: Thibault Godouet Date: Wed, 26 Dec 2012 17:43:29 +0000 (+0000) Subject: Added readline support to fcrondyn X-Git-Tag: ver3_1_1~9 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=73626dde8526ea960a1ba466244da1a26d6b55d6;p=thirdparty%2Ffcron.git Added readline support to fcrondyn --- diff --git a/config.h.in b/config.h.in index 84058a2..3dec978 100644 --- a/config.h.in +++ b/config.h.in @@ -351,9 +351,21 @@ /* Define if you have the header file. */ #undef HAVE_GRP_H +/* Define if you have the header file. */ +#undef HAVE_HISTORY_H + /* Define if you have the header file. */ #undef HAVE_LIBAUDIT_H +/* Define if you have the header file. */ +#undef HAVE_READLINE_H + +/* Define if you have the header file. */ +#undef HAVE_READLINE_HISTORY_H + +/* Define if you have the header file. */ +#undef HAVE_READLINE_READLINE_H + /* Define if you have the header file. */ #undef HAVE_LIMITS_H @@ -435,6 +447,9 @@ /* Define if you have the header file. */ #undef HAVE_UNISTD_H +/* ****************************************************************** */ +/* *** Libraries *** */ + /* Define if you have the audit library (-laudit). */ #undef HAVE_LIBAUDIT @@ -444,6 +459,12 @@ /* Define if you have the pam library (-lpam). */ #undef HAVE_LIBPAM +/* Define if you have the readline library. */ +#undef HAVE_LIBREADLINE + +/* Define if you have the readline history library. */ +#undef HAVE_READLINE_HISTORY + /* Have audit trails (libaudit) support */ #undef WITH_AUDIT diff --git a/configure.in b/configure.in index 4800e84..370b15f 100644 --- a/configure.in +++ b/configure.in @@ -8,6 +8,7 @@ dnl --------------------------------------------------------------------- AC_INIT(allow.c) AC_CONFIG_HEADER(config.h) AC_PREREQ(2.57) +m4_include([m4/ax_lib_readline.m4]) vers="3.1.1" vers_quoted="\"$vers\"" @@ -85,6 +86,7 @@ AC_FUNC_WAIT3 AC_CHECK_LIB(xnet, shutdown) AC_CHECK_LIB(selinux, getcon, [selinuxavail=1], [selinuxavail=0]) AC_CHECK_LIB(audit, audit_open, [auditavail=1], [auditavail=0]) +AX_LIB_READLINE AC_CHECK_FUNC(getloadavg, [getloadavg=1], [getloadavg=0]) AC_CHECK_LIB(kstat, kstat_open, [kstat=1], [kstat=0]) if test $getloadavg -eq 1; then @@ -1056,6 +1058,14 @@ else echo "no" fi +echo -n "Readline : " +if test "$ax_cv_lib_readline" = "no"; then + echo "no" +else + echo "yes" +fi + + echo -n "Run without root's rights : " if test "$run_non_privileged" -eq 1; then echo "yes" diff --git a/doc/en/changes.sgml b/doc/en/changes.sgml index fd34a60..f3ea05c 100644 --- a/doc/en/changes.sgml +++ b/doc/en/changes.sgml @@ -15,11 +15,14 @@ A copy of the license is included in gfdl.sgml. From version 3.0.6 to 3.1.1 - fixed fcrontab edition of *ly lines (hourly, daily, etc) + Added readline support for fcrondyn tweaked 'make indent' options and applied + + fixed fcrontab edition of *ly lines (hourly, daily, etc) + From version 3.0.6 to 3.1.0 diff --git a/dyncom.h b/dyncom.h index 41c1ce7..24547cc 100644 --- a/dyncom.h +++ b/dyncom.h @@ -62,7 +62,6 @@ /* commands : if you change something here, please update fcrondyn.c's cmd_list * and fcron's socket.c . */ -#define NUM_CMD 9 #define CMD_LIST_JOBS 101 #define CMD_LIST_LAVGQ 102 diff --git a/fcrondyn.c b/fcrondyn.c index f4e0ebc..7909c2f 100644 --- a/fcrondyn.c +++ b/fcrondyn.c @@ -34,6 +34,20 @@ #include "read_string.h" #include "mem.h" +#ifdef HAVE_LIBREADLINE +char **rl_cmpl_fcrondyn(const char *text, int start, int end); +char *rl_cmpl_command_generator(const char *text, int state); +#if defined(HAVE_READLINE_READLINE_H) +#include +#elif defined(HAVE_READLINE_H) +#include +#endif /* !defined(HAVE_READLINE_H) */ +#if defined(HAVE_READLINE_HISTORY_H) +#include +#elif defined(HAVE_HISTORY_H) +#include +#endif /* defined(HAVE_READLINE_HISTORY_H) */ +#endif /* HAVE_LIBREADLINE */ void info(void); void usage(void); @@ -71,32 +85,33 @@ char *user_str = NULL; uid_t user_uid = 0; gid_t user_gid = 0; -/* if you change this structure, please update NUM_CMD value in dyncom.h */ -struct cmd_list_ent cmd_list[NUM_CMD] = { +struct cmd_list_ent cmd_list[] = { /* name, desc, num opt, cmd code, cmd opts, cmd defaults */ - {"ls", "List all jobs of user", 1, CMD_LIST_JOBS, + {"ls", {NULL}, "List all jobs of user", 1, CMD_LIST_JOBS, {USER}, {CUR_USER}}, - {"ls_lavgq", "List jobs of user which are in lavg queue", 1, CMD_LIST_LAVGQ, + {"ls_lavgq", {}, "List jobs of user which are in lavg queue", 1, + CMD_LIST_LAVGQ, {USER}, {CUR_USER}}, + {"ls_serialq", {}, "List jobs of user which are in serial queue", + 1, CMD_LIST_SERIALQ, {USER}, {CUR_USER}}, + {"ls_exeq", {}, "List running jobs of user", 1, CMD_LIST_EXEQ, {USER}, {CUR_USER}}, - {"ls_serialq", "List jobs of user which are in serial queue", 1, - CMD_LIST_SERIALQ, - {USER}, {CUR_USER}}, - {"ls_exeq", "List running jobs of user", 1, CMD_LIST_EXEQ, - {USER}, {CUR_USER}}, - {"detail", "Print details on job", 1, CMD_DETAILS, + {"detail", {}, "Print details on job", 1, CMD_DETAILS, {JOBID}, {ARG_REQUIRED}}, -/* {"reschedule", "Reschedule next execution of job", 2, CMD_RESCHEDULE, - {TIME_AND_DATE, JOBID}, {ARG_REQUIRED, ARG_REQUIRED}}, */ - {"runnow", "Advance next execution of job to now", 1, CMD_RUNNOW, +/* {"reschedule", {NULL}, "Reschedule next execution of job", 2, + CMD_RESCHEDULE, {TIME_AND_DATE, JOBID}, {ARG_REQUIRED, ARG_REQUIRED}}, */ + {"runnow", {}, "Advance next execution of job to now", 1, CMD_RUNNOW, {JOBID}, {ARG_REQUIRED}}, - {"run", "Run job now (without changing its current schedule)", 1, CMD_RUN, - {JOBID}, {ARG_REQUIRED}}, - {"kill", "Send signal to running job", 2, CMD_SEND_SIGNAL, + {"run", {}, "Run job now (without changing its current schedule)", 1, + CMD_RUN, {JOBID}, {ARG_REQUIRED}}, + {"kill", {}, "Send signal to running job", 2, CMD_SEND_SIGNAL, {SIGNAL, JOBID}, {ARG_REQUIRED, ARG_REQUIRED}}, - {"renice", "Renice running job", 2, CMD_RENICE, - {NICE_VALUE, JOBID}, {ARG_REQUIRED, ARG_REQUIRED}} + {"renice", {}, "Renice running job", 2, CMD_RENICE, + {NICE_VALUE, JOBID}, {ARG_REQUIRED, ARG_REQUIRED}}, + {"quit", {"q", "exit"}, "Quit fcrondyn", 0, QUIT_CMD, {}, {}}, + {"help", {"h"}, "Display a help message", 0, HELP_CMD, {}, {}}, }; +const int cmd_list_len = sizeof(cmd_list) / sizeof(cmd_list_ent); void info(void) @@ -184,31 +199,34 @@ parse_cmd(char *cmd_str, long int **cmd, int *cmd_len) return ZEROLEN_CMD; } - if (Strncmp(cmd_str, "q", word_size) == 0 - || Strncmp(cmd_str, "quit", word_size) == 0 - || Strncmp(cmd_str, "exit", word_size) == 0) { - if (debug_opt) - fprintf(stderr, "quit command\n"); - return QUIT_CMD; - } - - if (Strncmp(cmd_str, "h", word_size) == 0 - || Strncmp(cmd_str, "help", word_size) == 0) { - if (debug_opt) - fprintf(stderr, "help command\n"); - return HELP_CMD; - } - - for (i = 0; i < NUM_CMD; i++) { + for (i = 0; i < cmd_list_len; i++) { + int j; if (Strncmp(cmd_str, cmd_list[i].cmd_name, word_size) == 0) { rank = i; break; } + for (j = 0; j < MAX_NUM_ALIAS && cmd_list[i].cmd_alias[j] != NULL; j++) { + if (Strncmp(cmd_str, cmd_list[i].cmd_alias[j], word_size) == 0) { + rank = i; + break; + } + } } if (rank == (-1)) { fprintf(stderr, "Error : Unknown command.\n"); return CMD_NOT_FOUND; } + else if (cmd_list[rank].cmd_code == QUIT_CMD) { + if (debug_opt) + fprintf(stderr, "quit command\n"); + return QUIT_CMD; + } + else if (cmd_list[rank].cmd_code == HELP_CMD) { + if (debug_opt) + fprintf(stderr, "Help command\n"); + return HELP_CMD; + } + Write_cmd(cmd_list[rank].cmd_code); if (debug_opt) @@ -475,7 +493,7 @@ talk_fcron(char *cmd_str, int fd) int i, j, len; printf("Command recognized by fcrondyn :\n"); printf("------------------------------\n"); - for (i = 0; i < NUM_CMD; i++) { + for (i = 0; i < cmd_list_len; i++) { len = printf("%s ", cmd_list[i].cmd_name); /* print args : */ @@ -509,11 +527,21 @@ talk_fcron(char *cmd_str, int fd) len += printf(" "); } /* Align correctly the descriptions : */ - printf("%*s%s\n", 24 - len, "", cmd_list[i].cmd_desc); + printf("%*s%s", 24 - len, "", cmd_list[i].cmd_desc); + + /* print alias list (if any) */ + if (cmd_list[i].cmd_alias[0] != NULL) { + printf(" (aliases:"); + for (j = 0; + j < MAX_NUM_ALIAS && cmd_list[i].cmd_alias[j] != NULL; + j++) { + printf(" %s", cmd_list[i].cmd_alias[j]); + } + printf(")"); + } + + printf("\n"); } - printf("\n"); - printf("help\t\t\tDisplay this help message\n"); - printf("quit\t\t\tQuit fcrondyn\n"); } return HELP_CMD; case QUIT_CMD: @@ -581,6 +609,58 @@ talk_fcron(char *cmd_str, int fd) return OK; } +#ifdef HAVE_LIBREADLINE +/* Attempt to complete on the contents of TEXT. START and END bound the + region of rl_line_buffer that contains the word to complete. TEXT is + the word to complete. We can use the entire contents of rl_line_buffer + in case we want to do some simple parsing. Return the array of matches, + or NULL if there aren't any. */ +char ** +rl_cmpl_fcrondyn(const char *text, int start, int end) +{ + char **matches; + + matches = (char **)NULL; + + /* If this word is at the start of the line, then it is a command + * to complete. Otherwise it is an argument which we ignore for now */ + if (start == 0) { + matches = rl_completion_matches(text, rl_cmpl_command_generator); + } + + return (matches); +} + +/* Generator function for command completion. STATE lets us know whether + to start from scratch; without any state (i.e. STATE == 0), then we + start at the top of the list. */ +char * +rl_cmpl_command_generator(const char *text, int state) +{ + static int list_index, len; + char *name = NULL; + + /* If this is a new word to complete, initialize now. This includes + * saving the length of TEXT for efficiency, and initializing the index + * variable to 0. */ + if (!state) { + list_index = 0; + len = strlen(text); + } + + /* Return the next name which partially matches from the command list. */ + while (list_index < cmd_list_len) { + name = cmd_list[list_index].cmd_name; + list_index++; + if (strncmp(name, text, len) == 0) { + return (strdup2(name)); + } + } + + /* If no names matched, then return NULL. */ + return ((char *)NULL); +} +#endif /* HAVE_LIBREADLINE */ int interactive_mode(int fd) @@ -589,15 +669,43 @@ interactive_mode(int fd) { char existing_connection = (fd < 0) ? 0 : 1; int return_code = 0; +#ifdef HAVE_LIBREADLINE + char *line_read = NULL; +#else /* HAVE_LIBREADLINE */ char buf[LINE_LEN]; +#endif /* HAVE_LIBREADLINE */ if (!existing_connection && (fd = connect_fcron()) == ERR) return ERR; +#ifdef HAVE_LIBREADLINE + /* Allow conditional parsing of the ~/.inputrc file. */ + rl_readline_name = "fcrondyn"; + + /* Tell the completer that we want a crack first. */ + rl_attempted_completion_function = rl_cmpl_fcrondyn; + + while (1) { + line_read = readline("fcrondyn> "); + return_code = talk_fcron(line_read, fd); + +#ifdef HAVE_READLINE_HISTORY + if (line_read && *line_read) { + add_history(line_read); + } +#endif + + free(line_read); + if (return_code == QUIT_CMD || return_code == ERR) { + break; + } + } +#else /* HAVE_LIBREADLINE */ while (fprintf(stderr, "fcrondyn> ") && fgets(buf, sizeof(buf), stdin) != NULL && (return_code = talk_fcron(buf, fd)) != QUIT_CMD && return_code != ERR) ; +#endif /* HAVE_LIBREADLINE */ if (!existing_connection) close(fd); diff --git a/fcrondyn.h b/fcrondyn.h index 30264bc..bef1312 100644 --- a/fcrondyn.h +++ b/fcrondyn.h @@ -36,9 +36,11 @@ extern uid_t rootuid; extern gid_t rootgid; /* types def */ +#define MAX_NUM_ALIAS 2 #define MAX_NUM_OPT 4 typedef struct cmd_list_ent { char *cmd_name; + char *cmd_alias[MAX_NUM_ALIAS]; char *cmd_desc; int cmd_numopt; long int cmd_code; diff --git a/m4/ax_lib_readline.m4 b/m4/ax_lib_readline.m4 new file mode 100644 index 0000000..056f25c --- /dev/null +++ b/m4/ax_lib_readline.m4 @@ -0,0 +1,107 @@ +# =========================================================================== +# http://www.gnu.org/software/autoconf-archive/ax_lib_readline.html +# =========================================================================== +# +# SYNOPSIS +# +# AX_LIB_READLINE +# +# DESCRIPTION +# +# Searches for a readline compatible library. If found, defines +# `HAVE_LIBREADLINE'. If the found library has the `add_history' function, +# sets also `HAVE_READLINE_HISTORY'. Also checks for the locations of the +# necessary include files and sets `HAVE_READLINE_H' or +# `HAVE_READLINE_READLINE_H' and `HAVE_READLINE_HISTORY_H' or +# 'HAVE_HISTORY_H' if the corresponding include files exists. +# +# The libraries that may be readline compatible are `libedit', +# `libeditline' and `libreadline'. Sometimes we need to link a termcap +# library for readline to work, this macro tests these cases too by trying +# to link with `libtermcap', `libcurses' or `libncurses' before giving up. +# +# Here is an example of how to use the information provided by this macro +# to perform the necessary includes or declarations in a C file: +# +# #ifdef HAVE_LIBREADLINE +# # if defined(HAVE_READLINE_READLINE_H) +# # include +# # elif defined(HAVE_READLINE_H) +# # include +# # else /* !defined(HAVE_READLINE_H) */ +# extern char *readline (); +# # endif /* !defined(HAVE_READLINE_H) */ +# char *cmdline = NULL; +# #else /* !defined(HAVE_READLINE_READLINE_H) */ +# /* no readline */ +# #endif /* HAVE_LIBREADLINE */ +# +# #ifdef HAVE_READLINE_HISTORY +# # if defined(HAVE_READLINE_HISTORY_H) +# # include +# # elif defined(HAVE_HISTORY_H) +# # include +# # else /* !defined(HAVE_HISTORY_H) */ +# extern void add_history (); +# extern int write_history (); +# extern int read_history (); +# # endif /* defined(HAVE_READLINE_HISTORY_H) */ +# /* no history */ +# #endif /* HAVE_READLINE_HISTORY */ +# +# LICENSE +# +# Copyright (c) 2008 Ville Laurikari +# +# Copying and distribution of this file, with or without modification, are +# permitted in any medium without royalty provided the copyright notice +# and this notice are preserved. This file is offered as-is, without any +# warranty. + +#serial 6 + +AU_ALIAS([VL_LIB_READLINE], [AX_LIB_READLINE]) +AC_DEFUN([AX_LIB_READLINE], [ + AC_CACHE_CHECK([for a readline compatible library], + ax_cv_lib_readline, [ + ORIG_LIBS="$LIBS" + for readline_lib in readline edit editline; do + for termcap_lib in "" termcap curses ncurses; do + if test -z "$termcap_lib"; then + TRY_LIB="-l$readline_lib" + else + TRY_LIB="-l$readline_lib -l$termcap_lib" + fi + LIBS="$ORIG_LIBS $TRY_LIB" + AC_TRY_LINK_FUNC(readline, ax_cv_lib_readline="$TRY_LIB") + if test -n "$ax_cv_lib_readline"; then + break + fi + done + if test -n "$ax_cv_lib_readline"; then + break + fi + done + if test -z "$ax_cv_lib_readline"; then + ax_cv_lib_readline="no" + fi + LIBS="$ORIG_LIBS" + ]) + + if test "$ax_cv_lib_readline" != "no"; then + LIBS="$LIBS $ax_cv_lib_readline" + AC_DEFINE(HAVE_LIBREADLINE, 1, + [Define if you have a readline compatible library]) + AC_CHECK_HEADERS(readline.h readline/readline.h) + AC_CACHE_CHECK([whether readline supports history], + ax_cv_lib_readline_history, [ + ax_cv_lib_readline_history="no" + AC_TRY_LINK_FUNC(add_history, ax_cv_lib_readline_history="yes") + ]) + if test "$ax_cv_lib_readline_history" = "yes"; then + AC_DEFINE(HAVE_READLINE_HISTORY, 1, + [Define if your readline library has \`add_history']) + AC_CHECK_HEADERS(history.h readline/history.h) + fi + fi +])dnl