[comp.sources.amiga] RunBack

ain@j.cc.purdue.edu (Patrick White) (07/08/88)

Submitted by:	barrett@crabcake.cs.JHU.EDU
Summary:	Runs a program in the background without attachments to the CLI.
Poster Boy:	Patrick White	(ain@j.cc.purdue.edu)
Archive Name:	sources/amiga/volume5/runback2.d.sh.Z binaries/amiga/volume8/runback2.d.sh.Z
tested.
 
NOTES:
   I tried it, but didn't tru compiling it.
   Reshared it to separate sources, docs, and binaries.
   It worked perfectly except when I tried to runback the info.library.. but
that is a dumb thing to be doing anyway :-)  (did it cause older versions
died when you tried to run soemthign that was not there).
.
 
 
-- Pat White   (co-moderator comp.sources/binaries.amiga)
ARPA/UUCP: j.cc.purdue.edu!ain  BITNET: PATWHITE@PURCCVM  PHONE: (317) 743-8421
U.S.  Mail:  320 Brown St. apt. 406,    West Lafayette, IN 47906
[archives at: j.cc.purdue.edu.ARPA]
 
========================================
 
#	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:
#	README
#	README.peck
# This archive created: Fri Jul  8 11:32:50 1988
# By:	Patrick White (PUCC Land, USA)
echo shar: extracting README '(2561 characters)'
cat << \SHAR_EOF > README
RunBack:	Run a program in the background.
	***	Now searches your command search path.	***
		A revision of Rob Peck's "RunBack" program and
		 Carolyn Scheppner's "Which" program.

Author:		Daniel Barrett
		Department of Computer Science
		The Johns Hopkins University
		Baltimore, MD  21218

		barrett@cs.jhu.edu
		ins_adjb@jhunix.UUCP

Note:		Both the original RunBackground and Which are in 
		the Public Domain.  So is all my code that I added.
		Use it however you please.

INTRODUCTION
------------
	This is my altered version of Rob Peck's fine program, RunBack.
RunBack, similar to Run, allows you to startup a CLI program and let
it run in the background.  Unlike Run, however, RunBack then allows 
the original CLI to be closed.  Run would hang the CLI if you did this.
See the file README.peck for Rob Peck's original documentation.

	Also unlike Run, the old RunBack did not search your command search
path; you always had to specify the complete pathname of your command.
My new version eliminates this hassle -- it searches your path.

	The path-searching code is largely taken from Carolyn Scheppner's
"Which" program.  Thanks, Carolyn!!

A PROBLEM I HAD TO OVERCOME
---------------------------

	The original RunBack program I obtained was a binary version, 
compiled with the Lattice C compiler.  I use the Manx (Aztec) compiler, 
version 3.6a.  When I compiled Rob's original program with Manx, something
did not work anymore... quoted arguments on the command line.  The Manx 
version completely drops the quotes!

The way RunBack works is that it translates:

	RunBack myProgram arg1 arg2

into:

	Run >NIL: <NIL: myProgram >NIL: <NIL: arg1 arg2

So a Lattice RunBack would translate from:

	RunBack c:emacs "my file"

into:

	Run >NIL: <NIL: c:emacs >NIL: <NIL: "my file"

HOWEVER, Manx-compiled RunBack translates it into:

	Run >NIL: <NIL: c:emacs >NIL: <NIL: my file

which is clearly WRONG.

	What did I do about it?  I added a few lines of #ifdef AZTEC_C
code to the runback.c program, plus the file aztec.c.  I am effectively
replacing quotes around quoted arguments.  My algorithm is this:  if
an argument has a blank space in it, then it must have been quoted, so
put quotes around it.
	If you don't like this algorithm, the source code is included 
and you can change it any way you like.
	Since I don't have the Lattice compiler, I cannot be sure that
my changes will work under Lattice.  That is why I made all my changes
#ifdef AZTEC_C, a constant that is automatically defined by Manx C
after version 3.4a.

	Enjoy the program!
