This is Unofficial EPICS BASE Doxygen Site
iocsh.cpp
Go to the documentation of this file.
1 /*************************************************************************\
2 * Copyright (c) 2002 The University of Chicago, as Operator of Argonne
3 * National Laboratory.
4 * Copyright (c) 2002 The Regents of the University of California, as
5 * Operator of Los Alamos National Laboratory.
6 * EPICS BASE is distributed subject to a Software License Agreement found
7 * in file LICENSE that is included with this distribution.
8 \*************************************************************************/
9 /* iocsh.cpp */
10 /* Author: Marty Kraimer Date: 27APR2000 */
11 /* Heavily modified by Eric Norum Date: 03MAY2000 */
12 /* Adapted to C++ by Eric Norum Date: 18DEC2000 */
13 
14 #include <exception>
15 
16 #include <stddef.h>
17 #include <string.h>
18 #include <stdio.h>
19 #include <ctype.h>
20 #include <errno.h>
21 
22 #include "epicsMath.h"
23 #include "errlog.h"
24 #include "macLib.h"
25 #include "epicsStdio.h"
26 #include "epicsString.h"
27 #include "epicsStdlib.h"
28 #include "epicsThread.h"
29 #include "epicsMutex.h"
30 #include "envDefs.h"
31 #include "registry.h"
32 #include "epicsReadline.h"
33 #include "cantProceed.h"
34 #include "iocsh.h"
35 
36 extern "C" {
37 
38 /*
39  * Global link to pdbbase
40  */
42 
43 /*
44  * File-local information
45  */
46 struct iocshCommand {
48  struct iocshCommand *next;
49 };
50 static struct iocshCommand *iocshCommandHead;
51 static char iocshCmdID[] = "iocshCmd";
52 struct iocshVariable {
55 };
56 static struct iocshVariable *iocshVariableHead;
57 static char iocshVarID[] = "iocshVar";
58 extern "C" { static void varCallFunc(const iocshArgBuf *); }
59 static epicsMutexId iocshTableMutex;
60 static epicsThreadOnceId iocshOnceId = EPICS_THREAD_ONCE_INIT;
61 static epicsThreadPrivateId iocshContextId;
62 
63 /*
64  * I/O redirection
65  */
66 #define NREDIRECTS 5
67 struct iocshRedirect {
68  const char *name;
69  const char *mode;
70  FILE *fp;
71  FILE *oldFp;
73 };
74 
75 /*
76  * Set up module variables
77  */
78 static void iocshOnce (void *)
79 {
80  iocshTableMutex = epicsMutexMustCreate ();
81  iocshContextId = epicsThreadPrivateCreate();
82 }
83 
84 static void iocshInit (void)
85 {
86  epicsThreadOnce (&iocshOnceId, iocshOnce, NULL);
87 }
88 
89 /*
90  * Lock the table mutex
91  */
92 static void
93 iocshTableLock (void)
94 {
95  iocshInit();
96  epicsMutexMustLock (iocshTableMutex);
97 }
98 
99 /*
100  * Unlock the table mutex
101  */
102 static void
103 iocshTableUnlock (void)
104 {
105  epicsMutexUnlock (iocshTableMutex);
106 }
107 
108 /*
109  * Register a command
110  */
111 void epicsStdCall iocshRegister (const iocshFuncDef *piocshFuncDef,
112  iocshCallFunc func)
113 {
114  struct iocshCommand *l, *p, *n;
115  int i;
116 
117  iocshTableLock ();
118  for (l = NULL, p = iocshCommandHead ; p != NULL ; l = p, p = p->next) {
119  i = strcmp (piocshFuncDef->name, p->def.pFuncDef->name);
120  if (i == 0) {
121  p->def.pFuncDef = piocshFuncDef;
122  p->def.func = func;
123  iocshTableUnlock ();
124  return;
125  }
126  if (i < 0)
127  break;
128  }
129  n = (struct iocshCommand *) callocMustSucceed (1, sizeof *n,
130  "iocshRegister");
131  if (!registryAdd(iocshCmdID, piocshFuncDef->name, (void *)n)) {
132  free (n);
133  iocshTableUnlock ();
134  errlogPrintf ("iocshRegister failed to add %s\n", piocshFuncDef->name);
135  return;
136  }
137  if (l == NULL) {
138  n->next = iocshCommandHead;
139  iocshCommandHead = n;
140  }
141  else {
142  n->next = l->next;
143  l->next = n;
144  }
145  n->def.pFuncDef = piocshFuncDef;
146  n->def.func = func;
147  iocshTableUnlock ();
148 }
149 
150 
151 /*
152  * Retrieves a previously registered function with the given name.
153  */
154 const iocshCmdDef * epicsStdCall iocshFindCommand(const char *name)
155 {
156  return (iocshCmdDef *) registryFind(iocshCmdID, name);
157 }
158 
159 /*
160  * Register the "var" command and any variable(s)
161  */
162 static const iocshArg varCmdArg0 = { "[variable", iocshArgString};
163 static const iocshArg varCmdArg1 = { "[value]]", iocshArgString};
164 static const iocshArg *varCmdArgs[2] = {&varCmdArg0, &varCmdArg1};
165 static const iocshFuncDef varFuncDef = {"var", 2, varCmdArgs};
166 
167 void epicsStdCall iocshRegisterVariable (const iocshVarDef *piocshVarDef)
168 {
169  struct iocshVariable *l, *p, *n;
170  int i;
171  int found;
172 
173  iocshTableLock ();
174  while ((piocshVarDef != NULL)
175  && (piocshVarDef->name != NULL)
176  && (*piocshVarDef->name != '\0')) {
177  if (iocshVariableHead == NULL)
178  iocshRegister(&varFuncDef,varCallFunc);
179  found = 0;
180  for (l = NULL, p = iocshVariableHead ; p != NULL ; l = p, p = p->next) {
181  i = strcmp (piocshVarDef->name, p->pVarDef->name);
182  if (i == 0) {
183  if (p->pVarDef != piocshVarDef) {
184  errlogPrintf("Warning: iocshRegisterVariable redefining %s.\n",
185  piocshVarDef->name);
186  p->pVarDef = piocshVarDef;
187  }
188  found = 1;
189  break;
190  }
191  if (i < 0)
192  break;
193  }
194  if (!found) {
195  n = (struct iocshVariable *) callocMustSucceed(1, sizeof *n,
196  "iocshRegisterVariable");
197  if (!registryAdd(iocshVarID, piocshVarDef->name, (void *)n)) {
198  free(n);
199  iocshTableUnlock();
200  errlogPrintf("iocshRegisterVariable failed to add %s.\n",
201  piocshVarDef->name);
202  return;
203  }
204  if (l == NULL) {
205  n->next = iocshVariableHead;
206  iocshVariableHead = n;
207  }
208  else {
209  n->next = l->next;
210  l->next = n;
211  }
212  n->pVarDef = piocshVarDef;
213  }
214  piocshVarDef++;
215  }
216  iocshTableUnlock ();
217 }
218 
219 /*
220  * Retrieves a previously registered variable with the given name.
221  */
222 const iocshVarDef * epicsStdCall iocshFindVariable(const char *name)
223 {
224  struct iocshVariable *temp = (iocshVariable *) registryFind(iocshVarID, name);
225  return temp ? temp->pVarDef : 0;
226 }
227 
228 /*
229  * Free storage created by iocshRegister/iocshRegisterVariable
230  */
231 void epicsStdCall iocshFree(void)
232 {
233  struct iocshCommand *pc;
234  struct iocshVariable *pv;
235 
236  iocshTableLock ();
237  for (pc = iocshCommandHead ; pc != NULL ; ) {
238  struct iocshCommand * nc = pc->next;
239  free (pc);
240  pc = nc;
241  }
242  for (pv = iocshVariableHead ; pv != NULL ; ) {
243  struct iocshVariable *nv = pv->next;
244  free (pv);
245  pv = nv;
246  }
247  iocshCommandHead = NULL;
248  iocshVariableHead = NULL;
249  iocshTableUnlock ();
250 }
251 
252 /*
253  * Report an error
254  */
255 static void
256 showError (const char *filename, int lineno, const char *msg, ...)
257 {
258  va_list ap;
259 
260  va_start (ap, msg);
261  if (filename)
262  fprintf(epicsGetStderr(), "%s line %d: ", filename, lineno);
263  vfprintf (epicsGetStderr(), msg, ap);
264  fputc ('\n', epicsGetStderr());
265  va_end (ap);
266 }
267 
268 static int
269 cvtArg (const char *filename, int lineno, char *arg, iocshArgBuf *argBuf,
270  const iocshArg *piocshArg)
271 {
272  char *endp;
273 
274  switch (piocshArg->type) {
275  case iocshArgInt:
276  if (arg && *arg) {
277  errno = 0;
278  argBuf->ival = strtol (arg, &endp, 0);
279  if (errno == ERANGE) {
280  errno = 0;
281  argBuf->ival = strtoul (arg, &endp, 0);
282  if (errno == ERANGE) {
283  showError(filename, lineno, "Integer '%s' out of range",
284  arg);
285  return 0;
286  }
287  }
288  if (*endp) {
289  showError(filename, lineno, "Illegal integer '%s'", arg);
290  return 0;
291  }
292  }
293  else {
294  argBuf->ival = 0;
295  }
296  break;
297 
298  case iocshArgDouble:
299  if (arg && *arg) {
300  argBuf->dval = epicsStrtod (arg, &endp);
301  if (*endp) {
302  showError(filename, lineno, "Illegal double '%s'", arg);
303  return 0;
304  }
305  }
306  else {
307  argBuf->dval = 0.0;
308  }
309  break;
310 
311  case iocshArgString:
312  argBuf->sval = arg;
313  break;
314 
316  if (arg != NULL) {
317  argBuf->sval = (char *) malloc(strlen(arg) + 1);
318  if (argBuf->sval == NULL) {
319  showError(filename, lineno, "Out of memory");
320  return 0;
321  }
322  strcpy(argBuf->sval, arg);
323  } else {
324  argBuf->sval = NULL;
325  }
326  break;
327 
328  case iocshArgPdbbase:
329  /* Argument must be missing or 0 or pdbbase */
330  if(!arg || !*arg || (*arg == '0') || (strcmp(arg, "pdbbase") == 0)) {
331  if(!iocshPpdbbase || !*iocshPpdbbase) {
332  showError(filename, lineno, "pdbbase not present");
333  return 0;
334  }
335  argBuf->vval = *iocshPpdbbase;
336  break;
337  }
338  showError(filename, lineno, "Expecting 'pdbbase' got '%s'", arg);
339  return 0;
340 
341  default:
342  showError(filename, lineno, "Illegal argument type %d",
343  piocshArg->type);
344  return 0;
345  }
346  return 1;
347 }
348 
349 /*
350  * Open redirected I/O
351  */
352 static int
353 openRedirect(const char *filename, int lineno, struct iocshRedirect *redirect)
354 {
355  int i;
356 
357  for (i = 0 ; i < NREDIRECTS ; i++, redirect++) {
358  if (redirect->name != NULL) {
359  redirect->fp = fopen(redirect->name, redirect->mode);
360  if (redirect->fp == NULL) {
361  showError(filename, lineno, "Can't open \"%s\": %s.",
362  redirect->name, strerror(errno));
363  redirect->name = NULL;
364  while (i--) {
365  redirect--;
366  if (redirect->fp) {
367  fclose(redirect->fp);
368  redirect->fp = NULL;
369  }
370  redirect->name = NULL;
371  }
372  return -1;
373  }
374  redirect->mustRestore = 0;
375  }
376  }
377  return 0;
378 }
379 
380 /*
381  * Start I/O redirection
382  */
383 static void
384 startRedirect(const char * /*filename*/, int /*lineno*/,
385  struct iocshRedirect *redirect)
386 {
387  int i;
388 
389  for (i = 0 ; i < NREDIRECTS ; i++, redirect++) {
390  if (redirect->fp != NULL) {
391  switch(i) {
392  case 0:
393  redirect->oldFp = epicsGetThreadStdin();
394  epicsSetThreadStdin(redirect->fp);
395  redirect->mustRestore = 1;
396  break;
397  case 1:
398  redirect->oldFp = epicsGetThreadStdout();
399  epicsSetThreadStdout(redirect->fp);
400  redirect->mustRestore = 1;
401  break;
402  case 2:
403  redirect->oldFp = epicsGetThreadStderr();
404  epicsSetThreadStderr(redirect->fp);
405  redirect->mustRestore = 1;
406  break;
407  }
408  }
409  }
410 }
411 
412 /*
413  * Finish up I/O redirection
414  */
415 static void
416 stopRedirect(const char *filename, int lineno, struct iocshRedirect *redirect)
417 {
418  int i;
419 
420  for (i = 0 ; i < NREDIRECTS ; i++, redirect++) {
421  if (redirect->fp != NULL) {
422  if (fclose(redirect->fp) != 0)
423  showError(filename, lineno, "Error closing \"%s\": %s.",
424  redirect->name, strerror(errno));
425  redirect->fp = NULL;
426  if (redirect->mustRestore) {
427  switch(i) {
428  case 0: epicsSetThreadStdin(redirect->oldFp); break;
429  case 1: epicsSetThreadStdout(redirect->oldFp); break;
430  case 2: epicsSetThreadStderr(redirect->oldFp); break;
431  }
432  }
433  }
434  redirect->name = NULL;
435  }
436 }
437 
438 /*
439  * "help" command
440  */
441 static const iocshArg helpArg0 = { "[command ...]",iocshArgArgv};
442 static const iocshArg *helpArgs[1] = {&helpArg0};
443 static const iocshFuncDef helpFuncDef =
444  {"help",1,helpArgs,
445  "With no arguments, list available command names.\n"
446  "With arguments, list arguments and usage for command(s).\n"
447  "Command names may contain wildcards\n"};
448 static void helpCallFunc(const iocshArgBuf *args)
449 {
450  int argc = args[0].aval.ac;
451  const char * const * argv = args[0].aval.av;
452  struct iocshFuncDef const *piocshFuncDef;
453  struct iocshCommand *pcmd;
454 
455  if (argc == 1) {
456  int l, col = 0;
457 
458  fprintf(epicsGetStdout(),
459  "Type 'help <command>' to see the arguments of <command>.\n");
460  iocshTableLock ();
461  for (pcmd = iocshCommandHead ; pcmd != NULL ; pcmd = pcmd->next) {
462  piocshFuncDef = pcmd->def.pFuncDef;
463  l = strlen (piocshFuncDef->name);
464  if ((l + col) >= 79) {
465  fputc('\n', epicsGetStdout());
466  col = 0;
467  }
468  fputs(piocshFuncDef->name, epicsGetStdout());
469  col += l;
470  if (col >= 64) {
471  fputc('\n', epicsGetStdout());
472  col = 0;
473  }
474  else {
475  do {
476  fputc(' ', epicsGetStdout());
477  col++;
478  } while ((col % 16) != 0);
479  }
480  }
481  if (col)
482  fputc('\n', epicsGetStdout());
483  iocshTableUnlock ();
484  }
485  else {
486  for (int iarg = 1 ; iarg < argc ; iarg++) {
487  for (pcmd = iocshCommandHead ; pcmd != NULL ; pcmd = pcmd->next) {
488  piocshFuncDef = pcmd->def.pFuncDef;
489  if (epicsStrGlobMatch(piocshFuncDef->name, argv[iarg]) != 0) {
490  if(piocshFuncDef->usage) {
491  fputs("\nUsage: ", epicsGetStdout());
492  }
493  fputs(piocshFuncDef->name, epicsGetStdout());
494  for (int a = 0 ; a < piocshFuncDef->nargs ; a++) {
495  const char *cp = piocshFuncDef->arg[a]->name;
496  if ((piocshFuncDef->arg[a]->type == iocshArgArgv)
497  || (strchr (cp, ' ') == NULL)) {
498  fprintf(epicsGetStdout(), " %s", cp);
499  }
500  else {
501  fprintf(epicsGetStdout(), " '%s'", cp);
502  }
503  }
504  fprintf(epicsGetStdout(),"\n");;
505  if(piocshFuncDef->usage) {
506  fprintf(epicsGetStdout(), "\n%s", piocshFuncDef->usage);
507  }
508  }
509  }
510  }
511  }
512 }
513 
514 typedef enum {
518 } OnError;
519 
520 // per call to iocshBody()
521 struct iocshScope {
523  OnError onerr;
524  double timeout;
525  bool errored;
527  iocshScope() :outer(0), onerr(Continue), timeout(0.0), errored(false), interactive(false) {}
528 };
529 
530 // per thread executing iocshBody()
531 struct iocshContext {
534 };
535 
536 int iocshSetError(int err)
537 {
538  iocshContext *ctxt;
539  if (err && iocshContextId) {
540  ctxt = (iocshContext *) epicsThreadPrivateGet(iocshContextId);
541 
542  if(ctxt && ctxt->scope) ctxt->scope->errored = 1;
543  }
544  return err;
545 }
546 
547 /*
548  * The body of the command interpreter
549  */
550 static int
551 iocshBody (const char *pathname, const char *commandLine, const char *macros)
552 {
553  FILE *fp = NULL;
554  const char *filename = NULL;
555  int icin, icout;
556  char c;
557  int quote, inword, backslash;
558  const char *raw = NULL;;
559  char *line = NULL;
560  int lineno = 0;
561  int argc;
562  char **argv = NULL;
563  int argvCapacity = 0;
564  struct iocshRedirect *redirects = NULL;
565  struct iocshRedirect *redirect = NULL;
566  int sep;
567  const char *prompt = NULL;
568  const char *ifs = " \t(),\r";
569  iocshArgBuf *argBuf = NULL;
570  int argBufCapacity = 0;
571  struct iocshCommand *found;
572  void *readlineContext = NULL;
573  int wasOkToBlock;
574  static const char * pairs[] = {"", "environ", NULL, NULL};
575  iocshScope scope;
576  iocshContext *context;
577  char ** defines = NULL;
578  int ret = 0;
579 
580  iocshInit();
581 
582  /*
583  * See if command interpreter is interactive
584  */
585  if (commandLine == NULL) {
586  if ((pathname == NULL) || (strcmp (pathname, "<telnet>") == 0)) {
587  if ((prompt = envGetConfigParamPtr(&IOCSH_PS1)) == NULL) {
588  prompt = "epics> ";
589  }
590  scope.interactive = true;
591  }
592  else {
593  fp = fopen (pathname, "r");
594  if (fp == NULL) {
595  fprintf(epicsGetStderr(), "Can't open %s: %s\n", pathname,
596  strerror (errno));
597  return -1;
598  }
599  if ((filename = strrchr (pathname, '/')) == NULL)
600  filename = pathname;
601  else
602  filename++;
603  prompt = NULL;
604  }
605 
606  /*
607  * Create a command-line input context
608  */
609  if ((readlineContext = epicsReadlineBegin(fp)) == NULL) {
610  fprintf(epicsGetStderr(), "Can't allocate command-line object.\n");
611  if (fp)
612  fclose(fp);
613  return -1;
614  }
615 
616  } else {
617  // use of iocshCmd() implies "on error break"
618  scope.onerr = Break;
619  }
620 
621  /*
622  * Set up redirection
623  */
624  redirects = (struct iocshRedirect *)calloc(NREDIRECTS, sizeof *redirects);
625  if (redirects == NULL) {
626  fprintf(epicsGetStderr(), "Out of memory!\n");
627  return -1;
628  }
629 
630  /*
631  * Parse macro definitions, this check occurs before creating the
632  * macro handle to simplify cleanup.
633  */
634 
635  if (macros) {
636  if (macParseDefns(NULL, macros, &defines) < 0) {
637  free(redirects);
638  return -1;
639  }
640  }
641 
642  // Check for existing context or construct a new one.
643  context = (iocshContext *) epicsThreadPrivateGet(iocshContextId);
644 
645  if (!context) {
646  context = (iocshContext*)calloc(1, sizeof(*context));
647  if (!context || macCreateHandle(&context->handle, pairs)) {
648  errlogMessage("iocsh: macCreateHandle failed.");
649  free(redirects);
650  free(context);
651  return -1;
652  }
653 
654  epicsThreadPrivateSet(iocshContextId, (void *) context);
655  }
656  MAC_HANDLE *handle = context->handle;
657 
658  scope.outer = context->scope;
659  context->scope = &scope;
660 
661  macPushScope(handle);
662  macInstallMacros(handle, defines);
663 
664  wasOkToBlock = epicsThreadIsOkToBlock();
666 
667  /*
668  * Read commands till EOF or exit
669  */
670  for (;;) {
671  if(!scope.interactive && scope.errored) {
672  if(scope.onerr==Continue) {
673  /* do nothing */
674 
675  } else if(scope.onerr==Break) {
676  ret = -1;
677  fprintf(epicsGetStderr(), "iocsh Error: Break\n" );
678  break;
679 
680  } else if(scope.onerr==Halt) {
681  ret = -1;
682  if(scope.timeout<=0.0 || isinf(scope.timeout)) {
683  fprintf(epicsGetStderr(), "iocsh Error: Halt\n" );
685  break;
686 
687  } else {
688  fprintf(epicsGetStderr(), "iocsh Error: Waiting %.1f sec ...\n", scope.timeout);
689  epicsThreadSleep(scope.timeout);
690  }
691  }
692  }
693 
694  /*
695  * Read a line
696  */
697  if (commandLine) {
698  if (raw != NULL)
699  break;
700  raw = commandLine;
701  }
702  else {
703  if ((raw = epicsReadline(prompt, readlineContext)) == NULL)
704  break;
705  }
706  lineno++;
707 
708  /*
709  * Skip leading white-space
710  */
711  icin = 0;
712  while ((c = raw[icin]) && isspace(c)) {
713  icin++;
714  }
715 
716  /*
717  * Ignore comment lines other than to echo
718  * them if they came from a script (disable echoing
719  * with '#-'). This avoids macLib errors from comments.
720  */
721  if (c == '#') {
722  if ((prompt == NULL) && (commandLine == NULL))
723  if (raw[icin + 1] != '-')
724  puts(raw);
725  continue;
726  }
727 
728  /*
729  * Expand macros
730  */
731  free(line);
732  if ((line = macDefExpand(raw, handle)) == NULL) {
733  scope.errored = true;
734  continue;
735  }
736 
737  /*
738  * Skip leading white-space coming from a macro
739  */
740  while ((c = line[icin]) && isspace(c)) {
741  icin++;
742  }
743 
744  /*
745  * Echo non-empty lines read from a script.
746  * Comments delineated with '#-' aren't echoed.
747  */
748  if ((prompt == NULL) && *line && (commandLine == NULL)) {
749  if ((c != '#') || (line[icin + 1] != '-')) {
750  puts(line);
751  }
752  }
753 
754  /*
755  * Ignore lines that became a comment or empty after macro expansion
756  */
757  if (!c || c == '#')
758  continue;
759 
760  /*
761  * Break line into words
762  */
763  icout = 0;
764  inword = 0;
765  argc = 0;
766  quote = EOF;
767  backslash = 0;
768  redirect = NULL;
769  for (;;) {
770  if (argc >= argvCapacity) {
771  int newCapacity = argvCapacity + 20;
772  char **newv = (char **)realloc (argv, newCapacity * sizeof *argv);
773  if (newv == NULL) {
774  fprintf (epicsGetStderr(), "Out of memory!\n");
775  argc = -1;
776  scope.errored = true;
777  break;
778  }
779  argv = newv;
780  argvCapacity = newCapacity;
781  }
782  c = line[icin++];
783  if (c == '\0')
784  break;
785  if ((quote == EOF) && !backslash && (strchr (ifs, c)))
786  sep = 1;
787  else
788  sep = 0;
789  if ((quote == EOF) && !backslash) {
790  int redirectFd = 1;
791  if (c == '\\') {
792  backslash = 1;
793  continue;
794  }
795  if (c == '<') {
796  if (redirect != NULL) {
797  break;
798  }
799  redirect = &redirects[0];
800  sep = 1;
801  redirect->mode = "r";
802  }
803  if ((c >= '1') && (c <= '9') && (line[icin] == '>')) {
804  redirectFd = c - '0';
805  c = '>';
806  icin++;
807  }
808  if (c == '>') {
809  if (redirect != NULL)
810  break;
811  if (redirectFd >= NREDIRECTS) {
812  redirect = &redirects[1];
813  break;
814  }
815  redirect = &redirects[redirectFd];
816  sep = 1;
817  if (line[icin] == '>') {
818  icin++;
819  redirect->mode = "a";
820  }
821  else {
822  redirect->mode = "w";
823  }
824  }
825  }
826  if (inword) {
827  if (c == quote) {
828  quote = EOF;
829  }
830  else {
831  if ((quote == EOF) && !backslash) {
832  if (sep) {
833  inword = 0;
834  line[icout++] = '\0';
835  }
836  else if ((c == '"') || (c == '\'')) {
837  quote = c;
838  }
839  else {
840  line[icout++] = c;
841  }
842  }
843  else {
844  line[icout++] = c;
845  }
846  }
847  }
848  else {
849  if (!sep) {
850  if (((c == '"') || (c == '\'')) && !backslash) {
851  quote = c;
852  }
853  if (redirect != NULL) {
854  if (redirect->name != NULL) {
855  argc = -1;
856  break;
857  }
858  redirect->name = line + icout;
859  redirect = NULL;
860  }
861  else {
862  argv[argc++] = line + icout;
863  }
864  if (quote == EOF)
865  line[icout++] = c;
866  inword = 1;
867  }
868  }
869  backslash = 0;
870  }
871  if (redirect != NULL) {
872  showError(filename, lineno, "Illegal redirection.");
873  scope.errored = true;
874  continue;
875  }
876  if (argc < 0) {
877  break;
878  }
879  if (quote != EOF) {
880  showError(filename, lineno, "Unbalanced quote.");
881  scope.errored = true;
882  continue;
883  }
884  if (backslash) {
885  showError(filename, lineno, "Trailing backslash.");
886  scope.errored = true;
887  continue;
888  }
889  if (inword)
890  line[icout++] = '\0';
891  argv[argc] = NULL;
892 
893  /*
894  * Special case -- Redirected input but no command
895  * Treat as if 'iocsh filename'.
896  */
897  if ((argc == 0) && (redirects[0].name != NULL)) {
898  const char *commandFile = redirects[0].name;
899  redirects[0].name = NULL;
900  if (openRedirect(filename, lineno, redirects) < 0)
901  continue;
902  startRedirect(filename, lineno, redirects);
903  if(iocshBody(commandFile, NULL, macros))
904  scope.errored = true;
905  stopRedirect(filename, lineno, redirects);
906  continue;
907  }
908 
909  /*
910  * Special command?
911  */
912  if ((argc > 0) && (strcmp(argv[0], "exit") == 0))
913  break;
914 
915  /*
916  * Set up redirection
917  */
918  if ((openRedirect(filename, lineno, redirects) == 0) && (argc > 0)) {
919  // error unless a function is actually called.
920  // handles command not-found and arg parsing errors.
921  scope.errored = true;
922  /*
923  * Look up command
924  */
925  found = (iocshCommand *)registryFind (iocshCmdID, argv[0]);
926  if (found) {
927  /*
928  * Process arguments and call function
929  */
930  struct iocshFuncDef const *piocshFuncDef = found->def.pFuncDef;
931  for (int iarg = 0 ; ; ) {
932  if (iarg == piocshFuncDef->nargs) {
933  startRedirect(filename, lineno, redirects);
934  /* execute */
935  scope.errored = false;
936  try {
937  (*found->def.func)(argBuf);
938  } catch(std::exception& e){
939  fprintf(epicsGetStderr(), "c++ error: %s\n", e.what());
940  scope.errored = true;
941  } catch(...) {
942  fprintf(epicsGetStderr(), "c++ error unknown\n");
943  scope.errored = true;
944  }
945  break;
946  }
947  if (iarg >= argBufCapacity) {
948  int newCapacity = argBufCapacity + 20;
949  void *newBuf = realloc(argBuf, newCapacity * sizeof *argBuf);
950  if (newBuf == NULL) {
951  fprintf (epicsGetStderr(), "Out of memory!\n");
952  break;
953  }
954  argBuf = (iocshArgBuf *) newBuf;
955  argBufCapacity = newCapacity;
956  }
957  if (piocshFuncDef->arg[iarg]->type == iocshArgArgv) {
958  argBuf[iarg].aval.ac = argc-iarg;
959  argBuf[iarg].aval.av = argv+iarg;
960  iarg = piocshFuncDef->nargs;
961  }
962  else {
963  if (!cvtArg (filename, lineno,
964  ((iarg < argc) ? argv[iarg+1] : NULL),
965  &argBuf[iarg], piocshFuncDef->arg[iarg]))
966  break;
967  iarg++;
968  }
969  }
970  if ((prompt != NULL) && (strcmp(argv[0], "epicsEnvSet") == 0)) {
971  const char *newPrompt;
972  if ((newPrompt = envGetConfigParamPtr(&IOCSH_PS1)) != NULL)
973  prompt = newPrompt;
974  }
975  }
976  else {
977  showError(filename, lineno, "Command %s not found.", argv[0]);
978  }
979  }
980  stopRedirect(filename, lineno, redirects);
981  }
982  macPopScope(handle);
983 
984  if (!scope.outer) {
985  macDeleteHandle(handle);
986  free(context);
987  epicsThreadPrivateSet(iocshContextId, NULL);
988  } else {
989  context->scope = scope.outer;
990  }
991  if (fp && (fp != stdin))
992  fclose (fp);
993  if (redirects != NULL) {
994  stopRedirect(filename, lineno, redirects);
995  free (redirects);
996  }
997  free(line);
998  free (argv);
999  free (argBuf);
1000  errlogFlush();
1001  if (readlineContext)
1002  epicsReadlineEnd(readlineContext);
1003  epicsThreadSetOkToBlock(wasOkToBlock);
1004  return ret;
1005 }
1006 
1007 /*
1008  * External access to the command interpreter
1009  */
1010 int epicsStdCall
1011 iocsh (const char *pathname)
1012 {
1013  return iocshLoad(pathname, NULL);
1014 }
1015 
1016 int epicsStdCall
1017 iocshCmd (const char *cmd)
1018 {
1019  return iocshRun(cmd, NULL);
1020 }
1021 
1022 int epicsStdCall
1023 iocshLoad(const char *pathname, const char *macros)
1024 {
1025  if (pathname)
1026  epicsEnvSet("IOCSH_STARTUP_SCRIPT", pathname);
1027  return iocshBody(pathname, NULL, macros);
1028 }
1029 
1030 int epicsStdCall
1031 iocshRun(const char *cmd, const char *macros)
1032 {
1033  if (cmd == NULL)
1034  return 0;
1035  return iocshBody(NULL, cmd, macros);
1036 }
1037 
1038 /*
1039  * Needed to work around the necessary limitations of macLib and
1040  * environment variables. In every other case of macro expansion
1041  * it is the expected outcome that defined macros override any
1042  * environment variables.
1043  *
1044  * iocshLoad/Run turn this on its head as it is very likely that
1045  * an epicsEnvSet command may be run within the context of their
1046  * calls. Thus, it would be expected that the new value would be
1047  * returned in any future macro expansion.
1048  *
1049  * To do so, the epicsEnvSet command needs to be able to access
1050  * and update the shared MAC_HANDLE that the iocsh uses. Which is
1051  * what this function is provided for.
1052  */
1053 void epicsStdCall
1054 iocshEnvClear(const char *name)
1055 {
1056  iocshContext *context;
1057 
1058  if (iocshContextId) {
1059  context = (iocshContext *) epicsThreadPrivateGet(iocshContextId);
1060 
1061  if (context != NULL) {
1062  macPutValue(context->handle, name, NULL);
1063  }
1064  }
1065 }
1066 
1067 /*
1068  * Internal commands
1069  */
1070 static void varHandler(const iocshVarDef *v, const char *setString)
1071 {
1072  switch(v->type) {
1073  default:
1074  fprintf(epicsGetStderr(), "Can't handle variable %s of type %d.\n",
1075  v->name, v->type);
1076  return;
1077  case iocshArgInt: break;
1078  case iocshArgDouble: break;
1079  }
1080  if(setString == NULL) {
1081  switch(v->type) {
1082  default: break;
1083  case iocshArgInt:
1084  fprintf(epicsGetStdout(), "%s = %d\n", v->name, *(int *)v->pval);
1085  break;
1086  case iocshArgDouble:
1087  fprintf(epicsGetStdout(), "%s = %g\n", v->name, *(double *)v->pval);
1088  break;
1089  }
1090  }
1091  else {
1092  switch(v->type) {
1093  default: break;
1094  case iocshArgInt:
1095  {
1096  char *endp;
1097  long ltmp = strtol(setString, &endp, 0);
1098  if((*setString != '\0') && (*endp == '\0'))
1099  *(int *)v->pval = ltmp;
1100  else
1101  fprintf(epicsGetStderr(),
1102  "Invalid integer value. Var %s not changed.\n", v->name);
1103  break;
1104  }
1105  case iocshArgDouble:
1106  {
1107  char *endp;
1108  double dtmp = epicsStrtod(setString, &endp);
1109  if((*setString != '\0') && (*endp == '\0'))
1110  *(double *)v->pval = dtmp;
1111  else
1112  fprintf(epicsGetStderr(),
1113  "Invalid double value. Var %s not changed.\n", v->name);
1114  break;
1115  }
1116  }
1117  }
1118 }
1119 
1120 static void varCallFunc(const iocshArgBuf *args)
1121 {
1122  struct iocshVariable *v;
1123  if(args[0].sval == NULL) {
1124  for (v = iocshVariableHead ; v != NULL ; v = v->next)
1125  varHandler(v->pVarDef, args[1].sval);
1126  }
1127  else {
1128  v = (iocshVariable *)registryFind(iocshVarID, args[0].sval);
1129  if (v == NULL) {
1130  fprintf(epicsGetStderr(), "Var %s not found.\n", args[0].sval);
1131  }
1132  else {
1133  varHandler(v->pVarDef, args[1].sval);
1134  }
1135  }
1136 }
1137 
1138 /* iocshCmd */
1139 static const iocshArg iocshCmdArg0 = { "command",iocshArgString};
1140 static const iocshArg *iocshCmdArgs[1] = {&iocshCmdArg0};
1141 static const iocshFuncDef iocshCmdFuncDef = {"iocshCmd",1,iocshCmdArgs};
1142 static void iocshCmdCallFunc(const iocshArgBuf *args)
1143 {
1144  iocshCmd(args[0].sval);
1145 }
1146 
1147 /* iocshLoad */
1148 static const iocshArg iocshLoadArg0 = { "pathname",iocshArgString};
1149 static const iocshArg iocshLoadArg1 = { "macros", iocshArgString};
1150 static const iocshArg *iocshLoadArgs[2] = {&iocshLoadArg0, &iocshLoadArg1};
1151 static const iocshFuncDef iocshLoadFuncDef = {"iocshLoad",2,iocshLoadArgs};
1152 static void iocshLoadCallFunc(const iocshArgBuf *args)
1153 {
1154  iocshLoad(args[0].sval, args[1].sval);
1155 }
1156 
1157 /* iocshRun */
1158 static const iocshArg iocshRunArg0 = { "command",iocshArgString};
1159 static const iocshArg iocshRunArg1 = { "macros", iocshArgString};
1160 static const iocshArg *iocshRunArgs[2] = {&iocshRunArg0, &iocshRunArg1};
1161 static const iocshFuncDef iocshRunFuncDef = {"iocshRun",2,iocshRunArgs};
1162 static void iocshRunCallFunc(const iocshArgBuf *args)
1163 {
1164  iocshRun(args[0].sval, args[1].sval);
1165 }
1166 
1167 /* on */
1168 static const iocshArg onArg0 = { "'error' 'continue' | 'break' | 'wait' [value] | 'halt'", iocshArgArgv };
1169 static const iocshArg *onArgs[1] = {&onArg0};
1170 static const iocshFuncDef onFuncDef = {"on", 1, onArgs,
1171  "Change IOC shell error handling.\n"
1172  " continue (default) - Ignores error and continue with next commands.\n"
1173  " break - Return to caller without executing futher commands.\n"
1174  " halt - Suspend process.\n"
1175  " wait - stall process for [value] seconds, the continue.\n"};
1176 static void onCallFunc(const iocshArgBuf *args)
1177 {
1178  iocshContext *context = (iocshContext *) epicsThreadPrivateGet(iocshContextId);
1179 
1180 #define USAGE() fprintf(epicsGetStderr(), "Usage: on error [continue | break | halt | wait <delay>]\n")
1181 
1182  if(!context || !context->scope) {
1183  // we are not called through iocshBody()...
1184 
1185  } else if(args->aval.ac<3 || strcmp(args->aval.av[1], "error")!=0) {
1186  USAGE();
1187 
1188  } else if(context->scope->interactive) {
1189  fprintf(epicsGetStderr(), "Interactive shell ignores on error ...\n");
1190 
1191  } else {
1192  // don't fault on previous, ignored, errors
1193  context->scope->errored = false;
1194 
1195  if(strcmp(args->aval.av[2], "continue")==0) {
1196  context->scope->onerr = Continue;
1197 
1198  } else if(strcmp(args->aval.av[2], "break")==0) {
1199  context->scope->onerr = Break;
1200 
1201  } else if(strcmp(args->aval.av[2], "halt")==0) {
1202  context->scope->onerr = Halt;
1203  context->scope->timeout = 0.0;
1204 
1205  } else if(strcmp(args->aval.av[2], "wait")==0) {
1206  context->scope->onerr = Halt;
1207  if(args->aval.ac<=3) {
1208  USAGE();
1209  } else if(epicsParseDouble(args->aval.av[3], &context->scope->timeout, NULL)) {
1210  context->scope->timeout = 5.0;
1211  } else {
1212  USAGE();
1213  fprintf(epicsGetStderr(), "Unable to parse 'on error wait' time %s\n", args->aval.av[3]);
1214  }
1215 
1216  } else {
1217  fprintf(epicsGetStderr(), "Usage: on error [continue | break | halt | wait <delay>]\n");
1218  context->scope->errored = true;
1219  }
1220  }
1221 
1222 #undef USAGE
1223 }
1224 
1225 /*
1226  * Dummy internal commands -- register and install in command table
1227  * so they show up in the help display
1228  */
1229 
1230 /* comment */
1231 static const iocshArg commentArg0 = { "newline-terminated comment",
1232  iocshArgArgv};
1233 static const iocshArg *commentArgs[1] = {&commentArg0};
1234 static const iocshFuncDef commentFuncDef = {"#",1,commentArgs};
1235 static void commentCallFunc(const iocshArgBuf *)
1236 {
1237 }
1238 
1239 /* exit */
1240 static const iocshFuncDef exitFuncDef =
1241  {"exit",0,0,
1242  "Return to caller. IOCs exit() from process.\n"};
1243 static void exitCallFunc(const iocshArgBuf *)
1244 {
1245 }
1246 
1247 static void localRegister (void)
1248 {
1249  iocshRegister(&commentFuncDef,commentCallFunc);
1250  iocshRegister(&exitFuncDef,exitCallFunc);
1251  iocshRegister(&helpFuncDef,helpCallFunc);
1252  iocshRegister(&iocshCmdFuncDef,iocshCmdCallFunc);
1253  iocshRegister(&iocshLoadFuncDef,iocshLoadCallFunc);
1254  iocshRegister(&iocshRunFuncDef,iocshRunCallFunc);
1255  iocshRegister(&onFuncDef, onCallFunc);
1256 }
1257 
1258 } /* extern "C" */
1259 
1260 /*
1261  * Register local commands on application startup
1262  */
1264  public:
1265  IocshRegister() { localRegister(); }
1266 };
1267 static IocshRegister iocshRegisterObj;
FILE * fp
Definition: iocsh.cpp:70
void errlogFlush(void)
Definition: errlog.c:529
void epicsStdCall epicsThreadSetOkToBlock(int isOkToBlock)
int epicsStdCall iocshRun(const char *cmd, const char *macros)
Definition: iocsh.cpp:1031
iocshCallFunc func
Definition: iocsh.h:73
Definition: iocsh.cpp:516
MAC_HANDLE * handle
Definition: iocsh.cpp:532
const char * name
Definition: iocsh.cpp:68
FILE *epicsStdCall epicsGetStderr(void)
Definition: epicsStdio.c:56
LIBCOM_API long epicsStdCall macParseDefns(MAC_HANDLE *handle, const char *defns, char **pairs[])
Parse macro definitions into an array of {name, value} pairs.
Definition: macUtil.c:32
FILE *epicsStdCall epicsGetThreadStderr(void)
Definition: epicsStdio.c:77
void epicsStdCall iocshEnvClear(const char *name)
Definition: iocsh.cpp:1054
LIBCOM_API int epicsStdCall registryAdd(void *registryID, const char *name, void *data)
Definition: registry.c:45
struct iocshArgBuf::@13 aval
iocshFuncDef const * pFuncDef
Definition: iocsh.h:72
void epicsStdCall epicsSetThreadStderr(FILE *fp)
Definition: epicsStdio.c:102
#define quote(v)
int lineno
Definition: antelope.c:33
int i
Definition: scan.c:967
LIBCOM_API const char *epicsStdCall envGetConfigParamPtr(const ENV_PARAM *pParam)
Get a configuration parameter&#39;s value or default string.
Definition: envSubr.c:81
LIBCOM_API void *epicsStdCall epicsThreadPrivateGet(epicsThreadPrivateId)
Definition: osdThread.c:973
Definition: tool_lib.h:67
const iocshVarDef *epicsStdCall iocshFindVariable(const char *name)
Definition: iocsh.cpp:222
int epicsStrGlobMatch(const char *str, const char *pattern)
Definition: epicsString.c:279
Routines to get and set EPICS environment parameters.
void *epicsStdCall epicsReadlineBegin(FILE *in)
Create a command-line context.
Definition: epicsReadline.c:64
void epicsStdCall iocshRegister(const iocshFuncDef *piocshFuncDef, iocshCallFunc func)
Definition: iocsh.cpp:111
char *epicsStdCall epicsReadline(const char *prompt, void *context)
Read a line of input.
Definition: epicsReadline.c:81
#define isinf(x)
Definition: epicsMath.h:16
#define epicsMutexMustCreate()
Create an epicsMutex semaphore for use from C code.
Definition: epicsMutex.h:179
const char * name
Definition: iocsh.h:57
#define NULL
Definition: catime.c:38
char * sval
Definition: iocsh.h:42
void epicsStdCall epicsMutexUnlock(epicsMutexId pmutexNode)
Release the semaphore.
Definition: epicsMutex.cpp:140
#define epicsStrtod
Definition: osdStrtod.h:15
const char * mode
Definition: iocsh.cpp:69
LIBCOM_API const ENV_PARAM IOCSH_PS1
Definition: iocsh.cpp:517
char ** av
Definition: iocsh.h:46
struct iocshVariable * next
Definition: iocsh.cpp:54
const char * name
Definition: iocsh.h:51
iocshCmdDef def
Definition: iocsh.cpp:47
LIBCOM_API void epicsStdCall epicsEnvSet(const char *name, const char *value)
Set an environment variable&#39;s value.
Definition: osdEnv.c:35
bool errored
Definition: iocsh.cpp:525
iocshArgType type
Definition: iocsh.h:52
Command-line editing functions.
int epicsStdCall iocsh(const char *pathname)
Definition: iocsh.cpp:1011
struct dbBase ** iocshPpdbbase
Definition: iocsh.cpp:41
const iocshArg *const * arg
Definition: iocsh.h:64
FILE *epicsStdCall epicsGetStdout(void)
Definition: epicsStdio.c:47
#define EPICS_THREAD_ONCE_INIT
Definition: epicsThread.h:109
struct iocshCommand * next
Definition: iocsh.cpp:48
LIBCOM_API void epicsStdCall epicsThreadSuspendSelf(void)
Definition: osdThread.c:664
iocshScope * outer
Definition: iocsh.cpp:522
double dval
Definition: iocsh.h:41
#define puts
Definition: epicsStdio.h:46
Macro substitution context, for use by macLib routines only.
Definition: macLib.h:42
APIs for the epicsMutex mutual exclusion semaphore.
int ac
Definition: iocsh.h:45
LIBCOM_API void epicsStdCall epicsThreadOnce(epicsThreadOnceId *id, EPICSTHREADFUNC, void *arg)
LIBCOM_API void epicsStdCall epicsThreadPrivateSet(epicsThreadPrivateId, void *)
Definition: osdThread.c:961
char * line
Definition: reader.c:25
void epicsStdCall epicsSetThreadStdout(FILE *fp)
Definition: epicsStdio.c:96
LIBCOM_API int epicsParseDouble(const char *str, double *to, char **units)
Definition: epicsStdlib.c:149
int iocshSetError(int err)
Signal error from an IOC shell function.
Definition: iocsh.cpp:536
const char * name
Definition: iocsh.h:62
char *epicsStdCall macDefExpand(const char *str, MAC_HANDLE *macros)
Expands macros and environment variables in a string.
Definition: macEnv.c:26
int mustRestore
Definition: iocsh.cpp:72
int epicsStdCall iocshLoad(const char *pathname, const char *macros)
Definition: iocsh.cpp:1023
LIBCOM_API void * callocMustSucceed(size_t count, size_t size, const char *msg)
A calloc() that never returns NULL.
Definition: cantProceed.c:22
int errlogPrintf(const char *pFormat,...)
Definition: errlog.c:105
LIBCOM_API void *epicsStdCall registryFind(void *registryID, const char *name)
Definition: registry.c:67
int epicsStdCall iocshCmd(const char *cmd)
Definition: iocsh.cpp:1017
int ival
Definition: iocsh.h:40
FILE *epicsStdCall epicsGetThreadStdin(void)
Definition: epicsStdio.c:65
int errlogMessage(const char *message)
Definition: errlog.c:180
void epicsStdCall iocshRegisterVariable(const iocshVarDef *piocshVarDef)
Definition: iocsh.cpp:167
long epicsStdCall macDeleteHandle(MAC_HANDLE *handle)
Marks a handle invalid, and frees all storage associated with it.
Definition: macCore.c:360
long epicsStdCall macPopScope(MAC_HANDLE *handle)
Retrieve the last pushed scope (like stack operations)
Definition: macCore.c:427
void epicsStdCall epicsReadlineEnd(void *context)
Destroy a command-line context.
iocshScope()
Definition: iocsh.cpp:527
const iocshCmdDef *epicsStdCall iocshFindCommand(const char *name)
Definition: iocsh.cpp:154
#define stdin
Definition: epicsStdio.h:28
int epicsStdCall epicsThreadIsOkToBlock(void)
long epicsStdCall macCreateHandle(MAC_HANDLE **pHandle, const char *pairs[])
Creates a new macro substitution context.
Definition: macCore.c:100
LIBCOM_API void epicsStdCall epicsThreadSleep(double seconds)
Block the calling thread for at least the specified time.
Definition: osdThread.c:790
Definition: dbBase.h:170
Text macro substitution routines.
void * vval
Definition: iocsh.h:43
Routines for code that can&#39;t continue or return after an error.
void epicsStdCall iocshFree(void)
Definition: iocsh.cpp:231
long epicsStdCall macPutValue(MAC_HANDLE *handle, const char *name, const char *value)
Sets the value of a specific macro.
Definition: macCore.c:233
void epicsStdCall epicsSetThreadStdin(FILE *fp)
Definition: epicsStdio.c:90
const char * usage
Definition: iocsh.h:65
OnError
Definition: iocsh.cpp:514
void * pval
Definition: iocsh.h:53
OnError onerr
Definition: iocsh.cpp:523
C++ and C descriptions for a thread.
#define epicsMutexMustLock(ID)
Claim a semaphore (see epicsMutexLock()).
Definition: epicsMutex.h:214
LIBCOM_API long epicsStdCall macInstallMacros(MAC_HANDLE *handle, char *pairs[])
Install set of {name, value} pairs as definitions.
Definition: macUtil.c:253
#define NREDIRECTS
Definition: iocsh.cpp:66
double timeout
Definition: iocsh.cpp:524
LIBCOM_API epicsThreadPrivateId epicsStdCall epicsThreadPrivateCreate(void)
Definition: osdThread.c:934
#define USAGE()
FILE *epicsStdCall epicsGetThreadStdout(void)
Definition: epicsStdio.c:71
iocshVarDef const * pVarDef
Definition: iocsh.cpp:53
#define false
Definition: flexdef.h:85
int nargs
Definition: iocsh.h:63
iocshArgType type
Definition: iocsh.h:58
iocshScope * scope
Definition: iocsh.cpp:533
FILE * oldFp
Definition: iocsh.cpp:71
long epicsStdCall macPushScope(MAC_HANDLE *handle)
Marks the start of a new scoping level.
Definition: macCore.c:392
Definition: iocsh.h:56
bool interactive
Definition: iocsh.cpp:526
void(* iocshCallFunc)(const iocshArgBuf *argBuf)
Definition: iocsh.h:69