/* Copyright 1996 Acorn Computers Ltd
 *
 * Licensed 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.
 */
/*main.c - entry points for BootCommands module*/

/*History

   12th Sep 1994 J R C Started

*/

/*From CLib*/
#include <kernel.h>
#include <stdarg.h>
#include <stdio.h>
#include <stddef.h>
#include <stdlib.h>
#include <string.h>
#include <stdbool.h>

/*From OSLib*/
#include "econet.h"
#include "macros.h"
#include "messagetrans.h"
#include "netfs.h"
#include "os.h"
#include "osargs.h"
#include "osbyte.h"
#include "osfile.h"
#include "osfscontrol.h"
#include "osfind.h"
#include "osgbpb.h"
#include "osmodule.h"
#include "resourcefs.h"
#include "territory.h"

/*From Support*/
#include "riscos.h"
#include "jc_trace.h"

/*Local*/
#include "files.h"
#include "main.h"
#include "run.h"

static void *Workspace;

static os_error *(*Commands [main_COMMAND_COUNT]) (char *);

static messagetrans_control_block Control_Block;

/* A useful global buffer */
static char buffer[os_CLI_LIMIT + 1];

static os_error *Register (resourcefs_file *file, char *name,
      int data_size, byte *data, bits load_addr, bits exec_addr,
      int *size_out)

{  os_error *error = NULL;
   int fh_size, fd_size, name_len;
   resourcefs_file_header *fh;
   resourcefs_file_data *fd;

   tracef ("Register: file \"%s\", data \"%.*s\"\n" _
         name _ data_size _ data);

   name_len = strlen (name);
   fh_size  = resourcefs_SIZEOF_FILE_HEADER (ALIGN (name_len + 1));
   fd_size  = resourcefs_SIZEOF_FILE_DATA (ALIGN (data_size));

   if (file != NULL)
   {  fh = &file->header;
      fh->data_size = fh_size + fd_size;
      fh->load_addr = load_addr;
      fh->exec_addr = exec_addr;
      fh->size      = data_size;
      fh->attr      = fileswitch_ATTR_OWNER_READ | fileswitch_ATTR_OWNER_WRITE;
      strcpy (fh->name, name);

      fd = (resourcefs_file_data *) &fh->name [ALIGN (name_len + 1)];
      fd->size = data_size + 4;
      memcpy (fd->data, data, data_size);
         /*Supposed to 0-fill this.*/
   }

   if (size_out != NULL) *size_out = fh_size + fd_size;
   tracef ("-> size %d\n" _ fh_size + fd_size);

/*finish:*/
   return error;
}
/*------------------------------------------------------------------------*/
static os_error *Add_App (char *tail)

