{
  $Id: getcwd.inc,v 1.1 2003/05/26 21:29:16 jonas Exp $
}
{*
 * Copyright (c) 1999 Apple Computer, Inc. All rights reserved.
 *
 * @APPLE_LICENSE_HEADER_START@
 * 
 * The contents of this file constitute Original Code as defined in and
 * are subject to the Apple Public Source License Version 1.1 (the
 * "License").  You may not use this file except in compliance with the
 * License.  Please obtain a copy of the License at
 * http://www.apple.com/publicsource and read it before using this file.
 * 
 * This Original Code and all software distributed under the License are
 * distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, EITHER
 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT.  Please see the
 * License for the specific language governing rights and limitations
 * under the License.
 * 
 * @APPLE_LICENSE_HEADER_END@
 *
 *
 * Copyright (c) 1989, 1991, 1993
 *  The Regents of the University of California.  All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 * 3. All advertising materials mentioning features or use of this software
 *    must display the following acknowledgement:
 *  This product includes software developed by the University of
 *  California, Berkeley and its contributors.
 * 4. Neither the name of the University nor the names of its contributors
 *    may be used to endorse or promote products derived from this software
 *    without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 *}

const
  MAXPATHLEN = 1024;
  MAXNAMLEN = 255;

function getcwd_physical(pt: pchar; size: size_t): pchar;
var
  dp: pdirent;
  dir: pdir;
  dev: dev_t;
  ino: ino_t;
  first: longint;
  bpt, bup: pchar;
  s: stat;
  root_dev: dev_t;
  root_ino: ino_t;
  ptsize, upsize: size_t;
  save_errno: longint;
  ept, eup, up: pchar;
  len, off: size_t;

  err, notfound: boolean;

  {*
   * If no buffer specified by the user, allocate one as necessary.
   * If a buffer is specified, the size has to be non-zero.  The path
   * is built from the end of the buffer backwards.
   *}
