/*
** 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 <a.out.h>
#include <unistd.h>

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;
    unsigned char *buf;
    struct nlist sym[2];
    unsigned long strsize;
    struct stat s;

    /* print the greet message */
    puts("Amiga Linux filetodata version 1.0");
    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 */
    datasize = s.st_size;

    /* setup exec header for object file */
    outex.a_magic  = OMAGIC;
    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 = malloc (datasize))) {
	fprintf (stderr, "Failed to allocate memory for buffer\n");
	exit (EXIT_FAILURE);
    }

    /* read contents of input file */
    if (read (fd, buf, datasize) != datasize) {
	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 datasize variable value */
    if (write (objfd, &datasize, sizeof(datasize)) != sizeof(datasize)) {
	fprintf (stderr, "Failed to write datasize 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);
}
