/*
**
** FUNCTIONAL DESCRIPTION:
** 
**      Given a stripped image, create a bootblock image. 
**
** CALLING ENVIRONMENT: 
**
**	user mode
** 
** AUTHOR: David A Rusling, david.rusling@reo.mts.dec.com
**
*/

#include <stdio.h>
#include <ctype.h>
#include <string.h>
#include <stdlib.h>
#include <sys/stat.h>

/*
 *  Macros
 */
#define TRUE 1
#define FALSE 0

#define int16 unsigned short
#define int32 unsigned long
#define int64 unsigned long long

#define _error(string1)\
     {	\
	fprintf(stderr, "ERROR: %s\n", string1);	\
	exit(1);    \
     }

#define _SEEN(o) seen[o-'a']
#define _OPTION(o) (_SEEN(o) == TRUE)


/*
 *  Global data
 */
#define SEEN_SIZE 100
char seen[SEEN_SIZE];			/* upper and lower case */

/*
 *  Forward routine descriptions.
 */
extern int main(int argc, char **argv);
extern void usage();
extern int build_disk_boot_block(FILE *ofile, int size);
extern void put_padding(FILE *ofile, unsigned long padding);
extern int output_image(FILE *ifile, FILE *ofile, int size);
extern int64 fsize(FILE *fp);


/*
 *  External routines.
 */

int main(int argc, char **argv)
{
    char *image = NULL;
    char *out = NULL;

    FILE *ifile;
    FILE *ofile;

    int size, written;

    char *arg, option;
    int i;
    unsigned long int padding, offset, first;
/*
 * Initialise
 */

    for (i = 0; i < SEEN_SIZE; i++)
	seen[i] = FALSE;

/*
 * Parse arguments, but Skip argv[0].
 */
    for (i = 1; i < argc; i++) {
	arg = argv[i];
	if (*arg == '-') {
/*
 * This is a -xyz style options list.  Work out the options specified.
 */
	    arg++;			/* skip the '-' */
	    while (option = *arg++) {	/* until we reach the '0' string
					 * terminator */
		option = tolower(option);
		switch (option) {
		    case 'h': 
			usage();
			exit(1);
		    case 'v': 		/* verbose, allow upper and lower case
					 */
			_SEEN(option) = TRUE;
			break;
		    case 'b':           /* boot, just put out the header */
			_SEEN(option) = TRUE;
			break;
		    default: 
			usage();
			exit(0);
			break;
		}
	    }
	} else {
/*
 *  Filename.
 */
	    if (image == NULL)
		image = arg;
	    else {
		if (out == NULL) 
		    out = arg;
		else
		    _error("too many filenames given");
	    }
	}
    }
/*
 *  Announce ourselves.
 */
    fprintf(stderr, "build, bootblock image builder [V1.0]\n");
/*
 *  Check the arguments passed.
 */
    /* check for an image file */
    if (image == NULL)
	_error("no filenames given");

/*
 *  Tell the world what the arguments are.
 */
    if _OPTION('v') {
	fprintf(stderr, "\timage file is %s\n", image);
	fprintf(stderr, "\toutput file is %s\n", out == NULL ? "stdout" : out);
	if _OPTION('b') 
	    fprintf(stderr, "\tboot block only\n");
	else
	    fprintf(stderr, "\timage file\n");
    }

/*
 *  Open the files.
 */
    ifile = fopen(image, "rb");
    if (ifile == NULL) 
	_error("failed to open input file");

    if (out == NULL)
	ofile = stdout;
    else {
	ofile = fopen(out, "wb");
	if (ofile == NULL) {
	    fclose(ifile);
	    _error("failed to open output file");
	}
    }

/*
 *  How big is the input file?
 */
    size = fsize(ifile);

    if _OPTION('b') 
/*
 *  Output the disk boot block
 */
	written = build_disk_boot_block(ofile, size);
    else {
/*
 *  Now output the image itself.
 */
	written += output_image(ifile, ofile, size);

/*
 *  Check to see if we need some padding
 */
	put_padding(ofile, 512 - (written % 512));

    }
/*
 *  Close up shop and exit.
 */
    fclose(ifile);
    if (out != NULL) fclose(ofile);

    return EXIT_SUCCESS;

}					/* end of main() */

/****************************************************************
 * Put out some part of the image                               *
 ****************************************************************/
int put_something(FILE *ofile, void *what, int size)
{
    char *ptr;
    int count;

    count = size;
    ptr = (char *)what;
    while (size--)
	fputc(*ptr++, ofile);
    return count;
}
/****************************************************************
 * Build and output the disk boot block                         *
 ****************************************************************/
int build_disk_boot_block(FILE *ofile, int size)
{
    unsigned long bootblock[64], tmp;
    int i;

    /* zero it out first */
    memset(bootblock, 0, 512);

    /* who's boot block is this? */
    memcpy(bootblock, "Linux Miniloader", 16 );

    /* how big is it? */
    bootblock[60] = (size + 512)/ 512;	        /* count */
    if _OPTION('v') {
	fprintf(stderr, "\t%d blocks in the image\n", bootblock[60]);
    }

    bootblock[61] = 1;			/* starting LBM */
    bootblock[62] = 0;			/* flags */

    /* work out the checksum */
    tmp = 0;
    for (i = 0 ; i < 63 ; i++)
	tmp += bootblock[i];
    bootblock[63] = tmp;
    put_something(ofile, (void *)bootblock, 512);
    return 512;
}

/****************************************************************
 * Put out some padding                                         *
 ****************************************************************/
void put_padding(FILE *ofile, unsigned long padding)
{
    if _OPTION('v') {
	fprintf(stderr, "\tpadding image with %d bytes\n", padding);
    }
    while (padding--)
	fputc(0xfe, ofile);
}

/****************************************************************
 * Output the image to the output file                          *
 ****************************************************************/
int output_image(FILE *ifile, FILE *ofile, int size) 
{
    int c;
    int written = size;

    if _OPTION('v') {
	fprintf(stderr, "\twriting out image of size %d bytes\n", size);
    }

    while (size--) {
	c = getc(ifile);
	fputc(c, ofile);
    }
    return written;
}

/****************************************************************
 * How do you use this thing?                                   *
 ****************************************************************/
void usage()
{
    fprintf(stderr, "build - builds boot block image\n\n");
    fprintf(stderr, "> arc [-v] <input-file> [<output-file>]\n");
    fprintf(stderr, "\nwhere:\n");
    fprintf(stderr, "\nFlags: -hv\n");
    fprintf(stderr, "\t-h: print this help text\n");
    fprintf(stderr, "\t-v: verbose\n");
    fprintf(stderr, "Example\n\n");
    fprintf(stderr, "\tbuild -v miniloader miniloader.bb\n\n");
}

int64 fsize(FILE *fp)
{
    int c;
    int64 size;

    size = 0;
    fseek(fp, 0, 0);
    while ((c = getc(fp)) != EOF)
	 size++;
    fseek(fp, 0, 0);
    return size;
}
