/* lilo.c  -  LILO command-line parameter processing */

/* Written 1992-1994 by Werner Almesberger */


#include <stdlib.h>
#include <unistd.h>
#include <stdio.h>
#include <string.h>
#include <fcntl.h>
#include <errno.h>

#include "config.h"
#include "common.h"
#include "lilo.h"
#include "temp.h"
#include "geometry.h"
#include "map.h"
#include "bsect.h"
#include "cfg.h"
#include "identify.h"


#define S2(x) #x
#define S(x) S2(x)


static void show_images(char *map_file)
{
    DESCR_SECTORS descrs;
    char *name;
    int fd,image,i;
    short unsigned checksum;

    if ((fd = open(map_file,O_RDONLY)) < 0)
	die("open %s: %s",map_file,strerror(errno));
    if (lseek(fd,SECTOR_SIZE,0) < 0)
	die("lseek %s: %s",map_file,strerror(errno));
    if (read(fd,(char *) &descrs,sizeof(descrs)) != sizeof(descrs))
	die("read %s: %s",map_file,strerror(errno));
    (void) close(fd);
    for (image = 0; image < MAX_IMAGES; image++)
	if (*(name = descrs.d.descr[image].name)) {
	    printf("%-" S(MAX_IMAGE_NAME) "s%s",name,image ? "  " : " *");
	    if (verbose > 0) {
		printf(" <dev=0x%02x,hd=%d,cyl=%d,sct=%d>",
		  descrs.d.descr[image].start.device,
		  descrs.d.descr[image].start.head,
		  descrs.d.descr[image].start.track,
		  descrs.d.descr[image].start.sector);
	    }
	    printf("\n");
	}
    checksum = INIT_CKS;
    for (i = 0; i < sizeof(descrs)/sizeof(unsigned short); i++)
	checksum ^= ((unsigned short *) &descrs)[i];
    if (!checksum) exit(0);
    fflush(stdout);
    fprintf(stderr,"Checksum error.\n");
    exit(1);
}


static void usage(char *name)
{
    char *here;

    if (here = strchr(name,'/')) name = here+1;
    fprintf(stderr,"usage: %s [ -C config_file ] [ -q ] [ -m map_file ] "
      "[ -v ]\n",name);
    fprintf(stderr,"%7s%s [ -C config_file ] [ -b boot_device ] [ -c ] "
      "[ -l ]\n","",name);
    fprintf(stderr,"%12s[ -i boot_sector ] [ -f disk_tab ] [ -m map_file ] "
      "[ -d delay ]\n","");
    fprintf(stderr,"%12s[ -v ] [ -t ] [ ( -s | -S ) save_file ] "
      "[ -P fix | -P ignore ]\n","");
    fprintf(stderr,"%12s[ -r root_dir ]\n","");
    fprintf(stderr,"%7s%s [ -C config_file ] [ -m map_file ] "
      "[ -P fix | -P ignore]\n","",name);
    fprintf(stderr,"%12s[ -R [ word ... ] ]\n","");
    fprintf(stderr,"%7s%s [ -C config_file ] -I name [ options ]\n","",name);
    fprintf(stderr,"%7s%s [ -C config_file ] [ -s save_file ] "
      "( -u | -U ) [ boot_device ]\n\n","",name);
    exit(1);
}


