[comp.sources.amiga] v89i002: launch - start workbench tasks from cli

page@swan.ulowell.edu (Bob Page) (01/31/89)

Submitted-by: peter@sugar.uu.net (Peter da Silva)
Posting-number: Volume 89, Issue 2
Archive-name: workbench/launch.1

This is an example of how you run a workbench application. As you can
see, it's pretty complex... even with the stuff encapsulated like
this. However, once you get it working it works just fine... unlike
the CLI. Just ask anyone who's tried to run a program under the CLI
from, say, runback.

[Originally posted Sept. 88 in alt.sources.amiga.  ..Bob]

#	This is a shell archive.
#	Remove everything above and including the cut line.
#	Then run the rest of the file through sh.
#----cut here-----cut here-----cut here-----cut here----#
#!/bin/sh
# shar:    Shell Archiver
#	Run the following text with /bin/sh to create:
#	Makefile
#	launch.c
#	launch.doc
#	util.c
#	wbcleanup.c
#	wbfree.c
#	wblaunch.c
# This archive created: Mon Jan 30 15:27:38 1989
cat << \SHAR_EOF > Makefile
TEXT= launch.doc launch.c wblaunch.c wbfree.c wbcleanup.c util.c Makefile
BIN=  launch wbcleanup
LOBJS= launch.o wblaunch.o wbfree.o util.o
WOBJS= wbcleanup.o wbfree.o

.SUFFIXES: .c .o .h .x

.c.o:
	-delete $*.o
	cc +P -B -DAMIGA $*.c

all: $(BIN)

launch: $(LOBJS)
	-delete launch
	ln -o launch $(LOBJS) -lcl32

wbcleanup: $(WOBJS)
	-delete wbcleanup
	ln -o wbcleanup $(WOBJS) -lcl32

launch.arc: $(TEXT) $(BIN)
	arc a launch  $(TEXT) $(BIN)

launch.shar: $(TEXT)
	shar >launch.shar $(TEXT)

clean:
	-delete #?.o #?.bak #?.arc #?.shar launch wbcleanup
SHAR_EOF
cat << \SHAR_EOF > launch.c
#include <libraries/dosextens.h>
#include <exec/ports.h>
#include <workbench/startup.h>
#include <workbench/workbench.h>
#include <stdio.h>

struct MsgPort *FindPort(), *CreatePort();
struct WBStartup *Launch(), *GetMsg();
struct Process *FindTask();

main(ac, av)
int ac;
char **av;
{
	struct MsgPort *rport;
	struct Process *me;
	struct CommandLineInterface *cli;
	struct WBStartup *startup;

	long stack;
	int pri;
	char *win;
	int wait;
	int got_arg;

	stack = 0;
	pri = 0;
	win = 0;
	wait = 0;

	if(ac < 2 || strcmp(av[1], "?") == 0) {
		fprintf(stderr,
"launch [stack nnnn] [window name] [priority PRI] [wait] program [arguments]\n"
			);
		exit((ac<2)?5:0);
	}
	got_arg = 1;
	while(got_arg == 1 && ac >= 3) {
		got_arg = 0;
		if(dictcmp(av[1], "window") == 0) {
			win = av[2];
			av += 2;
			ac -= 2;
			got_arg = 1;
		} else if(dictcmp(av[1], "stack") == 0) {
			stack = atol(av[2]);
			av += 2;
			ac -= 2;
			got_arg = 1;
		} else if(dictcmp(av[1], "priority") == 0) {
			pri = atoi(av[2]);
			av += 2;
			ac -= 2;
			got_arg = 1;
		} else if(dictcmp(av[1], "wait") == 0) {
			wait = 1;
			av ++;
			ac --;
			got_arg = 1;
		}
	}

	if(wait == 0) {
		if(!(rport = FindPort("Workbench.Cleanup"))) {
			fprintf(stderr, "Could not find Workbench.Cleanup port.\n");
			fprintf(stderr, "Are you sure you ran wbcleanup?\n");
			exit(20);
		}
	} else {
		if(!(rport = CreatePort(0, 0))) {
			printf("Can't create reply port.\n");
			exit(20);
		}
	}
	if(stack == 0) {
		me = FindTask(0);
		if(me->pr_CLI) {
			cli = (struct CommandLineInterface *)(me->pr_CLI<<2);
			stack = cli->cli_DefaultStack<<2; /* DefaultStack is in Longwords */
		} else
			stack = me->pr_StackSize;
	}
	if(!Launch(rport, av[1], &av[1], ac-1, pri, win, stack)) {
		fprintf(stderr, "Could not launch %s error %d\n", av[1], IoErr());
		exit(20);
	}
	if(wait != 0) {
		WaitPort(rport);
		startup = GetMsg(rport);
		FreeStartup(startup);
		DeletePort(rport);
	}
	exit(0);
}
SHAR_EOF
cat << \SHAR_EOF > launch.doc
LAUNCH -- start a workbench application from the CLI.

