#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);
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)
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)
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 : */
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:
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)
{
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);
--- /dev/null
+# ===========================================================================
+# 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