/*
** filetodata.c -- This program converts a file to an object file containing
**		   the file contents as data.
**		   The object file contains 2 variables:
**		      1) The contents of the file, labeled as <name>data
**		      2) A variable indicating the size of the contents,
**			 labeled as <name>size.
**
**		   The 1st argument is the file to convert.
**		   The 2nd argument is the string to substitute for <name>
**		     in the above label definitions.
**		   The 3rd argument (optional) is the output object file
**		     name.  It defaults to filedata.o
**
** Copyright 1993 by Hamish Macdonald.
**
** This file is subject to the terms and conditions of the GNU General Public
** License.  See the file README.legal in the main directory of this archive
** for more details.
**
*/

#include <stddef.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <sys/types.h>
#include <sys/file.h>
#include <sys/stat.h>
#include "linux/a.out.h"
#include <unistd.h>

#ifndef EXIT_FAILURE
#define EXIT_FAILURE 1
#endif
#ifndef EXIT_SUCCESS
#define EXIT_SUCCESS 0
#endif

void usage(void)
{
    fprintf (stderr, "Usage:\n"
	     "\tfiletodata file name [outputfilename]\n");
    exit (EXIT_FAILURE);
}

int main(int argc, char *argv[])
{
    int fd, objfd;
    struct exec outex;
    char *filename;
    char *objname = "filedata.o";
    char *labname;
    unsigned long datasize, filesize;
    unsigned char *buf;
    struct nlist sym[2];
    unsigned long strsize;
    struct stat s;

    /* print the greet message */
    puts("Amiga Linux filetodata version 1.2");
    puts("Copyright 1993 by Hamish Macdonald\n");

    if (argc < 3 || argc > 4) {
	usage();
	exit (EXIT_FAILURE);
    }

    filename = argv[1];
    labname = argv[2];
    if (argc > 3)
	objname = argv[3];

    /* open the input file and determine its size */
    if ((fd = open (filename, O_RDONLY)) == -1) {
	fprintf (stderr, "Unable to open input file %s\n", filename);
	exit (EXIT_FAILURE);
    }


    if (fstat(fd, &s) == -1) {
	fprintf (stderr, "stat on input file %s failed\n", filename);
	exit (EXIT_FAILURE);
    }

    /* record size file (rounded up to longword boundary) */
    filesize = s.st_size;
    datasize = (filesize + sizeof (unsigned long) - 1)
	    & ~(sizeof(unsigned long) -1);

    /* setup exec header for object file */
    N_SET_MAGIC (outex, OMAGIC);
    N_SET_MACHTYPE (outex, M_68020);
    N_SET_FLAGS (outex, 0);
    outex.a_text   = 0;
    outex.a_data   = datasize + sizeof (unsigned long);
    outex.a_bss    = 0;
    outex.a_syms   = 2 * sizeof (struct nlist);
    outex.a_entry  = 0;
    outex.a_trsize = 0;
    outex.a_drsize = 0;

    /* open the output file */
    if ((objfd = open (objname, O_WRONLY | O_CREAT | O_TRUNC)) == -1) {
	fprintf (stderr, "Unable to open objectfile %s\n", objname);
	exit (EXIT_FAILURE);
    }

    /* write exec header to object file */
    if (write (objfd, (void *)&outex, sizeof(outex)) != sizeof(outex)) {
	fprintf (stderr, "Unable to write exec header to %s\n",
		 objname);
	exit (EXIT_FAILURE);
    }

    /* allocate buffer to hold text and data */
    if (!(buf = calloc (datasize, sizeof (char)))) {
	fprintf (stderr, "Failed to allocate memory for buffer\n");
	exit (EXIT_FAILURE);
    }

    /* read contents of input file */
    if (read (fd, buf, filesize) != filesize) {
	fprintf (stderr, "Failed to read contents of input file\n");
	free (buf);
	exit (EXIT_FAILURE);
    }

    /* close the input file */
    close (fd);

    /* seek to data offset in object file */
    if (lseek (objfd, N_DATOFF(outex), L_SET) == -1) {
	fprintf (stderr, "Failed to seek to data in object file\n");
	free (buf);
	exit (EXIT_FAILURE);
    }

    /* write the contents */
    if (write (objfd, buf, datasize) != datasize) {
	fprintf (stderr, "Failed to write data to object file\n");
	free (buf);
	exit (EXIT_FAILURE);
    }

    /* write the filesize variable value */
    if (write (objfd, &filesize, sizeof(filesize)) != sizeof(filesize)) {
	fprintf (stderr, "Failed to write filesize to object file\n");
	free (buf);
	exit (EXIT_FAILURE);
    }
    /*
     * setup symbol table
     */

    /* sym entry for initdata */
    sym[0].n_un.n_strx = sizeof(strsize);
    sym[0].n_type      = N_DATA | N_EXT;
    sym[0].n_other     = 0;
    sym[0].n_desc      = 0;
    sym[0].n_value     = 0;

    /* sym entry for initsize */
    sym[1].n_un.n_strx = sizeof(strsize) + strlen(labname) + 6;
    sym[1].n_type      = N_DATA | N_EXT;
    sym[1].n_other     = 0;
    sym[1].n_desc      = 0;
    sym[1].n_value     = datasize;

    /* seek to symbol offset in object file */
    if (lseek (objfd, N_SYMOFF(outex), L_SET) == -1) {
	fprintf (stderr, "Failed to seek to symbols in object file\n");
	free (buf);
	exit (EXIT_FAILURE);
    }

    /* write symbols */
    if (write (objfd, sym, sizeof(sym)) != sizeof(sym)) {
	fprintf (stderr, "Failed to write symbols to object file\n");
	free (buf);
	exit (EXIT_FAILURE);
    }

    /* calculate string table size */
    strsize = sizeof(strsize) + strlen(labname) * 2 + 12;

    /* seek to string table offset in object file */
    if (lseek (objfd, N_STROFF(outex), L_SET) == -1) {
	fprintf (stderr, "Failed to seek to symbols in object file\n");
	free (buf);
	exit (EXIT_FAILURE);
    }

    if (write (objfd, &strsize, sizeof(strsize)) != sizeof(strsize)) {
	fprintf (stderr, "Failed to write str table size to object file\n");
	free (buf);
	exit (EXIT_FAILURE);
    }

    /* write strings to the string table */
    write (objfd, "_", 1);
    write (objfd, labname, strlen(labname));
    write (objfd, "data", 5);
    write (objfd, "_", 1);
    write (objfd, labname, strlen(labname));
    write (objfd, "size", 5);

    /* close the object file */
    close (objfd);

    /* free the buffer */
    free (buf);

    printf( "Converted input file %s to object file %s\n", filename,
	   objname);

    exit (EXIT_SUCCESS);
}
