/* * rotctl_parse.c - (C) Stephane Fillod 2000-2010 * (C) Nate Bargmann 2003,2007,2010,2011,2012,2013 * (C) The Hamlib Group 2002,2006,2011 * * This program test/control a rotator using Hamlib. * It takes commands in interactive mode as well as * from command line options. * * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. * */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include #include #include #include #include #include #ifdef HAVE_LIBREADLINE # if defined(HAVE_READLINE_READLINE_H) # include # elif defined(HAVE_READLINE_H) /* !defined(HAVE_READLINE_READLINE_H) */ # include # 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 # elif defined(HAVE_HISTORY_H) # include # else /* !defined(HAVE_HISTORY_H) */ extern void add_history (); extern int write_history (); extern int read_history (); # endif /* defined(HAVE_READLINE_HISTORY_H) */ /* no history */ #endif /* HAVE_READLINE_HISTORY */ #include #include "serial.h" #include "misc.h" #include "rotctl_parse.h" /* Hash table implementation See: http://uthash.sourceforge.net/ */ #include "uthash.h" #ifdef HAVE_PTHREAD #include static pthread_mutex_t rot_mutex = PTHREAD_MUTEX_INITIALIZER; #endif #define MAXNAMSIZ 32 #define MAXNBOPT 100 /* max number of different options */ #define ARG_IN1 0x01 #define ARG_OUT1 0x02 #define ARG_IN2 0x04 #define ARG_OUT2 0x08 #define ARG_IN3 0x10 #define ARG_OUT3 0x20 #define ARG_IN4 0x40 #define ARG_OUT4 0x80 #define ARG_IN_LINE 0x4000 #define ARG_NONE 0 #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; int (*rot_routine)(ROT*, FILE*, int, const struct test_table*, const char*, const char*, const char*, const char*, const char*, const char*); int flags; const char *arg1; const char *arg2; const char *arg3; const char *arg4; const char *arg5; const char *arg6; }; #define CHKSCN1ARG(a) if ((a) != 1) return -RIG_EINVAL; else do {} while(0) #define declare_proto_rot(f) static int (f)(ROT *rot, FILE *fout, int interactive, \ const struct test_table *cmd, const char *arg1, const char *arg2, \ const char *arg3, const char *arg4, const char *arg5, const char *arg6) declare_proto_rot(set_position); declare_proto_rot(get_position); declare_proto_rot(stop); declare_proto_rot(park); declare_proto_rot(reset); declare_proto_rot(move); declare_proto_rot(get_info); declare_proto_rot(inter_set_conf); /* interactive mode set_conf */ declare_proto_rot(send_cmd); declare_proto_rot(dump_state); declare_proto_rot(dump_caps); /* Follows are functions from locator.c */ declare_proto_rot(loc2lonlat); declare_proto_rot(lonlat2loc); declare_proto_rot(d_m_s2dec); declare_proto_rot(dec2d_m_s); declare_proto_rot(d_mm2dec); declare_proto_rot(dec2d_mm); declare_proto_rot(coord2qrb); declare_proto_rot(az_sp2az_lp); declare_proto_rot(dist_sp2dist_lp); /* * convention: upper case cmd is set, lowercase is get * * NB: 'q' 'Q' '?' are reserved by interactive mode interface */ struct test_table test_list[] = { { 'P', "set_pos", set_position, ARG_IN, "Azimuth", "Elevation" }, { 'p', "get_pos", get_position, ARG_OUT, "Azimuth", "Elevation" }, { 'K', "park", park, ARG_NONE, }, { 'S', "stop", stop, ARG_NONE, }, { 'R', "reset", reset, ARG_IN, "Reset" }, { 'M', "move", move, ARG_IN, "Direction", "Speed" }, { 'C', "set_conf", inter_set_conf, ARG_IN, "Token", "Value" }, { '_', "get_info", get_info, ARG_OUT, "Info" }, { 'w', "send_cmd", send_cmd, ARG_IN1|ARG_IN_LINE|ARG_OUT2, "Cmd", "Reply" }, { '1', "dump_caps", dump_caps, }, { 0x8f,"dump_state",dump_state, ARG_OUT }, { 'L', "lonlat2loc",lonlat2loc, ARG_IN1|ARG_IN2|ARG_IN3|ARG_OUT1, "Longitude", "Latitude", "Loc Len [2-12]", "Locator" }, { 'l', "loc2lonlat",loc2lonlat, ARG_IN1|ARG_OUT1|ARG_OUT2, "Locator", "Longitude", "Latitude" }, { 'D', "dms2dec", d_m_s2dec, ARG_IN1|ARG_IN2|ARG_IN3|ARG_IN4|ARG_OUT1, "Degrees", "Minutes", "Seconds", "S/W", "Dec Degrees" }, { 'd', "dec2dms", dec2d_m_s, ARG_IN1|ARG_OUT1|ARG_OUT2|ARG_OUT3|ARG_OUT4, "Dec Degrees", "Degrees", "Minutes", "Seconds", "S/W" }, { 'E', "dmmm2dec", d_mm2dec, ARG_IN1|ARG_IN2|ARG_IN3|ARG_OUT1, "Degrees", "Dec Minutes", "S/W", "Dec Deg" }, { 'e', "dec2dmmm", dec2d_mm, ARG_IN1|ARG_OUT1|ARG_OUT2|ARG_OUT3, "Dec Deg", "Degrees", "Dec Minutes", "S/W" }, { 'B', "qrb", coord2qrb, ARG_IN1|ARG_IN2|ARG_IN3|ARG_IN4|ARG_OUT1|ARG_OUT2, "Lon 1", "Lat 1", "Lon 2", "Lat 2", "QRB Distance", "QRB Azimuth" }, { 'A', "a_sp2a_lp", az_sp2az_lp, ARG_IN1|ARG_OUT1, "Short Path Deg", "Long Path Deg" }, { 'a', "d_sp2d_lp", dist_sp2dist_lp,ARG_IN1|ARG_OUT1, "Short Path km", "Long Path km" }, { 0x00, "", NULL }, }; struct test_table *find_cmd_entry(int cmd) { int i; for (i = 0; i < MAXNBOPT && test_list[i].cmd != 0x00; i++) if (test_list[i].cmd == cmd) break; if (i >= MAXNBOPT || test_list[i].cmd == 0x00) return NULL; return &test_list[i]; } /* Structure for hash table provided by uthash.h * * Structure and hash funtions patterned after/copied from example.c * distributed with the uthash package. See: http://uthash.sourceforge.net/ */ struct mod_lst { int id; /* caps->rot_model This is the hash key */ char mfg_name[32]; /* caps->mfg_name */ char model_name[32]; /* caps->model_name */ char version[32]; /* caps->version */ char status[32]; /* caps->status */ UT_hash_handle hh; /* makes this structure hashable */ }; /* Hash declaration. Must be initialized to NULL */ struct mod_lst *models = NULL; /* Add model information to the hash */ void hash_add_model(int id, const char *mfg_name, const char *model_name, const char *version, const char *status) { struct mod_lst *s; s = (struct mod_lst*)malloc(sizeof(struct mod_lst)); s->id = id; snprintf(s->mfg_name, sizeof(s->mfg_name), "%s", mfg_name); snprintf(s->model_name, sizeof(s->model_name), "%s", model_name); snprintf(s->version, sizeof(s->version), "%s", version); snprintf(s->status, sizeof(s->status), "%s", status); HASH_ADD_INT(models, id, s); /* id: name of key field */ } /* Hash sorting functions */ int hash_model_id_sort(struct mod_lst *a, struct mod_lst *b) { return (a->id - b->id); } void hash_sort_by_model_id() { HASH_SORT(models, hash_model_id_sort); } /* Delete hash */ void hash_delete_all() { struct mod_lst *current_model, *tmp; HASH_ITER(hh, models, current_model, tmp) { HASH_DEL(models, current_model); /* delete it (models advances to next) */ free(current_model); /* free it */ } } #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? */ char parse_arg(const char *arg) { int i; for (i = 0; i < MAXNBOPT && test_list[i].cmd != 0; i++) if (!strncmp(arg, test_list[i].name, MAXNAMSIZ)) return test_list[i].cmd; return 0; } /* * This scanf works even in presence of signals (timer, SIGIO, ..) */ static int scanfc(FILE *fin, const char *format, void *p) { int ret; do { ret = fscanf(fin, format, p); if (ret < 0) { if (errno == EINTR) continue; rig_debug(RIG_DEBUG_ERR, "fscanf: %s\n", strerror(errno)); } return ret; } while(1); } #define fprintf_flush(f, a...) \ ({ int __ret; \ __ret = fprintf((f), a); \ fflush((f)); \ __ret; \ }) #define MAXARGSZ 127 extern int interactive; extern int prompt; extern char send_cmd_term; int ext_resp = 0; unsigned char resp_sep = '\n'; /* Default response separator */ int rotctl_parse(ROT *my_rot, FILE *fin, FILE *fout, char *argv[], int argc) { int retcode; /* generic return code from functions */ unsigned char cmd; struct test_table *cmd_entry; 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; /* 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; 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) 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; } 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++; } cmd_entry = find_cmd_entry(cmd); if (!cmd_entry) { fprintf_flush(stderr, "Command '%c' not found!\n", cmd); return 0; } 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; } /* 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; } /* '#' 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; } /* 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((int)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; } /* 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; } 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; } 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 } #endif /* HAVE_LIBREADLINE */ /* * mutex locking needed because rotctld is multithreaded * and hamlib is not MT-safe */ #ifdef HAVE_PTHREAD pthread_mutex_lock(&rot_mutex); #endif if (!prompt) rig_debug(RIG_DEBUG_TRACE, "rotctl(d): %c '%s' '%s' '%s' '%s'\n", cmd, p1?p1:"", p2?p2:"", p3?p3:"", p4?p4:""); /* * Extended Response protocol: output received command name and arguments * response. */ if (interactive && ext_resp && !prompt) { char a1[MAXARGSZ + 1]; char a2[MAXARGSZ + 1]; char a3[MAXARGSZ + 1]; char a4[MAXARGSZ + 1]; p1 == NULL ? a1[0] = '\0' : snprintf(a1, sizeof(a1), " %s", p1); p2 == NULL ? a2[0] = '\0' : snprintf(a2, sizeof(a2), " %s", p2); p3 == NULL ? a3[0] = '\0' : snprintf(a3, sizeof(a3), " %s", p3); p4 == NULL ? a4[0] = '\0' : snprintf(a4, sizeof(a4), " %s", p4); fprintf(fout, "%s:%s%s%s%s%c", cmd_entry->name, a1, a2, a3, a4, resp_sep); } retcode = (*cmd_entry->rot_routine)(my_rot, fout, interactive, cmd_entry, p1, p2 ? p2 : "", p3 ? p3 : "", p4 ? p4 : "", p5 ? p5 : "", p6 ? p6 : ""); #ifdef HAVE_PTHREAD pthread_mutex_unlock(&rot_mutex); #endif if (retcode != RIG_OK) { /* only for rotctld */ if (interactive && !prompt) { fprintf(fout, NETROTCTL_RET "%d\n", retcode); ext_resp = 0; resp_sep = '\n'; } else fprintf(fout, "%s: error = %s\n", cmd_entry->name, rigerror(retcode)); } else { /* only for rotctld */ if (interactive && !prompt) { /* netrotctl RIG_OK */ if (!(cmd_entry->flags & ARG_OUT) && !ext_resp) fprintf(fout, NETROTCTL_RET "0\n"); /* Extended Response protocol */ else if (ext_resp && cmd != 0xf0) { fprintf(fout, NETROTCTL_RET "0\n"); ext_resp = 0; resp_sep = '\n'; } } } fflush(fout); return retcode != RIG_OK ? 2 : 0; } void version() { printf("rotctl, %s\n\n", hamlib_version); printf("%s\n", hamlib_copyright); } void usage_rot(FILE *fout) { int i, nbspaces; 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); nbspaces = 16; if (test_list[i].arg1 && (test_list[i].flags&ARG_IN1)) nbspaces -= fprintf(fout, "%s", test_list[i].arg1); if (test_list[i].arg2 && (test_list[i].flags&ARG_IN2)) nbspaces -= fprintf(fout, ", %s", test_list[i].arg2); if (test_list[i].arg3 && (test_list[i].flags&ARG_IN3)) nbspaces -= fprintf(fout, ", %s", test_list[i].arg3); if (test_list[i].arg4 && (test_list[i].flags&ARG_IN4)) nbspaces -= fprintf(fout, ", %s", test_list[i].arg4); fprintf(fout, ")\n"); } fprintf(fout, "\n\nPrepend long command names with '\\', e.g. '\\dump_state'\n"); } int print_conf_list(const struct confparams *cfp, rig_ptr_t data) { ROT *rot = (ROT*) data; int i; char buf[128] = ""; rot_get_conf(rot, cfp->token, buf); printf("%s: \"%s\"\n" "\tDefault: %s, Value: %s\n", cfp->name, cfp->tooltip, cfp->dflt, buf ); switch (cfp->type) { case RIG_CONF_NUMERIC: printf("\tRange: %.1f..%.1f, step %.1f\n", cfp->u.n.min, cfp->u.n.max, cfp->u.n.step); break; case RIG_CONF_COMBO: if (!cfp->u.c.combostr) break; printf("\tCombo: %s", cfp->u.c.combostr[0]); for (i = 1 ; i < RIG_COMBO_MAX && cfp->u.c.combostr[i]; i++) printf(", %s", cfp->u.c.combostr[i]); printf("\n"); break; default: break; } return 1; /* != 0, we want them all ! */ } static int hash_model_list(const struct rot_caps *caps, void *data) { hash_add_model(caps->rot_model, caps->mfg_name, caps->model_name, caps->version, rig_strstatus(caps->status)); return 1; /* !=0, we want them all ! */ } void print_model_list() { struct mod_lst *s; for (s = models; s != NULL; s = (struct mod_lst *)(s->hh.next)) { printf("%6d %-23s%-24s%-16s%s\n", s->id, s->mfg_name, s->model_name, s->version, s->status); } } void list_models() { int status; rot_load_all_backends(); printf(" Rig # Mfg Model Version Status\n"); status = rot_list_foreach(hash_model_list, NULL); if (status != RIG_OK ) { printf("rot_list_foreach: error = %s \n", rigerror(status)); exit(2); } hash_sort_by_model_id(); print_model_list(); hash_delete_all(); } int set_conf(ROT *my_rot, char *conf_parms) { char *p, *q, *n; int ret; p = conf_parms; while (p && *p != '\0') { /* FIXME: left hand value of = cannot be null */ q = strchr(p, '='); if ( !q ) return RIG_EINVAL; *q++ = '\0'; n = strchr(q, ','); if (n) *n++ = '\0'; ret = rot_set_conf(my_rot, rot_token_lookup(my_rot, p), q); if (ret != RIG_OK) return ret; p = n; } return RIG_OK; } /* * static int (f)(ROT *rot, int interactive, const void *arg1, const void *arg2, const void *arg3, const void *arg4) */ /* 'P' */ declare_proto_rot(set_position) { azimuth_t az; elevation_t el; CHKSCN1ARG(sscanf(arg1, "%f", &az)); CHKSCN1ARG(sscanf(arg2, "%f", &el)); return rot_set_position(rot, az, el); } /* 'p' */ declare_proto_rot(get_position) { int status; azimuth_t az; elevation_t el; status = rot_get_position(rot, &az, &el); if (status != RIG_OK) return status; if ((interactive && prompt) || (interactive && !prompt && ext_resp)) fprintf(fout, "%s: ", cmd->arg1); fprintf(fout, "%f%c", az, resp_sep); if ((interactive && prompt) || (interactive && !prompt && ext_resp)) fprintf(fout, "%s: ", cmd->arg2); fprintf(fout, "%f%c", el, resp_sep); return status; } /* 'S' */ declare_proto_rot(stop) { return rot_stop(rot); } /* 'K' */ declare_proto_rot(park) { return rot_park(rot); } /* 'R' */ declare_proto_rot(reset) { rot_reset_t reset; CHKSCN1ARG(sscanf(arg1, "%d", &reset)); return rot_reset(rot, reset); } /* '_' */ declare_proto_rot(get_info) { const char *s; s = rot_get_info(rot); if ((interactive && prompt) || (interactive && !prompt && ext_resp)) fprintf(fout, "%s: ", cmd->arg1); fprintf(fout, "%s%c", s ? s : "None", resp_sep); return RIG_OK; } /* 'M' */ declare_proto_rot(move) { int direction; int speed; if (!strcmp(arg1, "LEFT") || !strcmp(arg1, "CCW")) direction = ROT_MOVE_LEFT; else if (!strcmp(arg1, "RIGHT") || !strcmp(arg1, "CW")) direction = ROT_MOVE_RIGHT; else if (!strcmp(arg1, "UP")) direction = ROT_MOVE_UP; else if (!strcmp(arg1, "DOWN")) direction = ROT_MOVE_DOWN; else CHKSCN1ARG(sscanf(arg1, "%d", &direction)); CHKSCN1ARG(sscanf(arg2, "%d", &speed)); return rot_move(rot, direction, speed); } /* 'C' */ declare_proto_rot(inter_set_conf) { token_t token; CHKSCN1ARG(sscanf(arg1, "%ld", &token)); if (!arg2 || arg2[0] == '\0') return -RIG_EINVAL; return rot_set_conf(rot, token, arg2); } /* '1' */ declare_proto_rot(dump_caps) { dumpcaps_rot(rot, fout); return RIG_OK; } /* For rotctld internal use * '0x8f' */ declare_proto_rot(dump_state) { struct rot_state *rs = &rot->state; /* * - Protocol version */ #define ROTCTLD_PROT_VER 0 if ((interactive && prompt) || (interactive && !prompt && ext_resp)) fprintf(fout, "rotctld Protocol Ver: "); fprintf(fout, "%d%c", ROTCTLD_PROT_VER, resp_sep); if ((interactive && prompt) || (interactive && !prompt && ext_resp)) fprintf(fout, "Rotor Model: "); fprintf(fout, "%d%c", rot->caps->rot_model, resp_sep); if ((interactive && prompt) || (interactive && !prompt && ext_resp)) fprintf(fout, "Minimum Azimuth: "); fprintf(fout, "%lf%c", rs->min_az, resp_sep); if ((interactive && prompt) || (interactive && !prompt && ext_resp)) fprintf(fout, "Maximum Azimuth: "); fprintf(fout, "%lf%c", rs->max_az, resp_sep); if ((interactive && prompt) || (interactive && !prompt && ext_resp)) fprintf(fout, "Minimum Elevation: "); fprintf(fout, "%lf%c", rs->min_el, resp_sep); if ((interactive && prompt) || (interactive && !prompt && ext_resp)) fprintf(fout, "Maximum Elevation: "); fprintf(fout, "%lf%c", rs->max_el, resp_sep); return RIG_OK; } /* * special debugging purpose send command * display reply until there's a timeout * * 'w' */ declare_proto_rot(send_cmd) { int retval; struct rot_state *rs; int backend_num, cmd_len; #define BUFSZ 128 char bufcmd[BUFSZ]; char buf[BUFSZ]; char eom_buf[4] = { 0xa, 0xd, 0, 0 }; /* * binary protocols enter values as \0xZZ\0xYY.. * * Rem: no binary protocol for rotator as of now */ backend_num = ROT_BACKEND_NUM(rot->caps->rot_model); if (send_cmd_term == -1 || backend_num == -1) { const char *p = arg1, *pp = NULL; int i; for (i = 0; i < BUFSZ - 1 && p != pp; i++) { pp = p + 1; bufcmd[i] = strtol(p + 1, (char **) &p, 0); } /* must save length to allow 0x00 to be sent as part of a command */ cmd_len = i - 1; /* no End Of Message chars */ eom_buf[0] = '\0'; } else { /* text protocol */ strncpy(bufcmd,arg1, BUFSZ); bufcmd[BUFSZ - 2] = '\0'; cmd_len = strlen(bufcmd); /* Automatic termination char */ if (send_cmd_term != 0) bufcmd[cmd_len++] = send_cmd_term; eom_buf[2] = send_cmd_term; } rs = &rot->state; serial_flush(&rs->rotport); retval = write_block(&rs->rotport, bufcmd, cmd_len); if (retval != RIG_OK) return retval; if (interactive && prompt) fprintf(fout, "%s: ", cmd->arg2); do { /* * assumes CR or LF is end of line char * for all ascii protocols */ retval = read_string(&rs->rotport, buf, BUFSZ, eom_buf, strlen(eom_buf)); if (retval < 0) break; if (retval < BUFSZ) buf[retval] = '\0'; else buf[BUFSZ-1] = '\0'; fprintf(fout, "%s\n", buf); } while (retval > 0); if (retval > 0 || retval == -RIG_ETIMEOUT) retval = RIG_OK; return retval; } /* 'L' */ declare_proto_rot(lonlat2loc) { unsigned char loc[MAXARGSZ + 1]; double lat, lon; int err, pair; CHKSCN1ARG(sscanf(arg1, "%lf", &lon)); CHKSCN1ARG(sscanf(arg2, "%lf", &lat)); CHKSCN1ARG(sscanf(arg3, "%d", &pair)); pair /= 2; err = longlat2locator(lon, lat, (char *)&loc, pair); if (err != RIG_OK) return err; if ((interactive && prompt) || (interactive && !prompt && ext_resp)) fprintf(fout, "%s: ", cmd->arg4); fprintf(fout, "%s%c", loc, resp_sep); return err; } /* 'l' */ declare_proto_rot(loc2lonlat) { unsigned char loc[MAXARGSZ + 1]; double lat, lon; int status; CHKSCN1ARG(sscanf(arg1, "%s", (char *)&loc)); status = locator2longlat(&lon, &lat, (const char *)loc); if (status != RIG_OK) return status; if ((interactive && prompt) || (interactive && !prompt && ext_resp)) fprintf(fout, "%s: ", cmd->arg2); fprintf(fout, "%f%c", lon, resp_sep); if ((interactive && prompt) || (interactive && !prompt && ext_resp)) fprintf(fout, "%s: ", cmd->arg3); fprintf(fout, "%f%c", lat, resp_sep); return status; } /* 'D' */ declare_proto_rot(d_m_s2dec) { int deg, min, sw; double sec, dec_deg; CHKSCN1ARG(sscanf(arg1, "%d", °)); CHKSCN1ARG(sscanf(arg2, "%d", &min)); CHKSCN1ARG(sscanf(arg3, "%lf", &sec)); CHKSCN1ARG(sscanf(arg4, "%d", &sw)); dec_deg = dms2dec(deg, min, sec, sw); if ((interactive && prompt) || (interactive && !prompt && ext_resp)) fprintf(fout, "%s: ", cmd->arg5); fprintf(fout, "%lf%c", dec_deg, resp_sep); return RIG_OK; } /* 'd' */ declare_proto_rot(dec2d_m_s) { int deg, min, sw, err; double sec, dec_deg; CHKSCN1ARG(sscanf(arg1, "%lf", &dec_deg)); err = dec2dms(dec_deg, °, &min, &sec, &sw); if (err != RIG_OK) return err; if ((interactive && prompt) || (interactive && !prompt && ext_resp)) fprintf(fout, "%s: ", cmd->arg2); fprintf(fout, "%d%c", deg, resp_sep); if ((interactive && prompt) || (interactive && !prompt && ext_resp)) fprintf(fout, "%s: ", cmd->arg3); fprintf(fout, "%d%c", min, resp_sep); if ((interactive && prompt) || (interactive && !prompt && ext_resp)) fprintf(fout, "%s: ", cmd->arg4); fprintf(fout, "%lf%c", sec, resp_sep); if ((interactive && prompt) || (interactive && !prompt && ext_resp)) fprintf(fout, "%s: ", cmd->arg5); fprintf(fout, "%d%c", sw, resp_sep); return err; } /* 'E' */ declare_proto_rot(d_mm2dec) { int deg, sw; double dec_deg, min; CHKSCN1ARG(sscanf(arg1, "%d", °)); CHKSCN1ARG(sscanf(arg2, "%lf", &min)); CHKSCN1ARG(sscanf(arg3, "%d", &sw)); dec_deg = dmmm2dec(deg, min, sw); if ((interactive && prompt) || (interactive && !prompt && ext_resp)) fprintf(fout, "%s: ", cmd->arg4); fprintf(fout, "%lf%c", dec_deg, resp_sep); return RIG_OK; } /* 'e' */ declare_proto_rot(dec2d_mm) { int deg, sw, err; double min, dec_deg; CHKSCN1ARG(sscanf(arg1, "%lf", &dec_deg)); err = dec2dmmm(dec_deg, °, &min, &sw); if (err != RIG_OK) return err; if ((interactive && prompt) || (interactive && !prompt && ext_resp)) fprintf(fout, "%s: ", cmd->arg2); fprintf(fout, "%d%c", deg, resp_sep); if ((interactive && prompt) || (interactive && !prompt && ext_resp)) fprintf(fout, "%s: ", cmd->arg3); fprintf(fout, "%lf%c", min, resp_sep); if ((interactive && prompt) || (interactive && !prompt && ext_resp)) fprintf(fout, "%s: ", cmd->arg4); fprintf(fout, "%d%c", sw, resp_sep); return err; } /* 'B' */ declare_proto_rot(coord2qrb) { double lon1, lat1, lon2, lat2, dist, az; int err; CHKSCN1ARG(sscanf(arg1, "%lf", &lon1)); CHKSCN1ARG(sscanf(arg2, "%lf", &lat1)); CHKSCN1ARG(sscanf(arg3, "%lf", &lon2)); CHKSCN1ARG(sscanf(arg4, "%lf", &lat2)); err = qrb(lon1, lat1, lon2, lat2, &dist, &az); if (err != RIG_OK) return err; if ((interactive && prompt) || (interactive && !prompt && ext_resp)) fprintf(fout, "%s: ", cmd->arg5); fprintf(fout, "%lf%c", dist, resp_sep); if ((interactive && prompt) || (interactive && !prompt && ext_resp)) fprintf(fout, "%s: ", cmd->arg6); fprintf(fout, "%lf%c", az, resp_sep); return err; } /* 'A' */ declare_proto_rot(az_sp2az_lp) { double az_sp, az_lp; CHKSCN1ARG(sscanf(arg1, "%lf", &az_sp)); az_lp = azimuth_long_path(az_sp); if (az_lp < 0) return -RIG_EINVAL; if ((interactive && prompt) || (interactive && !prompt && ext_resp)) fprintf(fout, "%s: ", cmd->arg2); fprintf(fout, "%lf%c", az_lp, resp_sep); return RIG_OK; } /* 'a' */ declare_proto_rot(dist_sp2dist_lp) { double dist_sp, dist_lp; CHKSCN1ARG(sscanf(arg1, "%lf", &dist_sp)); dist_lp = distance_long_path(dist_sp); if ((interactive && prompt) || (interactive && !prompt && ext_resp)) fprintf(fout, "%s: ", cmd->arg2); fprintf(fout, "%lf%c", dist_lp, resp_sep); return RIG_OK; }