Thread: Function to do runtime relative directory mapping

Function to do runtime relative directory mapping

From
Bruce Momjian
Date:
Tom Lane wrote:
> Bruce Momjian <pgman@candle.pha.pa.us> writes:
> > Tom Lane wrote:
> >> I guess what you are saying is we should have a configure-time option to
> >> address configured directories via relative paths from the executable's
> >> directory, rather than absolute paths?  Seems reasonable ...
>
> > Yep.  In fact, why would we not use that by default?
>
> Because it'll be slower.  Instead of
>     /usr/local/pgsql/lib
> we'd be using something like
>     /usr/local/pgsql/bin/../lib
> which is not too bad here but would get worse if the directories are not
> so close.
>
> But perhaps we can arrange for the path to be simplified down to an
> absolute form when it's constructed at backend startup?  You'd need a
> routine anyway to combine the bindir path (determined by FindExec) with
> the relative path provided by configure, so maybe this routine could be
> smart about leading ../ in the configure path.

I wrote relative_path() which does the mapping from compiled src/dst to
a new path.  For example:

    $ tst1 /usr/local/pgsql/bin /usr/local/pgsql/lib /users/fred/pgsql/bin
    /users/fred/pgsql/lib

    $ tst1 /a/b/c /a/d /f/g/h
    /f/d

This can be used in the backend to map from your bindir to a relative
libdir at runtime, and share too.  It returns [mp]alloc'ed values, or
NULL if it can't do the tranformation.

Patch attached.

--
  Bruce Momjian                        |  http://candle.pha.pa.us
  pgman@candle.pha.pa.us               |  (610) 359-1001
  +  If your life is a hard drive,     |  13 Roberts Road
  +  Christ can be your backup.        |  Newtown Square, Pennsylvania 19073
Index: src/include/port.h
===================================================================
RCS file: /cvsroot/pgsql-server/src/include/port.h,v
retrieving revision 1.27
diff -c -c -r1.27 port.h
*** src/include/port.h    30 Apr 2004 17:52:07 -0000    1.27
--- src/include/port.h    4 May 2004 17:07:30 -0000
***************
*** 26,31 ****
--- 26,32 ----
  extern char *last_path_separator(const char *filename);
  extern void canonicalize_path(char *path);
  extern char *get_progname(char *argv0);
+ extern char *relative_path(const char *src, const char *dst, const char *target);

  /* Portable delay handling */
  extern void pg_usleep(long microsec);
Index: src/port/path.c
===================================================================
RCS file: /cvsroot/pgsql-server/src/port/path.c,v
retrieving revision 1.5
diff -c -c -r1.5 path.c
*** src/port/path.c    9 Mar 2004 04:49:02 -0000    1.5
--- src/port/path.c    4 May 2004 17:07:31 -0000
***************
*** 13,21 ****
--- 13,31 ----
   *-------------------------------------------------------------------------
   */

+ #ifndef FRONTEND
+ #include "postgres.h"
+ #else
  #include "c.h"
+ #endif
  #include <ctype.h>

+ #ifndef WIN32
+ #define ISSEP(c)    ((c) == '/')
+ #else
+ #define ISSEP(c)    ((c) == '/' || (c) == '\\')
+ #endif
+
  /*
   *    is_absolute_path
   */
***************
*** 33,39 ****
  }


-
  /*
   *    first_path_separator
   */
--- 43,48 ----
***************
*** 120,123 ****
--- 129,241 ----
          return argv0;
      else
          return last_path_separator(argv0) + 1;
