/*
 *				b u i l d 3 . c
 *
 * Link 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		6	/* Vax native				*/

/*
 * These tables define the library list to be appended to compilations
 */

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

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

static char *rtlib[] = {
	"DTOA", "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 VBOTH:
		objs(filestring, "VXOBJS", vxlib, VX_NOBJ, 3, ",-\n ");
	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");
	}
	expout("$(LINK)$(ALLOWEXECUTE)");
	if (deleteodl)
		expout("$(DELETEODL)");
}

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 && *--np != '.'
			&& *np != ')' && *np != ']' && *np != ':');
	if (*np != '.') {
		/*
		 * User didn't supply a .ext, so we shall do so.
		 */
		switch (op_type) {
		case	VBOTH:
		case	VRSX:
		case	VNATIVE:
		case	RSXNATIVE:
			sy->sy_value = csavest(filename, ".OLB");
			break;

		case	RSTSRT:
		case	RT11NATIVE:
			sy->sy_value = csavest(filename, ".OBJ");
			break;
		}
	}
	if (filestring == NULL) {
		fprintf(stderr, "warning: how can I build nothing\n");
		return;
	}
	switch (op_type) {
	case VBOTH:
		objs(filestring, "VXOBJS", nolib, VX_NOBJ, 3, ",-\n ");
	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 ");
		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");
	}
	expout("$(LIBRARY)$(ALLOWREAD)");
}

static int	obj_nfiles;
static int	obj_first;

static int
objs(filestring, name, lib, nperline, initial_nfiles, 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		*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 "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, separator, TRUE);
	while (*lib != NULL) {		/* Do all libraries for opsys	*/
		if ((libtext = syvalue(*lib)) != NULL) {
			wp = objexpand(libtext, wp, nperline,
			separator, FALSE);
		}
		lib++;
	}
	sysave(name, work, 0);
	return(obj_nfiles > nperline);
}

static char *
objexpand(string, buffer, nperline, separator, forcefiletype)
char		*string;		/* Where it comes from		*/
char		*buffer;		/* Where it goes to		*/
int		nperline;		/* Files per line		*/
char		*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()");
		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 {
			if ((obj_nfiles % nperline) != 0) {
				*b++ = ',';	/* Normal	*/
			}
			else {
				/*
				 * Big separation here
				 */
				for (nptr = separator; *nptr != EOS;)
					*b++ = *nptr++;
			}
		}
		/*
		 * C has the first byte of the new object file,
		 * copy the file name to the buffer.
		 */
		inacct = FALSE;		/* Not in [...]			*/
		skipit = 0;		/* Not at ".ext"		*/
		while (!iswhite(c) && c != EOS && (c != ',' || inacct > 0)) {
			if (c == '(' || c == '[')
				inacct++;	/* TRUE should suffice	*/
			else if (c == ']' || c == ')')
				inacct--;	/* FALSE should suffice	*/
			else if (!inacct && forcefiletype && c == '.')
				skipit = TRUE;	/* Skip over ".ext;foo"	*/
			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);
}

