#include "config.h"
#include "rtapi.h"
#include "hal.h"
#include "../hal_priv.h"
#include "halcmd.h"
#include "halcmd_commands.h"
#include "halcmd_completion.h"
#include <rtapi_mutex.h>
#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
#include <string.h>
#include <sys/stat.h>
#include <sys/wait.h>
#include <unistd.h>
#include <fcntl.h>
#include <errno.h>
#include <time.h>
#include <fnmatch.h>
#include <search.h>
static int get_input(FILE *srcfile, char *buf, size_t bufsize);
static void print_help_general(int showR);
static int release_HAL_mutex(void);
static int propose_completion(char *all, char *fragment, int start);
static char *prompt = "";
static char *prompt_script = "%%\n";
static char *prompt_interactive = "halcmd: ";
static char *prompt_continue = "halcmd+: ";
#define MAX_EXTEND_LINES 20
int main(int argc, char **argv)
{
int c, fd;
int keep_going, retval, errorcount;
int filemode = 0;
char *filename = NULL;
FILE *srcfile = NULL;
char raw_buf[MAX_CMD_LEN+1];
int linenumber = 1;
char *cf=NULL, *cw=NULL, *cl=NULL;
if (argc < 2) {
print_help_general(0);
exit(0);
}
rtapi_set_msg_level(RTAPI_MSG_ERR);
keep_going = 0;
while(1) {
c = getopt(argc, argv, "+RCfi:kqQsvVhe");
if(c == -1) break;
switch(c) {
case 'R':
if (release_HAL_mutex() < 0) {
printf("HALCMD: Release Mutex failed!\n");
return 1;
}
return 0;
break;
case 'h':
if (argc > optind) {
do_help_cmd(argv[optind]);
} else {
print_help_general(1);
}
return 0;
break;
case 'k':
keep_going = 1;
break;
case 'q':
rtapi_set_msg_level(RTAPI_MSG_ERR);
break;
case 'Q':
rtapi_set_msg_level(RTAPI_MSG_NONE);
break;
case 's':
scriptmode = 1;
break;
case 'v':
rtapi_set_msg_level(RTAPI_MSG_INFO);
break;
case 'V':
rtapi_set_msg_level(RTAPI_MSG_ALL);
break;
case 'e':
echo_mode = 1;
break;
case 'f':
filemode = 1;
break;
case 'C':
cl = getenv("COMP_LINE");
cw = getenv("COMP_POINT");
if (!cl || !cw) exit(0);
c = atoi(cw)-strlen(argv[0])-1;
if (c<0) c=0;
cl += strlen(argv[0])+1;
if (c>0 && cl[c]=='\0') c--; cf=&cl[c];
{
int n;
for (n=c; n>0; n--, cf--) {
if (isspace(*cf) || *cf == '=' || *cf == '<' || *cf == '>') {
cf++;
break;
}
}
halcmd_startup(1);
propose_completion(cl, cf, n);
}
if (comp_id >= 0) halcmd_shutdown();
exit(0);
break;
#ifndef NO_INI
case 'i':
if (halcmd_inifile == NULL) {
filename = optarg;
halcmd_inifile = fopen(filename, "r");
if (halcmd_inifile == NULL) {
fprintf(stderr,
"Could not open ini file '%s'\n",
filename);
exit(-1);
}
fd = fileno(halcmd_inifile);
fcntl(fd, F_SETFD, FD_CLOEXEC);
}
break;
#endif
case '?':
exit(-1);
break;
default:
printf("Unimplemented option '-%c'\n", c);
exit(-1);
break;
}
}
if(filemode) {
if (argc > optind) {
filename = argv[optind++];
srcfile = fopen(filename, "r");
halcmd_set_filename(filename);
if (srcfile == NULL) {
fprintf(stderr,
"Could not open command file '%s'\n",
filename);
exit(-1);
}
fd = fileno(srcfile);
fcntl(fd, F_SETFD, FD_CLOEXEC);
} else {
halcmd_set_filename("<stdin>");
srcfile = stdin;
}
}
if (srcfile && isatty(fileno(srcfile))) {
if (scriptmode) {
prompt = prompt_script;
} else {
prompt = prompt_interactive;
}
}
if ( halcmd_startup(0) != 0 ) return 1;
errorcount = 0;
if (srcfile == NULL) {
#ifndef NO_INI
if(halcmd_inifile) {
fprintf(stderr, "-i may only be used together with -f\n");
errorcount++;
}
#endif
if(errorcount == 0 && argc > optind) {
halcmd_set_filename("<commandline>");
halcmd_set_linenumber(0);
retval = halcmd_parse_cmd(&argv[optind]);
if (retval != 0) {
errorcount++;
}
}
} else {
int extend_ct = 0;
while (get_input(srcfile, raw_buf, MAX_CMD_LEN)) {
char *tokens[MAX_TOK+1];
char eline [(LINELEN + 2) * (MAX_EXTEND_LINES + 1)];
char *elineptr;
char *elinenext;
int newLinePos;
halcmd_set_linenumber(linenumber++);
newLinePos = (int)strlen(raw_buf) - 1; if (raw_buf[newLinePos] == '\n') { newLinePos--; }
if (newLinePos > 0 && raw_buf[newLinePos] == '\\') { raw_buf[newLinePos] = 0;
newLinePos++;
if (!extend_ct) { if (prompt == prompt_interactive) prompt = prompt_continue;
elineptr = eline;
strncpy(elineptr,raw_buf,strlen(raw_buf));
elinenext = elineptr + strlen(raw_buf);
} else { strncpy(elinenext,raw_buf,newLinePos);
elinenext = elinenext + strlen(raw_buf);
}
*elinenext = 0;
extend_ct++;
continue; } else { if (extend_ct) { strncpy(elinenext,raw_buf,strlen(raw_buf));
*(eline+strlen(eline)+0)='\n';
elinenext = elinenext + strlen(raw_buf);
*elinenext = 0;
elineptr = eline;
}
}
if (!extend_ct) { elineptr = (char*)raw_buf; }
extend_ct = 0;
if (prompt == prompt_continue) { prompt = prompt_interactive; }
retval = halcmd_preprocess_line(elineptr, tokens);
if(echo_mode) {
halcmd_echo("%s\n", eline);
}
if (retval == 0) {
if ( ( strcasecmp(tokens[0],"quit") == 0 ) ||
( strcasecmp(tokens[0],"exit") == 0 ) ) {
break;
}
retval = halcmd_parse_cmd(tokens);
}
if ( halcmd_done ) {
errorcount++;
break;
}
if ( retval != 0 ) {
errorcount++;
}
if (( errorcount > 0 ) && ( keep_going == 0 )) {
break;
}
} extend_ct=0;
}
halcmd_shutdown();
if ( errorcount > 0 ) {
return 1;
} else {
return 0;
}
}
static int release_HAL_mutex(void)
{
int comp_id, mem_id, retval;
void *mem;
hal_data_t *hal_data;
comp_id = rtapi_init("hal_unlocker");
if (comp_id < 0) {
rtapi_print_msg(RTAPI_MSG_ERR, "ERROR: rtapi init failed\n");
return -EINVAL;
}
mem_id = rtapi_shmem_new(HAL_KEY, comp_id, HAL_SIZE);
if (mem_id < 0) {
rtapi_print_msg(RTAPI_MSG_ERR,
"ERROR: could not open shared memory\n");
rtapi_exit(comp_id);
return -EINVAL;
}
retval = rtapi_shmem_getptr(mem_id, &mem);
if (retval < 0) {
rtapi_print_msg(RTAPI_MSG_ERR,
"ERROR: could not access shared memory\n");
rtapi_exit(comp_id);
return -EINVAL;
}
hal_data = (hal_data_t *) mem;
rtapi_mutex_give(&(hal_data->mutex));
rtapi_shmem_delete(mem_id, comp_id);
rtapi_exit(comp_id);
return 0;
}
static char **completion_callback(const char *text, hal_generator_func cb) {
int state = 0;
char *s = 0;
do {
s = cb(text, state);
if(s) printf("%s\n", s);
state = 1;
} while(s);
return NULL;
}
static int propose_completion(char *all, char *fragment, int start) {
int sp=0, len=strlen(all), i=0;
for(; i<len; i++) if(all[i] == ' ') sp = i;
if(sp) sp++;
len = strlen(all);
halcmd_completer(fragment, sp, len, completion_callback, all);
return 0;
}
static void print_help_general(int showR)
{
printf("\nUsage: halcmd [options] [cmd [args]]\n\n");
printf("\n halcmd [options] -f [filename]\n\n");
printf("options:\n\n");
printf(" -e echo the commands from stdin to stderr\n");
printf(" -f [filename] Read commands from 'filename', not command\n");
printf(" line. If no filename, read from stdin.\n");
#ifndef NO_INI
printf(" -i filename Open .ini file 'filename', allow commands\n");
printf(" to get their values from ini file.\n");
#endif
printf(" -k Keep going after failed command. Default\n");
printf(" is to exit if any command fails. (Useful with -f)\n");
printf(" -q Quiet - print errors only (default).\n");
printf(" -Q Very quiet - print nothing.\n");
if (showR != 0) {
printf(" -R Release mutex (for crash recovery only).\n");
}
printf(" -s Script friendly - don't print headers on output.\n");
printf(" -v Verbose - print result of every command.\n");
printf(" -V Very verbose - print lots of junk.\n");
printf(" -h Help - print this help screen and exit.\n\n");
printf("commands:\n\n");
printf(" loadrt, loadusr, waitusr, unload, lock, unlock, net, linkps, linksp,\n");
printf(" unlinkp, newsig, delsig, setp, getp, ptype, sets, gets, stype,\n");
printf(" addf, delf, show, list, save, status, start, stop, source, echo, unecho, quit, exit\n");
printf(" help Lists all commands with short descriptions\n");
printf(" help command Prints detailed help for 'command'\n\n");
}
#ifdef HAVE_READLINE
#include "halcmd_completion.h"
static int get_input(FILE *srcfile, char *buf, size_t bufsize) {
static int first_time = 1;
char *rlbuf;
if(!scriptmode && srcfile == stdin && isatty(0)) {
if(first_time) {
halcmd_init_readline();
first_time = 0;
}
rlbuf = readline(prompt);
if(!rlbuf) return 0;
strncpy(buf, rlbuf, bufsize);
buf[bufsize-1] = 0;
free(rlbuf);
if(*buf) add_history(buf);
return 1;
}
fprintf(stdout, "%s", prompt); fflush(stdout);
return fgets(buf, bufsize, srcfile) != NULL;
}
#else
static int get_input(FILE *srcfile, char *buf, size_t bufsize) {
fprintf(stdout, "%s", prompt); fflush(stdout);
return fgets(buf, bufsize, srcfile) != NULL;
}
#endif
void halcmd_output(const char *format, ...) {
va_list ap;
va_start(ap, format);
vfprintf(stdout, format, ap);
va_end(ap);
}
void halcmd_warning(const char *format, ...) {
va_list ap;
fprintf(stderr, "%s:%d: Warning: ", halcmd_get_filename(), halcmd_get_linenumber());
va_start(ap, format);
vfprintf(stderr, format, ap);
va_end(ap);
}
void halcmd_error(const char *format, ...) {
va_list ap;
fprintf(stderr, "%s:%d: ", halcmd_get_filename(), halcmd_get_linenumber());
va_start(ap, format);
vfprintf(stderr, format, ap);
va_end(ap);
}
void halcmd_info(const char *format, ...) {
va_list ap;
if(rtapi_get_msg_level() < RTAPI_MSG_INFO) return;
fprintf(stdout, "%s:%d: ", halcmd_get_filename(), halcmd_get_linenumber());
va_start(ap, format);
vfprintf(stdout, format, ap);
va_end(ap);
}
void halcmd_echo(const char *format, ...) {
va_list ap;
fprintf(stderr, "(%d)<echo>: ", halcmd_get_linenumber());
va_start(ap, format);
vfprintf(stderr, format, ap);
va_end(ap);
}