{  struct {char *applications; char argb [os_CLI_LIMIT + 1];} argl;
   os_error *error = NULL;
#if 0
   char *leaf_name, name [os_CLI_LIMIT + 1], boot_name [os_CLI_LIMIT + 1],
      help_name [os_CLI_LIMIT + 1], run_name [os_CLI_LIMIT + 1],
      boot_data [os_CLI_LIMIT + 1], help_data [os_CLI_LIMIT + 1],
      run_data [os_CLI_LIMIT + 1], canon [os_CLI_LIMIT + 1],
      dir_name [os_FILE_NAME_LIMIT + 1], entry [os_FILE_NAME_LIMIT + 1],
      application [os_FILE_NAME_LIMIT + 1];
#else
   char *leaf_name, *name, *boot_name, *help_name, *run_name, *boot_data,
        *help_data, *run_data, *canon, *dir_name, *entry, *application;
#endif
   int boot_type, help_type, sprites_type, resource_size, size, context, found;
   resourcefs_file_list *file_list;
   resourcefs_file *file;
   os_date_and_time *now;
   fileswitch_info_words info_words;

   tracef ("Add_App\n");
   /* Horrible, I know, but better than getting it off the SVC stack like it used to */
   name = (char *)malloc(11 * 257);
   if (!name)
      goto finish;
   boot_name = name + 257;
   help_name = boot_name + 257;
   run_name = help_name + 257;
   boot_data = run_name + 257;
   help_data = boot_data + 257;
   run_data = help_data + 257;
   canon = run_data + 257;
   dir_name = canon + 257;
   entry = dir_name + 257;
   application = entry + 257;

   if ((error = xos_read_args ("applications/a", tail, (char *) &argl,
         sizeof argl, NULL)) != NULL)
      goto finish;

   if ((leaf_name = strrchr (argl.applications, '.')) != NULL)
   {  sprintf (dir_name, "%.*s", leaf_name - argl.applications, argl.applications);
      leaf_name++;
   }
   else
   {  strcpy (dir_name, "@");
      leaf_name = argl.applications;
   }

   context = 0;
   while (context != osgbpb_NO_MORE)
   {  if ((error = xosgbpb_dir_entries (dir_name, (osgbpb_string_list *) entry,
            1, context, 257, leaf_name, &found, &context)) != NULL)
         goto finish;

      if (found == 1)
      {  tracef ("new entry is %s\n" _ entry);

         sprintf (application, "%s.%s", dir_name, entry);

         /*We must canonicalise the argument, or else the path names in
            ResourceFS won't be very much use.*/
         if ((error = xosfscontrol_canonicalise_path (application,
               canon, NULL, NULL, 257, NULL)) != NULL)
            goto finish;

         resource_size = 0;

         /*We make a !Boot file if there is already a !Boot file or a
            !Sprites file.*/
         sprintf (name, "%s.!Boot", canon);
         tracef ("OSFile_ReadStampedNoPath %s\n" _ name);
         if ((error = xosfile_read_stamped_no_path (name, &boot_type,
               NULL, NULL, NULL, NULL, NULL)) != NULL)
            goto finish;
         tracef ("boot_type %d\n" _ boot_type);

         sprintf (name, "%s.!Sprites", canon);
         tracef ("OSFile_ReadStampedNoPath %s\n" _ name);
         if ((error = xosfile_read_stamped_no_path (name,
               &sprites_type, NULL, NULL, NULL, NULL, NULL)) != NULL)
            goto finish;
         tracef ("sprites_type %d\n" _ sprites_type);

         if (boot_type != osfile_NOT_FOUND)
            sprintf (boot_data, "/%s.!Boot %%*0\n", canon);
         else if (sprites_type != osfile_NOT_FOUND)
            sprintf (boot_data, "IconSprites %s.!Sprites\n", canon);

         if (boot_type != osfile_NOT_FOUND || sprites_type !=
               osfile_NOT_FOUND)
         {  sprintf (boot_name, "Apps.%s.%s", entry, "!Boot");
            if ((error = Register (NULL, boot_name, strlen (boot_data),
                  (byte *) boot_data, SKIP, SKIP, &size)) != NULL)
               goto finish;
            resource_size += size;
            tracef ("!Boot needs %d bytes\n" _ size);
         }

         /*We make a !Help file if there is one already.*/
         sprintf (name, "%s.!Help", canon);
         tracef ("OSFile_ReadStampedNoPath %s\n" _ name);
         if ((error = xosfile_read_stamped_no_path (name, &help_type,
               NULL, NULL, NULL, NULL, NULL)) != NULL)
            goto finish;
         tracef ("type %d\n" _ help_type);

         if (help_type != osfile_NOT_FOUND)
         {  sprintf (help_data, "Filer_Run %s.!Help\n", canon);
            tracef ("help_data %s, len %d\n" _ help_data _
                 strlen (help_data));

            sprintf (help_name, "Apps.%s.%s", entry, "!Help");
            tracef ("help_name %s\n" _ help_name);
            if ((error = Register (NULL, help_name, strlen (help_data),
                  (byte *) help_data, SKIP, SKIP, &size)) != NULL)
               goto finish;
            tracef ("size %d\n" _ size);
            resource_size += size;
            tracef ("!Help needs %d bytes\n" _ size);
         }

         /*We always make a !Run file.*/
         sprintf (run_data, "/%s %%*0\n", canon);
         sprintf (run_name, "Apps.%s.%s", entry, "!Run");
         tracef ("run_name %s\n" _ run_name);
         if ((error = Register (NULL, run_name, strlen (run_data),
               (byte *) run_data, SKIP, SKIP, &size)) != NULL)
            goto finish;
         resource_size += size;
         tracef ("!Run needs %d bytes\n" _ size);

         resource_size += sizeof 0; /*terminator*/

         if ((error = xosmodule_alloc (resource_size, (void **) &file_list))
               != NULL)
            goto finish;
         tracef ("allocated %d bytes at 0x%X\n" _ resource_size _ file_list);

         /*Build common load and exec addresses for everything (they're all
               Obey files).*/
         if ((error = xos_get_env (NULL, NULL, &now)) != NULL)
            goto finish;
         info_words AS addrs.load_addr = 0xFFF00000u | osfile_TYPE_OBEY <<
               osfile_FILE_TYPE_SHIFT;
         memcpy (info_words AS date_and_time, *now,
               sizeof info_words AS date_and_time);

         file = &file_list->file [0]; /*just a cast*/

         /*Now fill in the real buffer.*/
         if (boot_type != osfile_NOT_FOUND || sprites_type !=
               osfile_NOT_FOUND)
         {  if ((error = Register (file, boot_name, strlen (boot_data),
                  (byte *) boot_data, info_words AS addrs.load_addr,
                  info_words AS addrs.exec_addr, &size)) != NULL)
               goto finish;
            *(char **) &file += size;
         }

         if (help_type != osfile_NOT_FOUND)
         {  if ((error = Register (file, help_name, strlen (help_data),
                  (byte *) help_data, info_words AS addrs.load_addr,
                  info_words AS addrs.exec_addr, &size)) != NULL)
               goto finish;
            *(char **) &file += size;
         }

         if ((error = Register (file, run_name, strlen (run_data),
               (byte *) run_data, info_words AS addrs.load_addr,
               info_words AS addrs.exec_addr, &size)) != NULL)
            goto finish;
         *(char **) &file += size;

         /*Terminator.*/
         file->header.data_size = 0;

         /*And now register the whole lot.*/
         if ((error = xresourcefs_register_files (file_list)) != NULL)
            goto finish;
         tracef ("done\n");
      }
   }

finish:
   free(name);
   return error;
}
/*------------------------------------------------------------------------*/
static os_error *App_Size (char *tail)