SHAR_EOF
if test 2561 -ne "`wc -c README`"
then
echo shar: error transmitting README '(should have been 2561 characters)'
fi
echo shar: extracting README.peck '(2695 characters)'
cat << \SHAR_EOF > README.peck
/* 

--------------
runbackground.c  
---------------

SUMMARY:  A Workbench Disk can be used to autostart an application
	  through the use of the startup script and close the startup CLI.


Users have commented that it is not possible to start a process going 
from the startup script and then cause the initial CLI to go away.   
Here is the solution to that problem, named appropriately:

	RUNBACKGROUND

which starts and runs a background task.  This does indeed allow you to
create a startup script that will set up your workbench running any
programs you might wish, removing the initial CLI in the process.

Your s/startup-sequence can contain lines such as the following:

	RUNBACKGROUND -3 clock
	RUNBACKGROUND utilities/calculator
	RUNBACKGROUND -5 utilities/notepad

where RUNBACKGROUND is the command and the second parameter is the filename
which may be preceded by a flag-variable that specifies an optional delay 
time.  The delay can be from 0 to 9, for the number of seconds that 
the startup script should sleep while allowing the background task to 
load and start.  I've put that in to minimize thrashing of the disk as it
tries to load several projects at once.


LIMITATIONS:

    The program that you run cannot require any input from an interactive
    CLI that starts it.    Additionally, you cannot specify any file 
    redirection in the command line since this program provides the
    redirection for you already.  If you need to use redirection for
    your command, you can modify the source code where shown, thus
    allowing the redirection to become one of the parameters passed
    through to your program.

    RUNBACKGROUND does pass your command line parameters to the program
    you wish to start, but limits the total length of your command
    string to 227 (255 minus the 28 characters for "RUN >NIL: <NIL: " 
    preceding your own file pathname and ">NIL: < NIL: " following it.)


LINKING INFORMATION:

    (Amiga/Lattice C)	use -v option for pass 2   (lc2 -v filename.q)
			to disable stack checking code installation.
			(stack checking code sometimes is incorrect).

    FROM lib:Astartup.obj runbackground.o
    TO runbackground
    LIBRARY lib:amiga.lib, lib:lc.lib

    ****************************  NOTE:  ********************************
    If you use Lstartup.obj, it won't let the startup CLI go away. This is
    because the source code for Lstartup.asm either opens its own window 
    or uses an existing CLI window (Open("*",....)), so that it has some 
    guaranteed place to put the output.   Astartup.obj does not do this.
    *********************************************************************

Hope this helps.


robp.
*/

SHAR_EOF
if test 2695 -ne "`wc -c README.peck`"
then
echo shar: error transmitting README.peck '(should have been 2695 characters)'
fi
#	End of shell archive
exit 0

ain@j.cc.purdue.edu (Patrick White) (07/09/88)

Submitted by:	barrett@crabcake.cs.JHU.EDU
Summary:	Runs a program in the background without attachments to the CLI.
Poster Boy:	Patrick White	(ain@j.cc.purdue.edu)
Archive Name:	sources/amiga/volume5/runback2.s.sh.Z
tested.
 
NOTES:
   I tried it, but didn't tru compiling it.
   Reshared it to separate sources, docs, and binaries.
   It worked perfectly except when I tried to runback the info.library.. but
that is a dumb thing to be doing anyway :-)  (did it cause older versions
died when you tried to run soemthign that was not there).
.
 
 
-- Pat White   (co-moderator comp.sources/binaries.amiga)
ARPA/UUCP: j.cc.purdue.edu!ain  BITNET: PATWHITE@PURCCVM  PHONE: (317) 743-8421
U.S.  Mail:  320 Brown St. apt. 406,    West Lafayette, IN 47906
[archives at: j.cc.purdue.edu.ARPA]
 
========================================
 
#	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
#	aztec.c
#	path.c
#	runback.c
# This archive created: Fri Jul  8 11:33:43 1988
# By:	Patrick White (PUCC Land, USA)
echo shar: extracting Makefile '(123 characters)'
cat << \SHAR_EOF > Makefile
CFLAGS=+L
OBJ=runback.o path.o aztec.o
PROG=RunBack

all:	$(OBJ)
	ln $(OBJ) -o $(PROG) -lc32

clean:
	delete \#?.o $(PROG)
