[comp.sys.amiga.tech] What is my name?

mwm@mica.berkeley.edu (Mike (I'll think of something yet) Meyer) (07/17/89)

Before I sit down & write it myself, does anyone have a piece of code
that will give me a full name for the binary it's running in? Not just
argv[0], but the name path & file name (not file, but c:file, or
sys:system/file, or whatever).

	Thanx,
	<mike
--
And then I saw her...					Mike Meyer
She was a bright red '64 GTO				mwm@berkeley.edu
With fins and gills like some giant piranha fish,	ucbvax!mwm
Some obscene phallic symbol on wheels.			mwm@ucbjade.BITNET

rap@peck.ardent.com (Rob Peck) (07/18/89)

In article <26385@agate.BERKELEY.EDU> mwm@mica.berkeley.edu (Mike (I'll think of something yet) Meyer) writes:
>Before I sit down & write it myself, does anyone have a piece of code
>that will give me a full name for the binary it's running in? Not just
>argv[0], but the name path & file name (not file, but c:file, or
>sys:system/file, or whatever).


Listing 2.9 in the Programmer's Guide To The Amiga gives you the
path to where the binary is located.  You can use argv[0] for the
binary's name.  The program is called mybranch.c


Rob Peck

mwm@eris.berkeley.edu (Mike (I'll think of something yet) Meyer) (07/18/89)

<In article <26385@agate.BERKELEY.EDU> mwm@mica.berkeley.edu (Mike (I'll think of something yet) Meyer) writes:
<>Before I sit down & write it myself, does anyone have a piece of code
<>that will give me a full name for the binary it's running in? Not just
<>argv[0], but the name path & file name (not file, but c:file, or
<>sys:system/file, or whatever).
<
<Listing 2.9 in the Programmer's Guide To The Amiga gives you the
<path to where the binary is located.  You can use argv[0] for the
<binary's name.  The program is called mybranch.c

I must have not made myself clear - you're the second person to make
that mistake. The code in listing 2.9 prints the name of the current
directory. This is may not be the name of the directory that the
running file is from.  Consider the following script, with "myname.c"
as the program I'm looking for:

1> path
Current directory
bin:Utilities
bin:System
bin:S
C:
1> cd
src:c
1> cc myname.c -o myname
1> myname
src:c/myname
1> copy myname ram:
1> ram:myname
ram:myname
1> rename ram:myname c:		; Note: no longer a copy in current directory
1> myname
c:myname			; Or bin:c/myname, or dh0:c/myname, or etc.
1> rename c:myname c:system
1> myname
bin:system/myname		; Or dh0:system/myname, or some such.
1>

You see the difference? The code is conceptually simple, but has some
non-trivial details (like dealing with "//x/y/myname") to make it
work right.

	<mike
--
He was your reason for living				Mike Meyer
So you once said					mwm@berkeley.edu
Now your reason for living				ucbvax!mwm
Has left you half dead					mwm@ucbjade.BITNET

thad@cup.portal.com (Thad P Floryan) (07/18/89)

Mike is asking about a program that will return more than just argv[0] re the
name of "what program am I?"  A program named "which" was posted to Usenet back
in November 1987 by Carolyn Scheppner (CATS); didn't see it in my Fish Index,
so if you need it, lemme know.  The sizes:

CLI2> ls -l src:which
----ar-e- 87-11-25 12:45:04   13     6180 which
----ar--- 87-11-25 12:44:46    9     4401 which.c
----ar--- 87-12-07 05:16:01    2      832 which.doc
Dirs:0   Files:3    Blocks:24    Bytes:11413   
CLI2>

It would be trivial to adapt "which" to match the first occurrence of */argv[0]
in the PATH searched by "which".

Thad Floryan [ thad@cup.portal.com (OR) ..!sun!portal!cup.portal.com!thad ]

rap@peck.ardent.com (Rob Peck) (07/18/89)

In article <26406@agate.BERKELEY.EDU> mwm@eris.berkeley.edu (Mike (I'll think of something yet) Meyer) writes:
><In article <26385@agate.BERKELEY.EDU> mwm@mica.berkeley.edu (Mike (I'll think of something yet) Meyer) writes:
><>Before I sit down & write it myself, does anyone have a piece of code
><>that will give me a full name for the binary it's running in? Not just
><>argv[0], but the name path & file name (not file, but c:file, or
><>sys:system/file, or whatever).

[examples deleted]

I now understand your question exactly and I agree that the current
directory path name listing is inadequate.  Looks like you have some
work on your hands.  To know exactly where-from the binary that is
running was drawn, you need to know both the PATH and the command
that was issued, so you'd have to get to the command history of
the originating CLI (if there was one).  Frankly, without doing
both of these, I cannot see how a binary can identify the directory
in which it resides and from which it was called.     AAARRRGGGHHH!

Rob Peck

louie@trantor.umd.edu (Louis A. Mamakos) (07/19/89)

Just a thought, but doesn't the program that's running have an open
I/O stream to the binary that it was loaded from?  At least programs
with overlays do, so that the overlay manager can load the appropriate
segments.  Now, given this struct FileHandle * or whatever, maybe you
can get a lock.  And from there do the normal sorts of things to get the
full path name to the binary.

Sounds ugly.



Louis A. Mamakos  WA3YMH    Internet: louie@TRANTOR.UMD.EDU
University of Maryland, Computer Science Center - Systems Programming

jwright@atanasoff.cs.iastate.edu (Jim Wright) (07/19/89)

In article <20565@cup.portal.com> thad@cup.portal.com (Thad P Floryan) writes:
| 
| It would be trivial to adapt "which" to match the first occurrence
| of */argv[0] in the PATH searched by "which".

Wouldn't this fail if, for example, I had a script file in S: which
then explicitly called the program and the program was not in a directory
on the PATH.

-- 
Jim Wright
jwright@atanasoff.cs.iastate.edu

mwm@eris.berkeley.edu (Mike (I'll think of something yet) Meyer) (07/19/89)

In article <1227@atanasoff.cs.iastate.edu> jwright@atanasoff.cs.iastate.edu.UUCP (Jim Wright) writes:
<In article <20565@cup.portal.com> thad@cup.portal.com (Thad P Floryan) writes:
<| 
<| It would be trivial to adapt "which" to match the first occurrence
<| of */argv[0] in the PATH searched by "which".
<
<Wouldn't this fail if, for example, I had a script file in S: which
<then explicitly called the program and the program was not in a directory
<on the PATH.

No, it won't. If the program is invoked explicitly, then argv[0]
should contain a ':', so the path is never searched. That's why you
have to adapt the code, instead of dropping it in as is.

Which, of course, doesn't check argv[0], so it will find the script -
which is what you want it to do.

	<mike

--
Come all you rolling minstrels,				Mike Meyer
And together we will try,				mwm@berkeley.edu
To rouse the spirit of the air,				ucbvax!mwm
And move the rolling sky.				mwm@ucbjade.BITNET

doug@xdos.UUCP (Doug Merritt) (07/19/89)

><In article <26385@agate.BERKELEY.EDU> mwm@mica.berkeley.edu (Mike (I'll think of something yet) Meyer) writes:
><>Before I sit down & write it myself, does anyone have a piece of code
><>that will give me a full name for the binary it's running in? Not just

Rob Peck said:
><Listing 2.9 in the Programmer's Guide To The Amiga gives you the

In article <26406@agate.BERKELEY.EDU> mwm@eris.berkeley.edu (Mike (I'll think of something yet) Meyer) writes:
> The code in listing 2.9 prints the name of the current
>directory. This is may not be the name of the directory that the
>running file is from.  Consider the following script, with "myname.c"

Well, for a workbench program that's easy...WBenchMsg->sm_ArgList[0].wa_Lock
is a lock on the file the executable came from, and the usual Examine()/
ParentDir() loop will turn that into a full path quite easily (SMOP).

I couldn't find anything comparable for CLI-launched programs, although
I wondered about (struct CommandLineInterface)->cli_CommandDir. If that
doesn't do it than I guess the only solution is the previously posted
answer of "which".
	Doug
-- 
Doug Merritt		{pyramid,apple}!xdos!doug
Member, Crusaders for a Better Tomorrow		Professional Wildeyed Visionary

doug@xdos.UUCP (Doug Merritt) (07/19/89)

In article <1227@atanasoff.cs.iastate.edu> jwright@atanasoff.cs.iastate.edu.UUCP (Jim Wright) writes:
>In article <20565@cup.portal.com> thad@cup.portal.com (Thad P Floryan) writes:
>| 
>| It would be trivial to adapt "which" to match the first occurrence
>| of */argv[0] in the PATH searched by "which".
>
>Wouldn't this fail if, for example, I had a script file in S: which
>then explicitly called the program and the program was not in a directory
>on the PATH.

But in this case the full path is in argv[0], making this the easiest
of the solutions.
	if (!argc)
		look at WBenchMsg
	else if (any {':','/'} in argv[0])
		use argv[0]
	else
		use PATH

The only case I can see that would fail is when someone gets some code
going using loadseg() explicitly, which (as far as I know, anyone want
to correct me?) does not save the executable's name anywhere.
	Doug
-- 
Doug Merritt		{pyramid,apple}!xdos!doug
Member, Crusaders for a Better Tomorrow		Professional Wildeyed Visionary

daveh@cbmvax.UUCP (Dave Haynie) (07/19/89)

in article <7374@ardent.UUCP>, rap@peck.ardent.com (Rob Peck) says:

> In article <26406@agate.BERKELEY.EDU> mwm@eris.berkeley.edu (Mike (I'll think of something yet) Meyer) writes:
>><In article <26385@agate.BERKELEY.EDU> mwm@mica.berkeley.edu (Mike (I'll think of something yet) Meyer) writes:
>><>Before I sit down & write it myself, does anyone have a piece of code
>><>that will give me a full name for the binary it's running in? Not just
>><>argv[0], but the name path & file name (not file, but c:file, or
>><>sys:system/file, or whatever).

> I now understand your question exactly and I agree that the current
> directory path name listing is inadequate.  Looks like you have some
> work on your hands.  AAARRRGGGHHH!
> Rob Peck

We had a big discussion on bix about this some time back, when I wanted to do
pretty much the same kind of thing.  The basis for it was this.  I have a
program which can get launched from Workbench or from the CLI.  I have a number
of parameters than can be fed to the program, and I'd like a way to set any
number of these to user-defined defaults, instead of the defaults I picked
when I wrote the code.  Under Workbench, the ToolTypes array does this just
dandy.  So I think to myself, all I need to make this the standard place to
put defaults is to have the program, when started under CLI, go hunt down it's
icon, and we're set.  But whoops; there's no easy parent lock or anything like
that passed down to a CLI program.  This is actually a case in which Workbench
does a better job than CLI.  The only obvious fix to this I could think of
would require the shell a CLI program is launched from to pass down a lock to
that image's parent as well as the name as it currently does.  This is obviously
known at the time the image is launched.  In any case, sure would be nice to 
have this stuff more consistent.  Maybe in 1.4?

-- 
Dave Haynie Commodore-Amiga (Systems Engineering) "The Crew That Never Rests"
   {uunet|pyramid|rutgers}!cbmvax!daveh      PLINK: D-DAVE H     BIX: hazy
           Be careful what you wish for -- you just might get it

new@udel.EDU (Darren New) (07/20/89)

In article <1227@atanasoff.cs.iastate.edu> jwright@atanasoff.cs.iastate.edu.UUCP (Jim Wright) writes:
>Wouldn't this fail if, for example, I had a script file in S: which
>then explicitly called the program and the program was not in a directory
>on the PATH.

Wouldn't argv[0] then contain the full path-name of the program?
You may need to use _main (before the parsing gets done). It must
be doable, since the CLI does it and you have all the information
in the process structure you need.  I would think you could do
it as follows:

1) attempt to obtain a lock on argv[0] (or the first string in the
command line, if _main). If that succeeds, use that lock to
generate the full path name.
2) walk down the path, attempting to lock argv[0] from each directory
so found.  If I remember, the path in some way hangs off of the 
current directory pointer in the process structure. When found,
use that lock to find the full path name.
3) try to loc strcat("C:", argv[0]);
4) If all of these fail, then my algorithm is wrong :-).

Note that you can CD to a file and not just a directory. Thus any
print-current-directory function should be able to give you the
full path of a lock.  (Er, from DOS calls, not with the CD command.)

		  -- Darren
		

trantow@csd4.milw.wisc.edu (Jerry J Trantow) (07/20/89)

>In article <26385@agate.BERKELEY.EDU> mwm@mica.berkeley.edu (Mike (I'll think of something yet) Meyer) writes:
>>Before I sit down & write it myself, does anyone have a piece of code
>>that will give me a full name for the binary it's running in? Not just
>>argv[0], but the name path & file name (not file, but c:file, or
>>sys:system/file, or whatever).

Try the ARP  PathName() function.  I believe it is defined 
char *PathName(lock)

jesup@cbmvax.UUCP (Randell Jesup) (07/20/89)

In article <7374@ardent.UUCP> rap@peck.ardent.com (Rob Peck) writes:
>I now understand your question exactly and I agree that the current
>directory path name listing is inadequate.  Looks like you have some
>work on your hands.  To know exactly where-from the binary that is
>running was drawn, you need to know both the PATH and the command
>that was issued, so you'd have to get to the command history of
>the originating CLI (if there was one).  Frankly, without doing
>both of these, I cannot see how a binary can identify the directory
>in which it resides and from which it was called.     AAARRRGGGHHH!

	Quite true, the current CLI/Shell doesn't preserve the needed
information (some other shells do).  The ability to get to the file the
program was run from should be addressed in 1.4.

-- 
Randell Jesup, Keeper of AmigaDos, Commodore Engineering.
{uunet|rutgers}!cbmvax!jesup, jesup@cbmvax.cbm.commodore.com  BIX: rjesup  
Common phrase heard at Amiga Devcon '89: "It's in there!"

jesup@cbmvax.UUCP (Randell Jesup) (07/20/89)

In article <432@xdos.UUCP> doug@xdos.UUCP (Doug Merritt) writes:
>But in this case the full path is in argv[0], making this the easiest
>of the solutions.
>	if (!argc)
>		look at WBenchMsg
>	else if (any {':','/'} in argv[0])
>		use argv[0]
>	else
>		use PATH

	That should work (though it is painful) under 1.2/1.3.  I thought of
that one 5 minutes after I posted my other response.  Glad to see someone
beat me to it.

-- 
Randell Jesup, Keeper of AmigaDos, Commodore Engineering.
{uunet|rutgers}!cbmvax!jesup, jesup@cbmvax.cbm.commodore.com  BIX: rjesup  
Common phrase heard at Amiga Devcon '89: "It's in there!"

doug@xdos.UUCP (Doug Merritt) (07/21/89)

In article <7372@cbmvax.UUCP> jesup@cbmvax.UUCP (Randell Jesup) writes:
>In article <432@xdos.UUCP> doug@xdos.UUCP (Doug Merritt) writes:
>>But in this case the full path is in argv[0], making this the easiest
>>of the solutions.
>>	if (!argc)
>>		look at WBenchMsg
>>	else if (any {':','/'} in argv[0])
>>		use argv[0]
>>	else
>>		use PATH
>
>	That should work (though it is painful) under 1.2/1.3.  I thought of
>that one 5 minutes after I posted my other response.  Glad to see someone
>beat me to it.

Yeah, and even aside from the complexity, it's painful to have to
look things up in the PATH...inelegant, since it's sort of guessing.
But Josh Rovero pointed out that cli_CommandName has what we need,
which made things easier and more robust, so I put together a full
solution based on that idea and some code I already had lying around
(this made it possible for me to go from start to finish in 1 hour).

Hope it's useful.
	Doug
Doug Merritt		{pyramid,apple}!xdos!doug
Member, Crusaders for a Better Tomorrow		Professional Wildeyed Visionary

--------------------- CUT HERE ----- CUT HERE ------- CUT HERE -------------
/*
 * whence -- Example program: show full path of executable the current
 *	process was launched from. Works under both CLI and Workbench.
 *
 * Thanks to Josh Rovero for pointing out that the info needed for
 * working under CLI was available in cli_CommandName.
 *
 * Written because Mike Meyer wanted it. Happy to oblige, Mike.
 */

/*
 * Copyright 1989 Doug Merritt. License to use hereby granted.
 * {pyramid,apple}!xdos!doug
 * (408) 370-7875
 */

/*
 * Compiled under Manx Aztec C V3.6A
 *	cc -Iinclude: whence.c
 *	ln -o whence whence.o -lc
 *
 * Note use of BufRound() macro instead of allocated memory; this
 * is widely useful.
 * Also the Lock2Path() function is very handy.
 *
 * Bugs: when called from workbench, or relative to current directory,
 * the device name is shown, but when called via absolute path from CLI,
 * the ASSIGN'ed name is shown (e.g. "mydisk:" rather than "df0:").
 * I have a function that'll fix that (i.e. convert assign names to
 * device names), if anyone wants it let me know.
 *
 * Please retain my copyright notice if/when you borrow this code.
 */
#include	"libraries/dos.h"
#include	"libraries/dosextens.h"
#include	"workbench/startup.h"
#include	"stdio.h"

/*
 * RoundUp0 -- ceiling (yields 0 if n % TO == 0)
 * BufRound -- given buffer address, rounds up to word boundary
 */
#define RoundUp0(N,TO)      ( (TO - (((ULONG)N) & (TO-1)) ) & (TO-1))
#define BufRound(BUF)       &BUF[ RoundUp0(BUF,4) ]

char *
bstr2str(d, bs)
	char		*d;
	unsigned long	bs;
{
	char	*s;
	int	len;

	s = (char *) (bs << 2);
	len = *s++ & 0xff;
	while (len-- > 0)
		*d++ = *s++;
	*d = '\0';
}

Lock2Path(curlock, pathbuf)
	struct	FileLock	*curlock;
	char	*pathbuf;
{
#define	LOCKSTACKSIZE	128
	struct	FileLock *locklist[LOCKSTACKSIZE];
	UBYTE	Ibuf [ sizeof(struct FileInfoBlock) + 4];
	struct	FileInfoBlock	*Ifib;
	int	i;
	char	*s, *path;
	extern	struct	FileLock	*ParentDir();

	Ifib  = (struct FileInfoBlock *) BufRound(Ibuf);
	if (!Examine(curlock, Ifib)) return(0);
	/*
	 * find full path name; build LIFO stack of
	 * pushed locks; when full use popped locks with
	 * Examine() to get each name in path.
	 */
	for (i=0; i<LOCKSTACKSIZE; i++) {
		locklist[i] = curlock;
		if (!(curlock = ParentDir(curlock)) )
			break; 	/* normal loop exit */
	}
	path = &pathbuf[0];
	if (i >= LOCKSTACKSIZE) {
		printf("Too many subdirectories\n");
		*path++ = '?';
		*path++ = '/';
		--i;
	}
	/*
	 * concatenate each component of path
	 */
	if (!Examine(locklist[i--], Ifib))
		goto wierd;
	for (s=Ifib->fib_FileName; *s; )
		*path++ = *s++;
	*path++ = ':';
	while (i >= 0) {
		if (!Examine(locklist[i], Ifib))
			goto wierd;
		for (s=Ifib->fib_FileName; *s; )
			*path++ = *s++;
		if (i>0) *path++ = '/';
		--i;
	}
	*path = '\0';
	return(path - &pathbuf[0]);
wierd:
	*path++ = '?';
	*path++ = '/';
	*path = '\0';
	return(path - &pathbuf[0]);
} /* Lock2Path() */

/*
 * get head of path by chopping off tail (delete anything after : or /)
 */
head(s)
	char	*s;
{
	char	*start;

	if (!*s) return(0);
	start = s;
	while (*s)
		++s;
	while (*s != ':' && *s != '/') {
		if (s == start) return(0);
		--s;
	}
	*++s = '\0';
	return(1);
}

main(argc, argv)
	int	argc;
	char	**argv;
{
	struct	Process			*proc;
	struct	CommandLineInterface	*cli;
	extern	struct	Process		*FindTask();
	extern	struct	WBStartup	*WBenchMsg;
	extern	struct	FileLock	*Lock();
	char	buf[256];
	FILE	*f;

	if (!argc) {
		if ((f = fopen("CON:0/0/640/200/whence", "w")) == NULL)
			exit(20);
		if (!Lock2Path(WBenchMsg->sm_ArgList[0].wa_Lock, buf)) {
			fprintf(f, "Error\n");
			Delay(150L);
			exit(10);
		}
		fprintf(f, "%s\n", buf);
		Delay(150L);
		fclose(f);
		exit(0);
	} else {
		proc = (struct Process *) FindTask(0L);
		cli = (struct CommandLineInterface *)
			( ((long) proc->pr_CLI) << 2);
		bstr2str(buf, cli->cli_CommandName);

		if (!head(buf)) { /* launched relative to current dir? */
			struct	FileLock	*lock;

			lock = Lock("", ACCESS_READ);
			if (!Lock2Path( lock, buf)) {
				UnLock(lock);
				printf("No current dir?\n");
				exit(10);
			}
			UnLock(lock);
		}
		printf("%s\n", buf);
	}
}
-- 
Doug Merritt		{pyramid,apple}!xdos!doug
Member, Crusaders for a Better Tomorrow		Professional Wildeyed Visionary

mwm@eris.berkeley.edu (Mike (I'll think of something yet) Meyer) (07/21/89)

I wish to thank everyone for the comments & help on this topic.  I
knew the basic algorithm implemented by Doug (thanx, Doug!) before I
asked, having done this for Unix. Like I said, this is a nontrivial
problem (doing it that way, anyway - I was hoping for a nicer way).
To wit, Doug failed to consider a number of cases that cause problems.
It's obvious the code won't deal with them, _if_ you know about them.
But they aren't obvious cases.

Script follows:

1> pwd
src:c
1> whence			; correct case
src:c
1> /c/whence			; mishandled example 1
/c/
1> :c/whence			; mishandled example 2
:c/
1> makedir c:x
1> copy whence c:x/whence
1> x/whence			; mishandled example 3
x/
1> cd /
1> x/whence			; mishandled example 4
x/
1> x//x/whence			; mishandled example 5
x//x/

All three are wrong, as the incorrectly give back a relative path.
Example 2 is absolute on a device, but doesn't specify the device.
For example 3, I'm running WShell; your milage may vary with other
shells. Example 5 doesn't matter now - but once the other bugs are
fixed, it will.

This problem ranks with 'more' as being "one of the worlds hardest
single-evening hacks", in that it _looks_ straightforward and simple;
it's just that to do either one right, there are lots of non-obvious
things you have to deal with. I would be surprised if anyone could sit
down and write code that dealt correctly with all the cases in a
single evening, unless you've done it before.

Note that my examples are pulled from Unix, and there are other
gotchas that can't happen on the Amiga. There's one on the list that
can't happen on Unix (#2), and there are probably others.

That's why I asked before trying to write it myself.

	<mike
--
Il brilgue: les toves lubricilleux			Mike Meyer
Se gyrent en vrillant dans le guave,			mwm@berkeley.edu
Enmimes sont les gougebosqueux,				ucbvax!mwm
Et le momerade horsgrave.				mwm@ucbjade.BITNET

mwm@eris.berkeley.edu (Mike (I'll think of something yet) Meyer) (07/21/89)

Having seen everyone's examples, I finally sat down and wrote my own.
For my uses, I needed a subroutine that handed back a "canonical" form
- i.e., one that gave me the same name every time, no matter how
invoked. Two things made this happen. One was taking the device part
of any absolute names to a lock, then using getpath() from the Lattice
library (this is basically just Lock2Path, as written by Doug) to turn
it back into a path. Thus, all path names come out with assigns, etc.
mapped out. The other step is to take any part of the file name not
handed back by getpath, and deal with occurences of "//" in the name.

There's one other Lattice library function here, findpath(). It walks
the path of a cli, and returns a lock on the first directory that
contains a file (subdirectory path) with the name of it's argument
file (subdirectory path). This is pretty trivial to derive from
which.c.

Since I was using those routines, I went ahead and wrote my preferred
function headers - ANSI prototype style. Those of you with Lattice 5.
or later should be able to just compile this. -L -dTEST to get a
binary that reports it's name in canonical form, sans those to get a
.o file with a "which" routine that will pass back that name. Those of
you with Manx will have to change it.

	<mike


---------------- cut here ----------------
/*
 * which - figure out where I was run from, and return the name
 *	of that binary.
 */

#include <dos.h>
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#include <libraries/dos.h>
#include <proto/dos.h>

static int
myname(char *, char *) ;

#ifdef	TEST
void
main(argc, argv) char **argv; {
	char pathbuf[FMSIZE] ;

	if (!which(argv[0], pathbuf)) puts("Can't find path") ;
	else puts(pathbuf) ;
	}
#endif

which(char *myname, char *pathbuf) {
	char	*ip ;
	BPTR	lock ;
	int	status ;

	if (*myname != ':' && (ip = strchr(myname, ':')) != NULL) {
		/*
		 * Absolute path, so just canonicalize that. This means we
		 * need to get a lock on the dev: file, and change myname
		 * to point at the rest of it.
		 */
		ip = '\0' ;
		lock = Lock(myname, ACCESS_READ) ;
		myname = ip + 1 ;
		}
	else {
		/*
		 * Get a lock on the directory we're from, if we're in
		 * a directory on the path.
		 * N.B.: findpath returns a lock on the current directory
		 * if we feed it an absolute path name. Strange, but true.
		 */
		lock = findpath(myname) ;
		if (lock == -1) {
			/*
			 * Not on the path, so see if we're in C:
			 */
			sprintf(pathbuf, "c:%s", myname) ;
			if (getfa(pathbuf) != -1) return 0 ;

			/*
			 * It exists in C:. So get a lock on C:, so get a lock
			 * on the c: directory for later use.
			 */
			lock = Lock("c:", ACCESS_READ) ;
			if (lock == -1) return 0 ;
			}

		}
	/*
	 * At this point, lock is a lock on the directory that we were
	 * run from (well, one hopes that, anyway). So we turn that
	 * into a path name, then combine the two correctly.
	 */
	status = getpath(lock, pathbuf) ;
	UnLock(lock) ;
	if (status) return 0 ;

	/*
	 * We now have a directory name known to be in canonical form,
	 * and the name of a file that isn't canonical, but can be found
	 * in that directory. So we canonicalize the catenation of the two.
	 *
	 * Step one: deal with ":". Note that getpath has the noxious habit
	 * of returning "dev", instead of the more proper "dev:". This leaves
	 * four cases:
 	 *
	 *	*myname		:			?
	 *
	 * status
	 * true		add ':' to pathbuf		add ':' to pathbuf
	 * false	ip = strchr(pathbuf, ':')	add '/' to pathbuf
	 *
	 *		both above need myname += 1
	 *
	 * "add x to pathbuf" means ip = memchr(pathbuf, '\0', FMSIZE)
	 * and *ip = x
	 */
	status = (ip = strchr(pathbuf, ':')) == NULL ;
	if (*myname == ':' && !status) myname += 1 ;
	else {
		ip = strchr(pathbuf, '\0') ;
		*ip = status ? ':' : '/' ;
		if (*myname == ':') myname += 1 ;
		}
	
	/*
	 * Step two: Deal with "//" in the non-canonical path. Since
	 * pathbuf now holds a valid "dev:" string plus some canonical
	 * path from it, and myname a path from that directory down, that's
	 * all we have to worry about. Actually, we ought to put in a check
	 * for *myname == '/' && *ip == ':' (meaning we're trying to back up
	 * over the device), but that should never happen.
	 */
	for (;*myname; myname += 1)
		if (*myname == '/' && *ip == '/')
			/* double '/', get rid of one of them! */
			while (*--ip != '/' && *ip != ':')
				;
		else *++ip = *myname ;

	return 1 ;
	}


--
Il brilgue: les toves lubricilleux			Mike Meyer
Se gyrent en vrillant dans le guave,			mwm@berkeley.edu
Enmimes sont les gougebosqueux,				ucbvax!mwm
Et le momerade horsgrave.				mwm@ucbjade.BITNET

toebes@sas.UUCP (John Toebes) (07/21/89)

One thing that seems to be forgotten in this discussion is that it is
entirely possible that a commands has no 'inherant' path.  Take for example
a resident command (written in C and made residentable) that is on the resident
list.  When it is run, there is no path that it came from...  The only way
to work in the presence of the various shells (which may have hidden paths
such as WShell) is to utilize the (admiditly ugly) method of creating an overlay
program which WILL retain an open handle on the original file.  This has
the one problem of not being able to locate the directory as all you have
is a file handle.  Converting from a file handle to a lock is not a legal
operation (currently)... However you do have the ability to store information
as part of the file itself.

Have you considered environment variables??? :-) :-) :-)

--
 |0|\   John A. Toebes, VIII                    usenet:..mcnc!rti!sas!toebes
 |.|/  Coordinator of The Software Distillery   BBS: (919)-471-6436
 ===    Bix: JTOEBES      Plink: JTOEBES     CI$:"sorry Charlie"
 

doug@xdos.UUCP (Doug Merritt) (07/21/89)

In article <26555@agate.BERKELEY.EDU> mwm@eris.berkeley.edu (Mike (I'll think of something yet) Meyer) writes:
>
>knew the basic algorithm implemented by Doug (thanx, Doug!) before I
>asked, having done this for Unix.

(You're welcome!)
Me too, years ago (I did a pwd/getcwd() for V6 Unix).

>To wit, Doug failed to consider a number of cases that cause problems.

Ouch! Mea maxima culpa. Ok, I didn't test it thoroughly enough. But the
fix (enclosed below) is much simpler than you imply. The bug in the code
amounts to:

	if (buggy check for relative path) use Lock2Path

The fix that makes it work for *all* of your counterexamples is:

	always get lock on path, use Lock2Path

This does a little unnecessary work when we've already got the full path,
but it saves some annoying code for correctly detecting relative versus
absolute paths, *and* it has the virtue that it now is fully consistent,
and always starts the path with the volume name, never an assigned name
nor a device name. Also it is more obvious that it will work on all
cases than it would be with a relative path check.

>Like I said, this is a nontrivial
>problem (doing it that way, anyway - I was hoping for a nicer way).

Actually I'm not sure how to arrange for a much nicer way. Slightly nicer,
yes, by making a lock on the executable available for cli just like for
Workbench. But that's minor; the only major improvement would be if
AmigaDOS left the absolute path lying around somewhere. Yet to do this,
AmigaDOS would have to do the same kind of work itself. Better to skip
the overhead that's usually not needed, and let the application do it
in the few cases it's wanted.

I *do* think that functions like my Lock2Path() should be in a standard
link or shared library somewhere, to save trouble.

>This problem ranks with 'more' as being "one of the worlds hardest
>single-evening hacks", in that it _looks_ straightforward and simple;
>it's just that to do either one right, there are lots of non-obvious
>things you have to deal with. I would be surprised if anyone could sit
>down and write code that dealt correctly with all the cases in a
>single evening, unless you've done it before.

I found out a couple of weeks ago that "more" is, as you say, much
harder than it looks. Well, "less" is, anyway. If you rigidly enforce
a restriction of only going forward/back by units of a page, it's not *too*
bad. But as soon as you allow units of a line, and goto line number, etc,
it starts getting wierd.

I don't think they're in the same class. After all, this one only took
me one hour yesterday, and ten minutes today to fix the bug I would've
found if I'd had more time for testing (I was in a hurry to get to work
by 10:00am yesterday). And aside from comments, it's relatively short.
If Lock2Path() were available in a library, as it should be, it would
be positively trivial (once we understand about both WBenchMsg
*and* cli_CommandName).
	Doug
P.S. Thanks for the gentle phrasing of your bug report.
------------------------ CUT HERE ---------- CUT HERE --------------------
/*
 * whence 2.0 -- Example program: show full path of executable from whence
 *	the current process was launched from. Works under both CLI and
 *	Workbench. This version works on all relative paths as well as absolute.
 *
 * Thanks to Josh Rovero for pointing out that the info needed for
 * working under CLI was available in cli_CommandName.
 *
 * Written because Mike Meyer wanted it. Happy to oblige, Mike.
 *
 * (Mike pointed out what looked like a major bug involving relative
 * paths in the first release yesterday. A minor logic change fixed that.
 * Mea Maxima Culpa. But hey, that's what I get for trying to release code
 * I wrote and tested before nine in the morning. Hmmm...it's 8:30am now :-)
 *		DRM Fri Jul 21 08:36:11 PDT 1989
 */

/*
 * Copyright 1989 Doug Merritt. License to use hereby granted.
 * {pyramid,apple}!xdos!doug
 * (408) 370-7875
 */

/*
 * Compiled under Manx Aztec C V3.6A
 *	cc -Iinclude: whence.c
 *	ln -o whence whence.o -lc
 *
 * Note use of BufRound() macro instead of allocated memory; this
 * is widely useful.
 * Also the Lock2Path() function is very handy.
 *
 * Please retain my copyright notice if/when you borrow this code.
 */
#include	"libraries/dos.h"
#include	"libraries/dosextens.h"
#include	"workbench/startup.h"
#include	"stdio.h"

/*
 * RoundUp0 -- ceiling (yields 0 if n % TO == 0)
 * BufRound -- given buffer address, rounds up to word boundary
 */
#define RoundUp0(N,TO)      ( (TO - (((ULONG)N) & (TO-1)) ) & (TO-1))
#define BufRound(BUF)       &BUF[ RoundUp0(BUF,4) ]

/*
 * convert BSTR to C string
 */
char *
bstr2str(d, bs)
	char		*d;
	unsigned long	bs;
{
	char	*s;
	int	len;

	s = (char *) (bs << 2);
	len = *s++ & 0xff;
	while (len-- > 0)
		*d++ = *s++;
	*d = '\0';
}

/*
 * convert file lock to a full path
 */
Lock2Path(curlock, pathbuf)
	struct	FileLock	*curlock;
	char	*pathbuf;
{
#define	LOCKSTACKSIZE	128
	struct	FileLock *locklist[LOCKSTACKSIZE];
	UBYTE	Ibuf [ sizeof(struct FileInfoBlock) + 4];
	struct	FileInfoBlock	*Ifib;
	int	i;
	char	*s, *path;
	extern	struct	FileLock	*ParentDir();

	Ifib  = (struct FileInfoBlock *) BufRound(Ibuf);
	if (!Examine(curlock, Ifib)) return(0);
	/*
	 * find full path name; build LIFO stack of
	 * pushed locks; when full use popped locks with
	 * Examine() to get each name in path.
	 */
	for (i=0; i<LOCKSTACKSIZE; i++) {
		locklist[i] = curlock;
		if (!(curlock = ParentDir(curlock)) )
			break; 	/* normal loop exit */
	}
	path = &pathbuf[0];
	if (i >= LOCKSTACKSIZE) {
		printf("Too many subdirectories\n");
		*path++ = '?';
		*path++ = '/';
		--i;
	}
	/*
	 * concatenate each component of path
	 */
	if (!Examine(locklist[i--], Ifib))
		goto wierd;
	for (s=Ifib->fib_FileName; *s; )
		*path++ = *s++;
	*path++ = ':';
	while (i >= 0) {
		if (!Examine(locklist[i], Ifib))
			goto wierd;
		for (s=Ifib->fib_FileName; *s; )
			*path++ = *s++;
		if (i>0) *path++ = '/';
		--i;
	}
	*path = '\0';
	return(path - &pathbuf[0]);
wierd:
	*path++ = '?';
	*path++ = '/';
	*path = '\0';
	return(path - &pathbuf[0]);
} /* Lock2Path() */

/*
 * get head of path by chopping off tail (delete anything after : or /)
 */
head(s)
	char	*s;
{
	char	*start;

	if (!*s) return(0);
	start = s;
	while (*s)
		++s;
	while (*s != ':' && *s != '/') {
		if (s == start) return(0);
		--s;
	}
	*++s = '\0';
	return(1);
}

main(argc, argv)
	int	argc;
	char	**argv;
{
	struct	Process			*proc;
	struct	CommandLineInterface	*cli;
	extern	struct	Process		*FindTask();
	extern	struct	WBStartup	*WBenchMsg;
	extern	struct	FileLock	*Lock();
	char	buf[256];
	FILE	*f;

	if (!argc) {
		/* minimal workbench support; this is only an example! */
		if ((f = fopen("CON:0/0/640/200/whence", "w")) == NULL)
			exit(20);
		if (!Lock2Path(WBenchMsg->sm_ArgList[0].wa_Lock, buf)) {
			fprintf(f, "Error\n");
			Delay(150L);
			exit(10);
		}
		fprintf(f, "%s\n", buf);
		Delay(150L);	/* delay 3 seconds to see example results */
		fclose(f);
		exit(0);
	} else {		/* cli support */
		struct FileLock *lock;

		proc = (struct Process *) FindTask(0L);
		cli = (struct CommandLineInterface *)
			( ((long) proc->pr_CLI) << 2);
		bstr2str(buf, cli->cli_CommandName);

		lock = Lock(buf, ACCESS_READ);
		if (!Lock2Path( lock, buf)) {
			printf("No current dir?\n");
			exit(10);
		}
		UnLock(lock);
		head(buf);
		printf("%s\n", buf);
	}
}
-- 
Doug Merritt		{pyramid,apple}!xdos!doug
Member, Crusaders for a Better Tomorrow		Professional Wildeyed Visionary

mwm@eris.berkeley.edu (Mike (I'll think of something yet) Meyer) (07/22/89)

In article <1114@sas.UUCP> toebes@sas.UUCP (John Toebes) writes:
<One thing that seems to be forgotten in this discussion is that it is
<entirely possible that a commands has no 'inherant' path.

No, that's not forgotten. I've been assuming that if there isn't a
file in the file system from which the binary came, you have to give
up. Nothing else to do, and no way around it.

It's also pretty obvious that checking the search path for a file that
matches your name may not return the right name.  But that
approximation is better than nothing. Especially when I want it to
replace similar code for Unix in a port :-).

	<mike
--
[Our regularly scheduled .signature preempted.]		Mike Meyer
The Amiga 1000: Let's build _the_ hackers machine.	mwm@berkeley.edu
The Amiga 500: Let's build one as cheaply as possible!	ucbvax!mwm
The Amiga 2000: Let's build one inside an IBM PC!	mwm@ucbjade.BITNET

peter@sugar.hackercorp.com (Peter da Silva) (07/22/89)

In article <7359@cbmvax.UUCP>, daveh@cbmvax.UUCP (Dave Haynie) writes:
> This is actually a case in which Workbench
> does a better job than CLI.

The Workbench startup message has LOTS of advantages over the CLI. For one
thing, the WBArgs structure is a lot closer to an ARGV: there's no limit to
thenumber of files, etc...

More importantly, it works a lot more reliably in a lot more environments.
CLI/Execute is hard to use and easy to break.

I have one BIG wish for 1.4. It doesn't require any code changes. It just
requires a documentation change:

In the WBArgs structure, document it that the LOCK may be 0, and if it is the
corresponding argument may not be a valid file name... i.e., an option or
a filename with embedded wildcards.

In the WBStartup structure, if sm_ToolWindow is NULL, then there's an extra
two BPTRs at the end of the structure, that are intended to correspond to
Input() and Output().

This will allow the Workbench to be expanded into a true shell. If you do this,
I'll *write* a shell that uses WBStartup structures, and a C startup that will
build a REAL argv/argc out of it...

See 'launch' for further details.
-- 
Peter "Have you hugged your wolf today" da Silva      `-_-'
...texbell!sugar!peter, or peter@sugar.hackercorp.com  'U`

peter@sugar.hackercorp.com (Peter da Silva) (07/22/89)

In article <432@xdos.UUCP>, doug@xdos.UUCP (Doug Merritt) writes:
> The only case I can see that would fail is when someone gets some code
> going using loadseg() explicitly, which (as far as I know, anyone want
> to correct me?) does not save the executable's name anywhere.

Well, Browser and Launch use LoadSeg, but they then produce a WBStartup
message, so everything's copacetic. (What does Copacetic mean, anyway?)
-- 
Peter "Have you hugged your wolf today" da Silva      `-_-'
...texbell!sugar!peter, or peter@sugar.hackercorp.com  'U`

aleks@well.UUCP (Brian J. Witt) (07/24/89)

Yes, you might have problems when using a script, but try this out
and see how far you get.  If your program was launched under CLI or
Shell (true programs officially from CA), then:
          myproc->pr_CLI->cli_CommandName
is a BSTR to the path used to invoke your program as typed in on the
command line.  If there are now path seperators here, then the
program was in the current directory (a quick Lock() should answer
that question).  If the lock fails, then walk the path seglist in:
          myproc->pr_CLI->cli_CommandDir
attempting locks on the path directories.  You can try this right
now by running one program using an explicit path.  In another CLI
type,  status full .  Check out those paths!!!

   Danger Will Robinson!!!!!  The prophets have announced that AmigaDOS
will receive some serious changes in the next few years.  As such, the
sturctures named here may wither and die.  The best long-term solution
may be to ask for a DOS function call to return the lock.  For short
term, allow this method to work while SysBase->version < 40...  If
program private resources are ever to become reality (ala Mac), then
this function call must be available.

   Danger Will Robinson!!!!!  If you program came into being from 
some one LoadSeg()ing-CreateProc()'ing, chances are there won't be
a CLI structure.  At that point, you're just an orphan... :-(


--- When you re-zone vacate cemetry land for apartment buildings,
    do you have to notify the next of kind of those buried nearby?
--- brian witt
--- well!aleks@ucbvax.Berkeley.EDU

thad@cup.portal.com (Thad P Floryan) (07/24/89)

Re: Peter da Silva's question ...

In my Funk & Wagnall's
"
co-pa-set-ic (pronunciation glyphs) See copesetic.

co-pe-set-ic ( "" ) adj. Slang. tip-top; first-rate; excellent; fine: said of
       one's condition: also spelled copasetic.  Also copesettic.
       [US coinage, circa 1930]
"

Thad Floryan [ thad@cup.portal.com (OR)  ..!sun!portal!cup.portal.com!thad ]