home *** CD-ROM | disk | FTP | other *** search
/ Nebula / nebula.bin / SourceCode / libcs / abspath.c next >
C/C++ Source or Header  |  1990-12-11  |  8KB  |  242 lines

  1. /*
  2.  * Copyright (c) 1990 Carnegie Mellon University
  3.  * All Rights Reserved.
  4.  * 
  5.  * Permission to use, copy, modify and distribute this software and its
  6.  * documentation is hereby granted, provided that both the copyright
  7.  * notice and this permission notice appear in all copies of the
  8.  * software, derivative works or modified versions, and any portions
  9.  * thereof, and that both notices appear in supporting documentation.
  10.  *
  11.  * THE SOFTWARE IS PROVIDED "AS IS" AND CARNEGIE MELLON UNIVERSITY
  12.  * DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL
  13.  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS.  IN NO EVENT
  14.  * SHALL CARNEGIE MELLON UNIVERSITY BE LIABLE FOR ANY SPECIAL, DIRECT,
  15.  * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER
  16.  * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF
  17.  * CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
  18.  * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  19.  *
  20.  * Users of this software agree to return to Carnegie Mellon any
  21.  * improvements or extensions that they make and grant Carnegie the
  22.  * rights to redistribute these changes.
  23.  *
  24.  * Export of this software is permitted only after complying with the
  25.  * regulations of the U.S. Deptartment of Commerce relating to the
  26.  * Export of Technical Data.
  27.  */
  28. /*
  29.  * abspath -- determine absolute pathname
  30.  *
  31.  * Originally written sometime around 1980 by James Gosling.
  32.  *
  33.  *----------------------------------------------------------------------
  34.  *
  35.  *     abspath (name,result)
  36.  *     char *name;
  37.  *     char *result;
  38.  *
  39.  * Abspath places the absolute pathname of the string name into
  40.  * the string result.
  41.  *
  42.  * Abspath takes a pathname and converts it to an absolute pathname by
  43.  * prepending the name of the current working directory if necessary.
  44.  * Then the absolute pathname is compacted by removing and resolving
  45.  * superfluous steps.
  46.  *
  47.  * Steps of "" (two adjacent slashes) and steps of "." are removed
  48.  * because they have no effect on the meaning of the pathname.
  49.  *
  50.  * Steps of ".." are resolved by removing them together with the
  51.  * preceeding step.  However, resolution is not possible if the
  52.  * preceeding step is also ".."
  53.  *
  54.  * Abspath calls getwd to obtain the name of the current working
  55.  * directory when needed.  To improve performance, the result from
  56.  * getwd is saved so that getwd need not be invoked again during
  57.  * subsequent calls on abspath.  If you change the current working
  58.  * directory (via chdir) you must call abspath(0,0) which causes
  59.  * abspath to flush its saved result from getwd.  If you do not do
  60.  * this abspath will continue to use its saved result from getwd
  61.  * and this will most likely cause it to produce erronious results.
  62.  *
  63.  * Abspath returns 0 on success and -1 on failure.  The only failure
  64.  * that can happen is a failure of getwd.  See the documentation on
  65.  * getwd.  Failures in getwd are pretty catastrophic.
  66.  *
  67.  *----------------------------------------------------------------------
  68.  *
  69.  * HISTORY
  70.  * $Log:    abspath.c,v $
  71.  * Revision 1.2  90/12/11  17:49:59  mja
  72.  *     Add copyright/disclaimer for distribution.
  73.  * 
  74.  * 30-Apr-85  Steven Shafer (sas) at Carnegie-Mellon University
  75.  *    Adapted for 4.2 BSD UNIX.  Changed to new getwd() routine.
  76.  *
  77.  * 15-Nov-82  Tom Rodeheffer (tlr) at Carnegie-Mellon University
  78.  *    Redid the rest of the routine so that now it has been completely
  79.  *    retouched, although still conserving most of the original design.
  80.  *    Increased curwd to 1024 characters to match what getwd can
  81.  *    produce.  Per suggestions by Steve Shafer, added the ability to
  82.  *    flush the remembered current working directory (which you should
  83.  *    do after calling chdir) and improved the initial construction of
  84.  *    the result so that the current working directory is not obtained
  85.  *    if the given pathname is already an absolute pathname.
  86.  *
  87.  * 14-Nov-82  Tom Rodeheffer (tlr) at Carnegie-Mellon University
  88.  *    Redid compaction of the absolute path name so that leading steps
  89.  *    of ".." are preserved.  Also fixed so that the trailing slash
  90.  *    is not deleted if it is also the initial slash.
  91.  *
  92.  *----------------------------------------------------------------------
  93.  */
  94.  
  95.  
  96. #define TRUE   1
  97. #define FALSE  0
  98.  
  99. char *getwd();
  100.  
  101. static char havecurwd = FALSE;
  102. static char curwd [1024];    /* remember the current working directory */
  103.  
  104. int abspath (name,result)
  105.  
  106. char * name;
  107. char * result;
  108. {
  109.     register char * src;    /* source pointer for copy operations */
  110.     register char * dst;    /* destination pointer for copy operations */
  111.     register char * fence;  /* pointer to slash that cannot be backed over */
  112.     register char * t;      /* scanback pointer in dst when we hit a slash */
  113.  
  114.  
  115.  
  116.     if (name == 0  ||  result == 0)
  117.     {
  118.     havecurwd = FALSE;  /* flush knowledge of current working directory */
  119.     return (0);
  120.     }
  121.  
  122.  
  123.     /*
  124.      * Construct the initial result pathname, which is basically just
  125.      * a concatenation of the current working directory (if the name
  126.      * is a relative pathname) and the name.  If we need to know the
  127.      * current working directory but don't have it saved away, we call
  128.      * getwd to figure it out for us.
  129.      */
  130.  
  131.     dst = result;
  132.  
  133.     if (name[0] != '/')
  134.     {
  135.     if (!havecurwd  &&  getwd(curwd) == 0)  return (-1);
  136.     havecurwd = TRUE;
  137.  
  138.     src = curwd;
  139.     while ((*dst++ = *src++) != 0)  ;    /* copy curwd to result */
  140.     dst[-1] = '/';                       /* tack on a trailing slash */
  141.     }
  142.  
  143.     src = name;
  144.     while ((*dst++ = *src++) != 0)  ;   /* copy name to result */
  145.     dst[-1] = '/';                      /* tack on a trailing slash */
  146.     *dst = 0;                           /* make it null-terminated */
  147.  
  148.  
  149.  
  150.     /*
  151.      * Now scan through result and compact the pathname.
  152.      *
  153.      *   "//"      =>  "/"
  154.      *   "/./"     =>  "/"
  155.      *   "/x/../"  =>  "/"
  156.      *
  157.      * where x is a string without a slash.  Note that x
  158.      * cannot be "", ".", or ".."
  159.      *
  160.      * There is guaranteed to be a trailing slash on result when
  161.      * we start, so that we don't need any special cases to handle
  162.      * trailing steps--all steps in the pathname end with a slash.
  163.      *
  164.      * The fence points to the most recent slash that ".." cannot
  165.      * back over.  Basically, all steps to the left of the fence
  166.      * are ".."  Initially the fence points to the first slash.  We
  167.      * are paranoid so we scan for the first slash.  Any characters
  168.      * coming before the first slash (which must be the result of
  169.      * getwd) are assumed to be magical incantations and we leave
  170.      * them alone.  This is never expected to happen, but who knows?
  171.      */
  172.  
  173.     src = result;
  174.     dst = result;
  175.  
  176.     while (*src)
  177.     {
  178.     if ((*dst++ = *src++) == '/')
  179.     {
  180.         fence = dst-1;  /* set fence to first slash */
  181.         break;
  182.     }
  183.     }
  184.  
  185.     while (*src)
  186.     {
  187.     if ((*dst++ = *src++) == '/')
  188.     {
  189.         t = dst-1;      /* address of slash */
  190.  
  191.  
  192.         switch (*--t)
  193.         {
  194.         case '/':           /* found "//" */
  195.         dst = t+1;      /* take off "/" */
  196.         break;
  197.  
  198.         case '.': 
  199.         switch (*--t)
  200.         {
  201.         case '/':           /* found "/./" */
  202.             dst = t+1;      /* take off "./" */
  203.             break;
  204.  
  205.         case '.': 
  206.             if (*--t == '/')
  207.             {                   /* found "/../" */
  208.             if (t == fence)
  209.             {                   /* it is a leading ".." */
  210.                 fence = dst-1;  /* move fence over it */
  211.             }
  212.             else
  213.             {
  214.                 while (*--t != '/')  ;
  215.                 dst = t+1;      /* take off "x/../" */
  216.             }
  217.             }
  218.             break;
  219.         }
  220.         break;
  221.         }
  222.     }
  223.     }
  224.  
  225.     *dst = 0;      /* null-terminate the result */
  226.  
  227.  
  228.     /*
  229.      * Now get rid of a trailing slash provided it is not also an
  230.      * initial slash.
  231.      *
  232.      * Note that we tacked on a trailing slash originally and the
  233.      * compaction shouldn't affect it so it should still be there,
  234.      * but we check anyway because we're paranoid.
  235.      */
  236.  
  237.     if (--dst > result  &&  *dst == '/')  *dst = 0;
  238.  
  239.  
  240.     return (0);
  241. }
  242.