begin
  err := false;
  notfound := false;
  if (pt <> nil) then
    begin
      ptsize := 0;
      if (size = 0) then
        begin
          errno := ESysEINVAL;
          getcwd_physical := nil;
          exit;
        end;
      ept := pt + size;
    end
  else
    begin
      ptsize := 1024;
      getmem(pt,ptsize);
      ept := pt + ptsize;
    end;
  bpt := ept - 1;
  bpt^ := #0;

  {*
   * Allocate bytes (1024 - malloc space) for the string of "../"'s.
   * Should always be enough (it's 340 levels).  If it's not, allocate
   * as necessary.  Special case the first stat, it's ".", not "..".
   *}
  upsize := 1024;
  getmem(up,1024);
  eup := up + MAXPATHLEN;
  bup := up;
  up[0] := '.';
  up[1] := #0;

  { Save root values, so know when to stop. }
  if (Fpstat('/', s) = 0) THEN
    begin
      root_dev := s.st_dev;
      root_ino := s.st_ino;
    
      errno := 0;      { XXX readdir has no error return. }

      first := 1;
      repeat
        { Stat the current level. }
        if (fplstat(up, s) <> 0) then
          err := true
        else
          begin
            { Save current node values. }
            ino := s.st_ino;
            dev := s.st_dev;
        
            { Check for reaching root. }
            if ((root_dev = dev) and (root_ino = ino)) then
              begin
                dec(bpt);
                bpt^ := '/';
                {*
                * It's unclear that it's a requirement to copy the
                * path to the beginning of the buffer, but it's always
                * been that way and stuff would probably break'.
                *}
                move(bpt^, pt^, ept - bpt);
                freemem(up);
                getcwd_physical := pt;
                exit;
              end;
        
            {
            * Build pointer to the parent directory, allocating memory
            * as necessary.  Max length is 3 for "../", the largest
            * possible component name, plus a trailing NULL.
            *}
            if (bup + 3  + MAXNAMLEN + 1 >= eup) then
              begin
                upsize := upsize*2;
                reallocmem(up,upsize);
                bup := up;
                eup := up + upsize;
              end;

            bup^ := '.';
            inc(bup);
            bup^ := '.';
            inc(bup);
            bup^ := #0;

            {* Open and stat parent directory. *}
            dir := Fpopendir(up);
            if (pdir <> nil) then
              begin
                if (fpfstat(dir^.dd_fd,s) <> 0) then
                  err := true;
              end
            else
              err := true;
            
            if not err then
              begin
                { Add trailing slash for next directory. }
                bup^ := '/';
                inc(bup);
            
                {*
                * If it's a mount point, have to stat each element because
                * the inode number in the directory is for the entry in the
                * parent directory, not the inode number of the mounted file.
                *'}
                save_errno := 0;
                if (s.st_dev = dev) then
                  begin
                    repeat
                      dp := Fpreaddir(dir);
                      notfound := dp = nil;
                    until notfound or
                          (dp^.d_fileno = ino);
                  end
                else
                  begin
                    repeat
                      dp := Fpreaddir(dir);
                      if (dp = nil) then
                        notfound := true
                      else
                        begin
                          if (dp^.d_name[0] = '.') and
                             ((dp^.d_name[1] = #0) or
                              ((dp^.d_name[1] = '.') and
                               (dp^.d_name[2] = #0))) then
                            continue;
                          move(dp^.d_name, bup^, dp^.d_namlen + 1);

                          { Save the first error for later. }
                          if (fplstat(up,s) <> 0) then
                            begin
                              if (save_errno = 0) then
                                save_errno := errno;
                              errno := 0;
                              continue;
                            end;
                        end;
                    until notfound or
                          ((s.st_dev = dev) and
                           (s.st_ino = ino));
                  end;
            
                {*
                * Check for length of the current name, preceding slash,
                * leading slash.
                *}
                if not (notfound) then
                  begin
                    // was: (first ? 1 : 2), first can only be 1 or 0
                    if (bpt - pt <= dp^.d_namlen + ((first xor 1) + 1)) then
                      begin
                        if ( ptsize <> 0) then
                          begin
                            errno := ESysERANGE;
                            err := true;
                          end
                        else
                          begin
                            off := bpt - pt;
                            len := ept - bpt;
                            ptsize := ptsize *2;
                            reallocmem(pt,ptsize);
                            bpt := pt + off;
                            ept := pt + ptsize;
                            move(bpt^, (ept - len)^, len);
                            bpt := ept - len;
                          end;
                      end;
                    if (first = 0) then
                      begin
                        dec(bpt);
                        bpt^ := '/';
                      end;
                    dec(bpt,dp^.d_namlen);
                    move(dp^.d_name, bpt^, dp^.d_namlen);
                    Fpclosedir(dir);
                
                    { Truncate any file name. }
                    bup^ := #0;
                    first := 0;
                  end;
              end;
          end;
      until err or notfound;
    
    if (notfound) then
      begin
        {*
        * If readdir set errno, use it, not any saved error; otherwise,
        * didn't find the current directory in its parent directory, set
        * errno to ENOENT.'
        *}
        if (errno = 0) then
          if save_errno <> 0 then
            errno := save_errno
          else
            errno := ESysENOENT;
      end;
  end;
  if (err) then
    begin
      if (ptsize <> 0) then
        freemem(pt);
      freemem(up);
      getcwd_physical := nil;
    end;
end;


function getcwd(pt: pchar; size: size_t): pchar;
var
  pwd: pchar;
  pwdlen: size_t;
  dev: dev_t;
  ino: ino_t;
  s: stat;
begin
(*
  { Check $PWD -- if it's right, it's fast. }
  if ((pwd = getenv("PWD")) != NULL && pwd[0] == '/' && stat(pwd, &s) != -1) {
    dev = s.st_dev;
    ino = s.st_ino;
    if (stat(".", &s) != -1 && dev == s.st_dev && ino == s.st_ino) {
      pwdlen = strlen(pwd);
      if (pt) {
        if (!size) {
          errno = EINVAL;
          return (NULL);
        }
        if (pwdlen + 1 > size) {
          errno = ERANGE;
          return (NULL);
        }
      } else if ((pt = malloc(pwdlen + 1)) == NULL) {
        errno = ENOMEM;
        return (NULL);
      }
      memmove(pt, pwd, pwdlen);
      pt[pwdlen] = '\0';
      return (pt);
    }
  }
*)
  getcwd := (getcwd_physical(pt, size));
end;

{
  $Log: getcwd.inc,v $
  Revision 1.1  2003/05/26 21:29:16  jonas
    - disabled nanosleep for darwin for now
    + getcwd for darwin

}