This is an example of how you run a workbench application. As you can see,
it's pretty complex... even with the stuff encapsulated like this. However,
once you get it working it works just fine... unlike the CLI. Just ask anyone
who's tried to run a program under the CLI from, say, runback.

Usage:
	run wbcleanup (only once, in your CLI)
	launch [options] program [filename]...

	Loads and executes program in a workbench environment, then returns to
	the CLI.

Options:
	stack nnnn
		Specifies initial stack for the program.
	priority pppp
		Specifies initial priority for the program.
	window wwww
		Specifies TOOLWINDOW for the program.
	wait
		Wait for reply, rather than passing it off to wbcleanup.

wblaunch.c:
	message = Launch(port, program, argv, argc, pri, win, stack);

	Launch loads and executes program in a workbench environment. The
	specified port is where the startup message will be returned. The
	arguments will be passed to the program, workbench-style (directory
	lock and filename). The first argument should be the same as the
	program, but it's possible to spawn a program in an illicit way. If
	you don't want to specify a window, use NULL. You need to specify a
	stack.

	If you don't want to specify a port, run wbcleanup and use the public
	message port "Workbench.Cleanup".

	You really don't need to do anything with the message when it's
	returned from Launch, but it's a convenient non-zero value to indicate
	success.

wbfree.c:
	FreeStartup(message);

	FreeStartup takes a startup message created by Launch and releases all
	the public memory, removes the lock, and so on. It should be called
	after the startup message is returned.

launch.c:
	This contains the mainline for the 'launch' program. Most of it is
	arg parsing.

wbcleanup.c:
	This contains the mainline for the 'wbcleanup' program.

util.c:
	dictcmp(s1, s2);

	An analog to strcmp, but ignores case differences in words. Not a full
	dictionary comparison, since it doesn't sort numbers by their spelling.
SHAR_EOF
cat << \SHAR_EOF > util.c
#include <ctype.h>

dictcmp(s1, s2)
char *s1;
char *s2;
{
	while(*s1 && *s2) {
		char c1 = *s1;
		char c2 = *s2;
		if(isupper(c1)) c1 = tolower(c1);
		if(isupper(c2)) c2 = tolower(c2);
		if(c1 != c2)
			return c1-c2;
		s1++;
		s2++;
	}
	return *s1 - *s2;
}
SHAR_EOF
cat << \SHAR_EOF > wbcleanup.c
#include <exec/memory.h>
#include <workbench/startup.h>
#include <workbench/workbench.h>

struct MsgPort *rport, *CreatePort();
struct WBStartup *startup, *GetMsg();

main()
{
	if(!(rport = CreatePort("Workbench.Cleanup", 0))) {
		printf("Can't create Workbench.Cleanup port.\n");
		exit(20);
	}
	while(1) {
		WaitPort(rport);
		startup = GetMsg(rport);
		FreeStartup(startup);
	}
}
SHAR_EOF
cat << \SHAR_EOF > wbfree.c
#include <exec/memory.h>
#include <workbench/startup.h>
#include <workbench/workbench.h>

FreeStartup(msg)
struct WBStartup *msg;
{
	/* Free the files. Assumes they have been allocated via Launch, so
	 * they're just AllocMemmed. UnLock all the Locks, and Free the
	 * names.
	 */
	if(msg->sm_ArgList) {
		int i;
		for(i = 0; i < msg->sm_NumArgs; i++) {
			if(msg->sm_ArgList[i].wa_Lock)
				UnLock(msg->sm_ArgList[i].wa_Lock);
			if(msg->sm_ArgList[i].wa_Name)
				FreeMem(msg->sm_ArgList[i].wa_Name,
					strlen(msg->sm_ArgList[i].wa_Name)+1);
		}
		FreeMem(msg->sm_ArgList,
			sizeof(struct WBArg) * msg->sm_NumArgs);
	}

	/* Here goes the program...
	 */
	if(msg->sm_Segment)
		UnLoadSeg(msg->sm_Segment);

	/* And the name of the window. Again, assuming it's allocated by
	 * Launch, so it just fits in its AllocMemmed buffer.
	 */
	if(msg->sm_ToolWindow)
		FreeMem(msg->sm_ToolWindow,
			strlen(msg->sm_ToolWindow)+1);

	/* And finally the message itself */
	FreeMem(msg, sizeof(struct WBStartup));
}
SHAR_EOF
cat << \SHAR_EOF > wblaunch.c
#include <exec/memory.h>
#include <workbench/startup.h>
#include <workbench/workbench.h>