{  os_error *error = NULL;
   char *cc, *end;
   int move, size, size_limit;
   unsigned int value;
   byte *ram_limit;

   tracef ("App_Size\n");
   cc = tail;
   while (*cc == ' ')
      cc++;

   if ((error = xos_read_unsigned (NONE, cc, SKIP, &end, (void *) &value)) != NULL)
      goto finish;

   switch (*end)
   {  case 'm': case 'M':
         value *= 1024;
      /*fall through*/

      case 'k': case 'K':
         value *= 1024;
         end++;
      break;
   }

   while (*end == ' ')
      end++;

   if (!('\0' <= *end && *end < ' '))
   {  tracef ("invalid terminator %d\n" _ *end);
      error = main_error_lookup (error_SYNTAX, "AppSizeSyntax");
      goto finish;
   }

   if ((error = xos_get_env (NULL, &ram_limit, NULL)) != NULL)
      goto finish;
   tracef ("top of application space 0x%X\n" _ ram_limit);

   /*We want to move the R M A by the difference between the application size
      and the value specified.*/
   move = (int) (ram_limit - 0x8000) - value;
   tracef ("wanted move is %d\n" _ move);

   /*How big is the R M A now?*/
   if ((error = xos_read_dynamic_area (os_DYNAMIC_AREA_RMA,
         NULL, &size, &size_limit)) != NULL)
      goto finish;
   tracef ("R M A expansion space %d\n" _ size_limit - size);

   /*Can't move it to more than its size limit.**/
   (void) MINAB (move, size_limit - size);
   tracef ("allowed move is %d\n" _ move);

   /*We don't care if this succeeds or fails - the attempt is the thing.*/
   (void) xos_change_dynamic_area (os_DYNAMIC_AREA_RMA, move, NULL);
   tracef ("done\n");

#if TRACE
   if ((error = xos_read_dynamic_area (os_DYNAMIC_AREA_RMA,
         NULL, &size, NULL)) != NULL)
      goto finish;
   tracef ("RMA size at end 0x%X\n" _ size);
#endif

finish:
   return error;
}
/*------------------------------------------------------------------------*/
static os_error *App_Slot (char *tail)

{  os_error *error = NULL;
   char *cc, *end;
   int move, size, size_limit, r;
   unsigned int value;
   byte *ram_limit;

   tracef ("App_Slot\n");
   cc = tail;
   while (*cc == ' ')
      cc++;

   if ((error = xos_read_unsigned (NONE, cc, SKIP, &end, (void *) &value)) != NULL)
      goto finish;

   switch (*end)
   {  case 'm': case 'M':
         value *= 1024;
      /*fall through*/

      case 'k': case 'K':
         value *= 1024;
         end++;
      break;
   }

   while (*end == ' ')
      end++;

   if (!('\0' <= *end && *end < ' '))
   {  tracef ("invalid terminator %d\n" _ *end);
      error = main_error_lookup (error_SYNTAX, "AppSlotSyntax");
      goto finish;
   }

   if ((error = xos_get_env (NULL, &ram_limit, NULL)) != NULL)
      goto finish;
   tracef ("top of application space 0x%X\n" _ ram_limit);

   /*We want to move by the difference between the application size
      and the value specified.*/
   move = (int) (ram_limit - 0x8000) - value;
   tracef ("wanted move is %d\n" _ move);

   /*What OS capabilities do we have*/
   if ((error = xos_byte (osbyte_IN_KEY, 0, 255, &r, NULL)) != NULL)
                goto finish;

   if (r >= 0xA5)
      {
      tracef ("look to the freepool\n");

      /* OS 3.50 and later juggle ram between the app slot and the free pool */
      if ((error = xos_change_dynamic_area (os_DYNAMIC_AREA_FREE_POOL, move, NULL)) != NULL)
         goto finish;
      }
   else
      {
      tracef ("look to the RMA\n");

      /* OS pre 3.50 so juggle ram between the app slot and the RMA */
      if ((error = xos_read_dynamic_area (os_DYNAMIC_AREA_RMA,
            NULL, &size, &size_limit)) != NULL)
         goto finish;
      tracef ("R M A expansion space %d\n" _ size_limit - size);

      /*Can't move it to more than its size limit.**/
      (void) MINAB (move, size_limit - size);
      tracef ("allowed move is %d\n" _ move);

      /*We don't care if this succeeds or fails - the attempt is the thing.*/
      (void) xos_change_dynamic_area (os_DYNAMIC_AREA_RMA, move, NULL);
      }

   tracef ("done\n");

#if TRACE
   if ((error = xos_read_dynamic_area (os_DYNAMIC_AREA_RMA,
         NULL, &size, NULL)) != NULL)
      goto finish;
   tracef ("RMA size at end 0x%X\n" _ size);
#endif

finish:
   return error;
}
/*------------------------------------------------------------------------*/
static os_error *Add_To_RMA (char *tail)

