/*
 *				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		6	/* Vax native				*/
#define UX_NOBJ		6	/* Unix native				*/
/*
 * These tables define the library list to be appended to compilations
 */

static char *vxlib[] = {
	"LIBS", "UTLIB", "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 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", 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 && *--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:
		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 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 bad optype at save link string", 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);
}

