/*
 * This file sends out and recieves REXX commands (well recieving them means
 * we hand the off to excline, but...)
 */
#include "rexx.h"

#ifdef	REXX

#include <exec/types.h>
#include <exec/ports.h>
#include <rexx/storage.h>
#include <rexx/rxslib.h>
#ifdef LATTICE
#include <proto/exec.h>
#include <proto/intuition.h>
#else
#include <functions.h>
#endif

#undef	TRUE
#undef	FALSE
#include "def.h"
#include "line.h"
#include "buffer.h"
#include "window.h"
#include "macro.h"
#include "region.h"

#ifdef	ANSI
#undef	EOF			/* Strange..., but stdio complains about it */
#include <stdio.h>		/* Only if we've got sprintf in here */
#include <stdlib.h>
#include <fcntl.h>
#undef	HUGE			/* Collides with math.h */
#include <math.h>
#include <string.h>
#endif

/*
 * The pointer to the Rexx library, which we really want when dealing with
 * Rexx, and some return value for messages.
 */
struct Library *RexxSysBase;
#define	FAIL_LOCKED	3
#define FAIL_NOREXX	4

/*
 * My static functions.
 */
static int rexxdocommand PROTO((char *, long));
static int getrexxargs PROTO((struct RexxMsg *, int));
static int rexxread PROTO((int));
static int rexxstemint PROTO((char *, int, int));
static int rexxstemstring PROTO((char *, int, char *, int));
static int findline PROTO((struct line *));
static struct MsgPort *lockport PROTO((char *));
static int spinport PROTO((struct MsgPort *));

#ifdef	LATTICE
long __stdargs SetRexxVar PROTO((struct RexxMsg *, char *, char *, long)) ;
#else
long SetRexxVar PROTO((struct RexxMsg *, char *, char *, long)) ;
static char *strupr PROTO((char *));
#endif

struct RexxArg *CreateArgstring PROTO((char *, long)) ;
long LengthArgstring PROTO((struct RexxArg *)) ;
void DeleteArgstring PROTO((struct RexxArg *)) ;
struct RexxMsg *CreateRexxMsg PROTO((struct MsgPort *, char *, char *)) ;
void ClearRexxMsg PROTO((struct RexxMsg *, long)) ;
void DeleteRexxMsg PROTO((struct RexxMsg *)) ;

/*
 * Pointer to rexx macro to run on exit.
 */
static char    *RexxExitCmd;

/*
 * Commands that return results can only do so if the RexxMsg that caused
 * them to be executed is willing to take results. To make this test easy,
 * the RexxMsg is stored in CurrentMsg between it's being recieved and
 * sending out the reply. Any other time, it will be NULL, and the Rexx
 * command check for that if they shouldn't be executed unless there is a
 * RexxMsg active. In addition, rexxlock needs to reply to this message
 * indicating that the lock succeeded before going into locked mode, so it
 * needs access to CurrentMsg. Result strings get passed from rexx commands
 * back to the script - and the other way, in the case of rexxdoregion - via
 * RexxResult. It's normally NULL, unless the executed command set a result,
 * or a macro that we were still locked to on exit returned a result. In
 * which case, it points to the RexxArgstring for the result. The consumer is
 * responsible for deleting it, and the producer is responsible for creating
 * it.
 * 
 */
static struct RexxMsg *CurrentMsg = NULL;
static struct RexxArg *RexxResult = NULL;

/*
 * Things for dealing with asynch REXX messages. rexxport is the port that
 * those messages come in on, and it's name is mgname. Since rexxport must be
 * checked for messages in the event loop, it's not static. RepliesNeeded is
 * the number of reply messages we need to see on rexxport. Lockadd is the
 * address of the port name that a given macro is locked to, or NULL if we're
 * not locked. ADDSIZE is the size of the port name we use for dynamically
 * allocated ports
 */
struct MsgPort *rexxport = NULL;
static char     mgname[5] = "mg\0\0";
static unsigned RepliesNeeded = 0;
static char    *lockadd = NULL;
#define ADDSIZE	8

/*
 * This routine dispatches any REXX messages coming in. However, to allow for
 * REXX commands that start other REXX commands, we must save and restore the
 * environment (CurrentMsg).
 */
int
disprexx(inport)
	struct MsgPort *inport;
{
	register int    s, t;
	register struct RexxMsg *savemsg;

	savemsg = CurrentMsg;

