/*
 *				b u i l d 3 . c
 *
 * Link and Unix magic
 */

#include	<stdio.h>
#include	"build.h"

/*
 * The following parameters define the number of files that are
 * written to each line of the link/libr command string.  They
 * were set (by painful inspection) so that command lines would
 * not exceed 80 bytes in length.  Note that very long command files
 * would be fatal on RSX-11M and RSTS/E where fixed-length command
 * buffers would overflow.
 */

#define	RT_NOBJ		4	/* RT11 native and RSTS/E RT		*/
#define	RX_NOBJ		3	/* RSX-11M all flavors			*/
#define	VX_NOBJ		4	/* Vax native				*/
#define UX_NOBJ		6	/* Unix native				*/
/*
 * These tables define the library list to be appended to compilations
 */

static char *vxlib[] = {
	"LIBS", "UTLIB", "DECUS_LIB", "VXLIB", NULL
};

static char *rxlib[] = {
	"DTOA", "ATOF", "LIBS", "UTLIB", "RXLIB", NULL
};

static char *rtlib[] = {
	"DTOA", "ATOF", "LIBS", "UTLIB", "SUPORT", "RTLIB", NULL
};

static char *nolib[] = {
	NULL
};


dolink()
/*
 * Build link strings.  Very magical.
 *
 * The basic problem is that, if there are more than (say) four files,
 * the link command must be broken into several lines.  This isn't
 * particularily easy.
 */
{
	register int	manyfiles;	/* Number of files		*/
	register char	*tp;		/* Text (odl) pointer		*/
	register char	*filestring;	/* File name string		*/
	int		deleteodl;	/* Must delete odl file if set	*/

	deleteodl = FALSE;
	filestring = syvalue((isdefault("OBJS") ? "FILES" : "OBJS"));
	if (filestring == NULL) {
	    fprintf(stderr, "warning: how can I link nothing\n");
	    return;
	}
	switch (op_type) {
	case VRSX:
	case RSTSRSX:
	case RSXNATIVE:
	    if ((tp = syvalue("ODL")) != NULL && *tp != EOS) {
		/*
		 * We have to create an odl file
		 */
		expout("$(COPYODL)");
		sysave("RXOBJS", "$(RXODL)");
		manyfiles = FALSE;
		deleteodl = TRUE;
	    }
	    else {
		manyfiles = objs(filestring, "RXOBJS",
			rxlib, RX_NOBJ, 0, ",", "\n");
	    }
	    if (op_type == RSXNATIVE && isdefault("TKBOPTIONS")) {
		/*
		 * Force a TASK=...FOO
		 */
		sprintf(work, "%sXXX", syvalue("PROGRAM"));
		work[3] = EOS;
		sysave("TASKNAME", work);
		sysave("TKBOPTIONS", "TASK = ...$(TASKNAME)\n");
		fprintf(stderr, "Note: task name of %s set to ...%s\n",
			syvalue("PROGRAM"), work);
	    }
	    if (!isdefault("TKBOPTIONS")) {
		sysave("RXARGS", (isdefault("ODL"))
		    ? "\n/\n$(TKBOPTIONS)//" : "\n$(TKBOPTIONS)//", 0);
		manyfiles = TRUE;
	    }
	    if (manyfiles) {
		sysave("LINK1", "\n", 0);
	    }
	    break;

	case VNATIVE:
	    objs(filestring, "VXOBJS", vxlib, VX_NOBJ, 3, ",", ",-\n ");
	    break;
	
	case RSTSRT:
	case RT11NATIVE:
	    if (!isdefault("OVR")) {
		sysave("RTOBJS", "$(OVR)");
		manyfiles = TRUE;
	    }
	    else {
		manyfiles = objs(filestring, "RTOBJS",
		    rtlib, RT_NOBJ, 0, ",", "\n");
	    }
	    if (manyfiles) {
		sysave("LINK1", "/B:$(STACK)//\n", 0);
		sysave("LINK2", "//\n$(EXIT)", 0);
	    }
	    else {
		sysave("LINK2", "/B:$(STACK)\n$(EXIT)", 0);
	    }
	    break;

	default:
	    fatal("dolink", NULL);
	}
	expout("$(LINK)$(ALLOWEXECUTE)");
	if (deleteodl)
	    expout("$(DELETEODL)");
}

