/*      $NetBSD: main.c,v 1.34 2024/09/11 20:15:36 andvar Exp $  */

/*
* Copyright (c) 1996, 1997
*      Matthias Drochner.  All rights reserved.
* Copyright (c) 1996, 1997
*      Perry E. Metzger.  All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
*    notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
*    notice, this list of conditions and the following disclaimer in the
*    documentation and/or other materials provided with the distribution.
* 3. All advertising materials mentioning features or use of this software
*    must display the following acknowledgements:
*      This product includes software developed for the NetBSD Project
*      by Matthias Drochner.
*      This product includes software developed for the NetBSD Project
*      by Perry E. Metzger.
* 4. The names of the authors may not be used to endorse or promote products
*    derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/


#include <sys/reboot.h>

#include <lib/libkern/libkern.h>
#include <lib/libsa/stand.h>
#include <lib/libsa/ufs.h>

#include <libi386.h>

int errno;

extern  char bootprog_name[], bootprog_rev[], bootprog_kernrev[];

#define MAXDEVNAME 16

static char    *current_fsmode;
static char    *default_devname;
static int      default_unit, default_partition;
static char    *default_filename;

char *sprint_bootsel(const char *);
static void bootit(const char *, int, int);
void usage(void);
int main(int, char **);

void    command_help(char *);
void    command_ls(char *);
void    command_quit(char *);
void    command_boot(char *);
void    command_mode(char *);
void    command_dev(char *);

const struct bootblk_command commands[] = {
       { "help",       command_help },
       { "?",          command_help },
       { "ls",         command_ls },
       { "quit",       command_quit },
       { "boot",       command_boot },
       { "mode",       command_mode },
       { "dev",        command_dev },
       { NULL,         NULL },
};

int
parsebootfile(const char *fname, char **fsmode, char **devname, int *unit, int *partition, const char **file)
       /* fsmode:  out */
       /* devname:  out */
       /* unit, *partition:  out */
       /* file:  out */
{
       const char     *col, *help;

       *fsmode = current_fsmode;
       *devname = default_devname;
       *unit = default_unit;
       *partition = default_partition;
       *file = default_filename;

       if (fname == NULL)
               return (0);

       if (strcmp(current_fsmode, "dos") && (col = strchr(fname, ':'))) {
               /* no DOS, device given */
               static char     savedevname[MAXDEVNAME + 1];
               int             devlen;
               unsigned int    u = 0, p = 0;
               int             i = 0;

               devlen = col - fname;
               if (devlen > MAXDEVNAME)
                       return (EINVAL);

#define isvalidname(c) ((c) >= 'a' && (c) <= 'z')
               if (!isvalidname(fname[i]))
                       return (EINVAL);
               do {
                       savedevname[i] = fname[i];
                       i++;
               } while (isvalidname(fname[i]));
               savedevname[i] = '\0';

#define isnum(c) ((c) >= '0' && (c) <= '9')
               if (i < devlen) {
                       if (!isnum(fname[i]))
                               return (EUNIT);
                       do {
                               u *= 10;
                               u += fname[i++] - '0';
                       } while (isnum(fname[i]));
               }

#define isvalidpart(c) ((c) >= 'a' && (c) <= 'z')
               if (i < devlen) {
                       if (!isvalidpart(fname[i]))
                               return (EPART);
                       p = fname[i++] - 'a';
               }
               if (i != devlen)
                       return (ENXIO);

               *devname = savedevname;
               *unit = u;
               *partition = p;
               help = col + 1;
       } else
               help = fname;

       if (*help)
               *file = help;

       return (0);
}

char *
sprint_bootsel(const char *filename)
{
       char *fsname, *devname;
       int unit, partition;
       const char *file;
       static char buf[80];

       if (parsebootfile(filename, &fsname, &devname, &unit,
                         &partition, &file) == 0) {
               if (!strcmp(fsname, "dos"))
                       snprintf(buf, sizeof(buf), "dos:%s", file);
               else if (!strcmp(fsname, "ufs"))
                       snprintf(buf, sizeof(buf), "%s%d%c:%s", devname, unit,
                               'a' + partition, file);
               else goto bad;
               return (buf);
       }
bad:
       return ("(invalid)");
}

