]> granicus.if.org Git - fcron/commitdiff
Added readline support to fcrondyn
authorThibault Godouet <fcron@free.fr>
Wed, 26 Dec 2012 17:43:29 +0000 (17:43 +0000)
committerThibault Godouet <fcron@free.fr>
Wed, 26 Dec 2012 17:43:29 +0000 (17:43 +0000)
config.h.in
configure.in
doc/en/changes.sgml
dyncom.h
fcrondyn.c
fcrondyn.h
m4/ax_lib_readline.m4 [new file with mode: 0644]

index 84058a226a4e83d863d2e0f81b9a3cdfad64e2a5..3dec978683a22b4a175add9d59492f1862a0d351 100644 (file)
 /* Define if you have the <grp.h> header file.  */
 #undef HAVE_GRP_H
 
+/* Define if you have the <history.h> header file.  */
+#undef HAVE_HISTORY_H
+
 /* Define if you have the <libaudit.h> header file.  */
 #undef HAVE_LIBAUDIT_H
 
+/* Define if you have the <readline.h> header file.  */
+#undef HAVE_READLINE_H
+
+/* Define if you have the <readline/history.h> header file.  */
+#undef HAVE_READLINE_HISTORY_H
+
+/* Define if you have the <readline/readline.h> header file.  */
+#undef HAVE_READLINE_READLINE_H
+
 /* Define if you have the <limits.h> header file.  */
 #undef HAVE_LIMITS_H
 
 /* Define if you have the <unistd.h> header file.  */
 #undef HAVE_UNISTD_H
 
+/* ****************************************************************** */
+/* *** Libraries *** */
+
 /* Define if you have the audit library (-laudit).  */
 #undef HAVE_LIBAUDIT
 
 /* 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
 
index 4800e8442576badac2e659f2d81edbe9591eeab0..370b15f0a187a687bfdea060794aa0fee051611c 100644 (file)
@@ -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"
index fd34a602bdf8ca5019a6294e41e33e39f65ff96f..f3ea05c29fd59f8ea42fe10ecec4f68242ffb44e 100644 (file)
@@ -15,11 +15,14 @@ A copy of the license is included in gfdl.sgml.
       <itemizedlist>
         <title>From version 3.0.6 to 3.1.1</title>
            <listitem>
-              <para>fixed fcrontab edition of *ly lines (hourly, daily, etc)</para>
+              <para>Added readline support for fcrondyn</para>
            </listitem>
            <listitem>
               <para>tweaked 'make indent' options and applied</para>
            </listitem>
+           <listitem>
+              <para>fixed fcrontab edition of *ly lines (hourly, daily, etc)</para>
+           </listitem>
       </itemizedlist>
       <itemizedlist>
         <title>From version 3.0.6 to 3.1.0</title>
index 41c1ce7e6c35283c3584c48b2b0ac50818f80d19..24547cc6a385c53a56df041fa66b15a91385b6c4 100644 (file)
--- 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
index f4e0ebc85b6643bafd61f9969c950ac0fa57e585..7909c2f2261297038ded0a64afabd81c0c44e304 100644 (file)
 #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 <readline/readline.h>
+#elif defined(HAVE_READLINE_H)
+#include <readline.h>
+#endif                          /* !defined(HAVE_READLINE_H) */
+#if defined(HAVE_READLINE_HISTORY_H)
+#include <readline/history.h>
+#elif defined(HAVE_HISTORY_H)
+#include <history.h>
+#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);
index 30264bc8dad3f00ff2296d3580f5ce2a0c085f06..bef1312255cada5616dd12820f072a5cd74b55f2 100644 (file)
@@ -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 (file)
index 0000000..056f25c
--- /dev/null
@@ -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 <readline/readline.h>
+#     #  elif defined(HAVE_READLINE_H)
+#     #    include <readline.h>
+#     #  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 <readline/history.h>
+#     #  elif defined(HAVE_HISTORY_H)
+#     #    include <history.h>
+#     #  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 <vl@iki.fi>
+#
+#   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