{  os_error *error = NULL;
   char *cc, *end;
   unsigned int value;

   tracef ("Add_To_RMA\n");
   cc = tail;
   while (*cc == ' ')
      cc++;

   if ((error = xos_read_unsigned (NONE, cc, SKIP, &end, (void *) &value)) != NULL)
      goto finish;

   switch (*end)
   {  case 'm': case 'M':
         value *= 1024;
      /*fall through*/

      case 'k': case 'K':
         value *= 1024;
         end++;
      break;
   }

   while (*end == ' ')
      end++;

   if (!('\0' <= *end && *end < ' '))
   {  tracef ("invalid terminator %d\n" _ *end);
      error = main_error_lookup (error_SYNTAX, "AddToRMASyntax");
      goto finish;
   }

   /*Pass back any problems verbatim*/
   if ((error = xos_change_dynamic_area (os_DYNAMIC_AREA_RMA, value, NULL)) != NULL)
      goto finish;

   tracef ("done\n");

#if TRACE
   if ((error = xos_read_dynamic_area (os_DYNAMIC_AREA_RMA,
         NULL, &size, NULL)) != NULL)
      goto finish;
   tracef ("RMA size at end 0x%X\n" _ size);
#endif

finish:
   return error;
}
/*------------------------------------------------------------------------*/
static os_error *Do (char *tail)

{  os_error *error = NULL;
   bits psr;

   tracef ("Do\n");

   if ((error = xos_gs_trans (tail, buffer, sizeof buffer, NULL,
         &psr)) != NULL)
      goto finish;

   if ((psr & _C) != NONE)
   {  error = main_error_lookup (os_GLOBAL_BUF_OFLO, "BufOFlo");
      goto finish;
   }

   if ((error = xos_cli (buffer)) != NULL)
      goto finish;

finish:
   return error;
}
/*------------------------------------------------------------------------*/
static os_error *If_There (char *tail)

{  os_error *error = NULL;
   char *cc;
   int i, obj_type;

   tracef ("If_There\n");

   /*Skip leading spaces.*/
   cc = tail;
   while (*cc == ' ')
      cc++;

   /*Copy non-spaces into |s| - this is the file name.*/
   i = 0;
   while (*cc > ' ')
      buffer[i++] = *cc++;
   buffer[i] = '\0';
   tracef ("file name \"%s\"\n" _ buffer);

   /*So, is it there?*/
   /*Fix MED-3984: if this fails, behave as if the file were absent. JRC 19th
      Dec 1994*/
   if (xosfile_read_stamped_no_path (buffer, &obj_type, NULL, NULL, NULL, NULL,
         NULL) != NULL)
      obj_type = osfile_NOT_FOUND;

   /*Wizard wheeze to avoid parsing the rest of the line.*/
   sprintf (buffer, "If %d%.*s",
         obj_type != osfile_NOT_FOUND, riscos_strlen (cc), cc);
   /*Fix bizarre fault - strip trailing spaces. J R C 23rd Feb 1995*/
   for (i = strlen (buffer) - 1; buffer [i] == ' '; i--)
      buffer [i] = '\0';
   tracef ("command \"%s\"\n" _ buffer);

   if ((error = xos_cli (buffer)) != NULL)
   {  /*Fix MED-3984: if this gives a syntax error, change it to our own
         message. JRC 19th Dec 1994*/
      if (error->errnum == error_SYNTAX)
         error = main_error_lookup (error_SYNTAX, "IfThereSyntax");
      goto finish;
   }

finish:
   return error;
}
/*--------------------------------------------------------------------*/
static os_error *Load_CMOS (char *tail)