	for (;;) {
		RexxResult = NULL;
		/* Check for commands */
		if (inport && (CurrentMsg = (struct RexxMsg *) GetMsg(inport))) {
			if (CurrentMsg->rm_Node.mn_Node.ln_Type != NT_REPLYMSG)
				s = excline(ARG0(CurrentMsg),
					LengthArgstring((struct RexxArg *) ARG0(CurrentMsg)));
		}
		/* And then for replies or asynch commands */
		else if (CurrentMsg = (struct RexxMsg *) GetMsg(rexxport)) {
			if (CurrentMsg->rm_Node.mn_Node.ln_Type != NT_REPLYMSG)
				if (!inport)
					s = FAIL_NOREXX;
				else if (lockadd)
					s = FAIL_LOCKED;
				else
					s = excline(ARG0(CurrentMsg),
						LengthArgstring((struct RexxArg *) ARG0(CurrentMsg)));
		}
		if (CurrentMsg == NULL)
			break;
		if (CurrentMsg->rm_Node.mn_Node.ln_Type == NT_REPLYMSG) {
			RepliesNeeded -= 1;
			s = CurrentMsg->rm_Result1 < 2
				? 1 - CurrentMsg->rm_Result1
				: ABORT;
			t = ((char *)CurrentMsg->rm_CommAddr == lockadd);
			if (s == TRUE && (CurrentMsg->rm_Action & RXFF_RESULT)
			    && CurrentMsg->rm_Result2 != NULL)
				/* Aha - a result. Deal with it. */
				if (t)
					RexxResult = (struct RexxArg *) CurrentMsg->rm_Result2;
				else
					DeleteArgstring((struct RexxArg *)
						    CurrentMsg->rm_Result2);
			/* Free the memory for this message */
			FreeMem(CurrentMsg->rm_CommAddr, ADDSIZE);
			ClearRexxMsg(CurrentMsg, 16);
			DeleteRexxMsg(CurrentMsg);
			if (!t)
				continue;
			CurrentMsg = savemsg;
			lockadd = NULL;
			return s;
		}
		if (s < 2)
			s = 1 - s;
		CurrentMsg->rm_Result1 = s;
		CurrentMsg->rm_Result2 = (RexxResult != NULL)
			? (LONG) RexxResult
			: 0L;
		ReplyMsg((struct Message *) CurrentMsg);
	}
	CurrentMsg = savemsg;
	return -1;
}

void
openrexx()
{
	register short  i, j;

	if ((RexxSysBase = OpenLibrary(RXSNAME, 0)) == NULL)
		return;
	srand((unsigned) &lockadd);	/* Just to make things change */
	for (i = 33; i < 127; i++) {
		for (j = 33; j < 127; j++) {
			Forbid();
			if (FindPort(mgname) == NULL)
				rexxport = CreatePort(mgname, 0L);
			Permit();
			if (rexxport != NULL)
				return;
			mgname[2] = j;
		}
		mgname[3] = i;
	}

	/* No port opened, so we don't do Rexx, so we close RexxSysBase */
	CloseLibrary(RexxSysBase);
	RexxSysBase = NULL;
}

void
closerexx()
{

	if (!RexxSysBase)
		return;		/* No Rexx to close */
	if (RexxExitCmd != NULL)
		rexxdocommand(RexxExitCmd, RXCOMM);
	if (rexxport) {
		while (RepliesNeeded) {
			WaitPort(rexxport);
			disprexx(NULL);
		}
		DeletePort(rexxport);
	}
	CloseLibrary(RexxSysBase);
}
/*
 * Commands that make Rexx macros execute.
 */

rexxexitcommand(f, n)
{
	int             s;
	static char     exitcom[NLINE];

	if (!RexxSysBase)
		return FALSE;
	if ((s = ereply("REXX command to run on exit: ", exitcom, NLINE)) != TRUE)
		return s;
	RexxExitCmd = exitcom;
	return TRUE;
}

rexxcommand(f, n)
{
	int             s;
	char            cmd[NLINE];

	if (!RexxSysBase)
		return FALSE;
	if ((s = ereply("REXX command: ", cmd, NLINE)) != TRUE)
		return s;

	return rexxdocommand(cmd, f & FFUNIV ? RXCOMM | RXFF_TOKEN : RXCOMM);
}