SHAR_EOF
if test 123 -ne "`wc -c Makefile`"
then
echo shar: error transmitting Makefile '(should have been 123 characters)'
fi
echo shar: extracting aztec.c '(199 characters)'
cat << \SHAR_EOF > aztec.c
/* HasASpace(s):	Return 1 if there is a space in string s.
 *			Return 0 otherwise.
*/

HasASpace(s)
char *s;
{
	char *temp=s;
	while (*temp) {
		if (*(temp++) == ' ')
			return(1);
	}
	return(0);
}
SHAR_EOF
if test 199 -ne "`wc -c aztec.c`"
then
echo shar: error transmitting aztec.c '(should have been 199 characters)'
fi
echo shar: extracting path.c '(3991 characters)'
cat << \SHAR_EOF > path.c
/* This code is taken largely from Carolyn Scheppner's "which.c" program
 * from 11/87.  There were no copyright notices in the original file,
 * so to the best of my knowledge it is in the Public Domain.
*/

#include <exec/types.h>
#include <exec/memory.h>
#include <libraries/dos.h>
#include <libraries/dosextens.h>

#define SBUFSZ 256
#define CBUFSZ 80

extern BOOL getPath();
extern struct Task *FindTask();		/* To scare away warning msgs */
extern VOID *AllocMem();
	
struct Path
   {
   BPTR  path_Next;
   LONG  path_Lock;
   };

char *FindIt(command)
char *command;
{
   struct Process *proc;
   struct CommandLineInterface *cli;
   struct Path *path;
   struct FileInfoBlock *fib;
   APTR oldWindowPtr;
   LONG lock, startcd;
   BOOL Found, InitialCD, FullPath;
   int  i;
   char sbuf[SBUFSZ], cbuf[CBUFSZ];

   /* Fail if not CLI process */
   proc = (struct Process *)FindTask(NULL);
   cli = (struct CommandLineInterface *)(proc->pr_CLI << 2);
   if(!cli)  exit(RETURN_ERROR);

   /* Allocate a FileInfoBlock - must be longword aligned */
   if(!(fib=(struct FileInfoBlock *)
     AllocMem(sizeof(struct FileInfoBlock),MEMF_PUBLIC|MEMF_CLEAR)))
      printf("Not enough memory\n"), exit(RETURN_FAIL);

   /* Save old WindowPtr, and disable volume requesters */
   oldWindowPtr = proc->pr_WindowPtr;
   proc->pr_WindowPtr = (APTR)-1L;

   /* Were we given full path in command name ? */
   for(FullPath = FALSE, i=0; i<strlen(command); i++)
     {
     if(command[i] == ':')
        {
        FullPath = TRUE;
        break;
        }
     }

   /* Check current directory */
   if(Found = getPath(command,fib,sbuf))
      {
      if((!FullPath)&&(command[0]))  strcpy(sbuf,command);
      }

   /* Check paths */
   if((!Found)&&(!FullPath))
      {
      InitialCD = TRUE;
      /* Follow the BPTR path list */
      for(path = (struct Path *) BADDR(cli->cli_CommandDir);
          (path) && (!Found);
            path = (struct Path *) BADDR(path->path_Next))
         {
         /* CD to each path */
         lock = CurrentDir(path->path_Lock);
         if(InitialCD)  startcd = lock, InitialCD = FALSE;

         /* See if command is there */
         Found = getPath(command,fib,sbuf);
         }
      /* If we CD'd anywhere, restore initial CD */
      if(! InitialCD)  CurrentDir(startcd);
      }

   /* Check C: */
   if((!Found)&&(!FullPath))
      {
      strcpy(cbuf,"C:");
      strcpy(&cbuf[2],command);
      if(Found = getPath(cbuf,fib,sbuf))  strcpy(sbuf,cbuf);
      }

   /* Re-enable volume requesters */
   proc->pr_WindowPtr = oldWindowPtr;

   /* Free fib */
   if (fib)
	FreeMem(fib, sizeof(struct FileInfoBlock));

   if(Found)
	return(sbuf);
   else
	return(NULL);
}


BOOL
getPath(command,fib,buf)
char *command;
struct FileInfoBlock *fib;
char *buf;
   {
   LONG lock;
   BOOL Success = FALSE;

   if(lock = Lock(command,ACCESS_READ))
      {
      if(Examine(lock,fib))
         {
         Success = TRUE;
         buildPath(lock,fib,buf);
         }
      UnLock(lock);
      }
   return(Success);
   }