+ }
+
+
+ /*
+  *    relative_path
+  *
+  *    Determine the relative path for getting from 'src' to 'dst' and
+  *    map that change from 'target' to a new directory.
+  */
+ char *
+ relative_path(const char *src, const char *dst, const char *target)
+ {
+     const char *dst_slash = NULL;
+     char    *ret;
+     int        dirs_up;
+
+ #ifdef WIN32
+     if (isalpha(*src) && src[1] == ':' &&
+         isalpha(*dst) && dst[1] == ':')
+     {
+         if (toupper(*src) != toupper(*src))
+             return NULL;    /* no relative path, can't cross drive letters */
+     }
+
+     /* Move past drive letters */
+     if (isalpha(*src) && src[1] == ':')
+         src += 2;
+     if (isalpha(*dst) && dst[1] == ':')
+         src += 2;
+ #endif
+
+     /* If we don't have two absolute paths, give up */
+     if (!is_absolute_path(src) ||
+         !is_absolute_path(dst))
+         return NULL;
+
+     dst_slash = dst;
+
+     while (1)
+     {
+         /* Move past adjacent slashes like //, and trailing ones */
+         while (ISSEP(*src) && (ISSEP(src[1]) || !src[1]))
+             src++;
+         while (ISSEP(*dst) && (ISSEP(dst[1]) || !dst[1]))
+             dst++;
+
+         /* Paths are done or not equal? */
+         if (!*src || !*dst)
+             break;
+ #ifndef WIN32
+         if (*src != *dst)
+             break;
+ #else
+         /* Win32 filesystem is case insensitive */
+         if (toupper(*src) != toupper(*dst) &&
+             (!ISSEP(*src) || !ISSEP(*dst)))    /* '/' == '\\' */
+             break;
+ #endif
+         if (ISSEP(*dst))
+             dst_slash = dst;
+
+         src++;
+         dst++;
+     }
+
+     /* Did the last part of the path match? */
+     dirs_up = ((*src == '\0' || ISSEP(*src)) &&
+                (*dst == '\0' || ISSEP(*dst))) ? 0 : 1;
+
+     /* How many directories do we have to move up? */
+     while (*src)
+     {
+         if (ISSEP(*src))
+         {
+             while (ISSEP(*src))
+                 src++;
+             if (*src != '\0')    /* skip trailing slash */
+                 dirs_up++;
+         }
+         else
+             src++;
+     }
+
+ #ifndef FRONTEND
+     ret = palloc(strlen(target) + strlen(dst) + 1);
+ #else
+     ret = malloc(strlen(target) + strlen(dst) + 1);
+ #endif
+
+     strcpy(ret, target);
+
+     /* Trim off trailing slash from target */
+     if (strlen(ret) > 0 && ISSEP(ret[strlen(ret)-1]))
+         dirs_up++;
+
+     while (dirs_up--)
+         if (last_path_separator(ret) != NULL)
+             *last_path_separator(ret) = '\0';    /* trim off directory */
+         else
+             return NULL;
+
+     /* Add dst to trimmed target */
+     strcat(ret, dst_slash);
+
+     /* Trim off trailing slash from result */
+     if (strlen(ret) > 0 && ISSEP(ret[strlen(ret)-1]))
+         *last_path_separator(ret) = '\0';    /* trim off slash */
+
+     return ret;
  }

Re: [pgsql-hackers-win32] Function to do runtime relative directory

From
Andrew Dunstan
Date:
Bruce Momjian wrote:

>+         if (toupper(*src) != toupper(*src))
>
>

Shouldn't this be

    if (toupper(*src) != toupper(*dst))

?


For completeness, you should probably also check for network drive paths ("\\machine\sharename\foo").

I also think we should just canonicalise everything early, and then never have to worry about / vs \ again.

I must confess I think this scheme is overkill - I can't think of a use case where one would want a relocatable
installationwhich would any pattern other than the one we are thinking of for the windows binary installer. Are we
takingflexibility too far? 

cheers

andrew





Re: [pgsql-hackers-win32] Function to do runtime relative directory

From
Bruce Momjian
Date:
Andrew Dunstan wrote:
> Bruce Momjian wrote:
>
> >+             if (toupper(*src) != toupper(*src))
> >
> >
>
> Shouldn't this be
>
> if (toupper(*src) != toupper(*dst))
>
> ?
>

Yep, fixed.

> For completeness, you should probably also check for network
> drive paths ("\\machine\sharename\foo").

Oh, OK.

>
> I also think we should just canonicalise everything early, and
> then never have to worry about / vs \ again.
>
> I must confess I think this scheme is overkill - I can't think
> of a use case where one would want a relocatable installation
> which would any pattern other than the one we are thinking of
> for the windows binary installer. Are we taking flexibility too
> far?