UBYTE *AllocMem();
BPTR LoadSeg();
struct MsgPort *CreateProc();

/* Launch(rport, name, argv, argc, pri, win, stack) -- launch a program.
 *
 * Launch builds a startup message, loads a program, and launches it.
 *
 * Arguments:
 *	rport -- reply port to receive startup message back when the program
 *	         finishes.
 *	name  -- File name for the program to load.
 *	argv  -- list of filenames to pass to loaded program.
 *	argc  -- length of argument list.
 *	pri   -- priority of the new process.
 *	win   -- ToolWindow for the new process, or NULL.
 *	stack -- Stack size for the new process.
 *
 * argv[0] should be the same as the program. pri should normally be 0.
 * stack should normally be at least 4K.
 */
struct WBStartup *
Launch(rport, name, argv, argc, pri, win, stack)
struct MsgPort *rport;
char *name;
char **argv;
int argc;
long pri;
char *win;
ULONG stack;
{
	ULONG flock;
	struct WBStartup *msg;
	char *s, *namep;
	int i;

	if(!rport)
		return 0;

	/* Get some space to work in -- the startup message */
	msg = (struct WBStartup *)
		AllocMem(sizeof(struct WBStartup), MEMF_PUBLIC|MEMF_CLEAR);
	if(!msg)
		return 0;

	/* Now load the program */
	msg->sm_Segment = LoadSeg(name);
	if(!msg->sm_Segment) {
		FreeStartup(msg);
		return 0;
	}

	/* Allocate and link in the window description */
	if(win) {
		msg->sm_ToolWindow = (char *)AllocMem(strlen(win)+1, MEMF_PUBLIC);
		if(!msg->sm_ToolWindow) {
			FreeStartup(msg);
			return 0;
		}
		strcpy(msg->sm_ToolWindow, win);
	} else
		msg->sm_ToolWindow = 0;

	/* Allocate the arg list */
	msg->sm_ArgList = (struct WBArg *)
		AllocMem(sizeof(struct WBArg) * argc, MEMF_PUBLIC | MEMF_CLEAR);
	if(!msg->sm_ArgList) {
		FreeStartup(msg);
		return 0;
	}

	/* Empty out all args, just in case this aborts, so cleanup
	 * can clean it up
	 */
	msg->sm_NumArgs = argc;
	for(i = 0; i < argc; i++) {
		msg->sm_ArgList[i].wa_Lock = 0;
		msg->sm_ArgList[i].wa_Name = 0;
	}

	/* Get lock and name for wbargs */
	for(i = 0; i < argc; i++) {
		flock = Lock(argv[i]);
		if(!flock) {
			FreeStartup(msg);
			return 0;
		}
		msg->sm_ArgList[i].wa_Lock = ParentDir(flock);
		UnLock(flock);
		if(!msg->sm_ArgList[i].wa_Lock) {
			FreeStartup(msg);
			return 0;
		}

		/* Get the tail end of the file name. I could also do an Examine
		 * on the lock, but didn't want to bother allocating a File Info
		 * Block that wouldn't fit under the tracking via the startup
		 * message.
		 */
		namep = argv[i];
		for(s = argv[i]; *s; s++)
			if(*s=='/' || *s==':')
				namep = s+1;
		msg->sm_ArgList[i].wa_Name =
			(char *)AllocMem(strlen(namep)+1, MEMF_PUBLIC);
		if(!msg->sm_ArgList[i].wa_Name) {
			FreeStartup(msg);
			return 0;
		}
		strcpy(msg->sm_ArgList[i].wa_Name, namep);
	}

	/* Create the process. It is now running, but will wait for the
	 * startup message.
	 */
	msg->sm_Process =
		CreateProc(msg->sm_ArgList[0].wa_Name, pri, msg->sm_Segment, stack);
	if(!msg->sm_Process) {
		FreeStartup(msg);
		return 0;
	}

	/* Initialise the message part of the startup message, and pass it to
	 * the process. At this point it will actually start ding work
	 */
	msg->sm_Message.mn_ReplyPort = rport;
	msg->sm_Message.mn_Length = sizeof(struct WBStartup);
	msg->sm_Message.mn_Node.ln_Type = NT_MESSAGE;

	PutMsg(msg->sm_Process, msg);

	/* return message. Not very useful, but it's as meaningful a response as
	 * any.
	 */
	return msg;
}
SHAR_EOF
#	End of shell archive
exit 0
-- 
Bob Page, U of Lowell CS Dept.  page@swan.ulowell.edu  ulowell!page
Have five nice days.