Merge branch 'readline'

Merging branch 'readline' into master due to no complaints or other feedback.
Adds Readline capability for the command prompts in rigctl and rotctl.
Hamlib-3.0
Nate Bargmann 2013-03-03 06:56:06 -06:00
commit 61ce272911
12 zmienionych plików z 1734 dodań i 343 usunięć

Wyświetl plik

@ -251,6 +251,7 @@ enabled with options beginning with '--with-'. At this time these options
are:
--with-xml-support build rigmem with XML support [default=no]
--without-readline disable readline in rigctl/rotctl [default=yes]
--without-cxx-binding do not build C++ binding and demo [default=yes]
--with-perl-binding build perl binding and demo [default=no]
--with-perl-inc directory containing perl includes

4
NEWS
Wyświetl plik

@ -27,6 +27,10 @@ Version 3.0
* IC-PCR1500/2500 default write_delay to 0, IC-746/756, IC-PCR8500
fixes, pcr.c, pcr1500.c: Add DSP support. TNX Paul, KE7ZZ
* WinRadio G313 updates. TNX Julian Campbel
* Readline editing and history support added to rigctl interactive
mode. Implement options for reading and writing history file.
* Readline editing and history support added to rotctl interactive
mode. Implement options for reading and writing history file.
Version 1.2.15.3
2012-11-01

Wyświetl plik

@ -1,6 +1,6 @@
Hamlib - (C) Frank Singleton 2000 (vk3fcs@ix.netcom.com)
(C) Stephane Fillod 2000-2011
(C) The Hamlib Group 2000-2012
(C) The Hamlib Group 2000-2013
Why does Hamlib need beta-testers?
==================================
@ -86,6 +86,7 @@ Optional, but highly recommended for a complete build:
* libxml2 devel # xml2-config --version
* libgd2 devel # gdlib-config --version
* libusb devel # libusb-config --version (not 1.0.0!)
* libreadline devel # ver 5.2 or newer
* Git for connection to hamlib.git.sourceforge.net
N.B The libusb package is required for building most of the 'kit' backend.
@ -315,4 +316,3 @@ Needless to say, patches are also very welcome (see README.developer). :-)
Stephane - F8CFE and The Hamlib Group

Wyświetl plik

@ -252,6 +252,7 @@ Optional, but highly recommended:
* libxml2 devel # xml2-config --version
* libgd2 devel # gdlib-config --version
* libusb devel # libusb-config --version (not 1.0.0!)
* libreadline devel # ver 5.2 or newer
N.B.: The libusb package is required for building most of the 'kit' backend.
The older version is needed, not 1.0.0 or higher. Debian and derivatives

Wyświetl plik