{  struct {char *file; char argb [os_CLI_LIMIT + 1];} argl;
   bool done_open = FALSE;
   os_f f;
   int r, w, i, size, obj_type, bootversion = 370, fileversion, xsum;
   bits file_type;
   os_error *error = NULL;
   const char *osversion;

   tracef ("Load_CMOS\n");

   if ((error = xos_read_args ("file/a", tail, (char *) &argl,
         sizeof argl, NULL)) != NULL)
      goto finish;

   if ((error = xosfile_read_stamped_no_path (argl.file, &obj_type,
         NULL, NULL, &size, NULL, &file_type)) != NULL)
      goto finish;

   if (obj_type != osfile_IS_FILE)
   {  error = xosfile_make_error (argl.file, obj_type);
      goto finish;
   }

   if ((error = xosfind_openin (osfind_NO_PATH | osfind_ERROR_IF_ABSENT |
         osfind_ERROR_IF_DIR, argl.file, NULL, &f)) != NULL)
      goto finish;
   done_open = TRUE;

   if (size != 240 && size != 244)
      {
      error = main_error_lookup (0, "BadFile");
      goto finish; /* Not a configuration */
      }

   osversion = getenv("Boot$OSVersion");
   if (osversion != NULL)
      {
      if ((error = xos_read_unsigned (os_READ_CONTROL_TERMINATED | (os_read_unsigned_flags)10,
                                      osversion, NULL, SKIP,(bits *) &bootversion)) != NULL)
         goto finish; /* Read the os version number x100 */;
      }

   /* Old file formats will only be valid pre-Ursula */
   if (size == 240 && bootversion > 370)
     {
     error = main_error_lookup (0, "BadVer");
     goto finish;
     }

   /* Check the file's OS version number (if any) matches the machine */
   if (size == 244)
   {
     if ((error = xosgbpb_read_at (f, (byte *) &fileversion, sizeof (fileversion), 240, NULL)) != NULL)
        goto finish;
     if (fileversion != bootversion)
     {
       xosfind_close (f);
       error = main_error_lookup (0, "BadVer");
       goto finish;
     }
     if ((error = xosargs_set_ptr (f, 0)) != NULL)
        goto finish;
   }

   /* Ensure the file's checksum byte is correct */
   xsum = CMOSxseed;
   for (i = 0; i < 239; i++)
   {
   if ((error = xosgbpb_read (f, (byte *) &w, 1, NULL)) != NULL)
         /*(Note that the top 24 bits of |w| are garbage.)*/
      goto finish;
   xsum += (w & 0xFF);
   }
   if ((error = xosgbpb_read (f, (byte *) &w, 1, NULL)) != NULL)
         /*(Note that the top 24 bits of |w| are garbage.)*/
      goto finish;
   if ((xsum & 0xFF) != (w & 0xFF))
   {
     xosfind_close (f);
     error = main_error_lookup (0, "BadFile");
     goto finish;
   }
   if ((error = xosargs_set_ptr (f, 0)) != NULL)
      goto finish;

   /* Actually load the values */
   for (i = 0; i < size; i++)
   {
      if ((error = xosgbpb_read (f, (byte *) &w, 1, NULL)) != NULL)
            /*(Note that the top 24 bits of |w| are garbage.)*/
         goto finish;

      switch (i)
      {  case osbyte_CONFIGURE_DST:
         {  /*Do not load bit 7.*/
            if ((error = xos_byte (osbyte_READ_CMOS, i, SKIP, NULL, &r)) != NULL)
               goto finish;

            if ((error = xos_byte (osbyte_WRITE_CMOS, i,
                  w & ~osbyte_CONFIGURE_DST_MASK | r & osbyte_CONFIGURE_DST_MASK,
                  NULL, NULL)) != NULL)
               goto finish;
         }
         break;

         case osbyte_CONFIGURE_YEAR0:
         case osbyte_CONFIGURE_YEAR1:
            /*Do nothing - these are never loaded.*/
         break;

         default:
            if ((error = xos_byte (osbyte_WRITE_CMOS, i, w, NULL, NULL)) !=
                  NULL)
               goto finish;
         break;
      }
   }

finish:
   if (done_open)
   {  os_error *error1 = xosfind_close (f);
      if (error == NULL) error = error1;
   }

   /*Never return an error from here - it would just mess up the boot
      sequence. J R C 23rd Aug 1995*/
   if (error != NULL) fprintf (stderr, "%s\n", error->errmess);

   return NULL;
}
/*--------------------------------------------------------------------*/
static os_error *Save_CMOS (char *tail)

{  struct {char *file; char argb [os_CLI_LIMIT + 1];} argl;
   bool done_open = FALSE;
   os_f f;
   int r, i;
   os_error *error = NULL;
   const char *osversion;

   tracef ("Save_CMOS\n");

   if ((error = xos_read_args ("file/a", tail, (char *) &argl,
         sizeof argl, NULL)) != NULL)
      goto finish;

   if ((error = xosfind_openout (osfind_NO_PATH | osfind_ERROR_IF_DIR,
         argl.file, NULL, &f)) != NULL)
      goto finish; /* Check the file can be written to */
   done_open = TRUE;

   for (i = 0; i <= osbyte_CONFIGURE_CHECKSUM; i++)
   {  if ((error = xos_byte (osbyte_READ_CMOS, i, SKIP, NULL, &r)) !=
            NULL)
         goto finish;
      if ((error = xosgbpb_write (f, (byte *) &r, 1, NULL)) != NULL)
         goto finish; /* Put the byte from CMOS */
   }

   osversion = getenv("Boot$OSVersion");
   if (osversion != NULL)
      {
      /* If we can't read the variable just save the 240 byte CMOS */
      if ((error = xos_read_unsigned (os_READ_CONTROL_TERMINATED | (os_read_unsigned_flags)10,
                                      osversion, NULL, SKIP,(bits *) &r)) != NULL)
         goto finish; /* Read the os version number x100 */;
      if ((error = xosgbpb_write (f, (byte *) &r, 4, NULL)) != NULL)
         goto finish; /* Append the word to the CMOS file */
      }

finish:
   if (done_open)
   {  os_error *error1 = xosfind_close (f);
      error = xosfile_set_type (argl.file, osfile_TYPE_CONFIG);
      if (error == NULL) error = error1;
   }

   tracef ("Save_CMOS DONE\n");
   if (error != NULL)
      tracef ("with error %s\n" _ error->errmess);
   return error;
}
/*------------------------------------------------------------------------*/
static os_error *Repeat (char *tail)

