/* 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 "config.h" #include "rtapi.h" #include "hal.h" #include "../hal_priv.h" #include "halcmd.h" #include "halcmd_commands.h" #include "halcmd_completion.h" #include #include #include #include #include #include #include #include #include #include #include #include #include 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); /*********************************************************************** * LOCAL FUNCTION DEFINITIONS * ************************************************************************/ 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) { /* no args specified, print help */ print_help_general(0); exit(0); } /* set default level of output - 'quiet' */ rtapi_set_msg_level(RTAPI_MSG_ERR); /* set default for other options */ keep_going = 0; /* start parsing the command line, options first */ while(1) { c = getopt(argc, argv, "+RCfi:kqQsvVhe"); if(c == -1) break; switch(c) { case 'R': /* force an unlock of the HAL mutex - to be used after a segfault in a hal program */ if (release_HAL_mutex() < 0) { printf("HALCMD: Release Mutex failed!\n"); return 1; } return 0; break; case 'h': /* -h = help */ if (argc > optind) { /* there are more arguments */ do_help_cmd(argv[optind]); } else { print_help_general(1); } return 0; break; case 'k': /* -k = keep going */ keep_going = 1; break; case 'q': /* -q = quiet (default) */ rtapi_set_msg_level(RTAPI_MSG_ERR); break; case 'Q': /* -Q = very quiet */ rtapi_set_msg_level(RTAPI_MSG_NONE); break; case 's': /* script friendly mode */ scriptmode = 1; break; case 'v': /* -v = verbose */ rtapi_set_msg_level(RTAPI_MSG_INFO); break; case 'V': /* -V = very verbose */ 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--; // if at end of line, back up one char 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': /* -i = allow reading 'setp' values from an ini file */ if (halcmd_inifile == NULL) { /* it's the first -i (ignore repeats) */ /* there is a following arg, and it's not an option */ filename = optarg; halcmd_inifile = fopen(filename, "r"); if (halcmd_inifile == NULL) { fprintf(stderr, "Could not open ini file '%s'\n", filename); exit(-1); } /* make sure file is closed on exec() */ fd = fileno(halcmd_inifile); fcntl(fd, F_SETFD, FD_CLOEXEC); } break; #endif /* NO_INI */ case '?': /* option not in getopt() list getopt already printed an error message */ exit(-1); break; default: /* option in getopt list but not in switch statement - bug */ printf("Unimplemented option '-%c'\n", c); exit(-1); break; } } if(filemode) { /* it's the first -f (ignore repeats) */ if (argc > optind) { /* there is a following arg, and it's not an option */ 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); } /* make sure file is closed on exec() */ fd = fileno(srcfile); fcntl(fd, F_SETFD, FD_CLOEXEC); } else { halcmd_set_filename(""); /* no filename followed -f option, use stdin */ srcfile = stdin; prompt_mode = 1; } } if ( halcmd_startup(0) != 0 ) return 1; errorcount = 0; /* HAL init is OK, let's process the command(s) */ 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(""); halcmd_set_linenumber(0); retval = halcmd_parse_cmd(&argv[optind]); if (retval != 0) { errorcount++; } } } else { /* read command line(s) from 'srcfile' */ while (get_input(srcfile, raw_buf, MAX_CMD_LEN)) { char *tokens[MAX_TOK+1]; halcmd_set_linenumber(linenumber++); /* remove comments, do var substitution, and tokenise */ retval = halcmd_preprocess_line(raw_buf, tokens); if(echo_mode) { halcmd_echo("%s\n", raw_buf); } if (retval == 0) { /* the "quit" command is not handled by parse_line() */ if ( ( strcasecmp(tokens[0],"quit") == 0 ) || ( strcasecmp(tokens[0],"exit") == 0 ) ) { break; } /* process command */ retval = halcmd_parse_cmd(tokens); } /* did a signal happen while we were busy? */ if ( halcmd_done ) { /* treat it as an error */ errorcount++; /* exit from loop */ break; } if ( retval != 0 ) { errorcount++; } if (( errorcount > 0 ) && ( keep_going == 0 )) { /* exit from loop */ break; } } } /* all done */ halcmd_shutdown(); if ( errorcount > 0 ) { return 1; } else { return 0; } } /* release_HAL_mutex() unconditionally releases the hal_mutex very useful after a program segfaults while holding the mutex */ static int release_HAL_mutex(void) { int comp_id, mem_id, retval; void *mem; hal_data_t *hal_data; /* do RTAPI init */ comp_id = rtapi_init("hal_unlocker"); if (comp_id < 0) { rtapi_print_msg(RTAPI_MSG_ERR, "ERROR: rtapi init failed\n"); return -EINVAL; } /* get HAL shared memory block from RTAPI */ 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; } /* get address of shared memory area */ 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; } /* set up internal pointers to shared mem and data structure */ hal_data = (hal_data_t *) mem; /* release mutex */ rtapi_mutex_give(&(hal_data->mutex)); /* release RTAPI resources */ rtapi_shmem_delete(mem_id, comp_id); rtapi_exit(comp_id); /* done */ 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; } /* completion text starts with "halcmd", so remove that from the string */ static int propose_completion(char *all, char *fragment, int start) { int sp=0, len=strlen(all), i=0; for(; i: ", halcmd_get_linenumber()); va_start(ap, format); vfprintf(stderr, format, ap); va_end(ap); }