int main(int argc,char **argv)
{
    char *name,*config_file,*reboot_arg,*identify,*ident_opt,*new_root;
    char *uninst_dev;
    int query,more,version,uninstall,validate;
    BOOT_SECTOR dummy;
    IMAGE_DESCR dummy2;

    config_file = DFL_CONFIG;
    reboot_arg = identify = ident_opt = new_root = uninst_dev = NULL;
    query = version = uninstall = validate = 0;
    name = *argv++;
    argc--;
    cfg_init(cf_options);
    while (argc && **argv == '-') {
	argc--;
	if (argv[0][2]) usage(name);
	switch ((*argv++)[1]) {
	    case 'b':
		if (!argc) usage(name);
		cfg_set(cf_options,"boot",*argv++,NULL);
		argc--;
		break;
	    case 'c':
		cfg_set(cf_options,"compact",NULL,NULL);
		compact = 1;
		break;
	    case 'd':
		if (!argc) usage(name);
		cfg_set(cf_options,"delay",*argv++,NULL);
		argc--;
		break;
	    case 'f':
		if (!argc) usage(name);
		cfg_set(cf_options,"disktab",*argv++,NULL);
		argc--;
		break;
	    case 'l':
		cfg_set(cf_options,"linear",NULL,NULL);
		linear = 1;
		break;
	    case 'm':
		if (!argc) usage(name);
		cfg_set(cf_options,"map",*argv++,NULL);
		argc--;
		break;
	    case 'i':
		if (!argc) usage(name);
		cfg_set(cf_options,"install",*argv++,NULL);
		argc--;
		break;
	    case 'I':
		if (!argc) usage(name);
		identify = *argv++;
		if (--argc) {
		    ident_opt = *argv++;
		    argc--;
		}
		break;
	    case 'X':
		printf("-DIMAGES=%d -DCODE_START_1=%d -DCODE_START_2=%d "
		  "-DDESCR_SIZE=%d -DDSC_OFF=%d -DDSC_OFF2=%d -DDFCMD_OFF=%d "
		  "-DMSG_OFF=%d -DFLAGS_OFF=%d\n",MAX_IMAGES,
		  sizeof(BOOT_PARAMS_1),sizeof(BOOT_PARAMS_2),
		  sizeof(IMAGE_DESCR),
		  (void *) &dummy.par_1.descr[0]-(void *) &dummy,
		  (void *) &dummy.par_1.descr[1]-(void *) &dummy,
		  (void *) &dummy.par_1.descr[2]-(void *) &dummy,
		  (void *) &dummy.par_1.msg_len-(void *) &dummy,
		  (void *) &dummy2.flags-(void *) &dummy2);
		exit(0);
	    case 'C':
		if (!argc) usage(name);
		config_file = *argv++;
		argc--;
		break;
	    case 'S':
		if (!argc) usage(name);
		cfg_set(cf_options,"force-backup",*argv++,NULL);
		argc--;
		break;
	    case 's':
		if (!argc) usage(name);
		cfg_set(cf_options,"backup",*argv++,NULL);
		argc--;
		break;
	    case 'P':
		if (!argc) usage(name);
		if (!strcmp(*argv,"fix"))
		    cfg_set(cf_options,"fix-table",NULL,NULL);
		else if (!strcmp(*argv,"ignore"))
			cfg_set(cf_options,"ignore-table",NULL,NULL);
		    else usage(name);
		argv++;
		break;
	    case 'q':
		query = 1;
		break;
	    case 'r':
		if (!argc) usage(name);
		new_root = *argv++;
		argc--;
		break;
	    case 'R':
		if (!argc) reboot_arg = "";
		else while (argc--) {
			if (!reboot_arg)
			    *(reboot_arg = alloc(strlen(reboot_arg)+1)) = 0;
			else strcat(reboot_arg = ralloc(reboot_arg,
			      strlen(reboot_arg)+strlen(*argv)+2)," ");
			strcat(reboot_arg,*argv++);
		    }
		break;
	    case 't':
		test = 1;
		break;
	    case 'u':
		validate = 1;
		/* fall through */
	    case 'U':
		uninstall = 1;
		if (argc) {
		    if (argc-- > 1) usage(name);
		    uninst_dev = *argv;
		}
	    case 'v':
		verbose++;
		break;
	    case 'V':
		version = 1;
		break;
	    default:
		usage(name);
	}
    }
    if (!new_root) new_root = getenv("ROOT");
    if (new_root && *new_root) {
	if (chroot(new_root) < 0) die("chroot %s: %s",new_root,strerror(errno));
	if (chdir("/") < 0) die("chdir /: %s",strerror(errno));
    }
    if (atexit(temp_remove)) die("atexit() failed");
    if (verbose > 0 || version) {
	printf("LILO version 0.14\n");
	if (version) return 0;
	printf("Written 1992-1994 by Werner Almesberger\n\n");
    }
#if 0
    if (((install || test || boot_device || disktab_file || compact) && !argc)
      || (compact && linear && 0)) usage(name);
#endif
    cfg_open(config_file);
    more = cfg_parse(cf_options);
    if (identify) identify_image(identify,ident_opt);
    if (uninstall)
	bsect_uninstall(uninst_dev ? uninst_dev : cfg_get_strg(cf_options,
	  "boot"),cfg_get_strg(cf_options,"backup"),validate);
    compact = cfg_get_flag(cf_options,"compact");
    linear = cfg_get_flag(cf_options,"linear");
    if (cfg_get_strg(cf_options,"verbose"))
	verbose += to_number(cfg_get_strg(cf_options,"verbose"));
    if (reboot_arg) {
	map_patch_first(cfg_get_strg(cf_options,"map") ? cfg_get_strg(
	  cf_options,"map") : MAP_FILE,reboot_arg);
	exit(0);
    }
    if (cfg_get_strg(cf_options,"delay") && cfg_get_flag(cf_options,"prompt"))
	die("Can't use DELAY and PROMPT at the same time.");
    if (argc) usage(name);
    if (query)
	show_images(!cfg_get_strg(cf_options,"map") ? MAP_FILE :
	  cfg_get_strg(cf_options,"map"));
    geo_init(cfg_get_strg(cf_options,"disktab"));
    bsect_open(cfg_get_strg(cf_options,"boot"),cfg_get_strg(cf_options,"map") ?
      cfg_get_strg(cf_options,"map") : MAP_FILE,cfg_get_strg(cf_options,
      "install"),cfg_get_strg(cf_options,"delay") ? to_number(cfg_get_strg(
      cf_options,"delay")) : 0,cfg_get_strg(cf_options,"timeout") ?
      to_number(cfg_get_strg(cf_options,"timeout")) : -1);
    if (more) {
        cfg_init(cf_top);
        if (cfg_parse(cf_top)) cfg_error("Syntax error");
    }
    if (!bsect_number()) die("No images have been defined.");
    if (!test)
	if (cfg_get_strg(cf_options,"force-backup"))
	    bsect_update(cfg_get_strg(cf_options,"force-backup"),1);
	else bsect_update(cfg_get_strg(cf_options,"backup"),0);
    else {
	bsect_cancel();
	fprintf(stderr,"The boot sector and the map file have *NOT* been "
	  "altered.\n");
    }
    return 0;
}
