/* clinkdata.c:
 *
 * COPYRIGHT (c) 1989 by AT&T Bell Laboratories.
 *
 * Group files into the text segment of a .o file.  This is done by creating an
 * assembly file "runtime/allmo.s" that allocates space for the code and deals
 * with the relative addressing.  This file is then assembled, and then the actual
 * code is patched into allmo.o.
 *
 * Specifically, this file produces an ML value of the type
 *
 *    datatype datalist
 *      = DATANIL
 *      | DATACONS of (string * (unit -> unit) * datalist)
 *
 * where the "unit -> unit" function is an empty closure refering to the
 * code from the mo file.
 */

#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/file.h>
#include "tags.h"

#define MAGICNUM	0x18273645

/* RUNTIME is defined to be the path prefix of the runtime source directory */
#ifndef RUNTIME
#define RUNTIME "runtime"
#endif

#ifdef RS6000
#define SYMBOL		"datalist"
#define TEXT(F)		fprintf(F, ".toc\n.csect [RW]\n.align 2\n")
#define GLOBAL(F,S)	fprintf(F, ".globl %s\n", S)
#define WORD(F,N)	fprintf(F, ".long %d\n", N)
#define ADDR(F,A)	fprintf(F, ".long %s\n", A)
#define ADDR1(F,A,N)	fprintf(F, ".long %s+%d\n", A,N)
#define SPACE(F,N)	fprintf(F, ".space %d\n", N)
#else /* !RS6000 */
#ifdef MIPS
#define SYMBOL		"datalist"
#define TEXT(F)         fprintf(F, ".text\n.set noreorder\n")
#define GLOBAL(F,S)	fprintf(F, ".globl %s\n", S)
#define WORD(F,N)	fprintf(F, ".word %d\n", N)
#define ADDR(F,A)	fprintf(F, ".word %s\n", A)
#define ADDR1(F,A,N)	fprintf(F, ".word %s+%d\n", A,N)
#define SPACE(F,N)	fprintf(F, ".space %d\n", N)
#else /* !RS6000 && !MIPS */
#ifdef SPARC
#define SYMBOL		"_datalist"
#define TEXT(F)		fprintf(F, ".seg \"text\"\n")
#define GLOBAL(F,S)	fprintf(F, ".global %s\n", S)
#define WORD(F,N)	fprintf(F, ".word %d\n", N)
#define ADDR(F,A)	fprintf(F, ".word %s\n", A)
#define ADDR1(F,A,N)	fprintf(F, ".word %s+%d\n", A,N)
#define SPACE(F,N)	fprintf(F, ".skip %d\n", N)
#else /* !RS6000 && !MIPS && !SPARC */
#ifdef sun3
#define SYMBOL		"_datalist"
#define TEXT(F)		fprintf(F, ".text\n")
#define GLOBAL(F,S)	fprintf(F, ".globl %s\n", S)
#define WORD(F,N)	fprintf(F, ".long %d\n", N)
#define ADDR(F,A)	fprintf(F, ".long %s\n", A)
#define ADDR1(F,A,N)	fprintf(F, ".long %s+%d\n", A, N)
#define SPACE(F,N)	fprintf(F, ".skip %d\n", N)
#else /* !RS6000 && !MIPS && !SPARC && !sun3 */
#if defined(HPUX) && !defined(HPPA)
#define SYMBOL		"_datalist"
#define TEXT(F)		fprintf(F, "text\n")
#define GLOBAL(F,S)	fprintf(F, "global %s\n", S)
#define WORD(F,N)	fprintf(F, "long %d\n", N)
#define ADDR(F,A)	fprintf(F, "long %s\n", A)
#define ADDR1(F,A,N)	fprintf(F, "long %s+%d\n", A, N)
#define SPACE(F,N)	fprintf(F, "space %d\n", N)
#else /* !RS6000 && !MIPS && !SPARC && !sun3 && !HPUX */
#ifdef AUX
#define SYMBOL		"datalist"
#define TEXT(F)		fprintf(F, ".file \"allmo.s\"\n.text\n")
#define GLOBAL(F,S)	fprintf(F, ".globl %s\n", S)
#define WORD(F,N)	fprintf(F, ".long %d\n", N)
#define ADDR(F,A)	fprintf(F, ".long %s\n", A)
#define ADDR1(F,A,N)	fprintf(F, ".long %s+%d\n", A, N)
#define SPACE(F,N)	fprintf(F, ".space %d\n", N)
#else
#ifdef HPPA
#define SYMBOL		"datalist"
#define TEXT(F)		fprintf(F, "\t.space $PRIVATE$,SORT=16\n\t.subspa $DATA$,QUAD=0,ALIGN=4,ACCESS=63,SORT=16\n");
#define GLOBAL(F,S)	fprintf(F, "\t.export %s,DATA\n", S)
#define WORD(F,N)	fprintf(F, "\t.word %d\n", N)
#define ADDR(F,A)	fprintf(F, "\t.word %s\n", A)
#define ADDR1(F,A,N)	fprintf(F, "\t.word %s+%d\n", A, N)
#define SPACE(F,N)	fprintf(F, "\t.blockz %d\n", N)
#else /* !RS6000 && !MIPS && !SPARC && !sun3 && !HPUX && !AUX && !HPPA*/
#define SYMBOL		"_datalist"
#define TEXT(F)		fprintf(F, ".text\n")
#define GLOBAL(F,S)	fprintf(F, ".globl %s\n", S)
#define WORD(F,N)	fprintf(F, ".long %d\n", N)
#define ADDR(F,A)	fprintf(F, ".long %s\n", A)
#define ADDR1(F,A,N)	fprintf(F, ".long %s+%d\n", A, N)
#define SPACE(F,N)	fprintf(F, ".space %d\n", N)
#endif /* HPPA */
#endif /* AUX */
#endif /* HPUX */
#endif /* sun3 */
#endif /* SPARC */
#endif /* MIPS */
#endif /* RS6000 */
#ifdef HPPA
#  define LABEL(F,L)	fprintf(F, "\t.export %s,DATA\n%s\n", L,L)
#else
#  define LABEL(F,L)	fprintf(F, "%s:\n", L)
#endif