{  os_error *error = NULL;

   tracef ("Repeat\n");

   /*Repeat is done by running a file, since it is then running in USR mode
      and handlers work.*/
   sprintf (buffer, "Resources:$.Resources.BootCmds.Repeat %.*s",
         riscos_strlen (tail), tail);
   tracef ("/%s\n" _ buffer);
   if ((error = xosfscontrol_run (buffer)) != NULL)
      goto finish;

   #if 0
   /*Repeat is done by entering the module, since it is then running in USR mode
      and handlers work.*/
   if ((error = xosmodule_enter ("BootCommands", tail)) != NULL)
      goto finish;
   #endif

finish:
   tracef ("Repeat DONE\n");
   if (error != NULL)
      tracef ("with error %s\n" _ error->errmess);
   return error;
}
/*------------------------------------------------------------------------*/
static os_error *Safe_Logon (char *tail)

{  struct safelogon_args {char *fs, *user, *password; char argb [os_CLI_LIMIT + 1];} *argl;
   int station, net, context, collate;
   fileswitch_fs_no temp_fs;
   netfs_file_server_context file_server_context;
   char *end;
   bool logon_required = TRUE, found_fs, named_fs;
   os_error *error = NULL;

   tracef ("Safe_Logon\n");

   argl = malloc(sizeof(struct safelogon_args));
   if (argl == 0)
       goto finish;

   /*Fix bug: check that the temporary filing system is indeed NetFS. JRC
      16th Feb 1995*/
   if ((error = xosargs_read_temporary_fs (&temp_fs)) != NULL)
      goto finish;

   if (temp_fs == fileswitch_FS_NUMBER_NETFS)
   {  if ((error = xos_read_args ("fs/a,user/a,password", tail,
            (char *) argl, sizeof *argl, NULL)) != NULL)
         goto finish;

      tracef ("fs \"%s\"\n" _ argl->fs);
      tracef ("user \"%s\"\n" _ argl->user);
      tracef ("password \"%s\"\n" _
            argl->password != NULL? argl->password: "NULL");

      /*If the first character of the fs is not ':', we steer clear.*/
      if (argl->fs [0] == ':')
      {  argl->fs++;

         /*Station number or name?*/
         named_fs = xeconet_read_station_number (argl->fs, NULL, &station,
               &net) != NULL;
         if (named_fs)
            tracef ("fs is named \"%s\"\n" _ argl->fs);
         else
            tracef ("fs is numbered %d.%d\n" _ net _ station);

         context = 0;
         while (TRUE)
         {  if ((error = xnetfs_enumerate_fs_contexts (context,
                  &file_server_context, sizeof file_server_context, 1,
                  &context, NULL)) != NULL)
               goto finish;
            if (context == netfs_NO_MORE) break;

            /*The useful parts of this are space-terminated.*/
            if ((end = strchr (file_server_context.disc_name, ' ')) != NULL)
               *end = '\0';
            if ((end = strchr (file_server_context.user_name, ' ')) != NULL)
               *end = '\0';

            tracef ("found fs %d.%d = \"%s\", user \"%s\"\n" _
                  file_server_context.net_no _
                  file_server_context.station_no _
                  file_server_context.disc_name _
                  file_server_context.user_name);

            if (named_fs)
            {  if ((error = xterritory_collate (territory_CURRENT, argl->fs,
                     file_server_context.disc_name, territory_IGNORE_CASE,
                     &collate)) != NULL)
                  goto finish;

               found_fs = collate == 0;
            }
            else
               found_fs = file_server_context.station_no == station &&
                  file_server_context.net_no == net;

            if (found_fs)
            {  if ((error = xterritory_collate (territory_CURRENT, argl->user,
                     file_server_context.user_name, territory_IGNORE_CASE,
                     &collate)) != NULL)
                  goto finish;

               if (collate == 0)
               {  /*We have found a match, and are about to set
                     |logon_required| to FALSE. One more thing to check,
                     though: does the file server agree that we are logged
                     on? JRC 19th Dec 1994*/
                  netfs_read_user_info info;

                  sprintf (info AS request.user_name, "%s\r", argl->user);

                  tracef ("doing fs op ...\n");
                  if ((error = xnetfs_do_fs_op_to_given_fs
                        (netfs_FS_OP_READ_USER_INFO, (netfs_op *) &info,
                        strlen (info AS request.user_name), sizeof info,
                        file_server_context.station_no,
                        file_server_context.net_no, NULL, NULL)) != NULL)
                     goto finish;

                  tracef ("privilege byte 0x%X\n" _ info AS reply.privilege);
                  tracef ("station %d\n" _ info AS reply.station);
                  tracef ("net %d\n" _ info AS reply.net);

                  logon_required = FALSE;
                  break;
   }  }  }  }  }

finish:
   free(argl);
   /*Ignore errors up to this point. If there have been any, it's a safe bet
      that |logon_required| is TRUE.*/
   if (error != NULL)
      tracef ("LOGON because of error %s\n" _ error->errmess);
   error = NULL;

   /*If that user is not logged on to that file server, do it now.*/
   if (logon_required)
   {
      sprintf (buffer, "%%Logon %.*s", riscos_strlen (tail), tail);
         /*Not Net:%%Logon! JRC 17th Feb 1995*/
      error = xos_cli (buffer);
   }

   return error;
}
/*------------------------------------------------------------------------*/
static os_error *Shrink_RMA( char *tail )
{
   os_error *error = NULL;
   int size;

   tracef("Shrink_RMA\n");

   if ((error = xos_read_dynamic_area (os_DYNAMIC_AREA_RMA,
         NULL, &size, NULL)) != NULL)
      goto finish;

   /*Ignore any errors here*/
   (void) xos_change_dynamic_area (os_DYNAMIC_AREA_RMA, -size, NULL);

   tracef ("done\n");

finish:
   return error;

   NOT_USED( tail );
}
/*------------------------------------------------------------------------*/
static os_error *Free_Pool( char *tail )
{
   int next_size;
   os_error *err = xwimp_slot_size( -1, -1, NULL, &next_size, NULL );
   if ( err == NULL )
   {
      int app_size;
      err = xos_change_environment( 0x0E, 0, 0, 0, (void **)&app_size, NULL, NULL );
      if ( err == NULL ) err = xos_read_dynamic_area( 6, NULL, NULL, NULL );
      if ( err == NULL ) err = xos_change_dynamic_area( 6, app_size-next_size, NULL );
   }
   return err;

   NOT_USED( tail );
}
/*------------------------------------------------------------------------*/
static os_error *X (char *tail)
{  os_error    *error   = NULL;
   int          context = 0;
   os_var_type  vartype = os_VARTYPE_STRING;

   tracef ("X\n");

   error = xos_cli(tail);
   if (error != NULL)
   {
     if (getenv(main_X_ENVVAR) == NULL)
     {
       error = xos_set_var_val(main_X_ENVVAR,
                               (byte *)(error->errmess),
                               strlen(error->errmess),
                               context,
                               vartype,
                               &context,
                               &vartype);
     }
     else
     {
       error = NULL;
     }
   }

   return error;
}