rexxcomregion(f, n)
	int             f, n;
{
	char           *out;
	register RSIZE  length, size;
	register struct line *lp;
	int             status, insertres;
	char           *string;
	struct region   reg;

	if (!RexxSysBase)
		return FALSE;
	if ((length = getregion(&reg)) != TRUE)
		return length;
	size = reg.r_size;

	if ((out = malloc(size + 1)) == NULL) {
		ewprintf("Can't get %d bytes", size + 1);
		return FALSE;
	}
	string = out;		/* Save the start of the thing... */

	/* Copy the region to out */
	lp = reg.r_linep;
	length = llength(lp) - reg.r_offset;
	bcopy(&ltext(lp)[reg.r_offset], out, length);
	size -= length;
	out += length;

	while (size > 0) {
		/* Add a newline to the output string */
		*out++ = '\n';
		size -= 1;

		/* Add part/all of the current line to the output string */
		lp = lforw(lp);
		length = llength(lp);
		if (length > size)
			length = size;
		bcopy(ltext(lp), out, length);
		out += length;
		size -= length;
	}
	*out = '\0';

	/* Figure out how many arguments we really have */
	insertres = (n <= 0);
	if ((f & FFARG) == 0)
		n = 0;
	else if (insertres)
		n = -n;
	if (n > MAXRMARG) {
		ewprintf("Can only have %d args for rexx commands", MAXRMARG);
		n = MAXRMARG;
	}
	status = rexxdocommand(string, RXFUNC | RXFF_RESULT | RXFF_STRING | n);

	/* Deal with result string if we've got one */
	if (status == TRUE && RexxResult != NULL) {
		if (insertres)
			sinsert((char *) RexxResult);
		else
			ewprintf("Result was: %s", (char *) RexxResult);
		DeleteArgstring(RexxResult);
	}
	RexxResult = NULL;
	return status;
}
/*
 * rexxgetargs - get N args from the user, returning ABORT if they abort,
 * etc.
 */
int
getrexxargs(msg, n)
	struct RexxMsg *msg;
	int             n;
{
	register int    count, status = TRUE;
	char            arg[NLINE];

	for (count = 1; count <= n; count += 1) {
		if ((status = ereply("Arg #%d? ", arg, NLINE, count)) != TRUE
		    || (msg->rm_Args[count] =
			(STRPTR) CreateArgstring(arg, strlen(arg))) == NULL)
			break;
	}
	return status;
}

/*
 * Finish filling in the msg, send the message to Rexx, and then call
 * spinport to get the work done. We get here from commands that can be
 * issued by macros, so we can be run when locked. So we have to save lockadd
 * upon entry, and restore it before we exit.
 */
static
rexxdocommand(command, action)
	char           *command;
	long            action;
{
	register struct RexxMsg *out = NULL;
	struct MsgPort *macport = NULL, *rexx;
	int             s;
	char           *savelock;

	if (rexxport == NULL)
		return FALSE;
	savelock = lockadd;
	if ((lockadd = AllocMem(ADDSIZE, 0L)) == NULL) {
		ewprintf("Can't get %d bytes", ADDSIZE);
		goto error;
	}
	if ((macport = lockport(lockadd)) == NULL)
		goto error;
	if ((out = CreateRexxMsg(rexxport, "mg", lockadd)) == NULL
	    || (out->rm_Args[0] = (STRPTR) CreateArgstring(command, strlen(command))) == NULL
	    || ((action & RXFUNC) && (s = getrexxargs(out, action & 0xF)) != TRUE))
		goto error;
	out->rm_Action = action;

	Forbid();
	rexx = FindPort("REXX");
	if (rexx)
		PutMsg(rexx, (struct Message *) out);
	Permit();
	if (!rexx) {
		s = FALSE;
		goto error;
	}
	RepliesNeeded += 1;

	/* Spin until macro finishes, or unlocked */
	s = spinport(macport);
	lockadd = savelock;
	return s;

	/* Something died, so clean up and tell the use about it */
error:
	if (lockadd)
		FreeMem(lockadd, ADDSIZE);
	if (macport)
		DeletePort(macport);
	if (out) {
		ClearRexxMsg(out, 16);
		DeleteRexxMsg(out);
	}
	lockadd = savelock;
	return s;
}

/*
 * Set lockadd to the name of a public port, and return the address of the
 * port.
 */
static struct MsgPort *
lockport(portname)
	char           *portname;
{
	struct MsgPort *port = NULL;

	while (port == NULL) {
		sprintf(portname, "mg_%04d", rand() % 10000);
		Forbid();
		if (FindPort(portname) == NULL)
			port = CreatePort(portname, 0L);
		Permit();
	}
	return port;
}

