[comp.sources.amiga] v89i138: runback - run commands in the background v4

page%swap@Sun.COM (Bob Page) (05/17/89)

Submitted-by: kim@uts.amdahl.com (Kim E. DeVaughn)
Posting-number: Volume 89, Issue 138
Archive-name: dos/runback4.1

RunBack now uses the NULL: device (by Gunnar Nordmark).  This is a
"real" device, so it solves the problems with previous versions of
RunBack that used the Nil: "fake" device which caused crashes.

[null: not included here, as it has already been posted.  The handler
is in the runback.zoo file in comp.binaries.amiga however.  ..bob]

# This is a shell archive.
# Remove anything above and including the cut line.
# Then run the rest of the file through 'sh'.
# Unpacked files will be owned by you and have default permissions.
#----cut here-----cut here-----cut here-----cut here----#
#!/bin/sh
# shar: SHell ARchive
# Run the following text through 'sh' to create:
#	Makefile
#	Mountlist
#	README
#	README.barrett
#	README.peck
#	amigaline-D1
#	aztec.c
#	path.c
#	runback.c
# This is archive 1 of a 1-part kit.
# This archive created: Wed May 17 19:53:12 1989
echo "extracting Makefile"
sed 's/^X//' << \SHAR_EOF > Makefile
XCFLAGS=+L
XOBJ=runback.o path.o aztec.o
XPROG=RunBack
X
Xall:	$(OBJ)
X	ln $(OBJ) -o $(PROG) -lc32
X
Xclean:
X	delete \#?.o $(PROG)
SHAR_EOF
echo "extracting Mountlist"
sed 's/^X//' << \SHAR_EOF > Mountlist
X
X/*  
X	Installs null-handler V 0.0 (C) Gunnar Nordmark
X*/
X
Xnull:      Handler = L:null-handler
X           Stacksize = 500
X           Priority = 5
X	   GlobVec = 1
X#
SHAR_EOF
echo "extracting README"
sed 's/^X//' << \SHAR_EOF > README
XRunBack:        Run a program in the background.
X        ***     Now searches your command search path.  ***
X                A revision of Daniel Barrett's version of
X                Rob Peck's "RunBack" program. (Got That ?)
X
XAuthor:         Tim Maffett
X                Molecular Design Ltd.
X                2132 Farallon Dr.
X                San Leandro, CA 94577
X
X
XNote:           All previous versions of RunBack have been placed in the
X                Public Domain.  So is all the code I've added.
X
X                See README.barrett and README.peck for info on
X                previous versions.
X
X
XINTRODUCTION
X------------
X        This version of RunBack has been changed to use the NULL: device
Xsupplied in this ZOO file. (NULL: device by Gunnar Nordmark).  This
Xis a "real" device, so it solve problems with previous versions of RunBack
Xwhich used the Nil: "fake" device which caused Crashes....
X
XNOTE:   Gunnar's NULL: device is supplied in this archive. (in the
X      null directory if you used zoo x// RUNBACK4.ZOO to unzoo)
X
X        See the "null.doc" file and Matt Dillon's "amigaline-d1" article for
Xmore info.
X
X1)      The "null-handler" handler needs to be placed in you l: directory
X and the MountList suplied in nul.zoo must be appended to Your MountList.
X (SEE "null.doc: for details).
X2)      RunBack will take care of mounting Null: for you, so there is no
X need for you to explicitly "mount null:" before using RunBack.
X3)      RunBack still contains Daniel Barrett's modifications so your
X command search path will be searched; ie. you do not have to supply full
X path names to the command you want to RunBack.
X
X
XSO WHAT ?
X---------
X        Commands which would previously cause RunBack (RunBack3) to
Xguru should now work (if the guru was cause by the program trying to
Xcommunicate with the Nil: "fake" device).  Using Gunnar's NULL: device,
Xa true "bit bucket", provides more functionality.
X
X
XSee README.barrett and README.peck for more info on RunBack and it's history.
XNote change all references to "Nil:" to "Null:"
X
XSee null.doc for more info on NULL:
X
SHAR_EOF
echo "extracting README.barrett"
sed 's/^X//' << \SHAR_EOF > README.barrett
XRunBack:	Run a program in the background.
X	***	Now searches your command search path.	***
X		A revision of Rob Peck's "RunBack" program and
X		 Carolyn Scheppner's "Which" program.
X
XAuthor:		Daniel Barrett
X		Department of Computer Science
X		The Johns Hopkins University
X		Baltimore, MD  21218
X
X		barrett@cs.jhu.edu
X		ins_adjb@jhunix.UUCP
X
XNote:		Both the original RunBackground and Which are in 
X		the Public Domain.  So is all my code that I added.
X		Use it however you please.
X
XINTRODUCTION
X------------
X	This is my altered version of Rob Peck's fine program, RunBack.
XRunBack, similar to Run, allows you to startup a CLI program and let
Xit run in the background.  Unlike Run, however, RunBack then allows 
Xthe original CLI to be closed.  Run would hang the CLI if you did this.
XSee the file README.peck for Rob Peck's original documentation.
X
X	Also unlike Run, the old RunBack did not search your command search
Xpath; you always had to specify the complete pathname of your command.
XMy new version eliminates this hassle -- it searches your path.
X
X	The path-searching code is largely taken from Carolyn Scheppner's
X"Which" program.  Thanks, Carolyn!!
X
XA PROBLEM I HAD TO OVERCOME
X---------------------------
X
X	The original RunBack program I obtained was a binary version, 
Xcompiled with the Lattice C compiler.  I use the Manx (Aztec) compiler, 
Xversion 3.6a.  When I compiled Rob's original program with Manx, something
Xdid not work anymore... quoted arguments on the command line.  The Manx 
Xversion completely drops the quotes!
X
XThe way RunBack works is that it translates:
X
X	RunBack myProgram arg1 arg2
X
Xinto:
X
X	Run >NIL: <NIL: myProgram >NIL: <NIL: arg1 arg2
X
XSo a Lattice RunBack would translate from:
X
X	RunBack c:emacs "my file"
X
Xinto:
X
X	Run >NIL: <NIL: c:emacs >NIL: <NIL: "my file"
X
XHOWEVER, Manx-compiled RunBack translates it into:
X
X	Run >NIL: <NIL: c:emacs >NIL: <NIL: my file
X
Xwhich is clearly WRONG.
X
X	What did I do about it?  I added a few lines of #ifdef AZTEC_C
Xcode to the runback.c program, plus the file aztec.c.  I am effectively
Xreplacing quotes around quoted arguments.  My algorithm is this:  if
Xan argument has a blank space in it, then it must have been quoted, so
Xput quotes around it.
X	If you don't like this algorithm, the source code is included 
Xand you can change it any way you like.
X	Since I don't have the Lattice compiler, I cannot be sure that
Xmy changes will work under Lattice.  That is why I made all my changes
X#ifdef AZTEC_C, a constant that is automatically defined by Manx C
Xafter version 3.4a.
X
X	Enjoy the program!
SHAR_EOF
echo "extracting README.peck"
sed 's/^X//' << \SHAR_EOF > README.peck
X/* 
X
X--------------
Xrunbackground.c  
X---------------
X
XSUMMARY:  A Workbench Disk can be used to autostart an application
X	  through the use of the startup script and close the startup CLI.
X
X
XUsers have commented that it is not possible to start a process going 
Xfrom the startup script and then cause the initial CLI to go away.   
XHere is the solution to that problem, named appropriately:
X
X	RUNBACKGROUND
X
Xwhich starts and runs a background task.  This does indeed allow you to
Xcreate a startup script that will set up your workbench running any
Xprograms you might wish, removing the initial CLI in the process.
X
XYour s/startup-sequence can contain lines such as the following:
X
X	RUNBACKGROUND -3 clock
X	RUNBACKGROUND utilities/calculator
X	RUNBACKGROUND -5 utilities/notepad
X
Xwhere RUNBACKGROUND is the command and the second parameter is the filename
Xwhich may be preceded by a flag-variable that specifies an optional delay 
Xtime.  The delay can be from 0 to 9, for the number of seconds that 
Xthe startup script should sleep while allowing the background task to 
Xload and start.  I've put that in to minimize thrashing of the disk as it
Xtries to load several projects at once.
X
X
XLIMITATIONS:
X
X    The program that you run cannot require any input from an interactive
X    CLI that starts it.    Additionally, you cannot specify any file 
X    redirection in the command line since this program provides the
X    redirection for you already.  If you need to use redirection for
X    your command, you can modify the source code where shown, thus
X    allowing the redirection to become one of the parameters passed
X    through to your program.
X
X    RUNBACKGROUND does pass your command line parameters to the program
X    you wish to start, but limits the total length of your command
X    string to 227 (255 minus the 28 characters for "RUN >NIL: <NIL: " 
X    preceding your own file pathname and ">NIL: < NIL: " following it.)
X
X
XLINKING INFORMATION:
X
X    (Amiga/Lattice C)	use -v option for pass 2   (lc2 -v filename.q)
X			to disable stack checking code installation.
X			(stack checking code sometimes is incorrect).
X
X    FROM lib:Astartup.obj runbackground.o
X    TO runbackground
X    LIBRARY lib:amiga.lib, lib:lc.lib
X
X    ****************************  NOTE:  ********************************
X    If you use Lstartup.obj, it won't let the startup CLI go away. This is
X    because the source code for Lstartup.asm either opens its own window 
X    or uses an existing CLI window (Open("*",....)), so that it has some 
X    guaranteed place to put the output.   Astartup.obj does not do this.
X    *********************************************************************
X
XHope this helps.
X
X
Xrobp.
X*/
X
SHAR_EOF
echo "extracting amigaline-D1"
sed 's/^X//' << \SHAR_EOF > amigaline-D1
X	AMIGALINE #D1,	Matthew Dillon
X
X	Disconnecting a program such that you can EndCLI and also allow the
X	program to call Execute().
X
XProblem:
X
X	You want to disconnect a program such that when you RUN <nil: >nil:
X	(using the new 1.3 RUN) you can then EndCLI the cli.
X
X	This program wants to be able to use Execute() to run other programs. 
X	The problem is that Execute() requires a valid pr_ConsoleTask (console)
X	or it will freeze.
X
XSolution: General
X
X	Run <nil: >nil: mycprogram
X
X	If using the main() entry point, you can fclose(stderr) to remove
X	the reference to "*".  If using the _main() entry point, stdio is
X	not setup and thus you do not need to do this (in fact, you can't
X	use stdio at all without crashing the computer).
X
X	note: being able to fclose(stderr) from the main() entry point 
X	works with Aztec C.  I don't know about Lattice.  Aztec always
X	does an Open("*", 1006) to setup stderr and this reference must
X	be removed.
X
X					--
X
X	At this point, you can EndCLI and the cli window goes away.  However,
X	the 'mycprogram' cannot call Execute() or otherwise run other 
X	external programs for two reasons:
X
X		(1) pr_ConsoleTask is still non-NULL and points to the now
X		    defunct window (i.e. you will cause a task-held requester)
X
X		(2) you cannot set pr_ConsoleTask to NULL... Execute() does
X		    not accept it and freezes up.
X
X					--
X
X	So, you must set pr_ConsoleTask to some other valid device.  Guess
X	what?  Any device will do except NIL: which isn't a real device.  For
X	example, RAM: :
X
X		extern APTR DeviceProc();
X		proc->pr_ConsoleTask = DeviceProc("ram:");
X
X		(assuming RAM: exists)
X
X	What does this do?  Any program which tries to open the console will
X	actually open the file "RAM:*", as in Open("RAM:*", 1006).  
X	Unfortunetly, there is no way to place "*" in anything but the 
X	root directory of the device.  This is essentially a garbage file.
X
X	But the ultimate goal is achieved ... you can kill the CLI window
X	and still arbitrarily run programs from the detached program with
X	impunity.
X
X	The only possible problem which I have yet to test is when several
X	programs try to access RAM:* as their console at the same time.  
X	Since the file is openned 1006, other programs trying to Open() it
X	will fail while the first programs is still running.  What happens?
X
X							-Matt
SHAR_EOF
echo "extracting aztec.c"
sed 's/^X//' << \SHAR_EOF > aztec.c
X/* HasASpace(s):	Return 1 if there is a space in string s.
X *			Return 0 otherwise.
X*/
X
XHasASpace(s)
Xchar *s;
X{
X	char *temp=s;
X	while (*temp) {
X		if (*(temp++) == ' ')
X			return(1);
X	}
X	return(0);
X}
SHAR_EOF
echo "extracting path.c"
sed 's/^X//' << \SHAR_EOF > path.c
X/* This code is taken largely from Carolyn Scheppner's "which.c" program
X * from 11/87.  There were no copyright notices in the original file,
X * so to the best of my knowledge it is in the Public Domain.
X*/
X
X#include <exec/types.h>
X#include <exec/memory.h>
X#include <libraries/dos.h>
X#include <libraries/dosextens.h>
X
X#define SBUFSZ 256
X#define CBUFSZ 80
X
Xextern BOOL getPath();
Xextern struct Task *FindTask();		/* To scare away warning msgs */
Xextern VOID *AllocMem();
X	
Xstruct Path
X   {
X   BPTR  path_Next;
X   LONG  path_Lock;
X   };
X
Xchar *FindIt(command)
Xchar *command;
X{
X   struct Process *proc;
X   struct CommandLineInterface *cli;
X   struct Path *path;
X   struct FileInfoBlock *fib;
X   APTR oldWindowPtr;
X   LONG lock, startcd;
X   BOOL Found, InitialCD, FullPath;
X   int  i;
X   char sbuf[SBUFSZ], cbuf[CBUFSZ];
X
X   /* Fail if not CLI process */
X   proc = (struct Process *)FindTask(NULL);
X   cli = (struct CommandLineInterface *)(proc->pr_CLI << 2);
X   if(!cli)  exit(RETURN_ERROR);
X
X   /* Allocate a FileInfoBlock - must be longword aligned */
X   if(!(fib=(struct FileInfoBlock *)
X     AllocMem(sizeof(struct FileInfoBlock),MEMF_PUBLIC|MEMF_CLEAR)))
X      printf("Not enough memory\n"), exit(RETURN_FAIL);
X
X   /* Save old WindowPtr, and disable volume requesters */
X   oldWindowPtr = proc->pr_WindowPtr;
X   proc->pr_WindowPtr = (APTR)-1L;
X
X   /* Were we given full path in command name ? */
X   for(FullPath = FALSE, i=0; i<strlen(command); i++)
X     {
X     if(command[i] == ':')
X        {
X        FullPath = TRUE;
X        break;
X        }
X     }
X
X   /* Check current directory */
X   if(Found = getPath(command,fib,sbuf))
X      {
X      if((!FullPath)&&(command[0]))  strcpy(sbuf,command);
X      }
X
X   /* Check paths */
X   if((!Found)&&(!FullPath))
X      {
X      InitialCD = TRUE;
X      /* Follow the BPTR path list */
X      for(path = (struct Path *) BADDR(cli->cli_CommandDir);
X          (path) && (!Found);
X            path = (struct Path *) BADDR(path->path_Next))
X         {
X         /* CD to each path */
X         lock = CurrentDir(path->path_Lock);
X         if(InitialCD)  startcd = lock, InitialCD = FALSE;
X
X         /* See if command is there */
X         Found = getPath(command,fib,sbuf);
X         }
X      /* If we CD'd anywhere, restore initial CD */
X      if(! InitialCD)  CurrentDir(startcd);
X      }
X
X   /* Check C: */
X   if((!Found)&&(!FullPath))
X      {
X      strcpy(cbuf,"C:");
X      strcpy(&cbuf[2],command);
X      if(Found = getPath(cbuf,fib,sbuf))  strcpy(sbuf,cbuf);
X      }
X
X   /* Re-enable volume requesters */
X   proc->pr_WindowPtr = oldWindowPtr;
X
X   /* Free fib */
X   if (fib)
X	FreeMem(fib, sizeof(struct FileInfoBlock));
X
X   if(Found)
X	return(sbuf);
X   else
X	return(NULL);
X}
X
X
XBOOL
XgetPath(command,fib,buf)
Xchar *command;
Xstruct FileInfoBlock *fib;
Xchar *buf;
X   {
X   LONG lock;
X   BOOL Success = FALSE;
X
X   if(lock = Lock(command,ACCESS_READ))
X      {
X      if(Examine(lock,fib))
X         {
X         Success = TRUE;
X         buildPath(lock,fib,buf);
X         }
X      UnLock(lock);
X      }
X   return(Success);
X   }
X
X
XbuildPath(inlock,fib,buf)
XLONG inlock;
Xstruct FileInfoBlock *fib;
Xchar *buf;
X   {
X   int i;
X   LONG lock,oldlock;
X   BOOL MyOldLock = FALSE;
X
X   buf[0] = NULL;
X   lock = inlock;
X
X   while(lock)
X      {
X      if(Examine(lock,fib))
X         {
X         if(fib->fib_FileName[0] > ' ')
X            {
X            if(buf[0]) insert(buf,"/");
X            insert(buf,fib->fib_FileName);
X            }
X         }
X      oldlock = lock;
X      lock = ParentDir(lock);
X      if(MyOldLock)  UnLock(oldlock);
X      else           MyOldLock = TRUE;
X      }
X
X   if(fib->fib_FileName[0] > ' ')
X      {
X      for(i=0; i<(strlen(buf)); i++)
X         {
X         if(buf[i] == '/')
X            {
X            buf[i] = ':';
X            break;
X            }
X         }
X      }
X   else  insert(buf,"RAM:");
X
X   return(strlen(buf));
X   }
X
Xinsert(buf,s)
Xchar *buf,*s;
X   {
X   char tmp[SBUFSZ];
X
X   strcpy(tmp,buf);
X   strcpy(buf,s);
X   strcpy(&buf[strlen(s)],tmp);
X   }
X
SHAR_EOF
echo "extracting runback.c"
sed 's/^X//' << \SHAR_EOF > runback.c
X/* runbackground.c */
X/* Author:  Rob Peck.  5/9/86 */
X
X/* Modified 5/21/88 by Dan Barrett to include PATH searching; added
X * the functions FullPathOf() and FindIt(), largely taken from C.
X * Scheppner's "which.c" program.
X *
X * Also, a few "#ifdef AZTEC_C" lines had to be added.  It seems that
X * Aztec C parses the command line differently from the way Lattice
X * does with respect to arguments in quotes.  When I compiled this
X * program with Aztec, all the quotes in quoted arguments were
X * disappearing.  I re-insert them around any argument that has a
X * space character (' ') in it.
X*/
X/* Modified 5/12/89 by Tim Maffett to use Gunnar Nordmark's NULL:
X * device.  RunBack also Mounts the NULL: device before using.
X*/
X
X/*#define DEBUG*/       /* Uncomment this line for debugging. */
X
X#define EQUAL(a,b)      !strcmp(a,b)
X
X#include "exec/types.h"
X#include "exec/memory.h"
X#include "libraries/dosextens.h"
X
Xextern struct FileHandle *Open();
Xextern struct FileLock *Lock();
Xextern VOID *AllocMem();
X
Xmain(argc, argv)
Xint argc;
Xchar *argv[];
X{
X    LONG success, delaywillbe;
X    UBYTE commandstring[255];
X    char *test, *filename;
X    LONG fromparm;
X    LONG *port;
X    struct FileInfoBlock *fib;
X    struct FileHandle *nilfh;   /* NOTE: will hang around until next reset */
X    struct FileLock *lock, *FullPathOf();
X#ifdef AZTEC_C
X    int hasSpace=0;             /* Does an string have a space in it. */
X#endif
X
X    fib = NULL;                 /* No file info block so far. */
X    delaywillbe = 1;
X
X    if(argc < 2 || EQUAL(argv[1],"?")) {
Xusage:
X        printf("Usage: RUNBACKGROUND [ -<loaddelay>] <name> [<parm(s)>]\n");
X        printf("          where optional loaddelay is 0-9,\n");
X        printf("          specified in seconds for the CLI\n");
X        printf("          to sleep, waiting for task to load\n");
X        printf("          (minimizes inter-task disk-thrashing)\n");
X        if(fib)
X                FreeMem(fib, sizeof(struct FileInfoBlock));
X        exit(0);
X    }
X
X    /* See if there is a delay parameter present */
X
X    test = argv[1];
X
X    if(*test++ == '-') {
X        filename = argv[2];     /* argv[1] is delay so argv[2] is file  */
X        fromparm = 3;           /* Copy parms from 3 to end             */
X
X        if(*test >= '0' && *test <= '9')
X            delaywillbe = 1 + (50 * (*test - '0'));
X
X        if (argc < 3)
X                goto usage; /* Only a delay, and no filename!! */
X
X        argc--;         /* one less parm to copy */
X    }
X    else {
X        filename = argv[1];
X        fromparm = 2;           /* Copy parms from 2 to end             */
X    }
X
X    /* Now see if the file exists!  If not, it can crash the background
X     * CLI and take the system along with it.
X     */
X
X    lock = FullPathOf(filename);
X    if(!lock) {
X        printf("%ls: Command not found\n",filename);
X        goto usage;
X    }
X    else {
X        /* If file exists, it better be a file and not a directory */
X
X/*DJB*/ /* With my (Dan's) changes, a file that is not found at all
X         * is falsely identified as a directory.  Irritating, but not
X         * fatal.
X         */
X
X        /* Unfortunately, it is difficult to tell if it is an executable
X         * file.  If not executable, we'll still get blown out of the
X         * water, but that is up to the user to do it right!
X         */
X
X        fib =  (struct FileInfoBlock *)
X                AllocMem(sizeof(struct FileInfoBlock),MEMF_CLEAR);
X        if(!fib) {
X            UnLock(lock);
X            printf("Ran out of memory!\n");
X            exit(0);
X        }
X        else {
X            success = Examine(lock,fib);
X            if(fib->fib_DirEntryType > 0) {
X/*              printf("%ls is a directory, not a file!\n",filename); */
X/*DJB*/         printf("Cannot open %ls... maybe a directory?\n",filename);
X                goto usage;
X            }
X        }
X        FreeMem(fib, sizeof(struct FileInfoBlock));
X        UnLock(lock);
X    }
X
X    nilfh = Open("NIL:",MODE_NEWFILE); /* will always succeed */
X
X/*TMM*/    /* Mount NULL: device
X              execute "Mount >nil: <nil: null:" command
X              this will ensure that null: is mounted
X              who cares if we try to mount it again ? (KLUDGE)
X           */
X
X    success = Execute( "mount >nil: <nil: null:", nilfh, nilfh );
X
X/*TMM*/ /* Run now redirected to >NULL: <NULL: ( instead of NIL: ) */
X    strcpy( &commandstring[0], "RUN >NULL: <NULL: " );
X    strcat( &commandstring[0], filename);
X
X    /* REMOVE THIS NEXT LINE IF YOU WANT TO INCLUDE REDIRECTION IN
X     * THE COMMAND LINE FOR RUNBACKGROUND.   (The line was installed
X     * to assure that something like "RUNBACKGROUND date ?" would
X     * not crash the system.  "Date ?" is expecting to have an interactive
X     * CLI, and unless it is specifically told to direct its output to nil:
X     * it causes a crash.  If the next line is removed, and you are careful
X     * about putting only NON-interactive commands in the command line,
X     * everything should be ok.  Notice that if you do not specify a
X     * non-interactive file handle (named_disk_file or NIL:) for your
X     * program, it may still prevent the originating CLI from going away
X     * until your program ends.  Also note that specifying two instances
X     * of the same redirection (">somewhere" or "<somewhere") for a
X     * background task crashes.
X     */
X
X/*TMM*/ /* Run now redirected to >NULL: <NULL: ( instead of NIL: ) */
X    strcat( &commandstring[0], " >NULL: <NULL: ");
X
X    argc--;
X
X    while(--argc > 0) {     /* Rebuild parameter string for passing it on */
X        strcat( &commandstring[0], " ");        /* add a blank */
X
X#ifdef AZTEC_C
X        hasSpace = HasASpace(argv[fromparm]);   /* Quoted argument?     */
X        if (hasSpace)                           /* Then quote it again! */
X                strcat( &commandstring[0], "\"");
X#endif
X
X        strcat( &commandstring[0], argv[fromparm++]);
X
X#ifdef AZTEC_C
X        if (hasSpace)
X                strcat( &commandstring[0], "\"");
X#endif
X
X    }
X
X#ifdef DEBUG
X    printf("Execute %s\n", &commandstring[0]);
X#else
X    success = Execute( &commandstring[0] , nilfh, nilfh);
X#endif
X
X        /* The full command passed to Execute now looks like this: */
X
X/*TMM*/ /*  "RUN >NULL: <NULL: FILENAME >NULL: <NULL: PARAMETER(s)" */
X
X
X    if(success) {
X        printf("Started %ls as a background task\n",filename);
X
X        /* Execute, in this case, returns IMMEDIATELY.  The process
X         * that is loading the code that is to be run as a background
X         * process is working to get everything in and started.
X         */
X    }
X    /* Now, to minimize thrashing between tasks, lets put this task to
X     * sleep so that the each task actually gets a chance to load.
X     */
X    Delay(delaywillbe);
X}
X
X
Xstruct FileLock *FullPathOf(filename)
Xchar *filename;
X{
X        struct FileLock *lock;
X        char realname[256], *FindIt();
X
X        strcpy(realname, FindIt(filename));
X        lock = Lock(realname,ACCESS_READ);
X        return(lock);
X}
SHAR_EOF
echo "End of archive 1 (of 1)"
# if you want to concatenate archives, remove anything after this line
exit