This code might be used on Unix too.

--
  Bruce Momjian                        |  http://candle.pha.pa.us
  pgman@candle.pha.pa.us               |  (610) 359-1001
  +  If your life is a hard drive,     |  13 Roberts Road
  +  Christ can be your backup.        |  Newtown Square, Pennsylvania 19073

Re: [pgsql-hackers-win32] Function to do runtime relative directory

From
Andrew Dunstan
Date:
Bruce Momjian wrote:

>Andrew Dunstan wrote:
>
>
>>I must confess I think this scheme is overkill - I can't think
>>of a use case where one would want a relocatable installation
>>which would any pattern other than the one we are thinking of
>>for the windows binary installer. Are we taking flexibility too
>>far?
>>
>>
>
>This code might be used on Unix too.
>
>
>

I know. I can't think of a Unix situation where you would want anything
else either.

If you want it relocatable, I would think that you would want

 movable-root/
    /bin
    /lib
    /share
    /whatever

and then you could just pick up that root and put it somewhere else and
it would still work, Windows or Unix, it wouldn't matter, and you
wouldn't break anything else. I guess using some other scheme you could
pick up the various bits from under, say, /usr and put them in
/usr/local, but it strikes me as being very messy and likely to be error
prone.

Maybe it's just a failure of my imagination.

cheers

andrew

Re: [pgsql-hackers-win32] Function to do runtime relative directory

From
Tom Lane
Date:
Bruce Momjian <pgman@candle.pha.pa.us> writes:
> Andrew Dunstan wrote:
>> Shouldn't this be
>> if (toupper(*src) != toupper(*dst))

> Yep, fixed.

Also, all of the ctype.h calls need to be like

    toupper((unsigned char) *src)

to avoid problems with high-bit-set characters.

            regards, tom lane

Re: Function to do runtime relative directory mapping

From
reina_ga@hotmail.com (Tony Reina)
Date:
pgman@candle.pha.pa.us (Bruce Momjian) wrote in message >   extern void canonicalize_path(char *path);
> + #ifndef WIN32
> + #define ISSEP(c)    ((c) == '/')
> + #else
> + #define ISSEP(c)    ((c) == '/' || (c) == '\\')
> + #endif


I've seen references to this "canonicalization" of the Windows \
character for the win32 port. I don't claim to be a programming guru,
but I recently read Jeff Cogswell's C++ for Dummies and he says (p.
546 "Separating a path name") that you can use Unix-style / to
separate directories in your programming and Windows will know what to
do. In other words, you don't need \\ to separate directory names. /
should work just fine.

Perhaps MinGW and the gcc compiler on Windows behaves differently(?)

Then again, perhaps there are other factors that I'm not considering.

-Tony

Re: Function to do runtime relative directory mapping

From
"Andrew Dunstan"
Date:
Tony Reina said:
> pgman@candle.pha.pa.us (Bruce Momjian) wrote in message >   extern void
> canonicalize_path(char *path);
>> + #ifndef WIN32
>> + #define ISSEP(c)    ((c) == '/')
>> + #else
>> + #define ISSEP(c)    ((c) == '/' || (c) == '\\')
>> + #endif
>
>
> I've seen references to this "canonicalization" of the Windows \
> character for the win32 port. I don't claim to be a programming guru,
> but I recently read Jeff Cogswell's C++ for Dummies and he says (p. 546
> "Separating a path name") that you can use Unix-style / to
> separate directories in your programming and Windows will know what to
> do. In other words, you don't need \\ to separate directory names. /
> should work just fine.
>
> Perhaps MinGW and the gcc compiler on Windows behaves differently(?)
>
> Then again, perhaps there are other factors that I'm not considering.
>

It has nothing to do with the compiler - it's a function of the underlying
MSVCRT library. And you are correct. See initdb.c for an example - after
canonicalising it can and does happily use / as the separator. The only
trouble you have is in dealing with Windows command interpreters, when you
have to make sure that you quote the path (you have to do that anyway, as
the path might also include spaces).

cheers

andrew