/*
 * Spin on the port we got passed until either rexxunlock is called, or until
 * we get a reply message for the macro we started. In the fomer case, return
 * true; in the latter, return the value the macro returned.
 */
static int
spinport(port)
	struct MsgPort *port;
{
	register int    s;
	register int    sigbits;

	sigbits = (1l << rexxport->mp_SigBit) | (1L << port->mp_SigBit);
	do {
		Wait(sigbits);
	} while ((s = disprexx(port)) < 0 && lockadd);

	DeletePort(port);
	if (lockadd)
		rexxunlock(FFRAND, 1);
	else
		s = TRUE;
	return s;
}

/*
 * rexx locking & unlocking functions. Rexxlock creates a new port for the
 * macro we're locking, and calls spinport on that port. Rexxunlock just
 * toggles the global lockadd to indicate we're not locked. Both return the
 * public port name that will now accept messages to the invoking rexx
 * program.
 */
rexxlock(f, n)
	int             f, n;
{
	struct MsgPort *port;
	char            lockname[ADDSIZE];

	/* Make sure we got called properly */
	if (!CurrentMsg || lockadd || !(CurrentMsg->rm_Action & RXFF_RESULT))
		return FALSE;

	if ((port = lockport(lockname)) == NULL)
		return FALSE;

	CurrentMsg->rm_Result1 = 0;
	lockadd = (char *) CreateArgstring(lockname, strlen(lockname));
	CurrentMsg->rm_Result2 = (long) lockadd;
	ReplyMsg((struct Message *) CurrentMsg);

	/* Tun off the reply to this message in disprexx */
	CurrentMsg = NULL;
	return spinport(port);
}

rexxunlock(f, n)
	int             f, n;
{

	lockadd = NULL;
	if (!(f & FFRAND) && CurrentMsg && CurrentMsg->rm_Action & RXFF_RESULT)
		RexxResult = CreateArgstring(mgname, strlen(mgname));
	return TRUE;
}
/*
 * rexx echo line functions.
 */
rexxdisplay(f, n)
	int             f, n;
{
	struct macro   *save;
	char            buf[NLINE];

	if (!CurrentMsg)
		return FALSE;
	eread("", buf, NLINE, 0);
	save = inmacro;
	inmacro = NULL;
	ewprintf("%s", buf);
	inmacro = save;
	return TRUE;
}

rexxrequest(f, n)
	int             f, n;
{

	return rexxread(EFNEW);
}

rexxreqbuf(f, n)
	int             f, n;
{

	return rexxread(EFNEW | EFBUF);
}

rexxreqfunc(f, n)
	int             f, n;
{

	return rexxread(EFNEW | EFFUNC);
}

rexxreqmacro(f, n)
	int             f, n;
{

	return rexxread(EFNEW | EFMACRO);
}

static
rexxread(flag)
{
	char            buf[NLINE], inbuf[NLINE];
	struct macro   *save;
	int             status;

	if (!CurrentMsg)
		return FALSE;
	eread("", buf, NLINE, 0);
	save = inmacro;
	inmacro = NULL;
	if ((status = eread("%s", inbuf, NLINE, flag, buf)) == TRUE
	    && (CurrentMsg->rm_Action & RXFF_RESULT))
		RexxResult = CreateArgstring(inbuf, strlen(inbuf));
	inmacro = save;
	return status;
}
/*
 * Misc. rexx functions.
 */
rexxinsert(f, n)
	int             f, n;
{
	KCHAR    	c;

	if (!CurrentMsg)
		return FALSE;
	c = getkey(SEESCR);
	while (c != SOFTCR) {
		if (((c == '\n') ? lnewline() : linsert(1, c)) != TRUE)
			return FALSE;
		c = getkey(SEESCR);
		}
	return TRUE;
}

rexxline(f, n)
	int             f, n;
{
	register struct line *clp;

	if ((CurrentMsg->rm_Action & RXFF_RESULT) == 0)
		return TRUE;
	if ((f & FFARG) == 0)
		clp = curwp->w_dotp;
	else if (n > 0) {
		clp = lforw(curbp->b_linep);	/* "clp" is first line	 */
		while (--n > 0) {
			if (lforw(clp) == curbp->b_linep)
				return FALSE;
			clp = lforw(clp);
		}
	} else {
		clp = lback(curbp->b_linep);	/* clp is last line */
		while (n < 0) {
			if (lback(clp) == curbp->b_linep)
				return FALSE;
			clp = lback(clp);
			n++;
		}
	}
	RexxResult = CreateArgstring(ltext(clp), llength(clp));
	return TRUE;
}