#define LONG_SZ	    (sizeof(long))
#define align(x)    (((x) + (LONG_SZ - 1)) & ~(LONG_SZ - 1))

void die(s, arg)
    char *s;
    int arg;
{
    char buf[64];
    sprintf(buf, s, arg);
    perror(buf);
    exit(1);
}

extern char *malloc();

/* make_sfile:
 * generate the assembly skeleton file
 */
void make_sfile (asfile, nfiles, mofiles)
    FILE	*asfile;
    int		nfiles;
    char	**mofiles;
{
    struct stat st;
    int n, len, str_sz, code_sz, i;
    char buf[64], buf2[64], lab_buf[8];
    static int lab_cnt = 0;

    TEXT(asfile);
    WORD(asfile, MAGICNUM);
    GLOBAL(asfile, SYMBOL);

    for (i = 0;  i < nfiles;  i++) {
	char *fname = mofiles[i];
      /* compute the space required for the file */
	if (stat(fname, &st) == -1)
	    die("stat(\"%s\")", fname);
	n = st.st_size;
	if (n == 0)
	    die("mo file is empty");
	len = strlen(fname);
	str_sz = align(len) + 2*LONG_SZ;  /* includes filename and code descriptors */
	code_sz = align(n);

      /* extract the structure name from the file name */
	{
	    register char *p, *begin;

	  /* find the beginning of the file name. */
	    for (p = begin = fname;  *p;  p++) {
		if (*p == '/')
		    begin = p+1;
	    }
	  /* find the ".mo" */
	    for (p = begin, len = 0;  *p && (*p != '.');  p++, len++)
		continue;
	    strncpy (buf, begin, len);
	    buf[len] = '\0';
	}

      /* add the datalist entry for this structure */
	if (i == 0)
	    LABEL(asfile, SYMBOL);
	WORD(asfile, MAKE_DESC(3, TAG_record));
	sprintf (lab_buf, "L%d", lab_cnt++);
	LABEL(asfile, lab_buf);
	ADDR1(asfile, lab_buf, 24);	/* mo filename string */
	ADDR1(asfile, lab_buf, 16);	/* closure address */
	if (i == (nfiles-1))
	    WORD(asfile, 1);		/* DATANIL */
	else
	    ADDR1(asfile, buf, code_sz+4);

      /* create the closure record */
	WORD(asfile, MAKE_DESC(1, TAG_record));
	ADDR1(asfile, buf, 4);		/* code string address */

      /* add a label and space for the structure to the "allmo.s" file. */
	SPACE(asfile, str_sz);
	LABEL(asfile, buf);
	SPACE(asfile, code_sz);		/* space for code (not including tag) */
    }

} /* end of make_sfile */


