/****************************************************************************
 * apps/nshlib/nsh_script.c
 *
 * Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.  The
 * ASF licenses this file to you under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance with the
 * License.  You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
 * License for the specific language governing permissions and limitations
 * under the License.
 *
 ****************************************************************************/

/****************************************************************************
 * Included Files
 ****************************************************************************/

#include <nuttx/config.h>

#include "nsh.h"
#include "nsh_console.h"

#if defined(CONFIG_FILE_STREAM) && !defined(CONFIG_NSH_DISABLESCRIPT)

/****************************************************************************
 * Public Functions
 ****************************************************************************/

/****************************************************************************
 * Name: nsh_script
 *
 * Description:
 *   Execute the NSH script at path.
 *
 ****************************************************************************/

int nsh_script(FAR struct nsh_vtbl_s *vtbl, FAR const char *cmd,
               FAR const char *path)
{
  FAR char *fullpath;
  FAR FILE *savestream;
  FAR char *buffer;
  FAR char *pret;
  int ret = ERROR;

  /* The path to the script may relative to the current working directory */

  fullpath = nsh_getfullpath(vtbl, path);
  if (!fullpath)
    {
      return ERROR;
    }

  /* Get a reference to the common input buffer */

  buffer = nsh_linebuffer(vtbl);
  if (buffer)
    {
      /* Save the parent stream in case of nested script processing */

      savestream = vtbl->np.np_stream;

      /* Open the file containing the script */

      vtbl->np.np_stream = fopen(fullpath, "r");
      if (!vtbl->np.np_stream)
        {
          nsh_error(vtbl, g_fmtcmdfailed, cmd, "fopen", NSH_ERRNO);

          /* Free the allocated path */

          nsh_freefullpath(fullpath);

          /* Restore the parent script stream */

          vtbl->np.np_stream = savestream;
          return ERROR;
        }

      /* Loop, processing each command line in the script file (or
       * until an error occurs)
       */

      do
        {
          /* Flush any output generated by the previous line */

          fflush(stdout);

#ifndef CONFIG_NSH_DISABLE_LOOPS
          /* Get the current file position.  This is used to control
           * looping.  If a loop begins in the next line, then this file
           * offset will be needed to locate the top of the loop in the
           * script file.  Note that ftell will return -1 on failure.
           */

          vtbl->np.np_foffs = ftell(vtbl->np.np_stream);
          vtbl->np.np_loffs = 0;

          if (vtbl->np.np_foffs < 0)
            {
              nsh_error(vtbl, g_fmtcmdfailed, "loop", "ftell", NSH_ERRNO);
            }
#endif

          /* Now read the next line from the script file */

          pret = fgets(buffer, CONFIG_NSH_LINELEN, vtbl->np.np_stream);
          if (pret)
            {
              /* Parse process the command.  NOTE:  this is recursive...
               * we got to cmd_source via a call to nsh_parse.  So some
               * considerable amount of stack may be used.
               */

              if ((vtbl->np.np_flags & NSH_PFLAG_SILENT) == 0)
                {
                  nsh_output(vtbl, "%s", buffer);
                }

              ret = nsh_parse(vtbl, buffer);
            }
        }
      while (pret && (ret == OK || (vtbl->np.np_flags & NSH_PFLAG_IGNORE)));

      /* Close the script file */

      fclose(vtbl->np.np_stream);

      /* Restore the parent script stream */

      vtbl->np.np_stream = savestream;
    }

  /* Free the allocated path */

  nsh_freefullpath(fullpath);
  return ret;
}

/****************************************************************************
 * Name: nsh_initscript
 *
 * Description:
 *   Attempt to execute the configured initialization script.  This script
 *   should be executed once when NSH starts.  nsh_initscript is idempotent
 *   and may, however, be called multiple times (the script will be executed
 *   once.
 *
 ****************************************************************************/

#ifdef CONFIG_NSH_ROMFSETC
int nsh_initscript(FAR struct nsh_vtbl_s *vtbl)
{
  static bool initialized;
  bool already;
  int ret = OK;

  /* Atomic test and set of the initialized flag */

  sched_lock();
  already     = initialized;
  initialized = true;
  sched_unlock();

  /* If we have not already executed the init script, then do so now */

  if (!already)
    {
      ret = nsh_script(vtbl, "init", NSH_INITPATH);

#ifndef CONFIG_NSH_DISABLESCRIPT
      /* Reset the option flags */

      vtbl->np.np_flags = NSH_NP_SET_OPTIONS_INIT;
#endif
    }

  return ret;
}

/****************************************************************************
 * Name: nsh_loginscript
 *
 * Description:
 *   Attempt to execute the configured login script.  This script
 *   should be executed when each NSH session starts.
 *
 ****************************************************************************/

#ifdef CONFIG_NSH_ROMFSRC
int nsh_loginscript(FAR struct nsh_vtbl_s *vtbl)
{
  return nsh_script(vtbl, "login", NSH_RCPATH);
}
#endif
#endif /* CONFIG_NSH_ROMFSETC */

#endif /* CONFIG_FILE_STREAM && !CONFIG_NSH_DISABLESCRIPT */
