static int nsh_parse_command()

in nshlib/nsh_parse.c [2442:2855]


static int nsh_parse_command(FAR struct nsh_vtbl_s *vtbl, FAR char *cmdline)
{
  struct nsh_param_s param =
    {
      .fd_in      = -1,
      .fd_out     = -1,
      .oflags_in  = 0,
      .oflags_out = 0,
      .file_in    = NULL,
      .file_out   = NULL
    };

#ifdef CONFIG_NSH_PIPELINE
  int pipefd[2] =
    {
      -1, -1
    };
#endif

  NSH_MEMLIST_TYPE memlist;
  NSH_ALIASLIST_TYPE alist;
  FAR char *argv[MAX_ARGV_ENTRIES];
  FAR char *saveptr;
  FAR char *cmd;
  int       argc;
  int       ret;
  bool      redirect_out_save = false;
  bool      redirect_in_save = false;
#ifdef CONFIG_NSH_PIPELINE
  bool      bg_save = false;
#endif

#ifdef CONFIG_SCHED_INSTRUMENTATION_DUMP
  char      tracebuf[LINE_MAX + 1];

  strlcpy(tracebuf, cmdline, sizeof(tracebuf));
  sched_note_beginex(NOTE_TAG_APP, tracebuf);
#endif

  /* Initialize parser state */

  memset(argv, 0, MAX_ARGV_ENTRIES*sizeof(FAR char *));
  NSH_MEMLIST_INIT(memlist);
  NSH_ALIASLIST_INIT(alist);

#ifndef CONFIG_NSH_DISABLEBG
  vtbl->np.np_bg       = false;
#endif

  vtbl->np.np_redir_out = false;
  vtbl->np.np_redir_in = false;

  /* Parse out the command at the beginning of the line */

  saveptr = cmdline;
  cmd = nsh_argument(vtbl, &saveptr, &memlist, &alist, NULL);

#ifndef CONFIG_NSH_DISABLESCRIPT
#ifndef CONFIG_NSH_DISABLE_LOOPS
  /* Handle while-do-done and until-do-done loops */

  if (nsh_loop(vtbl, &cmd, &saveptr, &memlist, &alist) != 0)
    {
      ret = nsh_saveresult(vtbl, true);
      goto dynlist_free;
    }
#endif

#ifndef CONFIG_NSH_DISABLE_ITEF
  /* Handle if-then-else-fi */

  if (nsh_itef(vtbl, &cmd, &saveptr, &memlist, &alist) != 0)
    {
      ret = nsh_saveresult(vtbl, true);
      goto dynlist_free;
    }

#endif
#endif

  /* Handle nice */

#ifndef CONFIG_NSH_DISABLEBG
  if (nsh_nice(vtbl, &cmd, &saveptr, &memlist, &alist) != 0)
    {
      ret = nsh_saveresult(vtbl, true);
      goto dynlist_free;
    }
#endif

  /* Check if any command was provided -OR- if command processing is
   * currently disabled.
   */

#ifndef CONFIG_NSH_DISABLESCRIPT
  if (!cmd || !nsh_cmdenabled(vtbl))
#else
  if (!cmd)
#endif
    {
      /* An empty line is not an error and an unprocessed command cannot
       * generate an error, but neither should it change the last command
       * status.
       */

      ret = OK;
      goto dynlist_free;
    }

  /* Parse all of the arguments following the command name.  The form
   * of argv is:
   *
   *   argv[0]:      The command name.
   *   argv[1]:      The beginning of argument (up to
   *                 CONFIG_NSH_MAXARGUMENTS)
   *   argv[argc]:   NULL terminating pointer
   *
   * Maximum size is CONFIG_NSH_MAXARGUMENTS+5
   */

  argv[0] = cmd;
  for (argc = 1; argc < MAX_ARGV_ENTRIES - 1; )
    {
      int isenvvar = 0; /* flag for if an environment variable gets expanded */

      argv[argc] = nsh_argument(vtbl, &saveptr, &memlist, NULL, &isenvvar);

      if (!argv[argc])
        {
          break;
        }

      if (isenvvar != 0)
        {
          while (argc < MAX_ARGV_ENTRIES - 1)
            {
              FAR char *pbegin = argv[argc];

              /* Find the end of the current token */

              for (; *pbegin && !strchr(g_token_separator, *pbegin);
                   pbegin++)
                {
                }

              /* If end of string, we've processed the last token and we're
               * done.
               */

              if ('\0' == *pbegin)
                {
                  break;
                }

              /* Terminate the token to complete the argv variable */

              *pbegin = '\0';

              /* We've inserted an extra parameter, so bump the count */

              argc++;

              /* Move to the next character in the string of tokens */

              pbegin++;

              /* Throw away any extra separator chars between tokens */

              for (; *pbegin && strchr(g_token_separator, *pbegin) != NULL;
                   pbegin++)
                {
                }

              /* Prepare to loop again on the next argument token */

              argv[argc] = pbegin;
            }
        }

      if (!strncmp(argv[argc], g_redirect_out2, g_redirect_out2_len))
        {
          FAR char *arg;
          if (argv[argc][g_redirect_out2_len])
            {
              arg = &argv[argc][g_redirect_out2_len];
            }
          else
            {
              arg = nsh_argument(vtbl, &saveptr, &memlist, NULL, &isenvvar);
            }

          if (!arg)
            {
              nsh_error(vtbl, g_fmtarginvalid, cmd);
              ret = ERROR;
              goto dynlist_free;
            }

          redirect_out_save     = vtbl->np.np_redir_out;
          vtbl->np.np_redir_out = true;
          param.oflags_out      = O_WRONLY | O_CREAT | O_APPEND;
          param.file_out        = nsh_getfullpath(vtbl, arg);
        }
      else if (!strncmp(argv[argc], g_redirect_out1, g_redirect_out1_len))
        {
          FAR char *arg;
          if (argv[argc][g_redirect_out1_len])
            {
              arg = &argv[argc][g_redirect_out1_len];
            }
          else
            {
              arg = nsh_argument(vtbl, &saveptr, &memlist, NULL, &isenvvar);
            }

          if (!arg)
            {
              nsh_error(vtbl, g_fmtarginvalid, cmd);
              ret = ERROR;
              goto dynlist_free;
            }

          redirect_out_save     = vtbl->np.np_redir_out;
          vtbl->np.np_redir_out = true;
          param.oflags_out      = O_WRONLY | O_CREAT | O_TRUNC;
          param.file_out        = nsh_getfullpath(vtbl, arg);
        }
      else if (!strncmp(argv[argc], g_redirect_in1, g_redirect_in1_len))
        {
          FAR char *arg;
          if (argv[argc][g_redirect_in1_len])
            {
              arg = &argv[argc][g_redirect_in1_len];
            }
          else
            {
              arg = nsh_argument(vtbl, &saveptr, &memlist, NULL, &isenvvar);
            }

          if (!arg)
            {
              nsh_error(vtbl, g_fmtarginvalid, cmd);
              ret = ERROR;
              goto dynlist_free;
            }

          redirect_in_save      = vtbl->np.np_redir_in;
          vtbl->np.np_redir_in  = true;
          param.oflags_in       = O_RDONLY;
          param.file_in         = nsh_getfullpath(vtbl, arg);
        }
#ifdef CONFIG_NSH_PIPELINE
      else if (!strncmp(argv[argc], g_pipeline1, g_pipeline1_len))
        {
          FAR char *arg;
          FAR char *sh_argv[4];
          FAR char *sh_arg2;

          if (argv[argc][g_pipeline1_len])
            {
              arg = &argv[argc][g_pipeline1_len];
            }
          else
            {
              arg = nsh_argument(vtbl, &saveptr, &memlist, NULL, &isenvvar);
            }

          if (!arg)
            {
              nsh_error(vtbl, g_fmtarginvalid, cmd);
              ret = ERROR;
              goto dynlist_free;
            }

          sh_arg2 = lib_get_tempbuffer(LINE_MAX);
          if (sh_arg2 == NULL)
            {
              nsh_error(vtbl, g_fmtcmdoutofmemory, cmd);
              ret = -errno;
              goto dynlist_free;
            }

          sh_arg2[0] = '\0';

          for (ret = 0; ret < argc; ret++)
            {
              strlcat(sh_arg2, argv[ret], LINE_MAX);

              if (ret < argc - 1)
                {
                  strcat(sh_arg2, " ");
                }
            }

          sh_argv[0] = "sh";
          sh_argv[1] = "-c";
          sh_argv[2] = sh_arg2;
          sh_argv[3] = NULL;

          ret = pipe2(pipefd, 0);
          if (ret < 0)
            {
              lib_put_tempbuffer(sh_arg2);
              ret = -errno;
              goto dynlist_free;
            }

          redirect_out_save = vtbl->np.np_redir_out;
          vtbl->np.np_redir_out = true;
          param.fd_out = pipefd[1];

          bg_save = vtbl->np.np_bg;
          vtbl->np.np_bg = true;

          ret = nsh_execute(vtbl, 4, sh_argv, &param);
          lib_put_tempbuffer(sh_arg2);

          vtbl->np.np_bg = bg_save;

          if (param.fd_in != -1)
            {
              close(param.fd_in);
              vtbl->np.np_redir_in = redirect_in_save;
            }

          close(param.fd_out);
          param.fd_out = -1;
          vtbl->np.np_redir_out = redirect_out_save;

          redirect_in_save = vtbl->np.np_redir_in;
          vtbl->np.np_redir_in = true;
          param.fd_in = pipefd[0];

          argv[0] = arg;
          argc = 1;

          if (ret == -1)
            {
              goto dynlist_free;
            }
        }
#endif
      else
        {
          argc++;
        }
    }

  /* Check if the command should run in background */

#ifndef CONFIG_NSH_DISABLEBG
  if (argc > 1 && strcmp(argv[argc - 1], "&") == 0)
    {
      vtbl->np.np_bg = true;
      argc--;
    }
#endif

  /* Last argument vector must be empty */

  argv[argc] = NULL;

  /* Check if the maximum number of arguments was exceeded */

  if (argc > CONFIG_NSH_MAXARGUMENTS)
    {
      nsh_error(vtbl, g_fmttoomanyargs, cmd);
    }

  /* Then execute the command */

  ret = nsh_execute(vtbl, argc, argv, &param);

dynlist_free:

  /* Free any allocated resources */

  /* Free the redirected output file path */

  if (param.file_out)
    {
      nsh_freefullpath((char *)param.file_out);
      vtbl->np.np_redir_out = redirect_out_save;
    }
#ifdef CONFIG_NSH_PIPELINE
  else if (param.fd_out != -1)
    {
      close(param.fd_out);
      vtbl->np.np_redir_out = redirect_out_save;
    }
#endif

  /* Free the redirected input file path */

  if (param.file_in)
    {
      nsh_freefullpath((char *)param.file_in);
      vtbl->np.np_redir_in = redirect_in_save;
    }
#ifdef CONFIG_NSH_PIPELINE
  else if (param.fd_in != -1)
    {
      close(param.fd_in);
      vtbl->np.np_redir_in = redirect_in_save;
    }
#endif

  NSH_ALIASLIST_FREE(vtbl, &alist);
  NSH_MEMLIST_FREE(&memlist);
#ifdef CONFIG_SCHED_INSTRUMENTATION_DUMP
  sched_note_endex(NOTE_TAG_APP, tracebuf);
#endif
  return ret;
}