dounix(files)
register char		*files;
/*
 * Build string for Unix make
 */
{
	extern char	*fixfiletype();
	register char		*filestring;

	if ((filestring = syvalue(files)) == NULL) {
	    fprintf(stderr, "warning: how can I make nothing\n");
	    return;
	}
	/*
	 * Get the object file names, then force newline where needed.
	 */
	files = savest(fixfiletype(filestring, ".o"));
	objs(files, "UXFILES", nolib, UX_NOBJ, 0, " ", "\\\n\t");
	free(files);
	/*
	 * Do the make, then name the include files
	 */
	expout("$(UXMAKE)");
	doeach("INCLUDE", "UXINCLUDE", NULL);
}

char *
fixfiletype(filestring, filetype)
char		*filestring;		/* separated file list		*/
char		*filetype;		/* ".c" or whatever		*/
/*
 * This routine forces the approved filetype.  Result in work[].
 */
{
	register char	*in;
	register char	*outp;
	register char	c;
	char		*tp;

	in = filestring;
	outp = work;
	while ((c = *(in = skipwhite(in))) != EOS) {
	    if (outp >= &work[WORKSIZE]) {
		fatal("Work buffer overflow in fixfiletype", filestring);
	    }
	    /*
 	     * skip over whitespace before filename
	     * c == *in -> first non-white (may be a comma).
	     */
	    while (iswhite(c) || c == ',')
		c = *++in;
	    if (c == EOS)
		break;
	    *outp++ = ' ';
	    /*
	     * copy file name.
	     */
	    while (!iswhite(c) && c != EOS && c != '.') {
		*outp++ = c;
		c = *++in;
	    }
	    /*
	     * c == *in -> first byte past filename
	     */
	    if (c == '.') {
		/*
		 * skip over existing filetype
		 */
		do {
		    c = *++in;
		} while (!iswhite(c) && c != EOS);
	    }
	    for (tp = filetype; (*outp = *tp++) != EOS;)
		outp++;
	    if (c == EOS)
		break;
	}
	*outp = EOS;
	return (work);
}

dolibr(filestring)
char		*filestring;
/*
 * Build link strings.  Very magical.
 *
 * The basic problem is that, if there are more than six files,
 * the link command must be broken into several lines.  This isn't
 * particularily easy.
 */
{
	register SYMBOL		*sy;
	register char		*np;
	register char		*filename;

	/*
	 * Hack the library name.  If the user supplied a filetype,
	 * we're all done.  Else, append .ODL or .OBJ as needed.
	 */
	setstrip(lib_name, EOS, "?", NULL);
	sy = sylookup("?", FALSE);
	filename = sy->sy_value;
	np = &filename[strlen(filename)];
	while (np > filename && strchr(".)]:", *--np) == NULL);
	if (*np != '.') {
	    /*
	     * User didn't supply a .ext, so we shall do so.
	     */
	    switch (op_type) {
	    case VRSX:
	    case VNATIVE:
	    case RSXNATIVE:
	    case RSTSRSX:
		sy->sy_value = csavest(filename, ".OLB");
		break;

	    case RSTSRT:
	    case RT11NATIVE:
		sy->sy_value = csavest(filename, ".OBJ");
		break;

	    default:
		fatal("dolibr -- bad optype @ def. ext.", NULL);
	    }
	}
	if (filestring == NULL) {
	    fprintf(stderr, "warning: how can I build nothing\n");
	    return;
	}
	switch (op_type) {
	case VRSX:
	case RSTSRSX:
	case RSXNATIVE:
	    objs(filestring, "RXOBJS", nolib, RX_NOBJ, 0, ",",
		"\n$(LBR.1) $?=");
	    break;

	case VNATIVE:
	    objs(filestring, "VXOBJS", nolib, VX_NOBJ, 3, ",",
		"\n$(VXLIBR.1) ");
	    break;
	
	case RSTSRT:
	case RT11NATIVE:
	    if (objs(filestring, "RTOBJS", nolib, RT_NOBJ, 0, ",", "\n")) {
		sysave("LINK1", "//\n", 0);
		sysave("LINK2", "//\n$(EXIT)", 0);
	    }
	    else {
		sysave("LINK2", "\n$(EXIT)", 0);
	    }
	    break;

	default:
	    fatal("dolibr", NULL);
	}
	expout("$(LIBRARY)$(ALLOWREAD)");
}

static int	obj_nfiles;
static int	obj_first;