/*------------------------------------------------------------------------*/
_kernel_oserror *main_initialise (char *tail, int podule_base,
      void *workspace)

{  os_error *error = NULL;
   bool done_open_file = FALSE;
   char *message_file_name;
   bool registered_repeat = FALSE;

#ifdef STANDALONE
   bool registered_messages = FALSE;
#endif

   NOT_USED (tail)
   NOT_USED (podule_base)

   Workspace = workspace;

   if ((error = trace_initialise ("BootCommands$Trace")) != NULL)
      goto finish;

   tracef ("main_initialise\n");

   if (getenv ("BootCommands$Path") == NULL)
      message_file_name = "Resources:Resources.BootCmds.Messages";
   else
      message_file_name = "BootCommands:Messages";

#ifdef STANDALONE
   if ((error = xresourcefs_register_files (files_messages ())) != NULL)
      goto finish;
   registered_messages = TRUE;
#endif

   if ((error = xresourcefs_register_files (files_repeat ())) != NULL)
      goto finish;
   registered_repeat = TRUE;

   /*Load files.*/
   if ((error = xmessagetrans_open_file (&Control_Block, message_file_name,
         NULL)) != NULL)
      goto finish;
   done_open_file = TRUE;

   Commands [main_ADD_APP]    = &Add_App;
   Commands [main_APP_SIZE]   = &App_Size;
   Commands [main_DO]         = &Do;
   Commands [main_IF_THERE]   = &If_There;
   Commands [main_LOAD_CMOS]  = &Load_CMOS;
   Commands [main_SAVE_CMOS]  = &Save_CMOS;
   Commands [main_REPEAT]     = &Repeat;
   Commands [main_SAFE_LOGON] = &Safe_Logon;
   Commands [main_FREE_POOL]  = *Free_Pool;
   Commands [main_SHRINK_RMA] = &Shrink_RMA;
   Commands [main_ADD_TO_RMA] = &Add_To_RMA;
   Commands [main_APP_SLOT]   = &App_Slot;
   Commands [main_X]          = &X;

finish:
   if (error != NULL)
   {  tracef ("ERROR: \"%s\" (error 0x%X)\n" _ error->errmess _ error->errnum);

      if (done_open_file)
      {  os_error *error1;

         error1 = xmessagetrans_close_file (&Control_Block);
         if (error == NULL) error = error1;
      }

      if (registered_repeat)
      {  os_error *error1;

         error1 = xresourcefs_deregister_files (files_repeat ());
         if (error == NULL) error = error1;
      }

   #ifdef STANDALONE
      if (registered_messages)
      {  os_error *error1;

         error1 = xresourcefs_deregister_files (files_messages ());
         if (error == NULL) error = error1;
      }
   #endif
   }

   return (_kernel_oserror *) error;
}
/*------------------------------------------------------------------------*/
_kernel_oserror *main_terminate (bool fatal, int instance, void *workspace)