/* copy:
 * append the code from file to output, preceded by name.
 */
int copy (file, ofd)
    char *file; int ofd;
{
    int fd;
    long i, x, len, n, nbytes;
    char *s;
    struct stat st;
    static int bufsize = 0;
    static long *buf;

    if ((fd = open(file, 0)) == -1) {
	char	buf[64];
	sprintf(buf, "open: %s", file);
	die(buf);
    }
    if (fstat(fd, &st) == -1)
	die("stat");

    n = st.st_size;
    if (n == 0)
	die("mo file is empty");
    len = strlen(file);
    nbytes = LONG_SZ + align(n) + LONG_SZ + align(len);

    if (nbytes > bufsize) {
	bufsize = nbytes + 4096;
	if (buf != NULL)
	    free(buf);
	buf = (long *)malloc(bufsize);
	if (buf == NULL)
	    die("malloc(buf)");
    }

    buf[0] = MAKE_DESC(len, TAG_string);	   /* tag for file name */
    strcpy((char *) &buf[1], file);
    s = ((char *) &buf[1]) + len;
    for (i = align(len); i > len; i--)
	*s++ = 0;

    *((long *) s) = MAKE_DESC(n, TAG_string);  /* tag for code */
    s += LONG_SZ;

    for (x = n;  x > 0; ) {
	i = read(fd, s, x);
	if (i <= 0) die("read");
	s += i; x -= i;
     }

    for (i = align(n); i > n; i--) *s++ = 0;

    if (close(fd) == -1) die("close");

  /* advance the file pointer past the mo_list object and closure */
    if (lseek(ofd, 6*LONG_SZ, 1 /* L_INCR */) == -1) die("lseek");

    s = (char*)buf;
    for (x = nbytes;  x > 0; ) {
	i = write(ofd, s, x);
	if (i <= 0) die ("write");
	x -= i; s += i;
    }
}


int main(argc, argv)
    int argc;
    char *argv[];
{
    int i, pos, sts;
    FILE *asfile, *ofile; int ofd;
    char *as_buf, *o_buf, *cmd_buf;

    sprintf(as_buf = malloc(sizeof(RUNTIME)+16), "%s/allmo.s", RUNTIME);
    sprintf(o_buf = malloc(sizeof(RUNTIME)+16), "%s/allmo.o", RUNTIME);
#ifdef AUX
    sprintf(cmd_buf = malloc(2*sizeof(RUNTIME)+32), "gas %s -o %s", as_buf, o_buf);
#else
    sprintf(cmd_buf = malloc(2*sizeof(RUNTIME)+32), "as %s -o %s", as_buf, o_buf);
#endif

    asfile = fopen(as_buf,"w");
    if (asfile == NULL)
	die("fopen %s", as_buf);

    make_sfile (asfile, argc-1, argv+1);

    fclose (asfile);

    fprintf (stderr, "%s> %s\n", argv[0], cmd_buf);
    if ((sts = system (cmd_buf)) != 0) {
	fprintf (stderr, "cannot assemble %s, status = %d\n", as_buf, sts);
	exit (1);
    }

    ofile = fopen(o_buf,"r");
    if (!ofile)
	die("fopen %s", o_buf);
    for (i = 0, pos = 0;  i != MAGICNUM;  pos += LONG_SZ) {
	if (fread (&i, LONG_SZ, 1, ofile) == 0)
	    die("can't find magic num");
    }
    fclose (ofile);

    ofd = open (o_buf, O_RDWR, 0);
    if (ofd < 0)
	die("open %s", o_buf);

  /* set the file pointer to the tag word of the first datalist item */
    lseek (ofd, pos, 0 /* L_SET */);

    for (i = 1;  i < argc;  i++)
	copy (argv[i], ofd);

    close (ofd);

    exit (0);
}