buildPath(inlock,fib,buf)
LONG inlock;
struct FileInfoBlock *fib;
char *buf;
   {
   int i;
   LONG lock,oldlock;
   BOOL MyOldLock = FALSE;

   buf[0] = NULL;
   lock = inlock;

   while(lock)
      {
      if(Examine(lock,fib))
         {
         if(fib->fib_FileName[0] > ' ')
            {
            if(buf[0]) insert(buf,"/");
            insert(buf,fib->fib_FileName);
            }
         }
      oldlock = lock;
      lock = ParentDir(lock);
      if(MyOldLock)  UnLock(oldlock);
      else           MyOldLock = TRUE;
      }

   if(fib->fib_FileName[0] > ' ')
      {
      for(i=0; i<(strlen(buf)); i++)
         {
         if(buf[i] == '/')
            {
            buf[i] = ':';
            break;
            }
         }
      }
   else  insert(buf,"RAM:");

   return(strlen(buf));
   }

insert(buf,s)
char *buf,*s;
   {
   char tmp[SBUFSZ];

   strcpy(tmp,buf);
   strcpy(buf,s);
   strcpy(&buf[strlen(s)],tmp);
   }

SHAR_EOF
if test 3991 -ne "`wc -c path.c`"
then
echo shar: error transmitting path.c '(should have been 3991 characters)'
fi
echo shar: extracting runback.c '(5865 characters)'
cat << \SHAR_EOF > runback.c
/* runbackground.c */
/* Author:  Rob Peck.  5/9/86 */

/* Modified 5/21/88 by Dan Barrett to include PATH searching; added
 * the functions FullPathOf() and FindIt(), largely taken from C.
 * Scheppner's "which.c" program.
 *
 * Also, a few "#ifdef AZTEC_C" lines had to be added.  It seems that
 * Aztec C parses the command line differently from the way Lattice
 * does with respect to arguments in quotes.  When I compiled this
 * program with Aztec, all the quotes in quoted arguments were 
 * disappearing.  I re-insert them around any argument that has a
 * space character (' ') in it.
*/

/*#define DEBUG*/ 	/* Uncomment this line for debugging. */

#define EQUAL(a,b)	!strcmp(a,b)
	
#include "exec/types.h"
#include "exec/memory.h"
#include "libraries/dosextens.h"

extern struct FileHandle *Open();
extern struct FileLock *Lock();
extern VOID *AllocMem();
	