{  os_error *error = NULL;

   NOT_USED (fatal)
   NOT_USED (instance)
   NOT_USED (workspace)

   tracef ("main_terminate\n");

   if ((error = xmessagetrans_close_file (&Control_Block)) != NULL)
      goto finish;

   if ((error = xresourcefs_deregister_files (files_repeat ())) != NULL)
      goto finish;

#ifdef STANDALONE
   if ((error = xresourcefs_deregister_files (files_messages ())) != NULL)
      goto finish;
#endif

finish:
   {  os_error *error1 = trace_terminate ();
      if (error == NULL) error = error1;
   }

   return (_kernel_oserror *) error;
}
/*------------------------------------------------------------------------*/
_kernel_oserror *main_command (char *tail, int argc, int cmd_no,
      void *workspace)

{  os_error *error = NULL;

   NOT_USED (argc)
   NOT_USED (workspace)

   tracef ("main_command: tail \"%.*s\"\n" _ riscos_strlen (tail) _ tail);

   if ((error = (*Commands [cmd_no]) (tail)) != NULL)
      goto finish;

finish:
   return (_kernel_oserror *) error;
}
#if 0
/*------------------------------------------------------------------------*/
int main (void)

{  int context;
   struct {char *prog, *command, *directory; osbool directories, applications,
         files; char *type, *tail; osbool tasks; char argb [os_CLI_LIMIT + 1];} argl;
   osgbpb_INFO_STAMPED (FILENAME_MAX + 1) info;
   bool done_initialise = FALSE;
   bits file_type;
   char *tail;
   os_error *error = NULL;

   tracef ("main\n");

   if ((error = xos_get_env (&tail, NULL, NULL)) != NULL)
      goto finish;

   tracef ("tail %s\n" _ tail);

   malloc (1);

   if ((error = xos_read_args ("prog/a,command/a,directory/a,directories/s,"
         "applications/s,files/s,type/k,tail,tasks/s",
         tail, (char *) &argl, sizeof argl, NULL)) != NULL)
      goto finish;

   tracef ("command \"%s\"\n" _      argl.command);
   tracef ("directory \"%s\"\n" _    argl.directory);
   tracef ("directories \"%s\"\n" _  WHETHER (argl.directories));
   tracef ("applications \"%s\"\n" _ WHETHER (argl.applications));
   tracef ("files \"%s\"\n" _        WHETHER (argl.files));
   tracef ("type \"%s\"\n" _         argl.type != NULL? argl.type: "NULL");
   tracef ("tail \"%s\"\n" _         argl.tail != NULL? argl.tail: "NULL");
   tracef ("tasks \"%s\"\n" _        WHETHER (argl.tasks));

   if (!argl.directories && !argl.applications && !argl.files &&
         !argl.type)
      argl.files = argl.directories = TRUE;

   if (argl.type)
      if ((error = xosfscontrol_file_type_from_string (argl.type,
            &file_type)) != NULL)
         goto finish;

   if (argl.tasks)
   {  if ((error = xwimp_initialise (wimp_VERSION_RO2, "Repeat",
            NULL, NULL, NULL)) != NULL)
         goto finish;
      done_initialise = TRUE;
   }

   context = 0;
   while (TRUE)
   {  if ((error = xosgbpb_dir_entries_info_stamped (argl.directory,
            (osgbpb_info_stamped_list *) &info, 1, context,
            sizeof info, "*", NULL, &context)) != NULL)
         goto finish;
      if (context == osgbpb_NO_MORE) break;

      if
      (  (  info.obj_type == osfile_IS_FILE &&
            (argl.files || info.file_type == file_type)
         ) ||
         (  info.obj_type == osfile_IS_DIR &&
            (  argl.directories ||
               (argl.applications && info.name [0] == '!')
      )  )  )
      {  char cmd [os_CLI_LIMIT + 1];

         if (!argl.tail)
            sprintf (cmd, "%s %s.%s", argl.command, argl.directory,
                  info.name);
         else
            sprintf (cmd, "%s %s.%s %s", argl.command, argl.directory,
                  info.name, argl.tail);

         if (argl.tasks)
         {  tracef (stderr, "WimpTask %s\n", cmd);

            if ((error = xwimp_start_task (cmd, NULL)) != NULL)
               goto finish;
         }
         else
         {  tracef (stderr, "\"%s\"\n", cmd);
            if ((error = run_call (cmd, Workspace)) != NULL)
               goto finish;
            tracef (stderr, "\"%s\" done\n", cmd);
   }  }  }

finish:
   if (done_initialise)
   {  os_error *error1 = xwimp_close_down (NULL);
      if (error == NULL) error = error1;
   }

   if (error != NULL)
   {  fprintf (stderr, "Repeat: %s\n", error->errmess);
      exit (1);
   }
   else
      exit (0);
}
#endif
/*------------------------------------------------------------------------*/
os_error *main_error_lookup (int errnum, char *token, ...)

{  va_list list;
   char *p [4];
   int i;
   os_error error_block;

   tracef ("main_error_lookup\n");

   /*Assume that 4 args are always given.*/
   va_start (list, token);
   for (i = 0; i < 4; i++) p [i] = va_arg (list, char *);
   va_end (list);

   error_block.errnum = errnum;
   strcpy (error_block.errmess, token);
   return xmessagetrans_error_lookup (&error_block, &Control_Block, NULL, 0,
         p [0], p[1], p [2], p [3]);
}