/*
 * A pair of tools for manipulating the window we use.
 */
rexxwait(f, n)
	int             f, n;
{

	if (!RexxSysBase)
		return FALSE;
	tthide(FALSE);
	WaitPort(rexxport);
	ttshow(FALSE);
	/* Go run the command and update the screen */
	disprexx(rexxport);
	update();
	return TRUE;
}

rexxtopwindow(f, n)
	int             f, n;
{
	extern struct Window *EmW;

	WindowToFront(EmW);
	return TRUE;
}

/*
 * rexx stem-variable functions.
 */
rexxwindow(f, n)
	int             f, n;
{
	char            stem[NLINE];
	register int    count;

	if (!CurrentMsg)
		return FALSE;
	eread("", stem, NLINE, 0);

	/* Upcase the variable name: required by the interface */
	strupr(stem);

	/* Window Dimensions */
	if (rexxstemint(stem, 1, curwp->w_ntrows) == FALSE)
		count = 0;
	else if (rexxstemint(stem, 2, ncol) == FALSE)
		count = 1;
	else if (rexxstemint(stem, 3, findline(curwp->w_linep)) == FALSE)
		count = 2;
	else if (curwp->w_bufp == NULL || curwp->w_bufp->b_bname == NULL
	    || rexxstemstring(stem, 4, curwp->w_bufp->b_bname, -1) == FALSE)
		count = 3;
	else if (rexxstemstring(stem, 5, curwp->w_bufp->b_fname, -1) == FALSE)
		count = 4;
	else
		count = 5;	/* Did them ALL! */

	rexxstemint(stem, 0, count);
	return count == 5;
}

rexxpoint(f, n)
	int             f, n;
{
	char            stem[NLINE];
	register int    count;

	if (!CurrentMsg)
		return FALSE;
	eread("", stem, NLINE, 0);

	/* Upcase the variable name: required by the interface */
	strupr(stem);

	if (rexxstemint(stem, 1, findline(curwp->w_dotp)) == FALSE)
		count = 0;
	else if (rexxstemint(stem, 2, curwp->w_doto + 1) == FALSE)
		count = 1;
	else if (rexxstemstring(stem, 3, ltext(curwp->w_dotp), llength(curwp->w_dotp)) == FALSE)
		count = 2;
	else
		count = 3;

	rexxstemint(stem, 0, count);
	return count == 3;
}

rexxmark(f, n)
	int             f, n;
{
	char            stem[NLINE];
	register int    count;

	if (!CurrentMsg)
		return FALSE;
	eread("", stem, NLINE, 0);

	if (curwp->w_markp == NULL) {	/* No mark */
		rexxstemint(stem, 0, 0);
		return FALSE;
	}
	/* Upcase the variable name: required by the interface */
	strupr(stem);

	if (rexxstemint(stem, 1, findline(curwp->w_markp)) == FALSE)
		count = 0;
	else if (rexxstemint(stem, 2, curwp->w_marko + 1) == FALSE)
		count = 1;
	else if (rexxstemstring(stem, 3, ltext(curwp->w_markp), llength(curwp->w_markp)) == FALSE)
		count = 2;
	else
		count = 3;

	rexxstemint(stem, 0, count);
	return count == 3;
}

rexxbuffer(f, n)
	int             f, n;
{
	char            stem[NLINE];
	register int    count;
	register RSIZE  chars, lines;
	RSIZE           mark, point;
	register struct line *clp;

	if (!CurrentMsg)
		return FALSE;
	eread("", stem, NLINE, 0);

	/* Upcase the variable name: required by the interface */
	strupr(stem);

	/* Find the line & dot numbers */
	clp = lforw(curbp->b_linep);
	mark = point = chars = 0;
	for (lines = 1;; lines += 1) {
		chars += llength(clp);
		if (clp == curwp->w_dotp)
			point = lines;
		if (clp == curwp->w_markp)
			mark = lines;
		if ((clp = lforw(clp)) == curbp->b_linep)
			break;
		chars += 1;	/* Newline, if present */
	}

	if (curbp->b_bname == NULL
	    || rexxstemstring(stem, 1, curbp->b_bname, -1) == FALSE)
		count = 0;
	else if (rexxstemstring(stem, 2, curbp->b_fname, -1) == FALSE)
		count = 1;
	else if (rexxstemint(stem, 3, lines) == FALSE)
		count = 2;
	else if (rexxstemint(stem, 4, chars) == FALSE)
		count = 3;
	else if (rexxstemint(stem, 5, point) == FALSE)
		count = 4;
	else if (mark == 0 || rexxstemint(stem, 6, mark) == FALSE)
		count = 5;
	else
		count = 6;

	rexxstemint(stem, 0, count);
	return count == 6;
}