static void
bootit(const char *filename, int howto, int tell)
{
       int floppy = strncmp(default_devname, "fd", 2) == 0;
       if (tell) {
               printf("booting %s", sprint_bootsel(filename));
               if (howto)
                       printf(" (howto 0x%x)", howto);
               printf("\n");
       }
       if (exec_netbsd(filename, 0, howto, floppy, NULL) < 0)
               printf("boot: %s: %s\n", sprint_bootsel(filename),
                      strerror(errno));
       else
               printf("boot returned\n");
}

static void
print_banner(void)
{
       int extmem = getextmem();

       clear_pc_screen();

       printf("\n"
              ">> %s, Revision %s (from NetBSD %s)\n"
              ">> Memory: %d/%d k\n",
              bootprog_name, bootprog_rev, bootprog_kernrev,
              getbasemem(), extmem);
}

void
usage(void)
{
       printf("dosboot [-u] [-c <commands>] [-i] [filename [-bootopts]]\n");
}

int
main(int argc, char **argv)
{
       int             ch;
       int             interactive = 0;
       int             howto;
       extern char    *optarg;
       extern int      optind;

#ifdef  SUPPORT_SERIAL
       initio(SUPPORT_SERIAL);
#else
       initio(CONSDEV_PC);
#endif
       gateA20();

       print_banner();

       current_fsmode = "dos";
       default_devname = "hd";
       default_unit = 0;
       default_partition = 0;
       default_filename = "netbsd";

       while ((ch = getopt(argc, argv, "c:iu")) != -1) {
               switch (ch) {
               case 'c':
                       docommand(optarg);
                       return (1);
                       break;
               case 'i':
                       interactive = 1;
                       break;
               case 'u':
                       current_fsmode = "ufs";
                       break;
               default:
                       usage();
                       return (1);
               }
       }

       if (interactive) {
               printf("type \"?\" or \"help\" for help.\n");
               bootmenu();
       }

       argc -= optind;
       argv += optind;

       if (argc > 2) {
               usage();
               return (1);
       }
       howto = 0;
       if (argc > 1 && !parseopts(argv[1], &howto))
               return (1);

       bootit((argc > 0 ? argv[0] : "netbsd"), howto, 1);
       return (1);
}

/* ARGSUSED */
void
command_help(char *arg)
{
       printf("commands are:\n"
              "boot [xdNx:][filename] [-acdqsv]\n"
              "     (ex. \"sd0a:netbsd.old -s\"\n"
              "ls [path]\n"
              "mode ufs|dos\n"
              "dev xd[N[x]]:\n"
              "help|?\n"
              "quit\n");
}

void
command_ls(char *arg)
{
       char *help = default_filename;
       default_filename = "/";
       ls(arg);
       default_filename = help;
}

/* ARGSUSED */
void
command_quit(char *arg)
{
       printf("Exiting... goodbye...\n");
       _rtt();
}

void
command_boot(char *arg)
{
       char *filename;
       int howto;

       if (parseboot(arg, &filename, &howto))
               bootit(filename, howto, 1);
}

void
command_mode(char *arg)
{
       if (!strcmp("dos", arg))
               current_fsmode = "dos";
       else if (!strcmp("ufs", arg))
               current_fsmode = "ufs";
       else
               printf("invalid mode\n");
}

void
command_dev(char *arg)
{
       static char savedevname[MAXDEVNAME + 1];
       char *fsname, *devname;
       const char *file; /* dummy */

       if (!strcmp(current_fsmode, "dos")) {
               printf("not available in DOS mode\n");
               return;
       }

       if (*arg == '\0') {
               printf("%s%d%c:\n", default_devname, default_unit,
                      'a' + default_partition);
               return;
       }

       if (!strchr(arg, ':') ||
           parsebootfile(arg, &fsname, &devname, &default_unit,
                         &default_partition, &file)) {
               command_help(NULL);
               return;
       }

       /* put to own static storage */
       strncpy(savedevname, devname, MAXDEVNAME + 1);
       default_devname = savedevname;
}