static int
objs(filestring, name, lib, nperline, initial_nfiles, f_separator, l_separator)
char		*filestring;	/* Files to expand			*/
char		*name;		/* Symbol to define			*/
register char	*lib[];		/* libraries to expand			*/
int		nperline;	/* Number of symbols per line		*/
int		initial_nfiles;	/* Initial value for obj_nfiles		*/
char		*f_separator;	/* File separator (on a line)		*/
char		*l_separator;	/* String to separate lines		*/
/*
 * Build a string containing the list of object files, including the
 * default library.  obj() returns true if more than nperline files
 * were saved.
 *
 * The string is savest'ed under "name".  If more than nperline
 * files are requested, lines will be separated using "l_separator".
 *
 * Uses work.
 *
 * Note: the purpose of nperline (and initial_nfiles) is to prevent
 * command lines from growing beyond column 80 (which is fatal on
 * RSX-11M).  The correct way to do this would be to expand the command
 * before inserting seperators.  The values chosen here may not be
 * small enough for 9-byte long RSX file names.
 */
{
	register char	*wp;
	register char	*libtext;
	char		*objexpand();

	obj_nfiles = initial_nfiles;
	obj_first = TRUE;		/* Supress first ','		*/
	wp = objexpand(filestring, work, nperline,
		f_separator, l_separator, TRUE);
	while (*lib != NULL) {		/* Do all libraries for opsys	*/
	    if ((libtext = syvalue(*lib)) != NULL) {
		wp = objexpand(libtext, wp, nperline,
			f_separator, l_separator, FALSE);
	    }
	    lib++;
	}
	sysave(name, work, 0);
	return(obj_nfiles > nperline);
}

static char *
objexpand(string, buffer, nperline, f_separator, l_separator, forcefiletype)
char		*string;		/* Where it comes from		*/
char		*buffer;		/* Where it goes to		*/
int		nperline;		/* Files per line		*/
char		*f_separator;		/* Between files string		*/
char		*l_separator;		/* Between lines string		*/
int		forcefiletype;		/* TRUE to force .obj		*/
/*
 * Copy the string to the work buffer, counting files.
 */
{
	register char	*s;		/* Input string pointer		*/
	register char	*b;		/* Output string pointer	*/
	register int	c;		/* Current character		*/
	char		*nptr;		/* Nperline pointer		*/
	int		inacct;		/* True if copying [...]	*/
	int		skipit;		/* True if not copying ".ext"	*/

	s = string;
	b = buffer;
	while ((c = *(s = skipwhite(s))) != EOS) {
	    s++;
	    if (b >= &work[WORKSIZE])
		fatal("work buffer overflow in objs()", string);
	    while (iswhite(c) || c == ',')
		c = *s++;
	    if (c == EOS)
		break;
	    /*
	     * Separate the new one from the previous ones.
	     */
	    if (obj_first) {
		obj_first = FALSE;		/* No comma first time	*/
	    }
	    else {
		nptr = ((obj_nfiles % nperline) != 0)
			? f_separator : l_separator;
		while (*nptr != EOS)
		    *b++ = *nptr++;
	    }
	    /*
	     * c has the first byte of the new object file,
	     * copy the file name to the buffer.
	     */
	    if (opsys == UNIX) {
		/*
		 * Just copy the string.  filetype is set by caller
		 */
		while (!iswhite(c) && c != EOS) {
		    *b++ = c;
		    c = *s++;
		}
	    }
	    else {
		inacct = FALSE;			/* Not in [...]		*/
		skipit = 0;			/* Not at ".ext"	*/
		while (!iswhite(c) && c != EOS && (c != ',' || inacct > 0)) {
		    if (c == '(' || c == '[')
			inacct++;
		    else if (c == ']' || c == ')')
			inacct--;
		    else if (!inacct && forcefiletype && c == '.')
			skipit = TRUE;		/* no ".ext;1"		*/
		    if (!skipit) {
			*b++ = toupper(c);
		    }
		    c = *s++;
		}
		if (inacct != 0) {
		    *b = EOS;
		    fprintf(stderr, "Bad file name in buffer: \"%s\"\n",
			buffer);
		}
		if (forcefiletype) {
		    *b++ = '.';			/* Force the .obj	*/
		    *b++ = 'O';			/* Filetype to fool the	*/
		    *b++ = 'B';			/* dreaded vms logical	*/
		    *b++ = 'J';			/* name expander	*/
		}
	    }
	    obj_nfiles++;			/* Here is a file	*/
	    if (c == EOS)
		break;
	}
	*b = EOS;
	return (b);
}