main(argc, argv)
int argc;
char *argv[];
{	
    LONG success, delaywillbe;
    UBYTE commandstring[255];
    char *test, *filename;
    LONG fromparm;
    struct FileInfoBlock *fib;
    struct FileHandle *nilfh;	/* NOTE: will hang around until next reset */
    struct FileLock *lock, *FullPathOf();
#ifdef AZTEC_C
    int hasSpace=0;		/* Does an string have a space in it. */
#endif
    
    fib = NULL;			/* No file info block so far. */
    delaywillbe = 1;

    if(argc < 2 || EQUAL(argv[1],"?")) {
usage:
	printf("Usage: RUNBACKGROUND [ -<loaddelay>] <name> [<parm(s)>]\n");
	printf("          where optional loaddelay is 0-9,\n");
	printf("          specified in seconds for the CLI\n");
	printf("          to sleep, waiting for task to load\n");
	printf("          (minimizes inter-task disk-thrashing)\n");
	if(fib)
		FreeMem(fib, sizeof(struct FileInfoBlock));
	exit(0);
    }

    /* See if there is a delay parameter present */

    test = argv[1];

    if(*test++ == '-') {
	filename = argv[2];	/* argv[1] is delay so argv[2] is file  */
	fromparm = 3;		/* Copy parms from 3 to end  		*/

	if(*test >= '0' && *test <= '9')
	    delaywillbe = 1 + (50 * (*test - '0')); 

	if (argc < 3)
		goto usage; /* Only a delay, and no filename!! */

	argc--;		/* one less parm to copy */
    }
    else {
	filename = argv[1];
	fromparm = 2;		/* Copy parms from 2 to end 		*/
    }

    /* Now see if the file exists!  If not, it can crash the background
     * CLI and take the system along with it.
     */

    lock = FullPathOf(filename);
    if(!lock) {
	printf("%ls: Command not found\n",filename);
	goto usage;
    }
    else {
	/* If file exists, it better be a file and not a directory */

/*DJB*/	/* With my (Dan's) changes, a file that is not found at all
	 * is falsely identified as a directory.  Irritating, but not
	 * fatal.
	 */

	/* Unfortunately, it is difficult to tell if it is an executable
	 * file.  If not executable, we'll still get blown out of the
	 * water, but that is up to the user to do it right!
	 */

	fib =  (struct FileInfoBlock *)
        	AllocMem(sizeof(struct FileInfoBlock),MEMF_CLEAR);
	if(!fib) {
	    UnLock(lock);
	    printf("Ran out of memory!\n");
	    exit(0);
	}
	else {
	    success = Examine(lock,fib);
	    if(fib->fib_DirEntryType > 0) {
/*		printf("%ls is a directory, not a file!\n",filename); */
/*DJB*/		printf("Cannot open %ls... maybe a directory?\n",filename);
		goto usage;
	    }
	}
   	FreeMem(fib, sizeof(struct FileInfoBlock));
	UnLock(lock);
    }
 
    nilfh = Open("NIL:",MODE_NEWFILE); /* will always succeed */

    strcpy( &commandstring[0], "RUN >NIL: <NIL: " );
    strcat( &commandstring[0], filename);  

    /* REMOVE THIS NEXT LINE IF YOU WANT TO INCLUDE REDIRECTION IN
     * THE COMMAND LINE FOR RUNBACKGROUND.   (The line was installed
     * to assure that something like "RUNBACKGROUND date ?" would
     * not crash the system.  "Date ?" is expecting to have an interactive
     * CLI, and unless it is specifically told to direct its output to nil:
     * it causes a crash.  If the next line is removed, and you are careful
     * about putting only NON-interactive commands in the command line,
     * everything should be ok.  Notice that if you do not specify a 
     * non-interactive file handle (named_disk_file or NIL:) for your
     * program, it may still prevent the originating CLI from going away
     * until your program ends.  Also note that specifying two instances 
     * of the same redirection (">somewhere" or "<somewhere") for a 
     * background task crashes.
     */

    strcat( &commandstring[0], " >NIL: <NIL: ");

    argc--;

    while(--argc > 0) {     /* Rebuild parameter string for passing it on */
	strcat( &commandstring[0], " ");	/* add a blank */

#ifdef AZTEC_C
	hasSpace = HasASpace(argv[fromparm]);	/* Quoted argument?     */
	if (hasSpace)				/* Then quote it again! */
		strcat( &commandstring[0], "\"");
#endif

	strcat( &commandstring[0], argv[fromparm++]);

#ifdef AZTEC_C
	if (hasSpace)
		strcat( &commandstring[0], "\"");
#endif

    }

#ifdef DEBUG
    printf("Execute %s\n", &commandstring[0]);
#else
    success = Execute( &commandstring[0] , nilfh, nilfh);
#endif

    /* The full command passed to Execute now looks like this:
     *
     *	"RUN >NIL: <NIL: FILENAME >NIL: <NIL: PARAMETER(s)"
     *
     */

    if(success) {
	printf("Started %ls as a background task\n",filename);

	/* Execute, in this case, returns IMMEDIATELY.  The process
	 * that is loading the code that is to be run as a background
	 * process is working to get everything in and started.  
	 */
    }
    /* Now, to minimize thrashing between tasks, lets put this task to 
     * sleep so that the each task actually gets a chance to load.
     */
    Delay(delaywillbe);
}

	
struct FileLock *FullPathOf(filename)
char *filename;
{
	struct FileLock *lock;
	char realname[256], *FindIt();

	strcpy(realname, FindIt(filename));
	lock = Lock(realname,ACCESS_READ);
	return(lock);
}
SHAR_EOF
if test 5865 -ne "`wc -c runback.c`"
then
echo shar: error transmitting runback.c '(should have been 5865 characters)'
fi
#	End of shell archive
exit 0