/* Copyright (C) 2007 Jeff Epler * Copyright (C) 2003 John Kasunich * * * Other contributers: * Martin Kuhnle * * Alex Joni * * Benn Lipkowitz * * Stephen Wille Padnos * * * This program is free software; you can redistribute it and/or * modify it under the terms of version 2 of the GNU General * Public License as published by the Free Software Foundation. * This library 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 library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111 USA * * THE AUTHORS OF THIS LIBRARY ACCEPT ABSOLUTELY NO LIABILITY FOR * ANY HARM OR LOSS RESULTING FROM ITS USE. IT IS _EXTREMELY_ UNWISE * TO RELY ON SOFTWARE ALONE FOR SAFETY. Any machinery capable of * harming persons must have provisions for completely removing power * from all motors, etc, before persons enter any danger area. All * machinery must be designed to comply with local and national safety * codes, and the authors of this software can not, and do not, take * any responsibility for such compliance. * * This code was written as part of the EMC HAL project. For more * information, go to www.linuxcnc.org. */ #include "halcmd_completion.h" #include "config.h" #include "rtapi.h" /* RTAPI realtime OS API */ #include "hal.h" /* HAL public API decls */ #include "../hal_priv.h" /* private HAL decls */ static int argno; static const char *command_table[] = { "loadrt", "loadusr", "unload", "lock", "unlock", "linkps", "linksp", "linkpp", "unlinkp", "net", "newsig", "delsig", "getp", "gets", "setp", "sets", "ptype", "stype", "addf", "delf", "show", "list", "status", "save", "source", "start", "stop", "quit", "exit", "help", "alias", "unalias", NULL, }; static const char *nonRT_command_table[] = { "-h", NULL, }; static const char *alias_table[] = { "param", "pin", NULL, }; static const char *show_table[] = { "all", "alias", "comp", "pin", "sig", "param", "funct", "thread", NULL, }; static const char *save_table[] = { "all", "alias", "comp", "sig", "link", "linka", "net", "neta", "param", "thread", NULL, }; static const char *list_table[] = { "comp", "alias", "pin", "sig", "param", "funct", "thread", NULL }; static const char *status_table[] = { "alias", "lock", "mem", "all", NULL }; static const char *pintype_table[] = { "bit", "float", "u32", "s32", NULL }; static const char *lock_table[] = { "none", "tune", "all", NULL }; static const char *unlock_table[] = { "tune", "all", NULL }; static const char **string_table = NULL; static char *table_generator(const char *text, int state) { static int len; static int list_index = 0; const char *name; if(state == 0) { list_index = 0; len = strlen(text); } while((name = string_table[list_index]) != NULL) { list_index ++; if(strncmp (name, text, len) == 0) return strdup(name); } return NULL; } static char **completion_matches_table(const char *text, const char **table, hal_completer_func func) { string_table = table; return func(text, table_generator); } static hal_type_t match_type = -1; static int match_writers = -1; static hal_pin_dir_t match_direction = HAL_DIR_UNSPECIFIED; static int direction_match(hal_pin_dir_t dir1, hal_pin_dir_t dir2) { if(dir1 == HAL_DIR_UNSPECIFIED || dir2 == -1) return 1; return (dir1 | dir2) == HAL_IO; } static int writer_match(hal_pin_dir_t dir, int writers) { if(writers == -1 || dir == -1) return 1; if(dir & HAL_IN || writers == 0) return 1; return 0; } static void check_match_type_pin(const char *name) { int next = hal_data->pin_list_ptr; int sz = strcspn(name, " \t"); while(next) { hal_pin_t *pin = SHMPTR(next); next = pin->next_ptr; if ( sz == strlen(pin->name) && strncmp(name, pin->name, sz) == 0 ) { match_type = pin->type; match_direction = pin->dir; return; } } } static void check_match_type_signal(const char *name) { int next = hal_data->sig_list_ptr; int sz = strcspn(name, " \t"); while(next) { hal_sig_t *sig = SHMPTR(next); next = sig->next_ptr; if ( sz == strlen(sig->name) && strncmp(name, sig->name, sz) == 0 ) { match_type = sig->type; match_writers = sig->writers; return; } } } static char *thread_generator(const char *text, int state) { static int len; static int next; if(!state) { next = hal_data->thread_list_ptr; len = strlen(text); } while(next) { hal_thread_t *thread = SHMPTR(next); next = thread->next_ptr; if ( strncmp(text, thread->name, len) == 0 ) return strdup(thread->name); } return NULL; } static char *parameter_generator(const char *text, int state) { static int len; static int next; static int aliased; char *name; if(!state) { next = hal_data->param_list_ptr; len = strlen(text); aliased = 0; } while(next) { hal_param_t *param = SHMPTR(next); switch (aliased) { case 0: // alias (if any) has not been output if (param->oldname != 0) { // there's an alias, so use that and do not update the pin pointer hal_oldname_t *oldname = SHMPTR(param->oldname); name = oldname->name; aliased = 1; } else { // no alias, so use the name and update the pin pointer name = param->name; next = param->next_ptr; } break; case 1: // there is an alias, and it has been processed already name = param->name; next = param->next_ptr; aliased = 0; break; default: // shouldn't be able to get here, so assume we're done rl_attempted_completion_over = 1; return NULL; break; } if ( strncmp(text, name, len) == 0 ) return strdup(name); } return NULL; } static char *funct_generator_common(const char *text, int state, int inuse) { static int len; static int next; if(!state) { next = hal_data->funct_list_ptr; len = strlen(text); } while(next) { hal_funct_t *funct = SHMPTR(next); next = funct->next_ptr; if (( strncmp(text, funct->name, len) == 0 ) && (inuse == funct->users)) return strdup(funct->name); } return NULL; } static char *funct_generator(const char *text, int state) { return funct_generator_common(text, state, 0); } static char *attached_funct_generator(const char *text, int state) { return funct_generator_common(text, state, 1); } static char *signal_generator(const char *text, int state) { static int len; static int next; if(!state) { next = hal_data->sig_list_ptr; len = strlen(text); } while(next) { hal_sig_t *sig = SHMPTR(next); next = sig->next_ptr; if ( match_type != HAL_TYPE_UNSPECIFIED && match_type != sig->type ) continue; if ( !writer_match( match_direction, sig->writers ) ) continue; if ( strncmp(text, sig->name, len) == 0 ) return strdup(sig->name); } return NULL; } static char *getp_generator(const char *text, int state) { static int len; static int next; static int what; if(!state) { what = 0; next = hal_data->param_list_ptr; len = strlen(text); } if(what == 0) { while(next) { hal_param_t *param = SHMPTR(next); next = param->next_ptr; if ( strncmp(text, param->name, len) == 0 ) return strdup(param->name); } what = 1; next = hal_data->pin_list_ptr; } while(next) { hal_pin_t *pin = SHMPTR(next); next = pin->next_ptr; if ( strncmp(text, pin->name, len) == 0 ) return strdup(pin->name); } return NULL; } static char *setp_generator(const char *text, int state) { static int len; static int next; static int what; if(!state) { what = 0; next = hal_data->param_list_ptr; len = strlen(text); } if(what == 0) { while(next) { hal_param_t *param = SHMPTR(next); next = param->next_ptr; if ( param->dir != HAL_RO && strncmp(text, param->name, len) == 0 ) return strdup(param->name); } what = 1; next = hal_data->pin_list_ptr; } while(next) { hal_pin_t *pin = SHMPTR(next); next = pin->next_ptr; if ( pin->dir != HAL_OUT && pin->signal == 0 && strncmp(text, pin->name, len) == 0 ) return strdup(pin->name); } return NULL; } static char *usrcomp_generator(const char *text, int state) { static int len; static int next; if(!state) { next = hal_data->comp_list_ptr; len = strlen(text); if(strncmp(text, "all", len) == 0) return strdup("all"); } while(next) { hal_comp_t *comp = SHMPTR(next); next = comp->next_ptr; if(comp->type) continue; if(strncmp(text, comp->name, len) == 0) return strdup(comp->name); } rl_attempted_completion_over = 1; return NULL; } static char *comp_generator(const char *text, int state) { static int len; static int next; if(!state) { next = hal_data->comp_list_ptr; len = strlen(text); if(strncmp(text, "all", len) == 0) return strdup("all"); } while(next) { hal_comp_t *comp = SHMPTR(next); next = comp->next_ptr; if ( strncmp(text, comp->name, len) == 0 ) return strdup(comp->name); } rl_attempted_completion_over = 1; return NULL; } static char *rtcomp_generator(const char *text, int state) { static int len; static int next; if(!state) { next = hal_data->comp_list_ptr; len = strlen(text); if(strncmp(text, "all", len) == 0) return strdup("all"); } while(next) { hal_comp_t *comp = SHMPTR(next); next = comp->next_ptr; if(!comp->type) continue; if ( strncmp(text, comp->name, len) == 0 ) return strdup(comp->name); } rl_attempted_completion_over = 1; return NULL; } static char *parameter_alias_generator(const char *text, int state) { static int len; static int next; if(!state) { next = hal_data->param_list_ptr; len = strlen(text); } while(next) { hal_param_t *param = SHMPTR(next); next = param->next_ptr; if (param->oldname==0) continue; // no alias here, move along if ( strncmp(text, param->name, len) == 0 ) return strdup(param->name); } return NULL; } static char *pin_alias_generator(const char *text, int state) { static int len; static int next; if(!state) { next = hal_data->pin_list_ptr; len = strlen(text); } while(next) { hal_pin_t *pin = SHMPTR(next); next = pin->next_ptr; if (pin->oldname==0) continue; // no alias here, move along if ( strncmp(text, pin->name, len) == 0 ) return strdup(pin->name); } return NULL; } static char *pin_generator(const char *text, int state) { static int len; static int next; static int aliased; char *name; if(!state) { next = hal_data->pin_list_ptr; len = strlen(text); aliased = 0; } while(next) { hal_pin_t *pin = SHMPTR(next); switch (aliased) { case 0: // alias (if any) has not been output if (pin->oldname != 0) { // there's an alias, so use that and do not update the pin pointer hal_oldname_t *oldname = SHMPTR(pin->oldname); name = oldname->name; aliased = 1; } else { // no alias, so use the name and update the pin pointer name = pin->name; next = pin->next_ptr; } break; case 1: // there is an alias, and it has been processed already name = pin->name; next = pin->next_ptr; aliased = 0; break; default: // shouldn't be able to get here, so assume we're done rl_attempted_completion_over = 1; return NULL; break; } if ( !writer_match( pin->dir, match_writers ) ) continue; if ( !direction_match( pin->dir, match_direction ) ) continue; if ( match_type != HAL_TYPE_UNSPECIFIED && match_type != pin->type ) continue; if ( strncmp(text, name, len) == 0 ) return strdup(name); } rl_attempted_completion_over = 1; return NULL; } #include static int startswith(const char *string, const char *stem) { return strncmp(string, stem, strlen(stem)) == 0; } const char *loadusr_table[] = {"-W", "-Wn", "-w", "-iw", NULL}; static char *loadusr_generator(const char *text, int state) { static int len; static DIR *d; struct dirent *ent; static int doing_table; if(!state) { if(argno == 1) doing_table = 1; string_table = loadusr_table; len = strlen(text); d = opendir(EMC2_BIN_DIR); } if(doing_table) { char *result = table_generator(text, state); if(result) return result; doing_table = 0; } while(d && (ent = readdir(d))) { char *result; if(!startswith(ent->d_name, "hal")) continue; if(startswith(ent->d_name, "halcmd")) continue; if(strncmp(text, ent->d_name, len) != 0) continue; result = strdup(ent->d_name); return result; } if (d != NULL) { closedir(d); } return NULL; } static char *loadrt_generator(const char *text, int state) { static int len; static DIR *d; struct dirent *ent; if(!state) { len = strlen(text); d = opendir(EMC2_RTLIB_DIR); } while(d && (ent = readdir(d))) { char *result; if(!strstr(ent->d_name, MODULE_EXT)) continue; if(startswith(ent->d_name, "rtapi.")) continue; if(strncmp(text, ent->d_name, len) != 0) continue; result = strdup(ent->d_name); result[strlen(result) - strlen(MODULE_EXT)] = 0; return result; } if (d != NULL) { closedir(d); } return NULL; } static inline int isskip(int ch) { return isspace(ch) || ch == '=' || ch == '<' || ch == '>'; } static char *nextword(char *s) { s = strchr(s, ' '); if(!s) return NULL; return s+strspn(s, " \t=<>"); } char **halcmd_completer(const char *text, int start, int end, hal_completer_func func, char *buffer) { int i; char **result = NULL, *n; while (isskip(*text)) text++; // skip initial whitespace while (isskip(*buffer)) { buffer++; start--; } if (start<0) start=0; if(start == 0) { if (comp_id >= 0) { return completion_matches_table(text, command_table, func); } else { return completion_matches_table(text, nonRT_command_table, func); } } for(i=0, argno=0; imutex)); if(startswith(buffer, "delsig ") && argno == 1) { result = func(text, signal_generator); } else if(startswith(buffer, "linkps ") && argno == 1) { result = func(text, pin_generator); } else if(startswith(buffer, "linkps ") && argno == 2) { check_match_type_pin(buffer + 7); result = func(text, signal_generator); } else if(startswith(buffer, "net ") && argno == 1) { result = func(text, signal_generator); } else if(startswith(buffer, "net ") && argno == 2) { check_match_type_signal(nextword(buffer)); result = func(text, pin_generator); } else if(startswith(buffer, "net ") && argno > 2) { check_match_type_signal(nextword(buffer)); if(match_type == HAL_TYPE_UNSPECIFIED) { check_match_type_pin(nextword(nextword(buffer))); if(match_direction == HAL_IN) match_direction = -1; } result = func(text, pin_generator); } else if(startswith(buffer, "linksp ") && argno == 1) { result = func(text, signal_generator); } else if(startswith(buffer, "linksp ") && argno == 2) { check_match_type_signal(buffer + 7); result = func(text, pin_generator); } else if (startswith(buffer, "alias ")) { if (argno == 1) { result = completion_matches_table(text, alias_table, func); } else if (argno == 2) { n = nextword(buffer); if (startswith(n, "pin")) { result = func(text, pin_generator); } else if (startswith(n, "param")) { result = func(text, parameter_generator); } } } else if(startswith(buffer, "unalias ")) { if (argno == 1) { result = completion_matches_table(text, alias_table, func); } else if (argno == 2) { n = nextword(buffer); if (startswith(n, "pin")) { result = func(text, pin_alias_generator); } else if (startswith(n, "param")) { result = func(text, parameter_alias_generator); } } } else if(startswith(buffer, "linkpp ") && argno == 1) { result = func(text, pin_generator); } else if(startswith(buffer, "linkpp ") && argno == 2) { check_match_type_pin(buffer + 7); result = func(text, pin_generator); } else if(startswith(buffer, "unlinkp ") && argno == 1) { result = func(text, pin_generator); } else if(startswith(buffer, "setp ") && argno == 1) { result = func(text, setp_generator); } else if(startswith(buffer, "sets ") && argno == 1) { result = func(text, signal_generator); } else if(startswith(buffer, "ptype ") && argno == 1) { result = func(text, getp_generator); } else if(startswith(buffer, "getp ") && argno == 1) { result = func(text, getp_generator); } else if(startswith(buffer, "stype ") && argno == 1) { result = func(text, signal_generator); } else if(startswith(buffer, "gets ") && argno == 1) { result = func(text, signal_generator); } else if(startswith(buffer, "list ")) { if (argno == 1) { result = completion_matches_table(text, list_table, func); } else if (argno==2) { n = nextword(buffer); if (startswith(n, "pin")) { result = func(text, pin_generator); } else if (startswith(n, "sig")) { result = func(text, signal_generator); } else if (startswith(n, "param")) { result = func(text, parameter_generator); } else if (startswith(n, "funct")) { result = func(text, funct_generator); } else if (startswith(n, "thread")) { result = func(text, thread_generator); } } } else if(startswith(buffer, "show ")) { if (argno == 1) { result = completion_matches_table(text, show_table, func); } else if (argno==2) { n = nextword(buffer); if (startswith(n, "pin")) { result = func(text, pin_generator); } else if (startswith(n, "sig")) { result = func(text, signal_generator); } else if (startswith(n, "param")) { result = func(text, parameter_generator); } else if (startswith(n, "funct")) { result = func(text, funct_generator); } else if (startswith(n, "thread")) { result = func(text, thread_generator); } } } else if(startswith(buffer, "save ") && argno == 1) { result = completion_matches_table(text, save_table, func); } else if(startswith(buffer, "status ") && argno == 1) { result = completion_matches_table(text, status_table, func); } else if(startswith(buffer, "newsig ") && argno == 2) { result = completion_matches_table(text, pintype_table, func); } else if(startswith(buffer, "lock ") && argno == 1) { result = completion_matches_table(text, lock_table, func); } else if(startswith(buffer, "unlock ") && argno == 1) { result = completion_matches_table(text, unlock_table, func); } else if(startswith(buffer, "addf ") && argno == 1) { result = func(text, funct_generator); } else if(startswith(buffer, "addf ") && argno == 2) { result = func(text, thread_generator); } else if(startswith(buffer, "delf ") && argno == 1) { result = func(text, attached_funct_generator); } else if(startswith(buffer, "delf ") && argno == 2) { result = func(text, thread_generator); } else if(startswith(buffer, "help ") && argno == 1) { result = completion_matches_table(text, command_table, func); } else if(startswith(buffer, "unloadusr ") && argno == 1) { result = func(text, usrcomp_generator); } else if(startswith(buffer, "waitusr ") && argno == 1) { result = func(text, usrcomp_generator); } else if(startswith(buffer, "unloadrt ") && argno == 1) { result = func(text, rtcomp_generator); } else if(startswith(buffer, "unload ") && argno == 1) { result = func(text, comp_generator); } else if(startswith(buffer, "source ") && argno == 1) { rtapi_mutex_give(&(hal_data->mutex)); // leaves rl_attempted_completion_over = 0 to complete from filesystem return 0; } else if(startswith(buffer, "loadusr ") && argno < 3) { rtapi_mutex_give(&(hal_data->mutex)); // leaves rl_attempted_completion_over = 0 to complete from filesystem return func(text, loadusr_generator); } else if(startswith(buffer, "loadrt ") && argno == 1) { result = func(text, loadrt_generator); } rtapi_mutex_give(&(hal_data->mutex)); rl_attempted_completion_over = 1; return result; } static char **rlcompleter(const char *text, int start, int end) { return halcmd_completer(text, start, end, rl_completion_matches, rl_line_buffer); } void halcmd_init_readline() { rl_readline_name = "halcmd"; rl_attempted_completion_function = rlcompleter; }