Path: utzoo!attcan!utgpu!jarvis.csri.toronto.edu!mailrus!ames!apple!motcsd!xdos!doug From: doug@xdos.UUCP (Doug Merritt) Newsgroups: comp.sys.amiga.tech Subject: Re: What is my name? Summary: Ok, ok: Full solution #2 enclosed: whence.c V2.0 Message-ID: <434@xdos.UUCP> Date: 21 Jul 89 15:44:27 GMT References: <26385@agate.BERKELEY.EDU> <20565@cup.portal.com> <1227@atanasoff.cs.iastate.edu> <432@xdos.UUCP> <7372@cbmvax.UUCP> <433@xdos.UUCP> <26555@agate.BERKELEY.EDU> Reply-To: doug@xdos.UUCP (Doug Merritt) Organization: Hunter Systems, Mountain View CA (Silicon Valley) Lines: 253 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) { 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