rexxbuflist(f, n)
	int             f, n;
{
	char            stem[NLINE], var[NLINE + 10], value[NLINE];
	register int    count;
	register struct buffer *bp;

	if (!CurrentMsg)
		return FALSE;
	eread("", stem, NLINE, 0);

	/* Upcase the variable name: required by the interface */
	strupr(stem);

	for (count = 1, bp = bheadp; bp; bp = bp->b_bufp, count += 1) {
		if (bp->b_bname == NULL)
			break;
		sprintf(var, "%s.%d.NAME", stem, count);
		strcpy(value, bp->b_bname);
		if (SetRexxVar(CurrentMsg, var, value, strlen(value)))
			break;
		sprintf(var, "%s.%d.FILE", stem, count);
		strcpy(value, bp->b_fname);
		if (SetRexxVar(CurrentMsg, var, value, strlen(value)))
			break;
		sprintf(var, "%s.%d.STATUS", stem, count);
		value[0] = '\0';
		if (bp->b_flag & BFCHG)
			strcat(value, "CHANGED ");
		if (bp == curbp)
			strcat(value, "CURRENT");
		if (SetRexxVar(CurrentMsg, var, value, strlen(value)))
			break;
	}

	rexxstemint(stem, 0, count - 1);
	return bp == NULL;
}

rexxregion(f, n)
	int             f, n;
{
	register struct line *lp;
	register RSIZE  count, length;
	char            stem[NLINE], out[NLINE + 2];
	struct region   reg;

	if (!CurrentMsg)
		return FALSE;
	eread("", stem, NLINE, 0);
	strupr(stem);

	if ((count = getregion(&reg)) != TRUE) {
		rexxstemint(stem, 0, 0);
		return count;
	}
	lp = reg.r_linep;

	/* Set up end of first line */
	length = llength(lp) - reg.r_offset;
	bcopy(&ltext(lp)[reg.r_offset], out, length);
	count = 1;

	while (reg.r_size > length) {
		out[length] = '\n';
		if (rexxstemstring(stem, count, out, length + 1) == FALSE)
			reg.r_size = -1;
		else {
			reg.r_size -= length + 1;
			count += 1;
			lp = lforw(lp);
			length = llength(lp);
			bcopy(ltext(lp), out, length);
		}
	}

	/* Now, if needed, do head of last line */
	if (reg.r_size <= 0)
		count -= 1;
	else if (rexxstemstring(stem, count, out, reg.r_size) == FALSE) {
		reg.r_size = -1;
		count -= 1;
	}
	rexxstemint(stem, 0, count);
	return reg.r_size != -1;
}

static int
rexxstemint(stem, count, in)
	char           *stem;
	int             count, in;
{
	char            var[NLINE + 2], value[NLINE];

	sprintf(var, "%s.%d", stem, count);
	sprintf(value, "%d", in);
	return SetRexxVar(CurrentMsg, var, value, strlen(value)) == 0;
}

static int
rexxstemstring(stem, count, value, length)
	char           *stem;
	int             count;
	char           *value;
	int             length;
{
	char            var[NLINE + 2];

	sprintf(var, "%s.%d", stem, count);
	if (length < 0)
		length = strlen(value);
	return SetRexxVar(CurrentMsg, var, value, length) == 0;
}

static int
findline(lp)
	struct line    *lp;
{
	register struct line *slp;
	register int    count;

	for (slp = curbp->b_linep, count = 0;
	     slp != lp;
	     slp = lforw(slp), count += 1);
	return count;
}

#ifndef LATTICE
static char *
strupr(s)
	char *s;
{
	char *c;
	extern char toupper();

	c=s;
	while(*s) {
		*s = toupper(*s);
	        s++;
	}
	return(c);
}
#endif
#else
#include "nullfile.h"
#endif