@ -112,7 +112,7 @@ AC_CHECK_HEADERS([errno.h fcntl.h getopt.h limits.h locale.h malloc.h \
netdb.h sgtty.h stddef.h termio.h termios.h values.h ws2tcpip.h \
arpa/inet.h dev/ppbus/ppbconf.hdev/ppbus/ppi.h \
linux/hidraw.h linux/ioctl.h linux/parport.h linux/ppdev.h netinet/in.h \
sys/ioccom.h sys/ioctl.h sys/param.h sys/socket.h sys/time.h])
sys/ioccom.h sys/ioctl.h sys/param.h sys/socket.h sys/stat.h sys/time.h])
## ------------------------------------ ##
@ -356,6 +356,28 @@ AC_SUBST([LIBXML2_LIBS])
AC_SUBST([LIBXML2_CFLAGS])
dnl Check if readline support in rigctl/rotctl is wanted
AC_MSG_CHECKING([whether to use readline in rigctl/rotctl])
AC_ARG_WITH([readline],
[AS_HELP_STRING([--without-readline],
[disable readline in rigctl/rotctl @<:@default=yes@:>@])],
[cf_with_readline_support=no],
[cf_with_readline_support=yes]
)
AC_MSG_RESULT([$cf_with_readline_support])
AS_IF([test x"$cf_with_readline_support" != "xno"], [
# macros/ax_lib_readline.m4
AX_LIB_READLINE
])
AS_IF([test x"$ax_cv_lib_readline" = "xno"], [
AC_MSG_WARN([readline support not found, using internal input handling.])
cf_with_readline_support=no
])
dnl Check if libgd-dev is installed, so we can enable rigmatrix
AC_MSG_CHECKING([whether to build HTML rig feature matrix])
AC_ARG_ENABLE([html-matrix],
@ -701,6 +723,7 @@ echo \
With TCL binding ${build_tcl}
With rigmem XML support ${cf_with_xml_support}
With included ltdl ${with_included_ltdl}
With Readline support ${cf_with_readline_support}
Enable HTML rig feature matrix ${cf_enable_html_matrix}
Enable WinRadio ${cf_with_winradio}

Wyświetl plik

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

Wyświetl plik

@ -2,7 +2,7 @@
.\" First parameter, NAME, should be all caps
.\" Second parameter, SECTION, should be 1-8, maybe w/ subsection
.\" other parameters are allowed: see man(7), man(1)
.TH RIGCTL "1" "January 23, 2013" "Hamlib" "Radio Control Program"
.TH RIGCTL "1" "February 20, 2013" "Hamlib" "Radio Control Program"
.\" Please adjust this date whenever revising the manpage.
.\"
.\" Some roff macros, for reference:
@ -31,14 +31,14 @@ interactive mode if none are provided on the command line.
Keep in mind that \fBHamlib\fP is BETA level software.
While a lot of backend libraries lack complete rig support, the basic functions
are usually well supported. The API may change without publicized notice,
while an advancement of the minor version (e.g. 1.1.x to 1.2.x) indicates such
while an advancement of the major version (e.g. 1.x to 3.x) indicates such
a change.
.PP
Please report bugs and provide feedback at the e-mail address given in the
REPORTING BUGS section. Patches and code enhancements are also welcome.
.SH OPTIONS
This program follows the usual GNU command line syntax, with long
options starting with two dashes (`-').
options starting with two dashes ('-').
.PP
Here is a summary of the supported options:
.TP
@ -120,7 +120,27 @@ Dump capabilities for the radio defined with -m above and exit.
.TP
.B \-o, --vfo
Set vfo mode, requiring an extra VFO argument in front of each appropriate
command. Otherwise, VFO_CURR is assumed when this option is not set.
command. Otherwise, 'currVFO' is assumed when this option is not set.
.TP
.B \-i, --read-history
Read previously saved command and argument history from a file
(default '~/.rigctl_history') for the current session. Available when
\fBrigctl\fP is built with Readline support (see READLINE below).
.sp
\fBN.B.\fP To read a history file stored in another directory, set the
RIGCTL_HIST_DIR environment variable, e.g. 'RIGCTL_HIST_DIR=~/tmp rigctl -i'.
When RIGCTL_HIST_DIR is not set, the value of HOME is used.
.TP
.B \-I, --save-history
Write current session and previous session(s), if -i option is given, command and
argument history to a file (default '~/.rigctl_history') at the end of the current
session. Complete commands with arguments are saved as a single line to be
recalled and used or edited. Available when \fBrigctl\fP is built with Readline
support (see READLINE below).
.sp
\fBN.B.\fP To write a history file in another directory, set the RIGCTL_HIST_DIR
environment variable, e.g. 'RIGCTL_HIST_DIR=~/tmp rigctl -I'. When RIGCTL_HIST_DIR
is not set, the value of HOME is used.
.TP
.B \-v, --verbose
Set verbose mode, cumulative (see DIAGNOSTICS below).
@ -142,7 +162,10 @@ the operation will fail with a \fBHamlib\fP error code.
Commands can be entered either as a single char, or as a long command name.
Basically, the commands do not take a dash in front of them on the command
line, as the options do. They may be typed in when in interactive mode
or provided as argument(s) in command line interface mode.
or provided as argument(s) in command line interface mode. In interactive
mode commands and their arguments may be entered on a single line:
.sp
Rig command: M LSB 2400
.PP
Since most of the \fBHamlib\fP operations have a \fIset\fP and a \fIget\fP
method, an upper case letter will be used for \fIset\fP method whereas the
@ -150,7 +173,7 @@ corresponding lower case letter refers to the \fIget\fP method. Each operation
also has a long name; in interactive mode, prepend a backslash to enter a long
command name.
.sp
Example: Use "\\dump_caps" to see what this radio can do.
Example: Use '\\dump_caps' to see what this radio and backend support.
.PP
Please note that the backend for the radio to be controlled, or the radio
itself may not support some commands. In that case, the operation will fail
@ -509,6 +532,45 @@ Connect to a running \fBrigctld\fP with rig model 2 ("NET rigctl") on the
local host and specifying the TCP port, setting frequency and mode:
.sp
$ rigctl -m 2 -r localhost:4532 F 7253500 M LSB 0
.SH READLINE
If Readline library development files are found at configure time, \fBrigctl\fP
will be conditonally built with Readline support for command and argument entry.
Readline command key bindings are at their defaults as described in the Readline
manual (\fIhttp://cnswww.cns.cwru.edu/php/chet/readline/rluserman.html\fP)
although \fBrigctl\fP sets the name 'rigctl' which can be used in Conditional
Init Constructs in the Readline Init File ('~/.inputrc' by default) for custom
keybindings unique to \fBrigctl\fP.
Command history is available with Readline support as described in the Readline
History manual
(\fIhttp://cnswww.cns.cwru.edu/php/chet/readline/history.html#SEC1\fP). Command
and argument strings are stored as single lines even when arguments are prompted
for input individually. Commands and arguments are not validated and are stored
as typed with values separated by a single space.
Normally session history is not saved, however, use of either of the
\fI-i/--read-history\fP or \fI-I/--save-history\fP options when starting
\fBrigctl\fP will cause any previously saved history to be read in and/or the
current and any previous session history (assuming the -i and -I options are
given together) will be written out when \fBrigctl\fP is closed. Each option is
mutually exclusive, i.e. either may be given separately or in combination. This
is useful to save a set of commands and then read them later but not write the
modified history for a consistent set of test commands in interactive mode, for
example.
History is stored in '~/.rigctl_history' by default although the destination
directory may be changed by setting the RIGCTL_HIST_DIR environment variable.
When RIGCTL_HIST_DIR is unset, the value of the HOME environment variable is
used instead. Only the destination directory may be changed at this time.
If Readline support is not found at configure time the original internal command
handler is used. Readline is not used for \fBrigctl\fP commands entered on the
command line regardless if Readline support is built in or not.
\fBN.B.\fP Readline support is not included in the Windows 32 binary builds
supplied by the Hamlib Project. Running \fBrigctl\fP on the Windows 32 platform
in the 'cmd' shell does give session command line history, however, it is not
saved to disk between sessions.
.SH DIAGNOSTICS
The \fB-v\fP, \fB--verbose\fP option allows different levels of diagnostics
to be output to \fBstderr\fP and correspond to -v for BUG, -vv for ERR,

Wyświetl plik

@ -34,9 +34,38 @@
#include <unistd.h>
#include <ctype.h>
#include <errno.h>
#include <getopt.h>
#ifdef HAVE_LIBREADLINE
# if defined(HAVE_READLINE_READLINE_H)
# include <readline/readline.h>
# elif defined(HAVE_READLINE_H) /* !defined(HAVE_READLINE_READLINE_H) */
# include <readline.h>
# else /* !defined(HAVE_READLINE_H) */
extern char *readline ();
# endif /* HAVE_READLINE_H */
#else
/* no readline */
#endif /* HAVE_LIBREADLINE */
#ifdef HAVE_READLINE_HISTORY
# include <sys/stat.h>
# define HST_SHRT_OPTS "iI"
# 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) */
#else
/* no history */
#define HST_SHRT_OPTS ""
#endif /* HAVE_READLINE_HISTORY */
#include <hamlib/rig.h>
#include "misc.h"
#include "iofunc.h"
@ -75,6 +104,10 @@ static struct option long_options[] =
{"show-conf", 0, 0, 'L'},
{"dump-caps", 0, 0, 'u'},
{"vfo", 0, 0, 'o'},
#ifdef HAVE_READLINE_HISTORY
{"read-history", 0, 0, 'i'},
{"save-history", 0, 0, 'I'},
#endif
{"verbose", 0, 0, 'v'},
{"help", 0, 0, 'h'},
{"version", 0, 0, 'V'},
@ -83,6 +116,14 @@ static struct option long_options[] =
#define MAXCONFLEN 128
/* variable for readline support */
#ifdef HAVE_LIBREADLINE
static const int have_rl = 1;
#else /* no readline */
static const int have_rl = 0;
#endif
int interactive = 1; /* if no cmd on command line, switch to interactive */
int prompt = 1; /* Print prompt in rigctl */
int vfo_mode = 0; /* vfo_mode = 0 means target VFO is 'currVFO' */
@ -100,6 +141,14 @@ int main (int argc, char *argv[])
int verbose = 0;
int show_conf = 0;
int dump_caps_opt = 0;
#ifdef HAVE_READLINE_HISTORY
int rd_hist = 0;
int sv_hist = 0;
const char *hist_dir = NULL;
const char hist_file[] = "/.rigctl_history";
char *hist_path = NULL;
struct stat hist_dir_stat;
#endif
const char *rig_file=NULL, *ptt_file=NULL, *dcd_file=NULL;
ptt_type_t ptt_type = RIG_PTT_NONE;
dcd_type_t dcd_type = RIG_DCD_NONE;
@ -111,7 +160,7 @@ int main (int argc, char *argv[])
int c;
int option_index = 0;
c = getopt_long (argc, argv, SHORT_OPTIONS,
c = getopt_long (argc, argv, SHORT_OPTIONS HST_SHRT_OPTS,
long_options, &option_index);
if (c == -1)
break;
@ -229,6 +278,14 @@ int main (int argc, char *argv[])
case 'o':
vfo_mode++;
break;
#ifdef HAVE_READLINE_HISTORY
case 'i':
rd_hist++;
break;
case 'I':
sv_hist++;
break;
#endif
case 'v':
verbose++;
break;
@ -327,6 +384,34 @@ int main (int argc, char *argv[])
exitcode = 0;
#ifdef HAVE_LIBREADLINE
if (interactive && prompt && have_rl) {
rl_readline_name = "rigctl";
#ifdef HAVE_READLINE_HISTORY
using_history(); /* Initialize Readline History */
if (rd_hist || sv_hist) {
if (!(hist_dir = getenv("RIGCTL_HIST_DIR")))
hist_dir = getenv("HOME");
if (((stat(hist_dir, &hist_dir_stat) == -1) && (errno == ENOENT))
|| !(S_ISDIR(hist_dir_stat.st_mode))) {
fprintf(stderr, "Warning: %s is not a directory!\n", hist_dir);
}
hist_path = (char *)calloc((sizeof(char) * (strlen(hist_dir) + strlen(hist_file) + 1)), sizeof(char));
strncpy(hist_path, hist_dir, strlen(hist_dir));
strncat(hist_path, hist_file, strlen(hist_file));
}
if (rd_hist && hist_path)
if (read_history(hist_path) == ENOENT)
fprintf(stderr, "Warning: Could not read history from %s\n", hist_path);
#endif
}
#endif /* HAVE_LIBREADLINE */
do {
retcode = rigctl_parse(my_rig, stdin, stdout, argv, argc);
if (retcode == 2)
@ -334,6 +419,20 @@ int main (int argc, char *argv[])
}
while (retcode == 0 || retcode == 2);
#ifdef HAVE_LIBREADLINE
if (interactive && prompt && have_rl) {
#ifdef HAVE_READLINE_HISTORY
if (sv_hist && hist_path)
if (write_history(hist_path) == ENOENT)
fprintf(stderr, "\nWarning: Could not write history to %s\n", hist_path);
if ((rd_hist || sv_hist) && hist_path) {
free(hist_path);
hist_path = (char *)NULL;
}
}
#endif
#endif
rig_close(my_rig); /* close port */
rig_cleanup(my_rig); /* if you care about memory */
@ -362,6 +461,10 @@ void usage(void)
" -l, --list list all model numbers and exit\n"
" -u, --dump-caps dump capabilities and exit\n"
" -o, --vfo do not default to VFO_CURR, require extra vfo arg\n"
#ifdef HAVE_READLINE_HISTORY
" -i, --read-history read prior interactive session history\n"
" -I, --save-history save current interactive session history\n"
#endif
" -v, --verbose set verbose mode, cumulative\n"
" -h, --help display this help and exit\n"
" -V, --version output version information and exit\n\n"

Wyświetl plik

@ -36,7 +36,31 @@
#include <ctype.h>
#include <errno.h>
#include <getopt.h>
#ifdef HAVE_LIBREADLINE
# if defined(HAVE_READLINE_READLINE_H)
# include <readline/readline.h>
# elif defined(HAVE_READLINE_H) /* !defined(HAVE_READLINE_READLINE_H) */
# include <readline.h>
# else /* !defined(HAVE_READLINE_H) */
extern char *readline ();
# endif /* HAVE_READLINE_H */
#else
/* 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 */
#include <hamlib/rig.h>
#include "misc.h"
@ -73,6 +97,21 @@ static pthread_mutex_t rig_mutex = PTHREAD_MUTEX_INITIALIZER;
#define ARG_IN (ARG_IN1|ARG_IN2|ARG_IN3|ARG_IN4)
#define ARG_OUT (ARG_OUT1|ARG_OUT2|ARG_OUT3|ARG_OUT4)
/* variables for readline support */
#ifdef HAVE_LIBREADLINE
static char *input_line = (char *)NULL;
static char *result = (char *)NULL;
static char *parsed_input[sizeof(char) * 5];
static const int have_rl = 1;
#ifdef HAVE_READLINE_HISTORY
static char *rp_hist_buf = (char *)NULL;
#endif
#else /* no readline */
static const int have_rl = 0;
#endif
struct test_table {
unsigned char cmd;
const char *name;
@ -308,6 +347,35 @@ void hash_delete_all() {
}
#ifdef HAVE_LIBREADLINE
/* Frees allocated memory and sets pointers to NULL before calling readline
* and then parses the input into space separated tokens.
*/
static void rp_getline(const char *s)
{
int i;
/* free allocated memory and set pointers to NULL */
if (input_line) {
free(input_line);
input_line = (char *)NULL;
}
if (result) {
result = (char *)NULL;
}
for (i = 0; i < 5; i++)
parsed_input[i] = NULL;
/* Action! Returns typed line with newline stripped. */
input_line = readline(s);
}
#endif
/*
* TODO: use Lex?
*/
@ -348,6 +416,7 @@ static int scanfc(FILE *fin, const char *format, void *p)
#define MAXARGSZ 127
extern int interactive;
extern int prompt;
extern int vfo_mode;
@ -359,194 +428,603 @@ int rigctl_parse(RIG *my_rig, FILE *fin, FILE *fout, char *argv[], int argc)
{
int retcode; /* generic return code from functions */
unsigned char cmd;
struct test_table *cmd_entry;
struct test_table *cmd_entry = NULL;
char arg1[MAXARGSZ+1], *p1;
char arg2[MAXARGSZ+1], *p2;
char arg3[MAXARGSZ+1], *p3;
char arg1[MAXARGSZ+1], *p1 = NULL;
char arg2[MAXARGSZ+1], *p2 = NULL;
char arg3[MAXARGSZ+1], *p3 = NULL;
static int last_was_ret = 1;
vfo_t vfo = RIG_VFO_CURR;
if (interactive) {
if (prompt)
fprintf_flush(fout, "\nRig command: ");
/* cmd, internal, rigctld */
if (!(interactive && prompt && have_rl)) {
if (interactive) {
if (prompt)
fprintf_flush(fout, "\nRig command: ");
do {
if (scanfc(fin, "%c", &cmd) < 1)
return -1;
/* Extended response protocol requested with leading '+' on command
* string--rigctld only!
*/
if (cmd == '+' && !prompt) {
ext_resp = 1;
do {
if (scanfc(fin, "%c", &cmd) < 1)
return -1;
} else if (cmd == '+' && prompt) {
return 0;
}
if (cmd != '\\' && cmd != '_' && cmd != '#' && ispunct(cmd) && !prompt) {
ext_resp = 1;
resp_sep = cmd;
if (scanfc(fin, "%c", &cmd) < 1)
return -1;
} else if (cmd != '\\' && cmd != '?' && cmd != '_' && cmd != '#' && ispunct(cmd) && prompt) {
return 0;
}
/* command by name */
if (cmd == '\\') {
unsigned char cmd_name[MAXNAMSIZ], *pcmd = cmd_name;
int c_len = MAXNAMSIZ;
if (scanfc(fin, "%c", pcmd) < 1)
return -1;
while(c_len-- && (isalnum(*pcmd) || *pcmd == '_' ))
if (scanfc(fin, "%c", ++pcmd) < 1)
/* Extended response protocol requested with leading '+' on command
* string--rigctld only!
*/
if (cmd == '+' && !prompt) {
ext_resp = 1;
if (scanfc(fin, "%c", &cmd) < 1)
return -1;
*pcmd = '\0';
cmd = parse_arg((char *)cmd_name);
break;
}
if (cmd == 0x0a || cmd == 0x0d) {
if (last_was_ret) {
if (prompt) {
fprintf(fout, "? for help, q to quit.\n");
fprintf_flush(fout, "\nRig command: ");
}
} else if (cmd == '+' && prompt) {
return 0;
}
last_was_ret = 1;
if (cmd != '\\' && cmd != '_' && cmd != '#' && ispunct(cmd) && !prompt) {
ext_resp = 1;
resp_sep = cmd;
if (scanfc(fin, "%c", &cmd) < 1)
return -1;
} else if (cmd != '\\' && cmd != '?' && cmd != '_' && cmd != '#' && ispunct(cmd) && prompt) {
return 0;
}
/* command by name */
if (cmd == '\\') {
unsigned char cmd_name[MAXNAMSIZ], *pcmd = cmd_name;
int c_len = MAXNAMSIZ;
if (scanfc(fin, "%c", pcmd) < 1)
return -1;
while(c_len-- && (isalnum(*pcmd) || *pcmd == '_' ))
if (scanfc(fin, "%c", ++pcmd) < 1)
return -1;
*pcmd = '\0';
cmd = parse_arg((char *)cmd_name);
break;
}
if (cmd == 0x0a || cmd == 0x0d) {
if (last_was_ret) {
if (prompt) {
fprintf(fout, "? for help, q to quit.\n");
fprintf_flush(fout, "\nRig command: ");
}
return 0;
}
last_was_ret = 1;
}
} while (cmd == 0x0a || cmd == 0x0d);
last_was_ret = 0;
/* comment line */
if (cmd == '#') {
while( cmd != '\n' && cmd != '\r')
if (scanfc(fin, "%c", &cmd) < 1)
return -1;
return 0;
}
} while (cmd == 0x0a || cmd == 0x0d);
if (cmd == 'Q' || cmd == 'q')
return 1;
if (cmd == '?') {
usage_rig(fout);
fflush(fout);
return 0;
}
} else {
/* parse rest of command line */
if (optind >= argc)
return 1;
if (argv[optind][1] == '\0')
cmd = argv[optind][0];
else
cmd = parse_arg(argv[optind]);
optind++;
}
last_was_ret = 0;
/* comment line */
if (cmd == '#') {
while( cmd != '\n' && cmd != '\r')
if (scanfc(fin, "%c", &cmd) < 1)
return -1;
cmd_entry = find_cmd_entry(cmd);
if (!cmd_entry) {
fprintf(stderr, "Command '%c' not found!\n", cmd);
return 0;
}
if (cmd == 'Q' || cmd == 'q')
return 1;
if (cmd == '?') {
if (!(cmd_entry->flags & ARG_NOVFO) && vfo_mode) {
if (interactive) {
if (prompt)
fprintf_flush(fout, "VFO: ");
if (scanfc(fin, "%s", arg1) < 1)
return -1;
vfo = rig_parse_vfo(arg1);
} else {
if (!argv[optind]) {
fprintf(stderr, "Invalid arg for command '%s'\n",
cmd_entry->name);
exit(1);
}
vfo = rig_parse_vfo(argv[optind++]);
}
}
if ((cmd_entry->flags & ARG_IN_LINE) &&
(cmd_entry->flags & ARG_IN1) && cmd_entry->arg1) {
if (interactive) {
char *nl;
if (prompt)
fprintf_flush(fout, "%s: ", cmd_entry->arg1);
if (fgets(arg1, MAXARGSZ, fin) == NULL)
return -1;
if (arg1[0] == 0xa)
if (fgets(arg1, MAXARGSZ, fin) == NULL)
return -1;
nl = strchr(arg1, 0xa);
if (nl) *nl = '\0'; /* chomp */
p1 = arg1[0] == ' ' ? arg1 + 1 : arg1;
} else {
if (!argv[optind]) {
fprintf(stderr, "Invalid arg for command '%s'\n",
cmd_entry->name);
exit(1);
}
p1 = argv[optind++];
}
} else
if ((cmd_entry->flags & ARG_IN1) && cmd_entry->arg1) {
if (interactive) {
if (prompt)
fprintf_flush(fout, "%s: ", cmd_entry->arg1);
if (scanfc(fin, "%s", arg1) < 1)
return -1;
p1 = arg1;
} else {
if (!argv[optind]) {
fprintf(stderr, "Invalid arg for command '%s'\n",
cmd_entry->name);
exit(1);
}
p1 = argv[optind++];
}
}
if (p1 && p1[0] != '?' && (cmd_entry->flags & ARG_IN2) && cmd_entry->arg2) {
if (interactive) {
if (prompt)
fprintf_flush(fout, "%s: ", cmd_entry->arg2);
if (scanfc(fin, "%s", arg2) < 1)
return -1;
p2 = arg2;
} else {
if (!argv[optind]) {
fprintf(stderr, "Invalid arg for command '%s'\n",
cmd_entry->name);
exit(1);
}
p2 = argv[optind++];
}
}
if (p1 && p1[0] != '?' && (cmd_entry->flags & ARG_IN3) && cmd_entry->arg3) {
if (interactive) {
if (prompt)
fprintf_flush(fout, "%s: ", cmd_entry->arg3);
if (scanfc(fin, "%s", arg3) < 1)
return -1;
p3 = arg3;
} else {
if (!argv[optind]) {
fprintf(stderr, "Invalid arg for command '%s'\n",
cmd_entry->name);
exit(1);
}
p3 = argv[optind++];
}
}
}
#ifdef HAVE_LIBREADLINE
if (interactive && prompt && have_rl) {
int j, x;
#ifdef HAVE_READLINE_HISTORY
/* Minimum space for 32+1+32+1+128+1+128+1+128+1 = 453 chars, so
* allocate 512 chars cleared to zero for safety.
*/
rp_hist_buf = (char *)calloc(512, sizeof(char));
#endif
rl_instream = fin;
rl_outstream = fout;
rp_getline("\nRig command: ");
/* EOF (Ctl-D) received on empty input line, bail out gracefully. */
if (!input_line) {
fprintf_flush(fout, "\n");
return 1;
}
/* Q or q to quit */
if (!(strncasecmp(input_line, "q", 1)))
return 1;
/* '?' for help */
if (!(strncmp(input_line, "?", 1))) {
usage_rig(fout);
fflush(fout);
return 0;
}
} else {
/* parse rest of command line */
if (optind >= argc)
/* '#' for comment */
if (!(strncmp(input_line, "#", 1)))
return 0;
/* Blank line entered */
if (!(strcmp(input_line, ""))) {
fprintf(fout, "? for help, q to quit.\n");
fflush(fout);
return 0;
}
rig_debug(RIG_DEBUG_BUG, "%s: input_line: %s\n", __func__, input_line);
/* Split input_line on any number of spaces to get the command token
* Tabs are intercepted by readline for completion and a newline
* causes readline to return the typed text. If more than one
* argument is given, it will be parsed out later.
*/
result = strtok(input_line, " ");
/* parsed_input stores pointers into input_line where the token strings
* start.
*/
if (result) {
parsed_input[0] = result;
} else {
/* Oops! Invoke GDB!! */
fprintf_flush(fout, "\n");
return 1;
if (argv[optind][1] == '\0')
cmd = argv[optind][0];
else
cmd = parse_arg(argv[optind]);
optind++;
}
cmd_entry = find_cmd_entry(cmd);
if (!cmd_entry) {
fprintf(stderr, "Command '%c' not found!\n", cmd);
return 0;
}
p1 = p2 = p3 = NULL;
if (!(cmd_entry->flags & ARG_NOVFO) && vfo_mode) {
if (interactive) {
if (prompt)
fprintf_flush(fout, "VFO: ");
if (scanfc(fin, "%s", arg1) < 1)
return -1;
vfo = rig_parse_vfo(arg1);
} else {
if (!argv[optind]) {
fprintf(stderr, "Invalid arg for command '%s'\n",
cmd_entry->name);
exit(1);
}
vfo = rig_parse_vfo(argv[optind++]);
}
}
if ((cmd_entry->flags & ARG_IN_LINE) &&
(cmd_entry->flags & ARG_IN1) && cmd_entry->arg1) {
if (interactive) {
char *nl;
if (prompt)
fprintf_flush(fout, "%s: ", cmd_entry->arg1);
if (fgets(arg1, MAXARGSZ, fin) == NULL)
return -1;
if (arg1[0] == 0xa)
if (fgets(arg1, MAXARGSZ, fin) == NULL)
return -1;
nl = strchr(arg1, 0xa);
if (nl) *nl = '\0'; /* chomp */
p1 = arg1[0] == ' ' ? arg1 + 1 : arg1;
} else {
if (!argv[optind]) {
fprintf(stderr, "Invalid arg for command '%s'\n",
cmd_entry->name);
exit(1);
}
p1 = argv[optind++];
/* At this point parsed_input contains the typed text of the command
* with surrounding space characters removed. If Readline History is
* available, copy the command string into a history buffer.
*/
/* Single character command */
if ((strlen(parsed_input[0]) == 1) && (*parsed_input[0] != '\\')) {
cmd = *parsed_input[0];
#ifdef HAVE_READLINE_HISTORY
/* Store what is typed, not validated, for history. */
if (rp_hist_buf)
strncpy(rp_hist_buf, parsed_input[0], 1);
#endif
}
} else
if ((cmd_entry->flags & ARG_IN1) && cmd_entry->arg1) {
if (interactive) {
if (prompt)
fprintf_flush(fout, "%s: ", cmd_entry->arg1);
if (scanfc(fin, "%s", arg1) < 1)
return -1;
/* Test the command token, parsed_input[0] */
else if ((*parsed_input[0] == '\\') && (strlen(parsed_input[0]) > 1)) {
char cmd_name[MAXNAMSIZ];
/* if there is no terminating '\0' character in the source string,
* srncpy() doesn't add one even if the supplied length is less
* than the destination array. Truncate the source string here.
*/
if (strlen(parsed_input[0] + 1) >= MAXNAMSIZ)
*(parsed_input[0] + MAXNAMSIZ) = '\0';
#ifdef HAVE_READLINE_HISTORY
if (rp_hist_buf)
strncpy(rp_hist_buf, parsed_input[0], MAXNAMSIZ);
#endif
/* The starting position of the source string is the first
* character past the initial '\'. Using MAXNAMSIZ for the
* length leaves enough space for the '\0' string terminator in the
* cmd_name array.
*/
strncpy(cmd_name, parsed_input[0] + 1, MAXNAMSIZ);
/* Sanity check as valid multiple character commands consist of
* alpha-numeric characters and the underscore ('_') character.
*/
for (j = 0; cmd_name[j] != '\0'; j++) {
if (!(isalnum(cmd_name[j]) || cmd_name[j] == '_')) {
fprintf(stderr, "Valid multiple character command names contain alpha-numeric characters plus '_'\n");
return 0;
}
}
cmd = parse_arg(cmd_name);
}
/* Single '\' entered, prompt again */
else if ((*parsed_input[0] == '\\') && (strlen(parsed_input[0]) == 1)) {
return 0;
}
/* Multiple characters but no leading '\' */
else {
fprintf(stderr, "Precede multiple character command names with '\\'\n");
return 0;
}
cmd_entry = find_cmd_entry(cmd);
if (!cmd_entry) {
if (cmd == '\0')
fprintf(stderr, "Command '%s' not found!\n", parsed_input[0]);
else
fprintf(stderr, "Command '%c' not found!\n", cmd);
return 0;
}
/* If vfo_mode is enabled (-o|--vfo) check if already given
* or prompt for it.
*/
if (!(cmd_entry->flags & ARG_NOVFO) && vfo_mode) {
/* Check if VFO was given with command. */
result = strtok(NULL, " ");
if (result) {
x = 1;
parsed_input[x] = result;
}
/* Need to prompt if a VFO string was not given. */
else {
x = 0;
rp_getline("VFO: ");
if (!input_line) {
fprintf_flush(fout, "\n");
return 1;
}
/* Blank line entered */
if (!(strcmp(input_line, ""))) {
fprintf(fout, "? for help, q to quit.\n");
fflush(fout);
return 0;
}
/* Get the first token of input, the rest, if any, will be
* used later.
*/
result = strtok(input_line, " ");
if (result) {
parsed_input[x] = result;
} else {
fprintf_flush(fout, "\n");
return 1;
}
}
/* VFO name tokens are presently quite short. Truncate excessively
* long strings.
*/
if (strlen(parsed_input[x]) >= MAXNAMSIZ)
*(parsed_input[x] + (MAXNAMSIZ - 1)) = '\0';
#ifdef HAVE_READLINE_HISTORY
if (rp_hist_buf) {
strncat(rp_hist_buf, " ", 1);
strncat(rp_hist_buf, parsed_input[x], MAXNAMSIZ);
}
#endif
/* Sanity check, VFO names are alpha only. */
for (j = 0; j < MAXNAMSIZ && parsed_input[x][j] != '\0'; j++) {
if (!(isalpha(parsed_input[x][j]))) {
parsed_input[x][j] = '\0';
break;
}
}
vfo = rig_parse_vfo(parsed_input[x]);
if (vfo == RIG_VFO_NONE) {
fprintf(stderr, "Warning: VFO '%s' unrecognized, using 'currVFO' instead.\n",
parsed_input[x]);
vfo = RIG_VFO_CURR;
}
}
/* \send_cmd, \send_morse */
if ((cmd_entry->flags & ARG_IN_LINE) &&
(cmd_entry->flags & ARG_IN1) && cmd_entry->arg1) {
/* Check for a non-existent delimiter so as to not break up
* remaining line into separate tokens (spaces OK).
*/
result = strtok(NULL, "\0");
if (vfo_mode && result) {
x = 2;
parsed_input[x] = result;
} else if (result) {
x = 1;
parsed_input[x] = result;
} else {
x = 0;
char pmptstr[(strlen(cmd_entry->arg1) + 3)];
strcpy(pmptstr, cmd_entry->arg1);
strcat(pmptstr, ": ");
rp_getline(pmptstr);
/* Blank line entered */
if (!(strcmp(input_line, ""))) {
fprintf(fout, "? for help, q to quit.\n");
fflush(fout);
return 0;
}
if (input_line)
parsed_input[x] = input_line;
else {
fprintf_flush(fout, "\n");
return 1;
}
}
/* The arg1 array size is MAXARGSZ + 1 so truncate it to fit if larger. */
if (strlen(parsed_input[x]) > MAXARGSZ)
parsed_input[x][MAXARGSZ] = '\0';
#ifdef HAVE_READLINE_HISTORY
if (rp_hist_buf) {
strncat(rp_hist_buf, " ", 1);
strncat(rp_hist_buf, parsed_input[x], MAXARGSZ);
}
#endif
strcpy(arg1, parsed_input[x]);
p1 = arg1;
} else {
if (!argv[optind]) {
fprintf(stderr, "Invalid arg for command '%s'\n",
cmd_entry->name);
exit(1);
}
p1 = argv[optind++];
}
}
if (p1 && p1[0] != '?' && (cmd_entry->flags & ARG_IN2) && cmd_entry->arg2) {
if (interactive) {
if (prompt)
fprintf_flush(fout, "%s: ", cmd_entry->arg2);
if (scanfc(fin, "%s", arg2) < 1)
return -1;
/* Normal argument parsing. */
else if ((cmd_entry->flags & ARG_IN1) && cmd_entry->arg1) {
result = strtok(NULL, " ");
if (vfo_mode && result) {
x = 2;
parsed_input[x] = result;
} else if (result) {
x = 1;
parsed_input[x] = result;
} else {
x = 0;
char pmptstr[(strlen(cmd_entry->arg1) + 3)];
strcpy(pmptstr, cmd_entry->arg1);
strcat(pmptstr, ": ");
rp_getline(pmptstr);
if (!(strcmp(input_line, ""))) {
fprintf(fout, "? for help, q to quit.\n");
fflush(fout);
return 0;
}
result = strtok(input_line, " ");
if (result) {
parsed_input[x] = result;
} else {
fprintf_flush(fout, "\n");
return 1;
}
}
if (strlen(parsed_input[x]) > MAXARGSZ)
parsed_input[x][MAXARGSZ] = '\0';
#ifdef HAVE_READLINE_HISTORY
if (rp_hist_buf) {
strncat(rp_hist_buf, " ", 1);
strncat(rp_hist_buf, parsed_input[x], MAXARGSZ);
}
#endif
strcpy(arg1, parsed_input[x]);
p1 = arg1;
}
if (p1 && p1[0] != '?' && (cmd_entry->flags & ARG_IN2) && cmd_entry->arg2) {
result = strtok(NULL, " ");
if (vfo_mode && result) {
x = 3;
parsed_input[x] = result;
} else if (result) {
x = 2;
parsed_input[x] = result;
} else {
x = 0;
char pmptstr[(strlen(cmd_entry->arg2) + 3)];
strcpy(pmptstr, cmd_entry->arg2);
strcat(pmptstr, ": ");
rp_getline(pmptstr);
if (!(strcmp(input_line, ""))) {
fprintf(fout, "? for help, q to quit.\n");
fflush(fout);
return 0;
}
result = strtok(input_line, " ");
if (result) {
parsed_input[x] = result;
} else {
fprintf_flush(fout, "\n");
return 1;
}
}
if (strlen(parsed_input[x]) > MAXARGSZ)
parsed_input[x][MAXARGSZ] = '\0';
#ifdef HAVE_READLINE_HISTORY
if (rp_hist_buf) {
strncat(rp_hist_buf, " ", 1);
strncat(rp_hist_buf, parsed_input[x], MAXARGSZ);
}
#endif
strcpy(arg2, parsed_input[x]);
p2 = arg2;
} else {
if (!argv[optind]) {
fprintf(stderr, "Invalid arg for command '%s'\n",
cmd_entry->name);
exit(1);
}
p2 = argv[optind++];
}
}
if (p1 && p1[0] != '?' && (cmd_entry->flags & ARG_IN3) && cmd_entry->arg3) {
if (interactive) {
if (prompt)
fprintf_flush(fout, "%s: ", cmd_entry->arg3);
if (scanfc(fin, "%s", arg3) < 1)
return -1;
if (p1 && p1[0] != '?' && (cmd_entry->flags & ARG_IN3) && cmd_entry->arg3) {
result = strtok(NULL, " ");
if (vfo_mode && result) {
x = 4;
parsed_input[x] = result;
} else if (result) {
x = 3;
parsed_input[x] = result;
} else {
x = 0;
char pmptstr[(strlen(cmd_entry->arg3) + 3)];
strcpy(pmptstr, cmd_entry->arg3);
strcat(pmptstr, ": ");
rp_getline(pmptstr);
if (!(strcmp(input_line, ""))) {
fprintf(fout, "? for help, q to quit.\n");
fflush(fout);
return 0;
}
result = strtok(input_line, " ");
if (result) {
parsed_input[x] = result;
} else {
fprintf_flush(fout, "\n");
return 1;
}
}
if (strlen(parsed_input[x]) > MAXARGSZ)
parsed_input[x][MAXARGSZ] = '\0';
#ifdef HAVE_READLINE_HISTORY
if (rp_hist_buf) {
strncat(rp_hist_buf, " ", 1);
strncat(rp_hist_buf, parsed_input[x], MAXARGSZ);
}
#endif
strcpy(arg3, parsed_input[x]);
p3 = arg3;
} else {
if (!argv[optind]) {
fprintf(stderr, "Invalid arg for command '%s'\n",
cmd_entry->name);
exit(1);
}
p3 = argv[optind++];
}
#ifdef HAVE_READLINE_HISTORY
if (rp_hist_buf) {
add_history(rp_hist_buf);
free(rp_hist_buf);
rp_hist_buf = (char *)NULL;
}
#endif
}
#endif /* HAVE_LIBREADLINE */
/*
* mutex locking needed because rigctld is multithreaded
* and hamlib is not MT-safe
@ -645,6 +1123,8 @@ void usage_rig(FILE *fout)
else
fprintf(fout, ")%*s", nbspaces, " ");
}
fprintf(fout, "\n\nPrepend long command names with '\\', e.g. '\\dump_state'\n");
}

Wyświetl plik

@ -2,7 +2,7 @@
.\" First parameter, NAME, should be all caps
.\" Second parameter, SECTION, should be 1-8, maybe w/ subsection
.\" other parameters are allowed: see man(7), man(1)
.TH ROTCTL "1" "March 1, 2012" "Hamlib" "Rotator Control Program"
.TH ROTCTL "1" "February 22, 2013" "Hamlib" "Rotator Control Program"
.\" Please adjust this date whenever revising the manpage.
.\"
.\" Some roff macros, for reference:
@ -29,23 +29,23 @@ interactive mode if none are provided on the command line.
.\" \fI<whatever>\fP escape sequences to invode bold face and italics,
.\" respectively.
Keep in mind that \fBHamlib\fP is BETA level software.
While a lot of backend libraries lack complete rig support, the basic functions
While a lot of backend libraries lack complete rotator support, the basic functions
are usually well supported. The API may change without publicized notice,
while an advancement of the minor version (e.g. 1.1.x to 1.2.x) indicates such
while an advancement of the minor version (e.g. 1.x to 3.x) indicates such
a change.
.PP
Please report bugs and provide feedback at the e-mail address given in the
REPORTING BUGS section. Patches and code enhancements are also welcome.
.SH OPTIONS
This program follows the usual GNU command line syntax, with long
options starting with two dashes (`-').
options starting with two dashes ('-').
Here is s summary of the supported options:
Here is a summary of the supported options:
.TP
.B \-m, --model=id
Select rotator model number. See model list (use 'rotctl -l').
.sp
NB: \fBrotctl\fP (or third party software) will use rig model 2
NB: \fBrotctl\fP (or third party software) will use rotator model 2
for NET rotctl (rotctld).
.TP
.B \-r, --rot-file=device
@ -86,6 +86,26 @@ Shift-PageDown, or using the scrollbars of a virtual terminal in X or
the cmd window in Windows. The output can be piped to 'more' or 'less',
e.g. 'rotctl -l | more'.
.TP
.B \-i, --read-history
Read previously saved command and argument history from a file
(default '~/.rotctl_history') for the current session. Available when
\fBrotctl\fP is built with Readline support (see READLINE below).
.sp
\fBN.B.\fP To read a history file stored in another directory, set the
ROTCTL_HIST_DIR environment variable, e.g. 'ROTCTL_HIST_DIR=~/tmp rotctl -i'.
When ROTCTL_HIST_DIR is not set, the value of HOME is used.
.TP
.B \-I, --save-history
Write current session and previous session(s), if -i option is given, command and
argument history to a file (default '~/.rotctl_history') at the end of the current
session. Complete commands with arguments are saved as a single line to be
recalled and used or edited. Available when \fBrotctl\fP is built with Readline
support (see READLINE below).
.sp
\fBN.B.\fP To write a history file in another directory, set the ROTCTL_HIST_DIR
environment variable, e.g. 'ROTCTL_HIST_DIR=~/tmp rotctl -I'. When ROTCTL_HIST_DIR
is not set, the value of HOME is used.
.TP
.B \-v, --verbose
Set verbose mode, cumulative (see DIAGNOSTICS below).
.TP
@ -106,7 +126,10 @@ the operation will fail with a \fBHamlib\fP error code.
Commands can be entered either as a single char, or as a long command name.
Basically, the commands do not take a dash in front of them, as
the options do. They may be typed in when in interactive mode
or provided as argument(s) in command line interface mode.
or provided as argument(s) in command line interface mode. In interactive
mode commands and their arguments may be entered on a single line:
.sp
Rotator command: P 123 45
.PP
Since most of the \fBHamlib\fP operations have a \fIset\fP and a \fIget\fP
method, an upper case letter will be used for \fIset\fP method whereas the
@ -188,8 +211,8 @@ Both are floating point values. The precision of the returned square is
controlled by 'Loc Len' which should be an even numbered integer value between
2 and 12.
.sp
For example, "+L -170.000000 -85.000000 12\\n" returns
"Locator: AA55AA00AA00\\n".
For example, "L -170.000000 -85.000000 12" returns
"Locator: AA55AA00AA00".
.TP
.B l, loc2lonlat 'Locator'
Returns 'Longitude' and 'Latitude' in decimal degrees at the approximate
@ -198,8 +221,8 @@ variables internally, some rounding error occurs). West longitude is
expressed as a negative value. South latitude is expressed as a negative
value. Locator can be from 2 to 12 characters in length.
.sp
For example, "+l AA55AA00AA00\\n" returns "Longitude: -169.999983\\nLatitude:
-84.999991\\n".
For example, "l AA55AA00AA00" returns "Longitude: -169.999983 Latitude:
-84.999991".
.TP
.B D, dms2dec 'Degrees' 'Minutes' 'Seconds' 'S/W'
Returns 'Dec Degrees', a signed floating point value.
@ -258,6 +281,45 @@ Connect to a running \fBrotctld\fP with rotor model 2 ("NET rotctl") on the
local host and specifying the TCP port, and querying the position:
.sp
$ rotctl -m 2 -r localhost:4533 \\get_pos
.SH READLINE
If Readline library development files are found at configure time, \fBrotctl\fP
will be conditonally built with Readline support for command and argument entry.
Readline command key bindings are at their defaults as described in the Readline
manual (\fIhttp://cnswww.cns.cwru.edu/php/chet/readline/rluserman.html\fP)
although \fBrotctl\fP sets the name 'rotctl' which can be used in Conditional
Init Constructs in the Readline Init File ('~/.inputrc' by default) for custom
keybindings unique to \fBrotctl\fP.
Command history is available with Readline support as described in the Readline
History manual
(\fIhttp://cnswww.cns.cwru.edu/php/chet/readline/history.html#SEC1\fP). Command
and argument strings are stored as single lines even when arguments are prompted
for input individually. Commands and arguments are not validated and are stored
as typed with values separated by a single space.
Normally session history is not saved, however, use of either of the
\fI-i/--read-history\fP or \fI-I/--save-history\fP options when starting
\fBrotctl\fP will cause any previously saved history to be read in and/or the
current and any previous session history (assuming the -i and -I options are
given together) will be written out when \fBrotctl\fP is closed. Each option is
mutually exclusive, i.e. either may be given separately or in combination. This
is useful to save a set of commands and then read them later but not write the
modified history for a consistent set of test commands in interactive mode, for
example.
History is stored in '~/.rotctl_history' by default although the destination
directory may be changed by setting the ROTCTL_HIST_DIR environment variable.
When ROTCTL_HIST_DIR is unset, the value of the HOME environment variable is
used instead. Only the destination directory may be changed at this time.
If Readline support is not found at configure time the original internal command
handler is used. Readline is not used for \fBrotctl\fP commands entered on the
command line regardless if Readline support is built in or not.
\fBN.B.\fP Readline support is not included in the Windows 32 binary builds
supplied by the Hamlib Project. Running \fBrotctl\fP on the Windows 32 platform
in the 'cmd' shell does give session command line history, however, it is not
saved to disk between sessions.
.SH DIAGNOSTICS
The \fB-v\fP, \fB--version\fP option allows different levels of diagnostics
to be output to \fBstderr\fP and correspond to -v for BUG, -vv for ERR,
@ -289,7 +351,7 @@ Written by Stephane Fillod, Nate Bargmann, and the Hamlib Group
.SH COPYRIGHT
Copyright \(co 2000-2011 Stephane Fillod
.br
Copyright \(co 2011-2012 Nate Bargmann
Copyright \(co 2011-2013 Nate Bargmann
.br
Copyright \(co 2000-2010 the Hamlib Group
.PP

Wyświetl plik

@ -33,9 +33,39 @@
#include <string.h>
#include <unistd.h>
#include <ctype.h>
#include <errno.h>
#include <getopt.h>
#ifdef HAVE_LIBREADLINE
# if defined(HAVE_READLINE_READLINE_H)
# include <readline/readline.h>
# elif defined(HAVE_READLINE_H) /* !defined(HAVE_READLINE_READLINE_H) */
# include <readline.h>
# else /* !defined(HAVE_READLINE_H) */
extern char *readline ();
# endif /* HAVE_READLINE_H */
#else
/* no readline */
#endif /* HAVE_LIBREADLINE */
#ifdef HAVE_READLINE_HISTORY
# include <sys/stat.h>
# define HST_SHRT_OPTS "iI"
# 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) */
#else
/* no history */
#define HST_SHRT_OPTS ""
#endif /* HAVE_READLINE_HISTORY */
#include <hamlib/rotator.h>
#include "misc.h"
@ -63,6 +93,10 @@ static struct option long_options[] =
{"set-conf", 1, 0, 'C'},
{"show-conf",0, 0, 'L'},
{"dump-caps",0, 0, 'u'},
#ifdef HAVE_READLINE_HISTORY
{"read-history", 0, 0, 'i'},
{"save-history", 0, 0, 'I'},
#endif
{"verbose", 0, 0, 'v'},
{"help", 0, 0, 'h'},
{"version", 0, 0, 'V'},
@ -71,6 +105,14 @@ static struct option long_options[] =
#define MAXCONFLEN 128
/* variable for readline support */
#ifdef HAVE_LIBREADLINE
static const int have_rl = 1;
#else /* no readline */
static const int have_rl = 0;
#endif
int interactive = 1; /* if no cmd on command line, switch to interactive */
int prompt = 1; /* Print prompt in rotctl */
@ -87,6 +129,14 @@ int main (int argc, char *argv[])
int verbose = 0;
int show_conf = 0;
int dump_caps_opt = 0;
#ifdef HAVE_READLINE_HISTORY
int rd_hist = 0;
int sv_hist = 0;
const char *hist_dir = NULL;
const char hist_file[] = "/.rotctl_history";
char *hist_path = NULL;
struct stat hist_dir_stat;
#endif
const char *rot_file=NULL;
int serial_rate = 0;
char conf_parms[MAXCONFLEN] = "";
@ -95,7 +145,7 @@ int main (int argc, char *argv[])
int c;
int option_index = 0;
c = getopt_long (argc, argv, SHORT_OPTIONS,
c = getopt_long (argc, argv, SHORT_OPTIONS HST_SHRT_OPTS,
long_options, &option_index);
if (c == -1)
break;
@ -147,6 +197,14 @@ int main (int argc, char *argv[])
else
send_cmd_term = optarg[0];
break;
#ifdef HAVE_READLINE_HISTORY
case 'i':
rd_hist++;
break;
case 'I':
sv_hist++;
break;
#endif
case 'v':
verbose++;
break;
@ -233,6 +291,34 @@ int main (int argc, char *argv[])
exitcode = 0;
#ifdef HAVE_LIBREADLINE
if (interactive && prompt && have_rl) {
rl_readline_name = "rotctl";
#ifdef HAVE_READLINE_HISTORY
using_history(); /* Initialize Readline History */
if (rd_hist || sv_hist) {
if (!(hist_dir = getenv("ROTCTL_HIST_DIR")))
hist_dir = getenv("HOME");
if (((stat(hist_dir, &hist_dir_stat) == -1) && (errno == ENOENT))
|| !(S_ISDIR(hist_dir_stat.st_mode))) {
fprintf(stderr, "Warning: %s is not a directory!\n", hist_dir);
}
hist_path = (char *)calloc((sizeof(char) * (strlen(hist_dir) + strlen(hist_file) + 1)), sizeof(char));
strncpy(hist_path, hist_dir, strlen(hist_dir));
strncat(hist_path, hist_file, strlen(hist_file));
}
if (rd_hist && hist_path)
if (read_history(hist_path) == ENOENT)
fprintf(stderr, "Warning: Could not read history from %s\n", hist_path);
#endif
}
#endif /* HAVE_LIBREADLINE */
do {
retcode = rotctl_parse(my_rot, stdin, stdout, argv, argc);
if (retcode == 2)
@ -240,6 +326,20 @@ int main (int argc, char *argv[])
}
while (retcode == 0 || retcode == 2);
#ifdef HAVE_LIBREADLINE
if (interactive && prompt && have_rl) {
#ifdef HAVE_READLINE_HISTORY
if (sv_hist && hist_path)
if (write_history(hist_path) == ENOENT)
fprintf(stderr, "\nWarning: Could not write history to %s\n", hist_path);
if ((rd_hist || sv_hist) && hist_path) {
free(hist_path);
hist_path = (char *)NULL;
}
}
#endif
#endif
rot_close(my_rot); /* close port */
rot_cleanup(my_rot); /* if you care about memory */
@ -261,6 +361,10 @@ void usage()
" -L, --show-conf list all config parameters\n"
" -l, --list list all model numbers and exit\n"
" -u, --dump-caps dump capabilities and exit\n"
#ifdef HAVE_READLINE_HISTORY
" -i, --read-history read prior interactive session history\n"
" -I, --save-history save current interactive session history\n"
#endif
" -v, --verbose set verbose mode, cumulative\n"
" -h, --help display this help and exit\n"
" -V, --version output version information and exit\n\n"

Wyświetl plik

@ -33,9 +33,34 @@
#include <string.h>
#include <unistd.h>
#include <ctype.h>
#include <errno.h>
#ifdef HAVE_LIBREADLINE
# if defined(HAVE_READLINE_READLINE_H)
# include <readline/readline.h>
# elif defined(HAVE_READLINE_H) /* !defined(HAVE_READLINE_READLINE_H) */
# include <readline.h>
# else /* !defined(HAVE_READLINE_H) */
extern char *readline ();
# endif /* HAVE_READLINE_H */
#else
/* 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 */
#include <hamlib/rotator.h>
#include "serial.h"
#include "misc.h"
@ -70,6 +95,21 @@ static pthread_mutex_t rot_mutex = PTHREAD_MUTEX_INITIALIZER;
#define ARG_IN (ARG_IN1|ARG_IN2|ARG_IN3|ARG_IN4)
#define ARG_OUT (ARG_OUT1|ARG_OUT2|ARG_OUT3|ARG_OUT4)
/* variables for readline support */
#ifdef HAVE_LIBREADLINE
static char *input_line = (char *)NULL;
static char *result = (char *)NULL;
static char *parsed_input[sizeof(char) * 7];
static const int have_rl = 1;
#ifdef HAVE_READLINE_HISTORY
static char *rp_hist_buf = (char *)NULL;
#endif
#else /* no readline */
static const int have_rl = 0;
#endif
struct test_table {
unsigned char cmd;
const char *name;
@ -212,6 +252,38 @@ void hash_delete_all() {
}
#ifdef HAVE_LIBREADLINE
/* Frees allocated memory and sets pointers to NULL before calling readline
* and then parses the input into space separated tokens.
*/
static void rp_getline(const char *s)
{
int i;
/* free allocated memory and set pointers to NULL */
if (input_line) {
free(input_line);
input_line = (char *)NULL;
}
if (result) {
result = (char *)NULL;
}
/* cmd, arg1, arg2, arg3, arg4, arg5, arg6
* arg5 and arg 6 are currently unused.
*/
for (i = 0; i < 7; i++)
parsed_input[i] = NULL;
/* Action! Returns typed line with newline stripped. */
input_line = readline(s);
}
#endif
/*
* TODO: use Lex?
*/
@ -264,194 +336,564 @@ int rotctl_parse(ROT *my_rot, FILE *fin, FILE *fout, char *argv[], int argc)
unsigned char cmd;
struct test_table *cmd_entry;
char arg1[MAXARGSZ + 1], *p1;
char arg2[MAXARGSZ + 1], *p2;
char arg3[MAXARGSZ + 1], *p3;
char arg4[MAXARGSZ + 1], *p4;
char *p5, *p6;
char arg1[MAXARGSZ + 1], *p1 = NULL;
char arg2[MAXARGSZ + 1], *p2 = NULL;
char arg3[MAXARGSZ + 1], *p3 = NULL;
char arg4[MAXARGSZ + 1], *p4 = NULL;
char *p5 = NULL;
char *p6 = NULL;
static int last_was_ret = 1;
if (interactive) {
if (prompt)
fprintf_flush(fout, "\nRotator command: ");
/* cmd, internal, rotctld */
if (!(interactive && prompt && have_rl)) {
if (interactive) {
if (prompt)
fprintf_flush(fout, "\nRotator command: ");
do {
if (scanfc(fin, "%c", &cmd) < 1)
return -1;
/* Extended response protocol requested with leading '+' on command
* string--rotctld only!
*/
if (cmd == '+' && !prompt) {
ext_resp = 1;
do {
if (scanfc(fin, "%c", &cmd) < 1)
return -1;
} else if (cmd == '+' && prompt) {
return 0;
}
if (cmd != '\\' && cmd != '_' && cmd != '#' && ispunct(cmd) && !prompt) {
ext_resp = 1;
resp_sep = cmd;
if (scanfc(fin, "%c", &cmd) < 1)
return -1;
} else if (cmd != '\\' && cmd != '?' && cmd != '_' && cmd != '#' && ispunct(cmd) && prompt) {
return 0;
}
/* command by name */
if (cmd == '\\') {
unsigned char cmd_name[MAXNAMSIZ], *pcmd = cmd_name;
int c_len = MAXNAMSIZ;
if (scanfc(fin, "%c", pcmd) < 1)
return -1;
while(c_len-- && (isalnum(*pcmd) || *pcmd == '_' ))
if (scanfc(fin, "%c", ++pcmd) < 1)
/* Extended response protocol requested with leading '+' on command
* string--rotctld only!
*/
if (cmd == '+' && !prompt) {
ext_resp = 1;
if (scanfc(fin, "%c", &cmd) < 1)
return -1;
*pcmd = '\0';
cmd = parse_arg((char *) cmd_name);
break;
}
if (cmd == 0x0a || cmd == 0x0d) {
if (last_was_ret) {
if (prompt) {
fprintf_flush(fout, "? for help, q to quit.\n");
}
} else if (cmd == '+' && prompt) {
return 0;
}
last_was_ret = 1;
if (cmd != '\\' && cmd != '_' && cmd != '#' && ispunct(cmd) && !prompt) {
ext_resp = 1;
resp_sep = cmd;
if (scanfc(fin, "%c", &cmd) < 1)
return -1;
} else if (cmd != '\\' && cmd != '?' && cmd != '_' && cmd != '#' && ispunct(cmd) && prompt) {
return 0;
}
/* command by name */
if (cmd == '\\') {
unsigned char cmd_name[MAXNAMSIZ], *pcmd = cmd_name;
int c_len = MAXNAMSIZ;
if (scanfc(fin, "%c", pcmd) < 1)
return -1;
while(c_len-- && (isalnum(*pcmd) || *pcmd == '_' ))
if (scanfc(fin, "%c", ++pcmd) < 1)
return -1;
*pcmd = '\0';
cmd = parse_arg((char *) cmd_name);
break;
}
if (cmd == 0x0a || cmd == 0x0d) {
if (last_was_ret) {
if (prompt) {
fprintf_flush(fout, "? for help, q to quit.\n");
}
return 0;
}
last_was_ret = 1;
}
} while (cmd == 0x0a || cmd == 0x0d);
last_was_ret = 0;
/* comment line */
if (cmd == '#') {
while( cmd != '\n' && cmd != '\r')
if (scanfc(fin, "%c", &cmd) < 1)
return -1;
return 0;
}
} while (cmd == 0x0a || cmd == 0x0d);
if (cmd == 'Q' || cmd == 'q')
return 1;
if (cmd == '?') {
usage_rot(fout);
fflush(fout);
return 0;
}
} else {
/* parse rest of command line */
if (optind >= argc)
return 1;
if (argv[optind][1] == '\0')
cmd = argv[optind][0];
else
cmd = parse_arg(argv[optind]);
optind++;
}
last_was_ret = 0;
/* comment line */
if (cmd == '#') {
while( cmd != '\n' && cmd != '\r')
if (scanfc(fin, "%c", &cmd) < 1)
return -1;
cmd_entry = find_cmd_entry(cmd);
if (!cmd_entry) {
fprintf_flush(stderr, "Command '%c' not found!\n", cmd);
return 0;
}
if (cmd == 'Q' || cmd == 'q')
if ((cmd_entry->flags & ARG_IN_LINE) &&
(cmd_entry->flags & ARG_IN1) && cmd_entry->arg1) {
if (interactive) {
char *nl;
if (prompt)
fprintf_flush(fout, "%s: ", cmd_entry->arg1);
if (fgets(arg1, MAXARGSZ, fin) == NULL)
return -1;
if (arg1[0] == 0xa)
if (fgets(arg1, MAXARGSZ, fin) == NULL)
return -1;
nl = strchr(arg1, 0xa);
if (nl) *nl = '\0'; /* chomp */
p1 = arg1[0]==' '?arg1+1:arg1;
} else {
if (!argv[optind]) {
fprintf(stderr, "Invalid arg for command '%s'\n",
cmd_entry->name);
exit(1);
}
p1 = argv[optind++];
}
} else if ((cmd_entry->flags & ARG_IN1) && cmd_entry->arg1) {
if (interactive) {
if (prompt)
fprintf_flush(fout, "%s: ", cmd_entry->arg1);
if (scanfc(fin, "%s", arg1) < 1)
return -1;
p1 = arg1;
} else {
if (!argv[optind]) {
fprintf(stderr, "Invalid arg for command '%s'\n",
cmd_entry->name);
exit(1);
}
p1 = argv[optind++];
}
}
if (p1 && p1[0]!='?' && (cmd_entry->flags & ARG_IN2) && cmd_entry->arg2) {
if (interactive) {
if (prompt)
fprintf_flush(fout, "%s: ", cmd_entry->arg2);
if (scanfc(fin, "%s", arg2) < 1)
return -1;
p2 = arg2;
} else {
if (!argv[optind]) {
fprintf(stderr, "Invalid arg for command '%s'\n",
cmd_entry->name);
exit(1);
}
p2 = argv[optind++];
}
}
if (p1 && p1[0]!='?' && (cmd_entry->flags & ARG_IN3) && cmd_entry->arg3) {
if (interactive) {
if (prompt)
fprintf_flush(fout, "%s: ", cmd_entry->arg3);
if (scanfc(fin, "%s", arg3) < 1)
return -1;
p3 = arg3;
} else {
if (!argv[optind]) {
fprintf(stderr, "Invalid arg for command '%s'\n",
cmd_entry->name);
exit(1);
}
p3 = argv[optind++];
}
}
if (p1 && p1[0]!='?' && (cmd_entry->flags & ARG_IN4) && cmd_entry->arg4) {
if (interactive) {
if (prompt)
fprintf_flush(fout, "%s: ", cmd_entry->arg4);
if (scanfc(fin, "%s", arg4) < 1)
return -1;
p4 = arg4;
} else {
if (!argv[optind]) {
fprintf(stderr, "Invalid arg for command '%s'\n",
cmd_entry->name);
exit(1);
}
p4 = argv[optind++];
}
}
}
#ifdef HAVE_LIBREADLINE
if (interactive && prompt && have_rl) {
int j, x;
#ifdef HAVE_READLINE_HISTORY
/* Minimum space for 32+1+128+1+128+1+128+1+128+1+128+1+128+1 = 807
* chars, so allocate 896 chars cleared to zero for safety.
*/
rp_hist_buf = (char *)calloc(896, sizeof(char));
#endif
rl_instream = fin;
rl_outstream = fout;
rp_getline("\nRotator command: ");
/* EOF (Ctl-D) received on empty input line, bail out gracefully. */
if (!input_line) {
fprintf_flush(fout, "\n");
return 1;
if (cmd == '?') {
}
/* Q or q to quit */
if (!(strncasecmp(input_line, "q", 1)))
return 1;
/* '?' for help */
if (!(strncmp(input_line, "?", 1))) {
usage_rot(fout);
fflush(fout);
return 0;
}
} else {
/* parse rest of command line */
if (optind >= argc)
/* '#' for comment */
if (!(strncmp(input_line, "#", 1)))
return 0;
/* Blank line entered */
if (!(strcmp(input_line, ""))) {
fprintf(fout, "? for help, q to quit.\n");
fflush(fout);
return 0;
}
rig_debug(RIG_DEBUG_BUG, "%s: input_line: %s\n", __func__, input_line);
/* Split input_line on any number of spaces to get the command token
* Tabs are intercepted by readline for completion and a newline
* causes readline to return the typed text. If more than one
* argument is given, it will be parsed out later.
*/
result = strtok(input_line, " ");
/* parsed_input stores pointers into input_line where the token strings
* start.
*/
if (result) {
parsed_input[0] = result;
} else {
/* Oops! Invoke GDB!! */
fprintf_flush(fout, "\n");
return 1;
if (argv[optind][1] == '\0')
cmd = argv[optind][0];
else
cmd = parse_arg(argv[optind]);
optind++;
}
cmd_entry = find_cmd_entry(cmd);
if (!cmd_entry) {
fprintf_flush(stderr, "Command '%c' not found!\n", cmd);
return 0;
}
p1 = p2 = p3 = p4 = p5 = p6 = NULL;
if ((cmd_entry->flags & ARG_IN_LINE) &&
(cmd_entry->flags & ARG_IN1) && cmd_entry->arg1) {
if (interactive) {
char *nl;
if (prompt)
fprintf_flush(fout, "%s: ", cmd_entry->arg1);
if (fgets(arg1, MAXARGSZ, fin) == NULL)
return -1;
if (arg1[0] == 0xa)
if (fgets(arg1, MAXARGSZ, fin) == NULL)
return -1;
nl = strchr(arg1, 0xa);
if (nl) *nl = '\0'; /* chomp */
p1 = arg1[0]==' '?arg1+1:arg1;
} else {
if (!argv[optind]) {
fprintf(stderr, "Invalid arg for command '%s'\n",
cmd_entry->name);
exit(1);
}
p1 = argv[optind++];
}
} else if ((cmd_entry->flags & ARG_IN1) && cmd_entry->arg1) {
if (interactive) {
if (prompt)
fprintf_flush(fout, "%s: ", cmd_entry->arg1);
if (scanfc(fin, "%s", arg1) < 1)
return -1;
/* At this point parsed_input contains the typed text of the command
* with surrounding space characters removed. If Readline History is
* available, copy the command string into a history buffer.
*/
/* Single character command */
if ((strlen(parsed_input[0]) == 1) && (*parsed_input[0] != '\\')) {
cmd = *parsed_input[0];
#ifdef HAVE_READLINE_HISTORY
/* Store what is typed, not validated, for history. */
if (rp_hist_buf)
strncpy(rp_hist_buf, parsed_input[0], 1);
#endif
}
/* Test the command token, parsed_input[0] */
else if ((*parsed_input[0] == '\\') && (strlen(parsed_input[0]) > 1)) {
char cmd_name[MAXNAMSIZ];
/* if there is no terminating '\0' character in the source string,
* srncpy() doesn't add one even if the supplied length is less
* than the destination array. Truncate the source string here.
*/
if (strlen(parsed_input[0] + 1) >= MAXNAMSIZ)
*(parsed_input[0] + MAXNAMSIZ) = '\0';
#ifdef HAVE_READLINE_HISTORY
if (rp_hist_buf)
strncpy(rp_hist_buf, parsed_input[0], MAXNAMSIZ);
#endif
/* The starting position of the source string is the first
* character past the initial '\'. Using MAXNAMSIZ for the
* length leaves enough space for the '\0' string terminator in the
* cmd_name array.
*/
strncpy(cmd_name, parsed_input[0] + 1, MAXNAMSIZ);
/* Sanity check as valid multiple character commands consist of
* alpha-numeric characters and the underscore ('_') character.
*/
for (j = 0; cmd_name[j] != '\0'; j++) {
if (!(isalnum(cmd_name[j]) || cmd_name[j] == '_')) {
fprintf(stderr, "Valid multiple character command names contain alpha-numeric characters plus '_'\n");
return 0;
}
}
cmd = parse_arg(cmd_name);
}
/* Single '\' entered, prompt again */
else if ((*parsed_input[0] == '\\') && (strlen(parsed_input[0]) == 1)) {
return 0;
}
/* Multiple characters but no leading '\' */
else {
fprintf(stderr, "Precede multiple character command names with '\\'\n");
return 0;
}
cmd_entry = find_cmd_entry(cmd);
if (!cmd_entry) {
if (cmd == '\0')
fprintf(stderr, "Command '%s' not found!\n", parsed_input[0]);
else
fprintf(stderr, "Command '%c' not found!\n", cmd);
return 0;
}
/* \send_cmd */
if ((cmd_entry->flags & ARG_IN_LINE) &&
(cmd_entry->flags & ARG_IN1) && cmd_entry->arg1) {
/* Check for a non-existent delimiter so as to not break up
* remaining line into separate tokens (spaces OK).
*/
result = strtok(NULL, "\0");
if (result) {
x = 1;
parsed_input[x] = result;
} else {
x = 0;
char pmptstr[(strlen(cmd_entry->arg1) + 3)];
strcpy(pmptstr, cmd_entry->arg1);
strcat(pmptstr, ": ");
rp_getline(pmptstr);
/* Blank line entered */
if (!(strcmp(input_line, ""))) {
fprintf(fout, "? for help, q to quit.\n");
fflush(fout);
return 0;
}
if (input_line)
parsed_input[x] = input_line;
else {
fprintf_flush(fout, "\n");
return 1;
}
}
/* The arg1 array size is MAXARGSZ + 1 so truncate it to fit if larger. */
if (strlen(parsed_input[x]) > MAXARGSZ)
parsed_input[x][MAXARGSZ] = '\0';
#ifdef HAVE_READLINE_HISTORY
if (rp_hist_buf) {
strncat(rp_hist_buf, " ", 1);
strncat(rp_hist_buf, parsed_input[x], MAXARGSZ);
}
#endif
strcpy(arg1, parsed_input[x]);
p1 = arg1;
} else {
if (!argv[optind]) {
fprintf(stderr, "Invalid arg for command '%s'\n",
cmd_entry->name);
exit(1);
}
p1 = argv[optind++];
}
}
if (p1 && p1[0]!='?' && (cmd_entry->flags & ARG_IN2) && cmd_entry->arg2) {
if (interactive) {
if (prompt)
fprintf_flush(fout, "%s: ", cmd_entry->arg2);
if (scanfc(fin, "%s", arg2) < 1)
return -1;
/* Normal argument parsing. */
else if ((cmd_entry->flags & ARG_IN1) && cmd_entry->arg1) {
result = strtok(NULL, " ");
if (result) {
x = 1;
parsed_input[x] = result;
} else {
x = 0;
char pmptstr[(strlen(cmd_entry->arg1) + 3)];
strcpy(pmptstr, cmd_entry->arg1);
strcat(pmptstr, ": ");
rp_getline(pmptstr);
if (!(strcmp(input_line, ""))) {
fprintf(fout, "? for help, q to quit.\n");
fflush(fout);
return 0;
}
result = strtok(input_line, " ");
if (result) {
parsed_input[x] = result;
} else {
fprintf_flush(fout, "\n");
return 1;
}
}
if (strlen(parsed_input[x]) > MAXARGSZ)
parsed_input[x][MAXARGSZ] = '\0';
#ifdef HAVE_READLINE_HISTORY
if (rp_hist_buf) {
strncat(rp_hist_buf, " ", 1);
strncat(rp_hist_buf, parsed_input[x], MAXARGSZ);
}
#endif
strcpy(arg1, parsed_input[x]);
p1 = arg1;
}
if (p1 && p1[0] != '?' && (cmd_entry->flags & ARG_IN2) && cmd_entry->arg2) {
result = strtok(NULL, " ");
if (result) {
x = 2;
parsed_input[x] = result;
} else {
x = 0;
char pmptstr[(strlen(cmd_entry->arg2) + 3)];
strcpy(pmptstr, cmd_entry->arg2);
strcat(pmptstr, ": ");
rp_getline(pmptstr);
if (!(strcmp(input_line, ""))) {
fprintf(fout, "? for help, q to quit.\n");
fflush(fout);
return 0;
}
result = strtok(input_line, " ");
if (result) {
parsed_input[x] = result;
} else {
fprintf_flush(fout, "\n");
return 1;
}
}
if (strlen(parsed_input[x]) > MAXARGSZ)
parsed_input[x][MAXARGSZ] = '\0';
#ifdef HAVE_READLINE_HISTORY
if (rp_hist_buf) {
strncat(rp_hist_buf, " ", 1);
strncat(rp_hist_buf, parsed_input[x], MAXARGSZ);
}
#endif
strcpy(arg2, parsed_input[x]);
p2 = arg2;
} else {
if (!argv[optind]) {
fprintf(stderr, "Invalid arg for command '%s'\n",
cmd_entry->name);
exit(1);
}
p2 = argv[optind++];
}
}
if (p1 && p1[0]!='?' && (cmd_entry->flags & ARG_IN3) && cmd_entry->arg3) {
if (interactive) {
if (prompt)
fprintf_flush(fout, "%s: ", cmd_entry->arg3);
if (scanfc(fin, "%s", arg3) < 1)
return -1;
if (p1 && p1[0] != '?' && (cmd_entry->flags & ARG_IN3) && cmd_entry->arg3) {
result = strtok(NULL, " ");
if (result) {
x = 3;
parsed_input[x] = result;
} else {
x = 0;
char pmptstr[(strlen(cmd_entry->arg3) + 3)];
strcpy(pmptstr, cmd_entry->arg3);
strcat(pmptstr, ": ");
rp_getline(pmptstr);
if (!(strcmp(input_line, ""))) {
fprintf(fout, "? for help, q to quit.\n");
fflush(fout);
return 0;
}
result = strtok(input_line, " ");
if (result) {
parsed_input[x] = result;
} else {
fprintf_flush(fout, "\n");
return 1;
}
}
if (strlen(parsed_input[x]) > MAXARGSZ)
parsed_input[x][MAXARGSZ] = '\0';
#ifdef HAVE_READLINE_HISTORY
if (rp_hist_buf) {
strncat(rp_hist_buf, " ", 1);
strncat(rp_hist_buf, parsed_input[x], MAXARGSZ);
}
#endif
strcpy(arg3, parsed_input[x]);
p3 = arg3;
} else {
if (!argv[optind]) {
fprintf(stderr, "Invalid arg for command '%s'\n",
cmd_entry->name);
exit(1);
}
p3 = argv[optind++];
}
if (p1 && p1[0] != '?' && (cmd_entry->flags & ARG_IN4) && cmd_entry->arg4) {
result = strtok(NULL, " ");
if (result) {
x = 4;
parsed_input[x] = result;
} else {
x = 0;
char pmptstr[(strlen(cmd_entry->arg4) + 3)];
strcpy(pmptstr, cmd_entry->arg4);
strcat(pmptstr, ": ");
rp_getline(pmptstr);
if (!(strcmp(input_line, ""))) {
fprintf(fout, "? for help, q to quit.\n");
fflush(fout);
return 0;
}
result = strtok(input_line, " ");
if (result) {
parsed_input[x] = result;
} else {
fprintf_flush(fout, "\n");
return 1;
}
}
if (strlen(parsed_input[x]) > MAXARGSZ)
parsed_input[x][MAXARGSZ] = '\0';
#ifdef HAVE_READLINE_HISTORY
if (rp_hist_buf) {
strncat(rp_hist_buf, " ", 1);
strncat(rp_hist_buf, parsed_input[x], MAXARGSZ);
}
#endif
strcpy(arg4, parsed_input[x]);
p4 = arg4;
}
#ifdef HAVE_READLINE_HISTORY
if (rp_hist_buf) {
add_history(rp_hist_buf);
free(rp_hist_buf);
rp_hist_buf = (char *)NULL;
}
#endif
}
if (p1 && p1[0]!='?' && (cmd_entry->flags & ARG_IN4) && cmd_entry->arg4) {
if (interactive) {
if (prompt)
fprintf_flush(fout, "%s: ", cmd_entry->arg4);
if (scanfc(fin, "%s", arg4) < 1)
return -1;
p4 = arg4;
} else {
if (!argv[optind]) {
fprintf(stderr, "Invalid arg for command '%s'\n",
cmd_entry->name);
exit(1);
}
p4 = argv[optind++];
}
}
#endif /* HAVE_LIBREADLINE */
/*
* mutex locking needed because rigctld is multithreaded
* mutex locking needed because rotctld is multithreaded
* and hamlib is not MT-safe
*/
#ifdef HAVE_PTHREAD
@ -531,7 +973,7 @@ void usage_rot(FILE *fout)
{
int i, nbspaces;
fprintf(fout, "Commands (some may not be available for this rig):\n");
fprintf(fout, "Commands (some may not be available for this rotator):\n");
for (i = 0; test_list[i].cmd != 0; i++) {
fprintf(fout, "%c: %-12s(", isprint(test_list[i].cmd) ?
test_list[i].cmd : '?', test_list[i].name);
@ -548,6 +990,8 @@ void usage_rot(FILE *fout)
fprintf(fout, ")\n");
}
fprintf(fout, "\n\nPrepend long command names with '\\', e.g. '\\dump_state'\n");
}