www.delorie.com/archives/browse.cgi   search  
Mail Archives: djgpp-workers/1999/12/17/12:18:24

Message-ID: <385A69D1.F8B20F7A@softhome.net>
Date: Fri, 17 Dec 1999 18:50:25 +0200
From: Laurynas Biveinis <lauras AT softhome DOT net>
X-Mailer: Mozilla 4.7 [en] (Win98; I)
X-Accept-Language: en
MIME-Version: 1.0
To: DJGPP Workers <djgpp-workers AT delorie DOT com>
Subject: The 5th symlink patch
Reply-To: djgpp-workers AT delorie DOT com

This is a multi-part message in MIME format.
--------------4C053DA2EC7A23114D04E754
Content-Type: text/plain; charset=us-ascii
Content-Transfer-Encoding: 7bit

There are two differences from the 4th patch:

   - I've found BSD manpage talking about symlinks, and there was mentioned
another function which does not follow symlinks - lchown(). It was missing 
from my implementation. Now it isn't. (As well as its documentation).

   - After successful GCC bootstrap I tried to build binutils, but encountered
the problem that C preprocessor still does not follow symlinks :( The fix was
changing one digit to another.

I've rebuilt lots of utilities with symlinks and no other problems encountered
so far.

Laurynas Biveinis
----------------------
--------------4C053DA2EC7A23114D04E754
Content-Type: text/plain; charset=us-ascii;
 name="symlink5.diff"
Content-Transfer-Encoding: 7bit
Content-Disposition: inline;
 filename="symlink5.diff"

diff -u -r -N --minimal djgpp.old/include/fcntl.h djgpp/include/fcntl.h
--- djgpp.old/include/fcntl.h	Mon Jun 29 00:27:10 1998
+++ djgpp/include/fcntl.h	Sat Dec  4 10:13:52 1999
@@ -1,3 +1,4 @@
+/* Copyright (C) 1999 DJ Delorie, see COPYING.DJ for details */
 /* Copyright (C) 1998 DJ Delorie, see COPYING.DJ for details */
 /* Copyright (C) 1995 DJ Delorie, see COPYING.DJ for details */
 #ifndef __dj_include_fcntl_h_
@@ -41,6 +42,7 @@
 #define O_TRUNC		0x0800
 #define O_APPEND	0x1000
 #define O_NONBLOCK	0x2000
+#define O_NOLINK        0x4000
 
 #include <sys/types.h>
 
diff -u -r -N --minimal djgpp.old/include/libc/stubs.h djgpp/include/libc/stubs.h
--- djgpp.old/include/libc/stubs.h	Mon Sep  8 00:07:18 1997
+++ djgpp/include/libc/stubs.h	Wed Dec  8 19:56:54 1999
@@ -49,6 +49,7 @@
 #define pow10 __pow10
 #define pow2 __pow2
 #define putenv __putenv
+#define readlink __readlink
 #define sbrk __sbrk
 #define setitimer __setitimer
 #define setmode __setmode
diff -u -r -N --minimal djgpp.old/include/libc/symlink.h djgpp/include/libc/symlink.h
--- djgpp.old/include/libc/symlink.h	Thu Jan  1 00:00:00 1970
+++ djgpp/include/libc/symlink.h	Sat Dec  4 09:52:38 1999
@@ -0,0 +1,39 @@
+/* Copyright (C) 1999 DJ Delorie, see COPYING.DJ for details */
+
+/* Written by Laurynas Biveinis                                            */
+/* This file contains some internal info related to symlinks   */
+/* Note: symlink file format is still in internal include file */
+/* because I don't think it's required for user apps           */
+#ifndef __dj_include_libc_symlink_h_
+#define __dj_include_libc_symlink_h_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#ifndef __dj_ENFORCE_ANSI_FREESTANDING
+
+#ifndef __STRICT_ANSI__
+
+#ifndef _POSIX_SOURCE
+
+/* A prototype for internal library function for fully resolving symlink   */
+/* chain. Standard function readlink() solves only one symlink level.      */
+/* If path name passed appears to be not a symlink, it is copied to result */
+/* string. Return code 1 means success, 0 - failure (to many links - errno */
+/* is set too).                                                            */
+
+int _solve_symlinks(const char * symlink_path, char * real_path);
+
+#endif /* !_POSIX_SOURCE */
+#endif /* !__STRICT_ANSI__ */
+#endif /* !__dj_ENFORCE_ANSI_FREESTANDING */
+
+#ifndef __dj_ENFORCE_FUNCTION_CALLS
+#endif /* !__dj_ENFORCE_FUNCTION_CALLS */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* !__dj_include_libc__h_ */
diff -u -r -N --minimal djgpp.old/include/limits.h djgpp/include/limits.h
--- djgpp.old/include/limits.h	Wed Sep  9 20:14:52 1998
+++ djgpp/include/limits.h	Sat Dec  4 10:14:00 1999
@@ -1,3 +1,4 @@
+/* Copyright (C) 1999 DJ Delorie, see COPYING.DJ for details */
 /* Copyright (C) 1998 DJ Delorie, see COPYING.DJ for details */
 /* Copyright (C) 1995 DJ Delorie, see COPYING.DJ for details */
 #ifndef __dj_include_limits_h_
@@ -34,7 +35,7 @@
 
 #define _POSIX_ARG_MAX		16384	/* but only for exec's to other djgpp programs */
 #define _POSIX_CHILD_MAX	7	/* limited by memory; 7 for 386MAX */
-#define _POSIX_LINK_MAX		1	/* POSIX says 8, but DOS says 1 */
+#define _POSIX_LINK_MAX		8	/* Symlink support allow this.     */
 #define _POSIX_MAX_CANON	126	/* POSIX says 255, but DOS says 126 */
 #define _POSIX_MAX_INPUT	126	/* POSIX says 255, but DOS says 126 */
 #define _POSIX_NAME_MAX		12	/* 8.3 */
diff -u -r -N --minimal djgpp.old/include/sys/stat.h djgpp/include/sys/stat.h
--- djgpp.old/include/sys/stat.h	Thu Aug 10 07:06:26 1995
+++ djgpp/include/sys/stat.h	Sat Dec  4 10:14:28 1999
@@ -1,3 +1,4 @@
+/* Copyright (C) 1999 DJ Delorie, see COPYING.DJ for details */
 /* Copyright (C) 1995 DJ Delorie, see COPYING.DJ for details */
 #ifndef __dj_include_sys_stat_h_
 #define __dj_include_sys_stat_h_
@@ -15,6 +16,7 @@
 #define S_ISDIR(m)	(((m) & 0xf000) == 0x3000)
 #define S_ISFIFO(m)	(((m) & 0xf000) == 0x4000)
 #define S_ISREG(m)	(((m) & 0xf000) == 0x0000)
+#define S_ISLNK(m)      (((m) & 0xf000) == 0x8000)
 
 #define S_ISUID		0x80000000
 #define S_ISGID		0x40000000
@@ -72,6 +74,7 @@
 #define S_IFDIR		0x3000
 #define S_IFIFO		0x4000
 #define S_IFFIFO	S_IFIFO
+#define S_IFLNK         0x8000
 
 #define S_IFLABEL	0x5000
 #define S_ISLABEL(m)	(((m) & 0xf000) == 0x5000)
@@ -79,6 +82,7 @@
 void	        _fixpath(const char *, char *);
 unsigned short  _get_magic(const char *, int);
 int             _is_executable(const char *, int, const char *);
+int	        lstat(const char *_path, struct stat *_buf);
 int		mknod(const char *_path, mode_t _mode, dev_t _dev);
 char          * _truename(const char *, char *);
 
diff -u -r -N --minimal djgpp.old/include/unistd.h djgpp/include/unistd.h
--- djgpp.old/include/unistd.h	Mon Jun 29 02:02:48 1998
+++ djgpp/include/unistd.h	Fri Dec 17 18:34:16 1999
@@ -1,3 +1,4 @@
+/* Copyright (C) 1999 DJ Delorie, see COPYING.DJ for details */
 /* Copyright (C) 1998 DJ Delorie, see COPYING.DJ for details */
 /* Copyright (C) 1995 DJ Delorie, see COPYING.DJ for details */
 #ifndef __dj_include_unistd_h_
@@ -134,7 +135,9 @@
 int		gethostname(char *buf, int size);
 int		getpagesize(void);
 char *		getwd(char *__buffer);
+int             lchown(const char * file, int owner, int group);
 int		nice(int _increment);
+int             readlink(const char * filename, char * buffer, size_t size);
 void *		sbrk(int _delta);
 int		symlink (const char *, const char *);
 int		sync(void);
diff -u -r -N --minimal djgpp.old/src/libc/ansi/errno/errno.txh djgpp/src/libc/ansi/errno/errno.txh
--- djgpp.old/src/libc/ansi/errno/errno.txh	Sat Mar 20 23:48:18 1999
+++ djgpp/src/libc/ansi/errno/errno.txh	Sat Nov  6 19:54:12 1999
@@ -121,8 +121,9 @@
 
 @item 18
 
-EMLINK -- Too many links.  Not used in DJGPP (as DOS doesn't support
-links).
+EMLINK -- Too many links. Can be throwed by any library function working
+with files or directories. Usually means encountered link loop (link1 -> link2,
+link2 -> link1).
 
 @item 19
 
diff -u -r -N --minimal djgpp.old/src/libc/dos/dir/ftw.c djgpp/src/libc/dos/dir/ftw.c
--- djgpp.old/src/libc/dos/dir/ftw.c	Mon Aug 28 05:38:52 1995
+++ djgpp/src/libc/dos/dir/ftw.c	Sat Dec  4 10:15:20 1999
@@ -1,3 +1,4 @@
+/* Copyright (C) 1999 DJ Delorie, see COPYING.DJ for details */
 /* Copyright (C) 1995 DJ Delorie, see COPYING.DJ for details */
 /* ftw() for DJGPP.
  *
@@ -12,6 +13,7 @@
  */
 
 #include <libc/stubs.h>
+#include <libc/symlink.h>
 #include <stdio.h>
 #include <string.h>
 #include <errno.h>
@@ -107,6 +109,7 @@
 {
   int flag;
   unsigned char pathbuf[FILENAME_MAX];
+  unsigned char real_path[FILENAME_MAX];
   int dirattr;
   int len;
   int e = errno;
@@ -135,7 +138,12 @@
 
   /* Fail for non-directories.  */
   errno = 0;
-  dirattr = _chmod(pathbuf, 0, 0);
+  /* _chmod() does not recognize symlinks,  */
+  /* so we give real name instead.          */
+  if (!_solve_symlinks(pathbuf, real_path))
+     return -1;
+  
+  dirattr = _chmod(real_path, 0, 0);
   if (errno == ENOENT)
     return -1;
   else if ((dirattr & 0x10) != 0x10)
diff -u -r -N --minimal djgpp.old/src/libc/dos/process/dosexec.c djgpp/src/libc/dos/process/dosexec.c
--- djgpp.old/src/libc/dos/process/dosexec.c	Thu Jun  3 19:27:36 1999
+++ djgpp/src/libc/dos/process/dosexec.c	Sun Nov  7 16:52:50 1999
@@ -3,6 +3,7 @@
 /* Copyright (C) 1996 DJ Delorie, see COPYING.DJ for details */
 /* Copyright (C) 1995 DJ Delorie, see COPYING.DJ for details */
 #include <libc/stubs.h>
+#include <libc/symlink.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
@@ -518,6 +519,7 @@
   int i;
   char *go32, *sip=0;
   char rpath[FILENAME_MAX];
+  char real_program[FILENAME_MAX];
   int argc=0;
 
   int si_la=0, si_off=0, rm_off, argv_off;
@@ -526,6 +528,11 @@
   int retval;
   int lfn = 2;	/* means don't know yet */
 
+  /* LB: resolve symlinks */
+  if (!_solve_symlinks(program, real_program))
+     return -1;
+
+
   type = _check_v2_prog (program, -1);
 
   /* Because this function is called only, when program
@@ -545,7 +552,7 @@
   if (!is_coff)
   {
     if (type->exec_format == _V2_EXEC_FORMAT_EXE)
-      return direct_exec(program, argv, envp);
+      return direct_exec(real_program, argv, envp);
     else
       return __dosexec_command_exec (program, argv, envp);
   }
@@ -559,7 +566,7 @@
 
   if (v2_0 && is_stubbed)
   {
-    strcpy(rpath, program);
+    strcpy(rpath, real_program);
   }
   else
   {
@@ -567,7 +574,7 @@
     if (!__dosexec_find_on_path(go32, envp, rpath))
     {
       errno = e;
-      return direct_exec(program, argv, envp); /* give up and just run it */
+      return direct_exec(real_program, argv, envp); /* give up and just run it */
     }
 
     if (found_si)
@@ -585,7 +592,7 @@
      usual DOS command line and the !proxy one (which will be put
      into the environment).  Sigh...  */
   save_argv0 = argv[0];
-  argv[0] = unconst(program, char *); /* since that's where we really found it */
+  argv[0] = unconst(program, char *);
   /* Construct the DOS command tail */
   for (argc=0; argv[argc]; argc++);
 
@@ -613,87 +620,81 @@
 
   /* Can't call any functions that use the transfer buffer beyond
      this point: they will overwrite the data already in __tb.  */
+
   tbuf_beg = tbuf_ptr = __tb;
   tbuf_len = _go32_info_block.size_of_transfer_buffer;
   tbuf_end = tbuf_ptr + tbuf_len - 1;
 
-  /* If called from `system' and we have a command line shorter
-     than the DOS limit, we don't need to use !proxy at all.
-     Note that v1.x programs are always run through !proxy,
-     to prevent go32.exe from printing its copyright line. */
-  if (!__dosexec_in_system || !v2_0 || cmdp - cmdline > CMDLEN_LIMIT)
+  /* LB: Starting from DJGPP v2.04, programs are always run through !proxy.
+     This allows correctly handle symlinks to .exes. */
+  if (!check_talloc(found_si ?
+		    type->stubinfo->struct_length : 0
+		    + (argc+1)*sizeof(short)))
   {
-    if (!check_talloc(found_si ?
-		      type->stubinfo->struct_length : 0
-		      + (argc+1)*sizeof(short)))
-    {
-      argv[0] = save_argv0;
-      return -1;
-    }
-    if (found_si)
-    {
-      si_la = talloc(type->stubinfo->struct_length);
-      si_off = si_la - tbuf_beg;
-      dosmemput(sip, type->stubinfo->struct_length, si_la);
-    }
+    argv[0] = save_argv0;
+    return -1;
+  }
+  if (found_si)
+  {
+    si_la = talloc(type->stubinfo->struct_length);
+    si_off = si_la - tbuf_beg;
+    dosmemput(sip, type->stubinfo->struct_length, si_la);
+  }
 
-    rm_off = argv_off = talloc((argc+1) * sizeof(short)) - tbuf_beg;
+  rm_off = argv_off = talloc((argc+1) * sizeof(short)) - tbuf_beg;
 #if 0
-    /* `alloca' could be dangerous with long command lines.  We
-       will instead move the offsets one by one with `_farpokew'.  */
-    rm_argv = (short *)alloca((argc+1) * sizeof(short));
+  /* `alloca' could be dangerous with long command lines.  We
+     will instead move the offsets one by one with `_farpokew'.  */
+  rm_argv = (short *)alloca((argc+1) * sizeof(short));
 #endif
 
-    for (i=0; i<argc; i++)
-    {
-      char *pargv = argv[i];
-      int sl = strlen(pargv) + 1;
-      unsigned long q;
+  for (i=0; i<argc; i++)
+  {
+    char *pargv = argv[i];
+    int sl = strlen(pargv) + 1;
+    unsigned long q;
 
-      if (check_talloc(sl))
-      {
-	q = talloc(sl);
-	dosmemput(pargv, sl, q);
-	_farpokew(_dos_ds, tbuf_beg + argv_off, (q - tbuf_beg) & 0xffff);
-	argv_off += sizeof(short);
-      }
-      else	/* not enough space to pass args */
-      {
-	argv[0] = save_argv0;
-	return -1;
-      }
+    if (check_talloc(sl))
+    {
+      q = talloc(sl);
+      dosmemput(pargv, sl, q);
+      _farpokew(_dos_ds, tbuf_beg + argv_off, (q - tbuf_beg) & 0xffff);
+      argv_off += sizeof(short);
     }
-
-    _farpokew (_dos_ds, tbuf_beg + argv_off, 0);
-    argv_off += sizeof(short);
-
-    argv[0] = save_argv0;
-    /* Environment variables are all malloced.  */
-    proxy_cmdline = (char *)malloc (34);
-    if (!proxy_cmdline)
-      return -1;
-    
-    sprintf(proxy_cmdline, "%s=%04x %04x %04x %04x %04x",
-	     __PROXY, argc,
-	    (unsigned)(tbuf_beg >> 4), rm_off & 0xffff,
-	    (unsigned)(tbuf_beg >> 4), si_off & 0xffff);
-    if (!found_si)
-      proxy_cmdline[22] = 0; /* remove stubinfo information */
-
-    if (__dosexec_in_system && v2_0)
-      pproxy = proxy_cmdline;
-    else
+    else	/* not enough space to pass args */
     {
-      /* `proxy_cmdline looks like an environment variable " !proxy=value".
-         This is used as the REAL command line specification by 2.01
-	 and later executables when called by `system'.  But if that's
-	 not the case, we need a blank instead of the `='.  */
-      proxy_cmdline[__PROXY_LEN] = ' ';
-      pcmd = proxy_cmdline;
+      argv[0] = save_argv0;
+      return -1;
     }
   }
+
+  _farpokew (_dos_ds, tbuf_beg + argv_off, 0);
+  argv_off += sizeof(short);
+
+  argv[0] = save_argv0;
+  /* Environment variables are all malloced.  */
+  proxy_cmdline = (char *)malloc (34);
+  if (!proxy_cmdline)
+    return -1;
+  
+  sprintf(proxy_cmdline, "%s=%04x %04x %04x %04x %04x",
+    __PROXY, argc,
+   (unsigned)(tbuf_beg >> 4), rm_off & 0xffff,
+   (unsigned)(tbuf_beg >> 4), si_off & 0xffff);
+  if (!found_si)
+    proxy_cmdline[22] = 0; /* remove stubinfo information */
+
+  if (__dosexec_in_system && v2_0)
+    pproxy = proxy_cmdline;
   else
-    argv[0] = save_argv0;
+  {
+    /* `proxy_cmdline looks like an environment variable " !proxy=value".
+        This is used as the REAL command line specification by 2.01
+        and later executables when called by `system'.  But if that's
+        not the case, we need a blank instead of the `='.  */
+    proxy_cmdline[__PROXY_LEN] = ' ';
+    pcmd = proxy_cmdline;
+  }
 
   retval = direct_exec_tail(rpath, pcmd, envp, pproxy, lfn);
   if (proxy_cmdline)
@@ -709,9 +710,14 @@
   int cmdlen;
   int i;
   int was_quoted = 0;	/* was the program name quoted? */
+  char real_program[FILENAME_MAX];
+
+  /* LB: solve symlinks here */
+  if (!_solve_symlinks(program, real_program))
+     return -1;
 
   /* Add spare space for possible quote characters.  */
-  cmdlen = strlen(program) + 4 + 2;
+  cmdlen = strlen(real_program) + 4 + 2;
   for (i=0; argv[i]; i++)
     cmdlen += 2*strlen(argv[i]) + 1;
   cmdline = (char *)alloca(cmdlen);
@@ -719,21 +725,21 @@
   /* FIXME: is this LFN-clean?  What special characters can
      the program name have and how should they be quoted?  */
   strcpy(cmdline, "/c ");
-  if (strchr(program, ' ') || strchr(program, '\t'))
+  if (strchr(real_program, ' ') || strchr(real_program, '\t'))
   {
     was_quoted = 1;
     cmdline[3] = '"';
   }
-  for (i = 0; program[i] > ' '; i++)
+  for (i = 0; real_program[i] > ' '; i++)
   {
     /* COMMAND.COM cannot grok program names with forward slashes.  */
-    if (program[i] == '/')
+    if (real_program[i] == '/')
       cmdline[i+3+was_quoted] = '\\';
     else
-      cmdline[i+3+was_quoted] = program[i];
+      cmdline[i+3+was_quoted] = real_program[i];
   }
-  for (; program[i]; i++)
-    cmdline[i+3+was_quoted] = program[i];
+  for (; real_program[i]; i++)
+    cmdline[i+3+was_quoted] = real_program[i];
   if (was_quoted)
   {
     cmdline[i+3+was_quoted] = '"';
@@ -813,7 +819,8 @@
   char line[130], interp[FILENAME_MAX], iargs[130];
   FILE *f;
   char **newargs;
-  int i, hasargs=0;
+  int hasargs=0;
+  unsigned i;
   char *base, *p;
   int has_extension = 0, has_drive = 0;
   char pinterp[FILENAME_MAX];
diff -u -r -N --minimal djgpp.old/src/libc/posix/dirent/opendir.c djgpp/src/libc/posix/dirent/opendir.c
--- djgpp.old/src/libc/posix/dirent/opendir.c	Sun Nov 15 14:48:38 1998
+++ djgpp/src/libc/posix/dirent/opendir.c	Wed Dec  8 16:25:00 1999
@@ -1,8 +1,10 @@
+/* Copyright (C) 1999 DJ Delorie, see COPYING.DJ for details */
 /* Copyright (C) 1998 DJ Delorie, see COPYING.DJ for details */
 /* Copyright (C) 1997 DJ Delorie, see COPYING.DJ for details */
 /* Copyright (C) 1996 DJ Delorie, see COPYING.DJ for details */
 /* Copyright (C) 1995 DJ Delorie, see COPYING.DJ for details */
 #include <libc/stubs.h>
+#include <libc/symlink.h>
 #include <stdlib.h>
 #include <string.h>
 #include <ctype.h>
@@ -14,6 +16,7 @@
 #include <fcntl.h>
 #include <libc/dosio.h>
 #include <dpmi.h>
+#include <stdio.h>
 #include "dirstruc.h"
 
 void
@@ -52,7 +55,14 @@
 opendir(const char *name)
 {
   int length;
-  DIR *dir = (DIR *)malloc(sizeof(DIR));
+  DIR *dir;
+  char name_copy[FILENAME_MAX + 1];
+
+  /* LB: symlink support here. */
+  if (!_solve_symlinks(name, name_copy))
+     return NULL;
+  
+  dir = (DIR *)malloc(sizeof(DIR));
   if (dir == 0)
     return 0;
   dir->num_read = 0;
@@ -68,7 +78,7 @@
     dir->flags |= __OPENDIR_PRESERVE_CASE;
 
   /* Make absolute path */
-  _fixpath(name, dir->name);
+  _fixpath(name_copy, dir->name);
 
   /* Ensure that directory to be accessed exists */
   if (access(dir->name, D_OK))
diff -u -r -N --minimal djgpp.old/src/libc/posix/fcntl/open.c djgpp/src/libc/posix/fcntl/open.c
--- djgpp.old/src/libc/posix/fcntl/open.c	Thu Jun  3 19:27:38 1999
+++ djgpp/src/libc/posix/fcntl/open.c	Sat Nov  6 21:54:06 1999
@@ -4,6 +4,8 @@
 /* Copyright (C) 1996 DJ Delorie, see COPYING.DJ for details */
 /* Copyright (C) 1995 DJ Delorie, see COPYING.DJ for details */
 #include <libc/stubs.h>
+#include <libc/symlink.h>
+#include <limits.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
@@ -58,7 +60,18 @@
     fd = _creatnew(filename, dmode, oflag & 0xff);
   else
   {
-    fd = _open(filename, oflag);
+    char real_name[FILENAME_MAX];
+
+    if (!(oflag & O_NOLINK))
+    {
+       /* LB: symlink support here. */
+       if (!_solve_symlinks(filename, real_name))
+          return -1;
+    }
+    else
+       strcpy(real_name, filename);
+
+    fd = _open(real_name, oflag);
 
     if (fd == -1)
     {
@@ -67,7 +80,7 @@
       if (errno == EMFILE || errno == ENFILE)
 	return fd;
 
-      if (__file_exists(filename))
+      if (__file_exists(real_name))
       {
 	/* Under multi-taskers, such as Windows, our file might be
 	   open by some other program with DENY-NONE sharing bit,
diff -u -r -N --minimal djgpp.old/src/libc/posix/sys/stat/chmod.c djgpp/src/libc/posix/sys/stat/chmod.c
--- djgpp.old/src/libc/posix/sys/stat/chmod.c	Mon Aug 28 05:42:36 1995
+++ djgpp/src/libc/posix/sys/stat/chmod.c	Sun Nov  7 16:49:00 1999
@@ -1,13 +1,22 @@
+/* Copyright (C) 1999 DJ Delorie, see COPYING.DJ for details */
 /* Copyright (C) 1995 DJ Delorie, see COPYING.DJ for details */
+#include <libc/symlink.h>
 #include <libc/stubs.h>
 #include <sys/stat.h>
 #include <io.h>
+#include <stdio.h>
  
 int
 chmod(const char *filename, int pmode)
 {
   int dmode;
-  unsigned attr = _chmod(filename, 0, 0);
+  char real_name[FILENAME_MAX];
+  int attr;
+
+  if (!_solve_symlinks(filename, real_name))
+     return -1;
+  
+  attr = _chmod(real_name, 0, 0);
  
   if (attr == -1)
     return -1;
@@ -19,7 +28,7 @@
  
   /* Must clear the directory and volume bits, otherwise 214301 fails.
      Unused bits left alone (some network redirectors use them).  */
-  if (_chmod(filename, 1, (attr & 0xffe6) | dmode) == -1)
+  if (_chmod(real_name, 1, (attr & 0xffe6) | dmode) == -1)
     return -1;
   return 0;
 }
diff -u -r -N --minimal djgpp.old/src/libc/posix/sys/stat/is_exec.c djgpp/src/libc/posix/sys/stat/is_exec.c
--- djgpp.old/src/libc/posix/sys/stat/is_exec.c	Sat Mar 20 22:59:52 1999
+++ djgpp/src/libc/posix/sys/stat/is_exec.c	Wed Dec  8 16:20:50 1999
@@ -1,3 +1,4 @@
+/* Copyright (C) 1999 DJ Delorie, see COPYING.DJ for details */
 /* Copyright (C) 1996 DJ Delorie, see COPYING.DJ for details */
 /* Copyright (C) 1995 DJ Delorie, see COPYING.DJ for details */
 /* IS_EXEC.C
@@ -26,6 +27,7 @@
 #include <io.h>
 #include <libc/farptrgs.h>
 #include <libc/dosio.h>
+#include <libc/symlink.h>
 
 extern unsigned short _djstat_flags;
 unsigned short        _get_magic(const char *, int);
@@ -166,10 +168,13 @@
 int
 _is_executable(const char *filename, int fhandle, const char *extension)
 {
-  if (!extension && filename)
+  char real_file_name[FILENAME_MAX + 1];
+  if (!_solve_symlinks(filename, real_file_name))
+     return -1;
+  if (!extension && real_file_name)
   {
     const char *cp, *ep=0;
-    for (cp=filename; *cp; cp++)
+    for (cp=real_file_name; *cp; cp++)
     {
       if (*cp == '.')
 	ep = cp;
@@ -181,7 +186,7 @@
   if ((_djstat_flags & _STAT_EXEC_EXT) == 0
       && extension
       && *extension
-      && strlen(extension) <= ((extension[0]=='.') ? 4 : 3))
+      && strlen(extension) <= ((extension[0]=='.') ? 4U : 3U))
     {
       /* Search the list of extensions in executables[]. */
       char tmp_buf[6], *tp = tmp_buf;
@@ -209,7 +214,7 @@
      TWO CHARACTERS, lose here.  Sorry, folks.  */
   if ( (_djstat_flags & _STAT_EXEC_MAGIC) == 0 )
     {
-      switch (_get_magic(filename, fhandle))
+      switch (_get_magic(real_file_name, fhandle))
         {
           case 0x5a4d:      /* "MZ" */
           case 0x010b:
diff -u -r -N --minimal djgpp.old/src/libc/posix/sys/stat/makefile djgpp/src/libc/posix/sys/stat/makefile
--- djgpp.old/src/libc/posix/sys/stat/makefile	Wed Apr  5 07:59:56 1995
+++ djgpp/src/libc/posix/sys/stat/makefile	Sat Sep 25 22:12:30 1999
@@ -6,6 +6,7 @@
 SRC += fixpath.c
 SRC += fstat.c
 SRC += is_exec.c
+SRC += lstat.c
 SRC += mkdir.c
 SRC += mkfifo.c
 SRC += st_loss.c
diff -u -r -N --minimal djgpp.old/src/libc/posix/sys/stat/mkdir.c djgpp/src/libc/posix/sys/stat/mkdir.c
--- djgpp.old/src/libc/posix/sys/stat/mkdir.c	Sun Jul 12 17:29:20 1998
+++ djgpp/src/libc/posix/sys/stat/mkdir.c	Wed Dec 15 12:49:26 1999
@@ -1,7 +1,9 @@
+/* Copyright (C) 1999 DJ Delorie, see COPYING.DJ for details */
 /* Copyright (C) 1998 DJ Delorie, see COPYING.DJ for details */
 /* Copyright (C) 1996 DJ Delorie, see COPYING.DJ for details */
 /* Copyright (C) 1995 DJ Delorie, see COPYING.DJ for details */
 #include <libc/stubs.h>
+#include <libc/symlink.h>
 #include <errno.h>
 #include <sys/stat.h>
 #include <go32.h>
@@ -11,15 +13,21 @@
 #include <io.h>
 #include <dos.h>
 #include <libc/dosio.h>
- 
+#include <stdio.h>
+
 int
 mkdir(const char *mydirname, mode_t mode)
 {
   __dpmi_regs r;
   int use_lfn = _USE_LFN;
   unsigned attr;
+  char dir_name[FILENAME_MAX + 1];
 
-  _put_path(mydirname);
+  /* LB: symlink support here. */
+  if (!_solve_symlinks(mydirname, dir_name))
+     return -1;
+  
+  _put_path(dir_name);
  
   if(use_lfn)
     r.x.ax = 0x7139;
@@ -47,7 +55,7 @@
     {
       /* see if the directory existed, in which case
 	 we should return EEXIST - DJ */
-      if (access(mydirname, D_OK) == 0)
+      if (access(dir_name, D_OK) == 0)
 	errno = EEXIST;
       else
 	errno = save_errno;
@@ -56,12 +64,12 @@
   }
 
   /* mkdir is stub'd, and we don't want to stub chmod also.  */
-  attr = _chmod(mydirname, 0, 0);
+  attr = _chmod(dir_name, 0, 0);
 
   /* Must clear the directory and volume bits, otherwise 214301 fails.
      Unused bits left alone (some network redirectors use them).  Only
      care about read-only attribute.  */
-  if (_chmod(mydirname, 1, (attr & 0xffe6) | ((mode & S_IWUSR) == 0)) == -1)
+  if (_chmod(dir_name, 1, (attr & 0xffe6) | ((mode & S_IWUSR) == 0)) == -1)
     return -1;
   return 0;
 }
diff -u -r -N --minimal djgpp.old/src/libc/posix/sys/stat/stat.c djgpp/src/libc/posix/sys/stat/stat.c
--- djgpp.old/src/libc/posix/sys/stat/stat.c	Thu Jun  3 19:27:40 1999
+++ djgpp/src/libc/posix/sys/stat/stat.c	Wed Dec  8 16:25:52 1999
@@ -3,950 +3,26 @@
 /* Copyright (C) 1997 DJ Delorie, see COPYING.DJ for details */
 /* Copyright (C) 1996 DJ Delorie, see COPYING.DJ for details */
 /* Copyright (C) 1995 DJ Delorie, see COPYING.DJ for details */
-/* This is file STAT.C */
-/*
- *   Almost a 100% U**X-compatible stat() substitute.
- *
- * Usage:
- *
- *   That's easy: put this into libc.a, then just call stat() as usual.
- *
- * Rationale:
- *
- *   Many Unix-born programs make heavy use of stat() library
- *   function to make decisions on files' equality, size, access
- *   attributes etc.  In the MS-DOS environment, many implementations
- *   of stat() are crippled, because DOS makes it very hard to get to
- *   certain pieces of information about files and directories.  Thus
- *   porting a program to DOS is usually an exercise in #ifdef'ing.
- *   This implementation facilitates porting Unix programs to MS-DOS
- *   by providing stat() which is much more Unix-compatible than those
- *   of most DOS-based C compilers (e.g., Borland's).
- *   Specifically, the following issues are taken care of:
- *
- *      1. This stat() doesn't fail for root directories, returning
- *         valid information.
- *      2. Directory size is not reported zero; the number of used
- *         directory entries multiplied by entry size is returned instead.
- *      3. Mode bits are set for all 3 groups (user, group, other).
- *      4. Directories are NOT reported read-only, unless one of R, H or S
- *         attributes is set.
- *      5. Directories have their execute bit set, as they do under Unix.
- *      6. Device names (such as /dev/con, lpt1, aux etc.) are treated as
- *         if they were on a special drive called `@:' (st_dev = -1).
- *         The "character special" mode bit is set for these devices.
- *      7. The inode number (st_ino) is taken from the starting cluster
- *         number of the file.  If the cluster number is unavailable, it
- *         is invented using the file's name in a manner that minimizes
- *         the possibility of inventing an inode which already belongs
- *         to another file.  See below for details.
- *      8. Executable files are found based on files' extensions and
- *         magic numbers present at their beginning, and their execute
- *         bits are set.
- *
- *   Lossage:
- *
- *      Beautiful as the above sounds, this implementation does fail
- *      under certain circumstances.  The following is a list of known
- *      problems:
- *
- *      1. The time fields for a root directory cannot be obtained, so
- *         they are set to the beginning of the Epoch.
- *      2. For files which reside on networked drives, the inode number
- *         is invented, because network redirectors usually do not
- *         bring that info with them.  This is not a total lossage, but
- *         it could get us a different inode for each program run.
- *      3. Empty files do not have a starting cluster number, because
- *         DOS doesn't allocate one until you actually write something
- *         to a file.  For these the inode is also invented.
- *      4. If the st_ino field is a 16 bit number, the invented inode
- *         numbers are from 65535 and down, assuming that most disks have
- *         unused portions near their end.  Valid cluster numbers are 16-bit
- *         unsigned integers, so a possibility of a clash exists, although
- *         the last 80 or more cluster numbers are unused on all drives
- *         I've seen.  If the st_ino is 32 bit, then invented inodes are
- *         all greater than 64k, which totally eliminates a possibility
- *         of a clash with an actual cluster number.
- *      5. The method of computing directory size is an approximation:
- *         a directory might consume much more space, if it has many
- *         deleted entries.  Still, this is a close approximation, and
- *         it does follow the logic of reporting size for a regular file:
- *         only the actually used space is returned.
- *      6. As this implementation relies heavily on undocumented DOS
- *         features, it will fail to get actual file info in environments
- *         other than native DOS, such as DR-DOS, OS/2 etc.  For these,
- *         the function will return whatever info is available with
- *         conventional DOS calls, which is no less than any other
- *         implementation could do.  This stat() might also fail for
- *         future DOS versions, if the layout of internal DOS data
- *         area is changed; however, this seems unlikely.
- *
- * Copyright (c) 1994-96 Eli Zaretskii <eliz AT is DOT elta DOT co DOT il>
- *
- * This software may be used freely so long as this copyright notice is
- * left intact.  There is no warranty on this software.
- *
- */
 
-/*
- * Tested with DJGPP port of GNU C compiler, versions 1.11maint5 and 1.12,
- * under MS-DOS 3.3, 4.01, 5.0, 6.20 (with and without DoubleSpace) and
- * with networked drives under XFS 1.86, Novell Netware 3.22, and
- * TSoft NFS 0.24Beta.
- *
+/* Main entry point.  This is library stat() function.
+   LB: actual code moved to lstat() in lstat.c - this one only
+   supports symlinks
  */
 
-#include <libc/stubs.h>
-#include <stdlib.h>
-#include <stddef.h>
-#include <unistd.h>
-#include <time.h>
-#include <stdio.h>
-#include <string.h>
-#include <ctype.h>
-#include <errno.h>
-#include <fcntl.h>
-#include <sys/types.h>
 #include <sys/stat.h>
-#include <dos.h>
-#include <dir.h>
-#include <sys/fsext.h>
-#include <dpmi.h>
-#include <go32.h>
-#include <libc/farptrgs.h>
-#include <libc/bss.h>
-#include <libc/dosio.h>
-
-#include "xstat.h"
-
-int __getdisk(void);
-int __findfirst(const char *, struct ffblk *, int);
-int __findnext(struct ffblk *);
-
-#define ALL_FILES   (FA_RDONLY|FA_HIDDEN|FA_SYSTEM|FA_DIREC|FA_ARCH)
-
-#define _STAT_INODE         1   /* should we bother getting inode numbers? */
-#define _STAT_EXEC_EXT      2   /* get execute bits from file extension? */
-#define _STAT_EXEC_MAGIC    4   /* get execute bits from magic signature? */
-#define _STAT_DIRSIZE       8   /* compute directory size? */
-#define _STAT_ROOT_TIME  0x10   /* try to get root dir time stamp? */
-#define _STAT_WRITEBIT   0x20   /* fstat() needs write bit? */
-
-/* Should we bother about executables at all? */
-#define _STAT_EXECBIT       (_STAT_EXEC_EXT | _STAT_EXEC_MAGIC)
-
-/* The structure of the full directory entry.  This is the 32-byte
-   record present for each file/subdirectory in a DOS directory.
-   Although the ``packed'' attribute seems to be unnecessary, I use
-   it to be sure it will still work for future versions of GCC.  */
-
-struct full_dirent {
-  char           fname[8]      __attribute__ ((packed));
-  char           fext[3]       __attribute__ ((packed));
-  unsigned char  fattr         __attribute__ ((packed));
-  unsigned char  freserved[10] __attribute__ ((packed));
-  unsigned short ftime         __attribute__ ((packed));
-  unsigned short fdate         __attribute__ ((packed));
-  unsigned short fcluster      __attribute__ ((packed));
-  unsigned int   fsize         __attribute__ ((packed));
-};
-
-
-/* Static variables to speed up SDA DOS Swappable Data Area access on
-   subsequent calls.  */
-
-/* The count of number of SDA's we have.  It is more than 1 for DOS
-   4.x only.  If it has a value of 0, the function init_dirent_table()
-   will be called to compute the addresses where we are to look for
-   directory entry of our file.  A value of -1 means this method is
-   unsupported for this version of DOS.  */
-static int  dirent_count;
-
-/* The table of places to look for our directory entry.
-   Each entry in the table is a linear offset from the beginning of
-   conventional memory which points to a particular location within
-   one of the SDA's, where the entry of a file being stat()'ed could
-   appear.  The offsets are computed once (when the routine is first
-   called) and then reused for other calls.  The actual storage for
-   the table is malloc()'ed when this function is first called.  */
-static unsigned int * dirent_table;
-
-/* When we have only one SDA, this is where its only place to look for
-   directory entry is stored.  */
-static unsigned int   dirent_place;
-
-/* This holds the fail bits from the last call to init_dirent_table(),
-   so we can return them every time get_inode_from_sda() is called.  */
-static unsigned short init_dirent_table_bits;
-
-/* Holds the last seen value of __bss_count, to be safe for
-   restarted programs (emacs).  */
-static int stat_count = -1;
-
-/*
- * Parts of the following code is derived from file DOSSWAP.C,
- * which came with ``Undocumented DOS'', 1st edition.
- */
-
-/* Compute table of pointers to look for directory entry of a file.  */
-static int
-init_dirent_table (void)
-{
-  short          get_sda_func;
-  unsigned short dirent_offset;
-  unsigned short true_dos_version;
-  unsigned short dos_major, dos_minor;
-  __dpmi_regs    regs;
-
-  if (dirent_count == -1)     /* we already tried and found we can't */
-    return 0;
-
-  /* Compute INT 21h function number and offset of directory entry
-     from start of SDA.  These depend on the DOS version.
-     We need exact knowledge about DOS internals, so we need the
-     TRUE DOS version (not the simulated one by SETVER), if that's
-     available.  */
-  true_dos_version = _get_dos_version(1);
-  dos_major = true_dos_version >> 8;
-  dos_minor = true_dos_version & 0xff;
-
-  if ((dos_major == 3) && (dos_minor >= 10))
-    {
-      get_sda_func  = 0x5d06;
-      dirent_offset = 0x1a7;
-    }
-  else if (dos_major == 4)
-    {
-      /* According to ``Undocumented DOS, 2nd edition'', I could have
-         used 5d06 here, as for DOS 5 and above, but I like to be
-         defensive.  In fact, the above book itself uses 5d0b, contrary
-         to its own recommendation.  */
-      get_sda_func  = 0x5d0b;
-      dirent_offset = 0x1b3;
-    }
-  else if (dos_major >= 5)
-    {
-      get_sda_func  = 0x5d06;
-      dirent_offset = 0x1b3;
-    }
-  else
-    {
-      _djstat_fail_bits |= _STFAIL_OSVER;
-      dirent_count = -1;
-      return 0;
-    }
-
-  _djstat_fail_bits &= ~_STFAIL_OSVER;  /* version is OK */
-
-  /* Get the pointer to SDA by calling undocumented function 5dh of INT 21. */
-  regs.x.ax = get_sda_func;
-  __dpmi_int(0x21, &regs);
-  if (regs.x.flags & 1)
-    {
-      _djstat_fail_bits |= _STFAIL_SDA;
-      dirent_count = -1;      /* if the call failed, never try this later */
-      return 0;
-    }
-
-  _djstat_fail_bits &= ~_STFAIL_SDA;    /* Get SDA succeeded */
-
-  /* DOS 4.x might have several SDA's, which means we might have more
-     than one place to look into.  (It is typical of DOS 4 to complicate
-     things.)
-     Compute all the possible addresses where we will have to look.  */
-  if (dos_major == 4)
-    {
-      /* The pointer returned by INT 21h, AX=5D0b points to a header
-         which holds a number of SDA's and then an array of that number
-         of records each one of which includes address of an SDA (DWORD)
-         and its length and type (encoded in a WORD).
-         While walking this list of SDA's, we add to each pointer the
-         offset of directory entry and stash the resulting address in
-         our table for later use.  */
-
-      int  sda_list_walker = MK_FOFF(regs.x.ds, regs.x.si);
-      int  i;
-      int *tbl;
-
-      dirent_count = _farpeekw(_dos_ds, sda_list_walker); /* number of SDA's */
-
-      /* Allocate storage for table.  */
-      tbl = dirent_table = (int *)malloc(dirent_count*sizeof(int));
-      if (!dirent_table)
-        {
-          /* If malloc() failed, maybe later it will succeed, so don't
-             store -1 in dirent_count.  */
-          dirent_count = 0;
-          _djstat_fail_bits |= _STFAIL_DCOUNT;
-          return 0;
-        }
-
-      memset(dirent_table, 0, dirent_count*sizeof(int));
-      _djstat_fail_bits &= ~_STFAIL_DCOUNT; /* dirent_count seems OK */
-
-      /* Walk the array of pointers, computing addresses of directory
-         entries and stashing them in our table.  */
-      _farsetsel(_dos_ds);
-      for (i = dirent_count, sda_list_walker += 2; i--; sda_list_walker += 6)
-        {
-          int            sda_start = _farnspeekl(sda_list_walker);
-          unsigned short sda_len   = _farnspeekw(sda_list_walker + 4) & 0x7fff;
-
-          /* Let's be defensive here: if this SDA is too short to have
-             place for directory entry, we won't use it.  */
-          if (sda_len > dirent_offset)
-            *tbl++ = sda_start + dirent_offset;
-          else
-            dirent_count--;
-        }
-    }
-
-  /* DOS 3.1 and 5.0 or later.  We have only one SDA pointed to by
-     whatever INT 21h, AH=5d returns.  */
-  else
-    {
-      dirent_count = 1;
-      dirent_place = MK_FOFF(regs.x.ds, regs.x.si) + dirent_offset;
-      dirent_table = &dirent_place;
-    }
-
-  return 1;
-}
-
-/* Get inode number by searching DOS Swappable Data Area.
-   The entire directory entry for a file found by FindFirst/FindNext
-   appears at a certain (version-dependent) offset in the SDA after
-   one of those function is called.
-   Should be called immediately after calling DOS FindFirst function,
-   before the info is overwritten by somebody who calls it again.  */
-static unsigned int
-get_inode_from_sda(const char *mybasename)
-{
-  int            count          = dirent_count;
-  unsigned int * dirent_p       = dirent_table;
-  unsigned short dos_mem_base   = _dos_ds;
-  unsigned short our_mem_base   = _my_ds();
-  char  * dot                   = strchr(mybasename, '.');
-  size_t  total_len             = strlen(mybasename);
-  int     name_len              = dot ? dot - mybasename : total_len;
-  int     ext_len               = dot ? total_len - name_len - 1 : 0;
-  int     cluster_offset        = offsetof(struct full_dirent, fcluster);
-
-  /* Restore failure bits set by last call to init_dirent_table(), so
-     they will be reported as if it were called now.  */
-  _djstat_fail_bits |= init_dirent_table_bits;
-
-  /* Force reinitialization in restarted programs (emacs).  */
-  if (stat_count != __bss_count)
-    {
-      stat_count = __bss_count;
-      dirent_count = 0;
-    }
-
-  /* Initialize the table of SDA entries where we are to look for
-     our file.  */
-  if (!dirent_count && !init_dirent_table())
-    {
-      /* Don't save the truename failure bit.  */
-      init_dirent_table_bits = (_djstat_fail_bits & ~_STFAIL_TRUENAME);
-      return 0;
-    }
-  init_dirent_table_bits = (_djstat_fail_bits & ~_STFAIL_TRUENAME);
-  if (dirent_count == -1)
-    return 0;
-
-  count = dirent_count;
-  dirent_p = dirent_table;
-
-  _farsetsel(dos_mem_base);
-
-  /* This is DOS 4.x lossage: this loop might execute many times.
-     For other DOS versions it is executed exactly once.  */
-  while (count--)
-    {
-      unsigned int  src_address = *dirent_p;
-      char          cmp_buf[sizeof(struct full_dirent)];
-
-      /* Copy the directory entry from the SDA to local storage.
-         The filename is stored there in infamous DOS format: name and
-         extension blank-padded to 8/3 characters, no dot between them.  */
-      movedata(dos_mem_base, src_address, our_mem_base, (unsigned int)cmp_buf,
-               sizeof(struct full_dirent));
-
-      /* If this is the filename we are looking for, return
-         its starting cluster. */
-      if (!strncmp(cmp_buf, mybasename, name_len) &&
-          (ext_len == 0 || !strncmp(cmp_buf + 8, dot + 1, ext_len)))
-        return (unsigned int)_farnspeekw(*dirent_p + cluster_offset);
-
-      /* This is not our file.  Search more, if more addresses left. */
-      dirent_p++;
-    }
-
-  /* If not found, give up.  */
-  _djstat_fail_bits |= _STFAIL_BADSDA;
-  return 0;
-}
-
-int _ioctl_get_first_cluster(const char *);
-
-/* Get the number of the first cluster of PATHNAME using
-   the IOCTL call Int 21h/AX=440Dh/CX=0871h, if that call
-   is supported by the OS.  Return the cluster number, or
-   a negative number if this service isn't supported.  */
-
-int
-_ioctl_get_first_cluster(const char *pathname)
-{
-  __dpmi_regs r;
-
-  /* See if the IOCTL GetFirstCluster call is supported.  */
-  r.x.ax = 0x4411;	       /* query generic IOCTL capability by drive */
-  r.h.bl = pathname[0] & 0x1f; /* drive number (1=A:) */
-  r.x.cx = 0x871;
-  __dpmi_int(0x21, &r);
-  if ((r.x.flags & 1) == 0 && r.x.ax == 0)
-    {
-      r.x.ax = 0x440d;	       /* Generic IOCTL */
-      r.x.cx = 0x0871;	       /* category code 08h, minor code 71h */
-      r.x.bx = 1;	       /* pathname uses current OEM character set */
-      r.x.ds = __tb >> 4;
-      r.x.dx = __tb & 0x0f;
-      _put_path(pathname);
-      __dpmi_int(0x21, &r);
-      if ((r.x.flags & 1) == 0)
-	return ( ((int)r.x.dx << 16) + r.x.ax );
-    }
-  return -1;
-}
-
-static char blanks_8[] = "        ";
-
-static int
-stat_assist(const char *path, struct stat *statbuf)
-{
-  struct   ffblk ff_blk;
-  char     canon_path[MAX_TRUE_NAME];
-  char     pathname[MAX_TRUE_NAME];
-  short    drv_no;
-  unsigned dos_ftime;
-
-  _djstat_fail_bits = 0;
-
-  memset(statbuf, 0, sizeof(struct stat));
-  memset(&dos_ftime, 0, sizeof(dos_ftime));
-
-  /* Fields which are constant under DOS.  */
-  statbuf->st_uid     = getuid();
-  statbuf->st_gid     = getgid();
-  statbuf->st_nlink   = 1;
-#ifndef  NO_ST_BLKSIZE
-  statbuf->st_blksize = _go32_info_block.size_of_transfer_buffer;
-#endif
-
-  /* Make the path explicit.  This makes the rest of our job much
-     easier by getting rid of some constructs which, if present,
-     confuse `_truename' and/or `findfirst'.  In particular, it
-     deletes trailing slashes, makes "d:" explicit, and allows us
-     to make an illusion of having a ".." entry in root directories.  */
-  _fixpath (path, pathname);
-
-  /* Get the drive number.  It is always explicit, since we
-     called `_fixpath' on the original pathname.  */
-  drv_no = toupper((unsigned char)pathname[0]) - 'A';
-
-  /* Produce canonical pathname, with all the defaults resolved and
-     all redundant parts removed.  This calls undocumented DOS
-     function 60h.  */
-  if (_truename(path, canon_path) || _truename(pathname, canon_path))
-    {
-      /* Detect character device names which must be treated specially.
-         We could simply call FindFirst and test the 6th bit, but some
-         versions of DOS have trouble with this (see Ralph Brown's
-         Interrupt List, ``214E'', under `Bugs').  Instead we use
-         truename() which calls INT 21/AX=6000H.  For character devices
-         it returns X:/DEVNAME, where ``X'' is the current drive letter
-         (note the FORWARD slash!).  E.g., for CON or \dev\con it will
-         return C:/CON.
-         We will pretend that devices all reside on a special drive
-         called `@', which corresponds to st_dev = -1.  This is because
-         these devices have no files, and we must invent inode numbers
-         for them; this scheme allows to lower a risk of clash between
-         invented inode and one which belongs to a real file.  This is
-         also compatible with what our fstat() does.
-      */
-    char_dev:
-      if (canon_path[2] == '/')
-        {
-          char dev_name[9];     /* devices are at most 8 characters long */
-
-          strncpy(dev_name, canon_path + 3, 8); /* the name without `X:/' */
-          dev_name[8] = '\0';
-          strcpy(canon_path, "@:\\dev\\");
-          strcat(canon_path, dev_name);
-          strncat(canon_path, blanks_8, 8 - strlen(dev_name)); /* blank-pad */
-          canon_path[15] = '\0';   /* ensure zero-termination */
-
-          /* Invent inode */
-          statbuf->st_ino = _invent_inode(canon_path, 0, 0);
-
-          /* Device code. */
-          statbuf->st_dev = -1;
-#ifdef  HAVE_ST_RDEV
-          statbuf->st_rdev = -1;
-#endif
-
-          /* Set mode bits, including character special bit.
-             Should we treat printer devices as write-only?  */
-          statbuf->st_mode |= (S_IFCHR | READ_ACCESS | WRITE_ACCESS);
-
-          /* We will arrange things so that devices have current time in
-             the access-time and modified-time fields of struct stat, and
-             zero (the beginning of times) in creation-time field.  This
-             is consistent with what DOS FindFirst function returns for
-             character device names (if it succeeds--see above).  */
-          statbuf->st_atime = statbuf->st_mtime = time(0);
-          statbuf->st_ctime = _file_time_stamp(dos_ftime);
-
-          return 0;
-        }
-      else if (canon_path[0] >= 'A' && canon_path[0] <= 'z' &&
-               canon_path[1] == ':' && canon_path[2] == '\\')
-        {
-          /* _truename() returned a name with a drive letter.  (This is
-             always so for local drives, but some network redirectors
-             also do this.)  We will take this to be the TRUE drive
-             letter, because _truename() knows about SUBST and JOIN.
-             If the canonicalized path returns in the UNC form (which
-             means the drive is remote), it cannot be SUBSTed or JOINed,
-             because SUBST.EXE and JOIN.EXE won't let you do it; so, for
-             these cases, there is no problem in believing the drive
-             number we've got from the original path (or is there?).  */
-          drv_no = toupper((unsigned char)canon_path[0]) - 'A';
-        }
-    }
-  else
-    {
-      /* _truename() failed.  (This really shouldn't happen, but who knows?)
-         At least uppercase all letters, convert forward slashes to backward
-         ones, and pray... */
-      register const char *src = pathname;
-      register       char *dst = canon_path;
-
-      while ( (*dst = (*src > 'a' && *src < 'z'
-                       ? *src++ - ('a' - 'A')
-                       : *src++)) != '\0')
-        {
-          if (*dst == '/')
-            *dst = '\\';
-          dst++;
-        }
-
-      _djstat_fail_bits |= _STFAIL_TRUENAME;
-    }
-
-  /* Call DOS FindFirst function, which will bring us most of the info.  */
-  if (!__findfirst(pathname, &ff_blk, ALL_FILES))
-    {
-      /* Time fields. */
-      dos_ftime =
-        ( (unsigned short)ff_blk.ff_fdate << 16 ) +
-          (unsigned short)ff_blk.ff_ftime;
-
-      /* If the IOCTL GetFirstCluster call is available, try it first.  */
-      if ( (_djstat_flags & _STAT_INODE) == 0
-	   && (statbuf->st_ino = _ioctl_get_first_cluster(pathname)) <= 0)
-        {
-
-          /* For networked drives, don't believe the starting cluster
-             that the network redirector feeds us; always invent inode.
-             This is because network redirectors leave bogus values there,
-             and we don't have enough info to decide if the starting
-             cluster value is real or just a left-over from previous call.
-             For local files, try first using DOS SDA to get the inode from
-             the file's starting cluster number; if that fails, invent inode.
-             Note that the if clause below tests for non-zero value returned
-             by is_remote_drive(), which includes possible failure (-1).
-             This is because findfirst() already succeeded for our pathname,
-             and therefore the drive is a legal one; the only possibility that
-             is_remote_drive() fails is that some network redirector takes
-             over IOCTL functions in an incompatible way, which means the
-             drive is remote.  QED.  */
-          if (statbuf->st_ino == 0  /* don't try SDA if IOCTL call succeeded */
-	      || _is_remote_drive(drv_no)
-              || (statbuf->st_ino = get_inode_from_sda(ff_blk.ff_name)) == 0)
-            {
-              _djstat_fail_bits |= _STFAIL_HASH;
-              statbuf->st_ino =
-                _invent_inode(canon_path, dos_ftime, ff_blk.ff_fsize);
-            }
-	  else if (toupper ((unsigned char)canon_path[0]) != toupper ((unsigned char)pathname[0])
-		   && canon_path[1] == ':'
-		   && canon_path[2] == '\\'
-		   && canon_path[3] == '\0')
-	    /* The starting cluster in SDA for the root of JOINed drive
-	       actually belongs to the directory where that drive is
-	       ``mounted''.  This can potentially be the cluster of
-	       another file on the JOINed drive.  We cannot allow this.  */
-	    statbuf->st_ino = 1;
-        }
-
-      /* File size. */
-      statbuf->st_size = ff_blk.ff_fsize;
-
-      /* Mode bits. */
-      statbuf->st_mode |= READ_ACCESS;
-      if ( !(ff_blk.ff_attrib & 0x07) )  /* no R, H or S bits set */
-        statbuf->st_mode |= WRITE_ACCESS;
-
-      /* Sometimes `_truename' doesn't return X:/FOO for character
-	 devices.  However, FindFirst returns attribute 40h for them.  */
-      if (ff_blk.ff_attrib == 0x40)
-	{
-	  size_t cplen = strlen (canon_path);
-	  char *pslash = canon_path + cplen - 1;
-
-	  while (pslash > canon_path + 2 && *pslash != '\\')
-	    pslash--;
-
-	  /* Force it into X:/FOO form.  */
-	  if (canon_path[1] == ':')
-	    {
-	      if (pslash > canon_path + 2)
-		memmove (canon_path + 2, pslash,
-			 cplen - (pslash - canon_path) + 1);
-	      canon_path[2] = '/';
-	      goto char_dev;
-	    }
-	}
-
-      /* Directories should have Execute bits set. */
-      if (ff_blk.ff_attrib & 0x10)
-        statbuf->st_mode |= (S_IFDIR | EXEC_ACCESS);
-
-      else
-        {
-          /* This is a regular file. */
-          char *extension  = strrchr(ff_blk.ff_name, '.');
-
-          /* Set regular file bit.  */
-          statbuf->st_mode |= S_IFREG;
-
-          if ((_djstat_flags & _STAT_EXECBIT) != _STAT_EXECBIT)
-            {
-              /* Set execute bits based on file's extension and
-                 first 2 bytes. */
-              if (extension)
-                extension++;    /* get past the dot */
-              if (_is_executable(pathname, -1, extension))
-                statbuf->st_mode |= EXEC_ACCESS;
-            }
-        }
-    }
-  else if ((_djstat_fail_bits & _STFAIL_TRUENAME))
-    {
-      /* If both `findfirst' and `_truename' failed, this must
-	 be a non-existent file or an illegal/inaccessible drive.  */
-      if (errno == ENMFILE)
-	errno = ENODEV;
-      return -1;
-    }
-  else if (pathname[3] == '\0')
-    {
-      /* Detect root directories.  These are special because, unlike
-	 subdirectories, FindFirst fails for them.  We look at PATHNAME
-	 because a network redirector could tweak what `_truename'
-	 returns to be utterly unrecognizable as root directory.  PATHNAME
-	 always begins with "d:/", so it is root if PATHNAME[3] = 0.  */
-
-      /* Mode bits. */
-      statbuf->st_mode |= (S_IFDIR|READ_ACCESS|WRITE_ACCESS|EXEC_ACCESS);
-
-      /* Root directory will have an inode = 1.  Valid cluster numbers
-         for real files under DOS start with 2. */
-      statbuf->st_ino = 1;
-
-      /* Simulate zero size.  This is what FindFirst returns for every
-         sub-directory.  Later we might compute a better approximation
-         (see below).  */
-      ff_blk.ff_fsize = 0L;
-
-      /* The time fields are left to be zero, unless the user wants us
-         to try harder.  In the latter case, we check if the root has
-         a volume label entry, and use its time if it has. */
-
-      if ( (_djstat_flags & _STAT_ROOT_TIME) == 0 )
-	{
-	  char buf[7];
-	  int volume_found = 0;
-
-	  strcpy(buf, pathname);
-	  strcat(buf, "*.*");
-	  /* Floppies written by Windows 9X and NT include entries
-	     that have volume label bit set, but are actually parts
-	     of an LFN entry.  Non-LFN platforms might be fooled to
-	     take them as volume labels, and report bogus time stamps.  */
-	  volume_found = __findfirst(buf, &ff_blk, FA_LABEL) == 0;
-	  while (volume_found
-		 && (ff_blk.ff_attrib & (FA_HIDDEN|FA_SYSTEM)) != 0)
-	    volume_found = __findnext(&ff_blk) == 0;
-	  if (volume_found)
-	    dos_ftime = ( (unsigned)ff_blk.ff_fdate << 16 ) + ff_blk.ff_ftime;
-	  else
-	    _djstat_fail_bits |= _STFAIL_LABEL;
-	}
-    }
-  else
-    {
-      int e = errno;	/* errno value from original FindFirst on PATHNAME */
-      int i = 0;
-      int j = strlen (pathname) - 1;
-
-      /* Check for volume labels.  We did not mix FA_LABEL with
-	 other attributes in the call to `__findfirst' above,
-	 because some environments will return bogus info in
-	 that case.  For instance, Win95 and WinNT seem to
-	 ignore `pathname' and return the volume label even if it
-	 doesn't fit the name in `pathname'.  This fools us to
-	 think that a non-existent file exists and is a volume
-	 label.  Hence we test the returned name to be PATHNAME.  */
-      if (!__findfirst(pathname, &ff_blk, FA_LABEL))
-	{
-	  i = strlen (ff_blk.ff_name) - 1;
-
-	  if (j >= i)
-	    {
-	      for ( ; i >= 0 && j >= 0; i--, j--)
-		if (toupper ((unsigned char)ff_blk.ff_name[i]) != toupper ((unsigned char)pathname[j]))
-		  break;
-	    }
-	}
-
-      if (i < 0 && pathname[j] == '/')
-	{
-	  /* Indeed a label.  */
-	  statbuf->st_mode = READ_ACCESS;
-#ifdef  S_IFLABEL
-	  statbuf->st_mode |= S_IFLABEL;
-#endif
-	  statbuf->st_ino = 1;
-	  statbuf->st_size = 0;
-	  dos_ftime = ( (unsigned)ff_blk.ff_fdate << 16 ) + ff_blk.ff_ftime;
-	}
-      else
-	{
-	  /* FindFirst on volume labels might set errno to ENMFILE or even
-	     to something more strange like EINVAl; correct that.  */
-	  errno = e;	/* restore errno from the original FindFirst */
-	  if (errno == ENMFILE)
-	    errno = ENOENT;
-	  return -1;
-	}
-    }
-
-  /* Device code. */
-  statbuf->st_dev = drv_no;
-#ifdef  HAVE_ST_RDEV
-  statbuf->st_rdev = drv_no;
-#endif
-
-  /* Time fields. */
-  statbuf->st_atime = statbuf->st_mtime = statbuf->st_ctime =
-    _file_time_stamp(dos_ftime);
-
-  if ( ! strcmp(ff_blk.lfn_magic,"LFN32") )
-    {
-      unsigned xtime;
-      xtime = *(unsigned *)&ff_blk.lfn_ctime;
-      if(xtime)			/* May be zero if file written w/o lfn active */
-        statbuf->st_ctime = _file_time_stamp(xtime);
-      xtime = *(unsigned *)&ff_blk.lfn_atime;
-      if(xtime > dos_ftime)	/* Accessed time is date only, no time */
-        statbuf->st_atime = _file_time_stamp(xtime);
-    }
-
-  if ( (statbuf->st_mode & S_IFMT) == S_IFDIR
-       && (_djstat_flags & _STAT_DIRSIZE) == 0 )
-    {
-      /* Under DOS, directory entries for subdirectories have
-         zero size.  Therefore, FindFirst brings us zero size
-         when called on a directory.  (Some network redirectors
-         might do a better job, thus below we also test for zero size
-         actually being returned.)  If we have zero-size directory,
-         we compute here the actual directory size by reading its
-         entries, then multiply their number by 32 (the size of a
-         directory entry under DOS).  This might lose in the case
-         that many files were deleted from a once huge directory,
-         because AFAIK, directories don't return unused clusters to
-         the disk pool.  Still, it is a good approximation of the
-         actual directory size.
-
-         We also take this opportunity to return the number of links
-         for directories as Unix programs expect it to be: the number
-         of subdirectories, plus 2 (the directory itself and the ``.''
-         entry).
-
-         The (max) size of the root directory could also be taken from
-         the disk BIOS Parameter Block (BPB) which can be obtained
-         by calling IOCTL (INT 21/AH=44H), subfunction 0DH, minor
-         function 60H.  But we will treat all directories the same,
-         even at performance cost, because it's more robust for
-         networked drives.  */
-
-      size_t pathlen = strlen (pathname);
-      char   lastc   = pathname[pathlen - 1];
-      char  *search_spec = (char *)alloca (pathlen + 10); /* need only +5 */
-      int nfiles = 0, nsubdirs = 0, done;
-      size_t extra = 0;
-      int add_extra = 0;
-
-      strcpy(search_spec, pathname);
-      if (lastc == '/')
-        strcat(search_spec, "*.*");
-      else
-        strcat(search_spec, "/*.*");
-
-      if (statbuf->st_size == 0 && _USE_LFN)
-	{
-	  /* VFAT filesystems use additional directory entries to
-	     store the long filenames.  */
-	  char fstype[40];
-
-	  if ((_get_volume_info(pathname,0,0,fstype) & _FILESYS_LFN_SUPPORTED)
-	      && strncmp(fstype, "FAT", 4) == 0)
-	    add_extra = 1;
-	}
-
-      /* Count files and subdirectories.  */
-      for (done = __findfirst(search_spec, &ff_blk, ALL_FILES);
-	   !done;
-	   done = __findnext(&ff_blk))
-	{
-	  register char *fname = ff_blk.ff_name;
-
-	  /* Don't count "." and ".." entries.  This will show empty
-	     directories as size 0.  */
-	  if (! (fname[0] == '.'
-		 && (fname[1] == '\0'
-		     || (fname[1] == '.'
-			 && fname[2] == '\0'))))
-	    {
-	      char fn[13];
-
-	      nfiles++;
-	      if (ff_blk.ff_attrib & 0x10)
-		nsubdirs++;
-	      /* For each 13 characters of the long filename, a
-		 32-byte directory entry is used.  */
-	      if (add_extra && strcmp(_lfn_gen_short_fname(fname, fn), fname))
-		extra += (strlen(fname) + 12) / 13;
-	    }
-	}
-
-      statbuf->st_nlink = nsubdirs + 2;
-      if (statbuf->st_size == 0)
-	statbuf->st_size  = (nfiles + extra) * sizeof(struct full_dirent);
-    }
-
-  return 0;
-}
-
-/* Main entry point.  This is library stat() function.
- */
+#include <errno.h>
+#include <stdio.h>
+#include <libc/symlink.h>
 
 int
 stat(const char *path, struct stat *statbuf)
 {
-  int  e = errno;
-  int  pathlen, ret;
-
-  if (!path || !statbuf)
-    {
-      errno = EFAULT;
-      return -1;
-    }
-
-  if ((pathlen = strlen (path)) >= MAX_TRUE_NAME)
-    {
-      errno = ENAMETOOLONG;
-      return -1;
-    }
-
-  /* Fail if PATH includes wildcard characters supported by FindFirst,
-     or if it is empty.  */
-  if (memchr(path, '*', pathlen) || memchr(path, '?', pathlen)
-      || path[0] == '\0')
-    {
-      errno = ENOENT;	/* since no such filename is possible */
+   char name_copy[FILENAME_MAX + 1];
+   
+   /* LB: symlink support here. */
+   if (!_solve_symlinks(path, name_copy))
       return -1;
-    }
-
-  if (__FSEXT_call_open_handlers(__FSEXT_stat, &ret, &path))
-   return ret;
-
-  if (stat_assist(path, statbuf) == -1)
-    {
-      return -1;      /* errno set by stat_assist() */
-    }
-  else
-    {
-      errno = e;
-      return 0;
-    }
-}
-
-#ifdef  TEST
-
-unsigned short _djstat_flags = 0;
-
-void
-main(int argc, char *argv[])
-{
-  struct stat stat_buf;
-  char *endp;
-
-  if (argc < 2)
-    {
-      fprintf (stderr, "Usage: %s <_djstat_flags> <file...>\n", argv[0]);
-      exit(0);
-    }
-
-  if (stat(*argv, &stat_buf) != 0)
-    perror ("stat failed on argv[0]");
-  else
-    fprintf(stderr, "DOS %d.%d (%s)\n", _osmajor, _osminor, _os_flavor);
-  argc--; argv++;
-
-  _djstat_flags = (unsigned short)strtoul(*argv, &endp, 0);
-  argc--; argv++;
-
-  while (argc--)
-    {
-      if (!stat(*argv, &stat_buf))
-        {
-          fprintf(stderr, "%s: %d %6u %o %d %d %ld %lu %s", *argv,
-                  stat_buf.st_dev,
-                  (unsigned)stat_buf.st_ino,
-                  stat_buf.st_mode,
-                  stat_buf.st_nlink,
-                  stat_buf.st_uid,
-                  (long)stat_buf.st_size,
-                  (unsigned long)stat_buf.st_mtime,
-                  ctime(&stat_buf.st_mtime));
-          _djstat_describe_lossage(stderr);
-        }
-      else
-        {
-          fprintf(stderr, "%s: lossage", *argv);
-          perror(" ");
-          _djstat_describe_lossage(stderr);
-        }
-
-      ++argv;
-    }
-
-    exit (0);
+   
+   return lstat(name_copy, statbuf); /* Real file */
 }
-
-#endif
 
diff -u -r -N --minimal djgpp.old/src/libc/posix/unistd/access.c djgpp/src/libc/posix/unistd/access.c
--- djgpp.old/src/libc/posix/unistd/access.c	Wed Aug  4 21:58:24 1999
+++ djgpp/src/libc/posix/unistd/access.c	Wed Dec  8 16:25:06 1999
@@ -3,17 +3,27 @@
 /* Copyright (C) 1996 DJ Delorie, see COPYING.DJ for details */
 /* Copyright (C) 1995 DJ Delorie, see COPYING.DJ for details */
 #include <libc/stubs.h>
+#include <libc/symlink.h>
 #include <unistd.h>
 #include <stdio.h>
 #include <sys/stat.h>
 #include <io.h>
 #include <dir.h>
-#include <errno.h>
 #include <dirent.h>
+#include <limits.h>
+#include <errno.h>
+
 
 int access(const char *fn, int flags)
 {
-  int attr = _chmod(fn, 0);
+  int attr;
+  char file_name[FILENAME_MAX + 1];
+
+  /* LB: symlink support here. */
+  if (!_solve_symlinks(fn, file_name))
+     return -1;
+
+  attr = _chmod(file_name, 0);
 
   if (attr == -1) {
     struct ffblk ff;
@@ -21,7 +31,7 @@
 
     /* Root directories on some non-local drives (e.g. CD-ROM)
        might fail `_chmod'.  `findfirst' to the rescue.  */
-    _fixpath(fn, fixed_path);
+    _fixpath(file_name, fixed_path);
     if (fixed_path[1] == ':' && fixed_path[2] == '/' && fixed_path[3] == 0)
       {
         char *fp = fixed_path + 3;
@@ -41,7 +51,7 @@
 
     /* Devices also fail `_chmod'; some programs won't write to
        a device unless `access' tells them they are writeable.  */
-    if (findfirst(fn, &ff, FA_RDONLY | FA_ARCH) == 0
+    if (findfirst(file_name, &ff, FA_RDONLY | FA_ARCH) == 0
 	&& (ff.ff_attrib & 0x40) == 0x40
 	&& (flags & (X_OK | D_OK)) == 0)
     {
diff -u -r -N --minimal djgpp.old/src/libc/posix/unistd/chdir.c djgpp/src/libc/posix/unistd/chdir.c
--- djgpp.old/src/libc/posix/unistd/chdir.c	Sun Dec 13 14:09:46 1998
+++ djgpp/src/libc/posix/unistd/chdir.c	Thu Dec  9 17:38:24 1999
@@ -1,35 +1,46 @@
+/* Copyright (C) 1999 DJ Delorie, see COPYING.DJ for details */
 /* Copyright (C) 1998 DJ Delorie, see COPYING.DJ for details */
 /* Copyright (C) 1996 DJ Delorie, see COPYING.DJ for details */
 /* Copyright (C) 1995 DJ Delorie, see COPYING.DJ for details */
 #include <libc/stubs.h>
-#include <unistd.h>
+#include <sys/stat.h>
 #include <errno.h>
 #include <fcntl.h>
 #include <go32.h>
-#include <ctype.h>
 #include <dpmi.h>
 #include <libc/dosio.h>
 #include <libc/farptrgs.h>
+#include <stdio.h>
+#include <libc/symlink.h>
+#include <limits.h>
+#include <unistd.h>
 
 int
 __chdir (const char *mydirname)
 {
   __dpmi_regs r;
   int drv_no = -1;
+  char real_name[FILENAME_MAX];
+  char path[FILENAME_MAX];
+  
+  /* LB: symlink support here. */
+  if (!_solve_symlinks(mydirname, real_name))
+     return -1;
+  _fixpath(real_name, path);
 
-  if (mydirname == 0)
+  if (path == 0)
   {
     errno = EINVAL;
     return -1;
   }
 
-  if (mydirname[0] == 0)
+  if (path[0] == 0)
   {
     errno = ENOENT;
     return -1;
   }
 
-  _put_path(mydirname);
+  _put_path(path);
 
   /* _put_path performs some magic conversions of file names, so
      the path in the transfer buffer can include a drive even though
diff -u -r -N --minimal djgpp.old/src/libc/posix/unistd/chown.c djgpp/src/libc/posix/unistd/chown.c
--- djgpp.old/src/libc/posix/unistd/chown.c	Sun Jun 18 09:43:52 1995
+++ djgpp/src/libc/posix/unistd/chown.c	Wed Dec 15 15:05:50 1999
@@ -11,7 +11,7 @@
 int
 chown(const char *path, uid_t owner, gid_t group)
 {
-  if (!__file_exists(path))       /* non-existent file */
+  if (access(path, F_OK))       /* non-existent file */
   {
     errno = ENOENT;
     return -1;
diff -u -r -N --minimal djgpp.old/src/libc/posix/unistd/getcwd.c djgpp/src/libc/posix/unistd/getcwd.c
--- djgpp.old/src/libc/posix/unistd/getcwd.c	Sun Sep  1 00:09:32 1996
+++ djgpp/src/libc/posix/unistd/getcwd.c	Sat Dec  4 10:06:22 1999
@@ -1,11 +1,10 @@
+/* Copyright (C) 1999 DJ Delorie, see COPYING.DJ for details */
 /* Copyright (C) 1996 DJ Delorie, see COPYING.DJ for details */
 /* Copyright (C) 1995 DJ Delorie, see COPYING.DJ for details */
 #include <libc/stubs.h>
 #include <unistd.h>
 #include <stdlib.h>
 #include <stdio.h>
-#include <ctype.h>
-#include <string.h>
 #include <fcntl.h>
 #include <errno.h>
 #include <dpmi.h>
@@ -19,7 +18,8 @@
 {
   char *bp;
   __dpmi_regs r;
-  int needed_length, c;
+  size_t needed_length;
+  int c;
   unsigned use_lfn = _USE_LFN;
   int preserve_case = _preserve_fncase();
   char *name_start;
diff -u -r -N --minimal djgpp.old/src/libc/posix/unistd/lchown.c djgpp/src/libc/posix/unistd/lchown.c
--- djgpp.old/src/libc/posix/unistd/lchown.c	Thu Jan  1 00:00:00 1970
+++ djgpp/src/libc/posix/unistd/lchown.c	Wed Dec 15 15:05:14 1999
@@ -0,0 +1,20 @@
+/* Copyright (C) 1995 DJ Delorie, see COPYING.DJ for details */
+#include <libc/stubs.h>
+#include <unistd.h>
+#include <sys/stat.h>
+#include <errno.h>
+ 
+/* MS-DOS couldn't care less about file ownerships, so we could
+   always succeed.  At least fail for non-existent files
+   and for devices.  */
+ 
+int
+lchown(const char *path, uid_t owner, gid_t group)
+{
+  if (!__file_exists(path))       /* non-existent file */
+  {
+    errno = ENOENT;
+    return -1;
+  }
+  return 0;
+}
diff -u -r -N --minimal djgpp.old/src/libc/posix/unistd/lchown.txh djgpp/src/libc/posix/unistd/lchown.txh
--- djgpp.old/src/libc/posix/unistd/lchown.txh	Thu Jan  1 00:00:00 1970
+++ djgpp/src/libc/posix/unistd/lchown.txh	Wed Dec 15 15:05:02 1999
@@ -0,0 +1,24 @@
+@node lchown, unix
+@subheading Syntax
+
+@example
+#include <unistd.h>
+
+int lchown(const char *file, int owner, int group);
+@end example
+
+@subheading Description
+
+This function does nothing under MS-DOS
+
+@subheading Return Value
+
+This function always returns zero if the file exists (it does not
+follow symbolic links), else it returns -1 and sets @var{errno} 
+to @code{ENOENT}.
+
+
+@subheading Portability
+
+@portability !ansi, posix
+
diff -u -r -N --minimal djgpp.old/src/libc/posix/unistd/makefile djgpp/src/libc/posix/unistd/makefile
--- djgpp.old/src/libc/posix/unistd/makefile	Fri Sep 20 01:40:46 1996
+++ djgpp/src/libc/posix/unistd/makefile	Wed Dec 15 15:04:04 1999
@@ -32,12 +32,14 @@
 SRC += getppid.c
 SRC += getuid.c
 SRC += isatty.c
+SRC += lchown.c
 SRC += link.c
 SRC += lseek.c
 SRC += pathconf.c
 SRC += pause.c
 SRC += pipe.c
 SRC += read.c
+SRC += readlink.c
 SRC += rmdir.c
 SRC += setgid.c
 SRC += setpgid.c
@@ -49,5 +51,6 @@
 SRC += ttyname.c
 SRC += unlink.s
 SRC += write.c
+SRC += xsymlink.c
 
 include $(TOP)/../makefile.inc
diff -u -r -N --minimal djgpp.old/src/libc/posix/unistd/readlink.c djgpp/src/libc/posix/unistd/readlink.c
--- djgpp.old/src/libc/posix/unistd/readlink.c	Thu Jan  1 00:00:00 1970
+++ djgpp/src/libc/posix/unistd/readlink.c	Thu Dec  9 17:41:08 1999
@@ -0,0 +1,61 @@
+/* Copyright (C) 1999 DJ Delorie, see COPYING.DJ for details */
+
+#include <libc/stubs.h>
+#include <dir.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <stddef.h>
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "xsymlink.h"
+
+int readlink(const char * filename, char * buffer, size_t size)
+{
+   ssize_t      bytes_read = 0;
+   char         buf[FILENAME_MAX];
+   int          fd;
+   struct ffblk file_info;
+   int          old_errno;
+   char         data_buf[_SYMLINK_FILE_LEN];
+
+   /* Now do the real job */
+   /* First DJGPP symlink check - is file size a fixed ``magic value''? */
+   if (findfirst(filename, &file_info, 0) ||
+      (file_info.ff_fsize != _SYMLINK_FILE_LEN))
+   {
+      errno = ENOENT;
+      return -1;
+   }
+
+   /* Now we check for special DJGPP symlink format */
+   old_errno = errno;
+   fd = open(filename, O_NOLINK | O_RDONLY | O_BINARY);
+   if (fd < 0)
+      return -1; /* errno from open() call */
+   bytes_read = read(fd, buf, _SYMLINK_PREFIX_LEN);
+   if ((unsigned)bytes_read < _SYMLINK_PREFIX_LEN) /* The cast is safe */
+   {
+      close(fd);
+      errno = EINVAL;
+      return -1;
+   }
+   if (strncmp(buf, _SYMLINK_PREFIX, _SYMLINK_PREFIX_LEN))
+   {
+      close(fd);
+      errno = EINVAL;
+      return -1;
+   }
+
+   /* Here we know that file is really DJGPP symlink, */
+   /* so we extract file name it points to            */
+   bytes_read = read(fd, &data_buf, _SYMLINK_FILE_LEN);
+   close(fd);
+   if (bytes_read == -1)
+      return -1; /* Return errno set by read() */
+   bytes_read = strchr(data_buf, '\n') - data_buf;
+   memcpy(buffer, data_buf, ((unsigned)bytes_read > size) ? size : bytes_read);
+   return bytes_read;
+}
+
diff -u -r -N --minimal djgpp.old/src/libc/posix/unistd/readlink.txh djgpp/src/libc/posix/unistd/readlink.txh
--- djgpp.old/src/libc/posix/unistd/readlink.txh	Thu Jan  1 00:00:00 1970
+++ djgpp/src/libc/posix/unistd/readlink.txh	Thu Dec  9 17:45:24 1999
@@ -0,0 +1,37 @@
+@node readlink, io
+@subheading Syntax
+
+@example
+#include <unistd.h>
+
+int readlink(const char *filename, char *buffer, size_t size);
+@end example
+
+@subheading Description
+MSDOS doesn't support symbolic links but DJGPP emulates them.
+This function checks if @var{filename} is a DJGPP symlink and
+the file name that the links points to is copied into buffer,
+up to maximum @var{size} characters. Only the last file name
+is resolved.
+It is @strong{not} terminated @code{'\0'}
+
+@subheading Return Value
+
+Number of copied characters; value -1 is returned in case of
+error and @code{errno} is set. When value returned is equal to 
+@var{size}, you cannot determine if there was enough room to 
+copy whole name. So increase @var{size} and try again.
+
+@subheading Portability
+
+@portability !ansi, !posix
+
+@subheading Example
+
+@example
+char buf[FILENAME_MAX];
+if (readlink("/djgpp/bin/sh.exe", buf, FILENAME_MAX) == -1)
+   if (errno == EINVAL)
+      puts("/djgpp/bin/sh.exe is not a symbolic link.");
+@end example
+
diff -u -r -N --minimal djgpp.old/src/libc/posix/unistd/symlink.c djgpp/src/libc/posix/unistd/symlink.c
--- djgpp.old/src/libc/posix/unistd/symlink.c	Wed Aug 25 11:24:48 1999
+++ djgpp/src/libc/posix/unistd/symlink.c	Thu Dec  9 18:39:10 1999
@@ -1,180 +1,52 @@
+/* Copyright (C) 1999 DJ Delorie, see COPYING.DJ for details */
 /* Copyright (C) 1997 DJ Delorie, see COPYING.DJ for details */
 #include <libc/stubs.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
+#include <libc/symlink.h>
 #include <errno.h>
-#include <fcntl.h>
 #include <unistd.h>
-#include <sys/system.h>
-#include <sys/stat.h>
-#include <process.h>
-#include <dpmi.h>
-#include <go32.h>
-
-static char EXE_SUFFIX[] = ".exe";
-static char STUBIFY[]	 = "stubify.exe";
-static char STUBEDIT[]	 = "stubedit.exe";
-
-/* Return a pointer to the tail of the pathname.  */
-static const char *
-tail (const char *path)
-{
-  const char *p = path && path[0] ? path + strlen (path) - 1 : path;
-
-  if (p)
-    {
-      while (p > path && *p != '/' && *p != '\\' && *p != ':')
-	p--;
-      if (p > path)
-	p++;
-    }
-  return p;
-}
-
-/*
-   This returns
-              -1, when the file does not exist
-               0, when it is not a v2 executable
-               1, when it is a v2 executable
-*/
-
-static int is_v2_prog(const char *program)
-{
-  const _v2_prog_type *type;
-
-  type = _check_v2_prog (program, -1);
-
-  if (!type->valid)
-    return -1;
-
-  if (type->object_format != _V2_OBJECT_FORMAT_COFF)
-    return 0;
-
-  if (type->version.v.major < 2)
-    return 0;
+#include <io.h>
+#include <stdio.h>
 
-  return 1;
-}
+#include "xsymlink.h"
 
-/* Support the DJGPP ``symlinks'' for .exe files.  */
-int
-symlink (const char *source, const char *dest)
+/* Support DJGPP symlinks for all files */
+int symlink(const char *source, const char *dest)
 {
-  char src_abs[FILENAME_MAX+5], src_short[FILENAME_MAX+5];
-  char dest_abs[FILENAME_MAX+5];
-  char *np, ropt[FILENAME_MAX+15]; /* some extra for ``runfile='' */
-  const char *src_base, *dest_base;
-
-  int v2_prog = 0;
-
-  _fixpath (source, src_abs);
-  _fixpath (dest, dest_abs);
-  src_base = tail (src_abs);
-  dest_base = tail (dest_abs);
+   int symlink_file;
+   char real_dest[FILENAME_MAX];
+   static char fill_buf[_SYMLINK_FILE_LEN + 1] =
+                             "This is just a text to force symlink file to "
+                             "be 510 bytes long. Do not delete it nor spaces "
+                             "following it.";
+   memset(fill_buf + strlen(fill_buf), ' ',
+                                        _SYMLINK_FILE_LEN - strlen(fill_buf));
 
-  /* DJGPP symlinks must be in the same directory.  */
-  if (src_base - src_abs != dest_base - dest_abs
-      || strnicmp (src_abs, dest_abs, src_base - src_abs))
-    {
-      errno = EXDEV;
+   /* Common error conditions */
+   if (!source || !dest)
+   {
+      errno = EINVAL;
       return -1;
-    }
-
-  /* Any file is already a link to itself.  */
-  if (stricmp (src_abs, dest_abs) == 0)
-    return 0;
-
-  /* Check at first, if the given name is a v2 executable (may be
-     unstubbed COFF image) */
-  v2_prog = is_v2_prog(src_abs);
-
-  /* It is an existing file but no v2 executable */
-  if (v2_prog == 0)
-  {
-    errno = EXDEV;
-    return -1;
-  }
-
-  /* Allow to say `ln -s src dest' when we really
-     mean `src.exe' and `dest.exe'  */
-  np = src_abs + strlen (src_abs);
-  if (np - src_abs > 4 && stricmp (np - 4, EXE_SUFFIX) != 0)
-  {
-    strcat (src_abs, EXE_SUFFIX);
-    /* Now test again for v2 executable, but only if not already
-       succeed. */
-    v2_prog = v2_prog == 1 ? v2_prog : is_v2_prog(src_abs);
-  }
-
-  /* It is an existing file but no v2 executable */
-  if (v2_prog == 0)
-  {
-    errno = EXDEV;
-    return -1;
-  }
-
-  /* When we are here, either the file exists and is a v2 executable
-     or it does not exist and we hope, the the user knows what he
-     does. */
-
-  /* Under LFN, we need the short version of the program name, since that
-     is what the stub stores (and what a program gets in its argv[0]).  */
-  if (_USE_LFN)
-    {
-      if (__file_exists (src_abs))
-	{
-	  /* File exists.  Get its 8+3 alias.  */
-	  __dpmi_regs r;
-
-	  dosmemput(src_abs, strlen (src_abs)+1, __tb);
-	  r.x.ax = 0x7160;		/* Truename */
-	  r.x.cx = 1;			/* Get short name */
-	  r.x.ds = r.x.es = __tb / 16;
-	  r.x.si = r.x.di = __tb & 15;
-	  __dpmi_int(0x21, &r);
-	  if (r.x.flags & 1 || r.x.ax == 0x7100)
-	    /* Shouldn't happen: LFN *is* supported and file *does* exist.  */
-	    {
-	      errno = EIO;
-	      return -1;
-	    }
-	  dosmemget (__tb, FILENAME_MAX, src_short);
-	}
-      else
-	{
-	  /* File doesn't exist.  Generate short name that would be used.
-	     FIXME: this will lose if the generated name collides with
-	     another file already in that directory; however, the only
-	     alternative is to disallow symlinks to non-existing files.  */
-	  char *p = strncpy (src_short, src_abs, src_base - src_abs);
-	  _lfn_gen_short_fname (src_base, p + (src_base - src_abs));
-	}
-    }
-  else
-    strcpy (src_short, src_abs);
+   }
 
-  /* Need the basename of SRC_SHORT sans the extension.  */
-  strcpy (ropt, "runfile=");
-  strcat (ropt, tail (src_short));
-  for (np = ropt + strlen (ropt) - 1; np > ropt; np--)
-    if (*np == '.')
-      {
-	*np = '\0';
-	break;
-      }
+   /* The ``dest'' may have symlinks somewhere in the path itself. */
+   if (!_solve_symlinks(dest, real_dest))
+      return -1;
 
-  /* `stubedit' needs its argument with the .EXE suffix explicit.  */
-  np = dest_abs + strlen (dest_abs);
-  if (np - dest_abs > 4 && stricmp (np - 4, EXE_SUFFIX) != 0)
-    strcat (dest_abs, EXE_SUFFIX);
+   /* Check if there already is file with symlink's name */
+   if (__file_exists(real_dest))
+   {
+      errno = EEXIST;
+      return -1;
+   }
 
-  /* Any file is already a link to itself.  */
-  if (stricmp (src_abs, dest_abs) == 0)
-    return 0;
+   symlink_file = _creat(real_dest, 0);
+   if (symlink_file < 0)
+      return -1; /* Return errno from creat() call */
+   write(symlink_file, _SYMLINK_PREFIX, _SYMLINK_PREFIX_LEN);
+   write(symlink_file, source, strlen(source));
+   write(symlink_file, "\n", 1);
+   write(symlink_file, fill_buf, _SYMLINK_FILE_LEN - _SYMLINK_PREFIX_LEN - strlen(source) - 1);
+   _close(symlink_file);
 
-  if (spawnlp (P_WAIT, STUBIFY, STUBIFY, "-g", dest_abs, (char *)0)
-      || spawnlp (P_WAIT, STUBEDIT, STUBEDIT, dest_abs, ropt, (char *)0))
-    return -1;
-  return 0;
+   return 0;
 }
diff -u -r -N --minimal djgpp.old/src/libc/posix/unistd/symlink.txh djgpp/src/libc/posix/unistd/symlink.txh
--- djgpp.old/src/libc/posix/unistd/symlink.txh	Sun Sep 27 17:22:28 1998
+++ djgpp/src/libc/posix/unistd/symlink.txh	Sun Dec 12 15:20:14 1999
@@ -8,29 +8,12 @@
 @end example
 
 @subheading Description
-MSDOS doesn't support symbolic links.  However, DJGPP supports
-``symlinks'' to DJGPP programs.  This function simulates a symlink
-between two @file{.exe} files in the DJGPP style.  It creates a program
-whose name is pointed to by @var{new} which, when run, will actually
-execute the program @var{exists} passing it the string pointed by
-@var{new} in @code{argv[0]} (some programs change their behavior
-depending on what's passed in @code{argv[0]}).  The file referred to by
-@var{exists} doesn't really have to exist when this function is called.
-If @var{exists} points to an @emph{existing} file, the function checks
-that it is a DJGPP executable; if not, the call will fail with
-@code{EXDEV}.
-
-Both @var{new} and @var{exists} can point to a name with or without the
-@file{.exe} extension.
-
-Note that both @var{exists} and @var{new} must specify file names which
-reside in the same
-directory (this is a restriction of the DJGPP ``symlinks''); the
-function will fail and set @code{errno} to @code{EXDEV} if they aren't.
-
-This functions runs the @samp{stubify} and @samp{stubedit} programs, so
-they should be somewhere on your @samp{PATH} for the function to
-succeed.  (These programs come with the DJGPP development distribution.)
+DOS does not support symbolic links. However, DJGPP emulates them---
+this function creates a file with special size and format, so other
+DJGPP library functions transparently work with file which is pointed to
+by symlink. Of course, it does not work outside DJGPP programs. Those
+library functions which are simple wrappers about DOS calls do not
+use symlinks neither.
 
 @subheading Return Value
 
@@ -46,3 +29,4 @@
 @example
 symlink ("c:/djgpp/bin/grep", "c:/djgpp/bin/fgrep");
 @end example
+
diff -u -r -N --minimal djgpp.old/src/libc/posix/unistd/xsymlink.c djgpp/src/libc/posix/unistd/xsymlink.c
--- djgpp.old/src/libc/posix/unistd/xsymlink.c	Thu Jan  1 00:00:00 1970
+++ djgpp/src/libc/posix/unistd/xsymlink.c	Fri Dec 17 18:26:30 1999
@@ -0,0 +1,95 @@
+/* Copyright (C) 1999 DJ Delorie, see COPYING.DJ for details */
+
+/* Written by Laurynas Biveinis                                      */
+/* Internal source file specifying DJGPP symlink prefix and internal */
+/* function which fully resolves given symlink. (Function readlink() */
+/* resolves only last filename component and one symlink level.)     */
+
+#include <libc/stubs.h>
+#include <libc/symlink.h>
+#include <errno.h>
+#include <limits.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <string.h>
+
+#include "xsymlink.h"
+
+char _SYMLINK_PREFIX[] = "!<symlink>";
+
+unsigned _SYMLINK_PREFIX_LEN = sizeof(_SYMLINK_PREFIX) - 1;
+
+int _solve_symlinks(const char * symlink_path, char * real_path)
+{
+   int    bytes_copied;
+   char * dir_sep_ptr;
+   int    old_errno;
+   char   fn_buf[FILENAME_MAX + 1];
+   char   path_copy[FILENAME_MAX + 1];
+   int    link_level;
+   int    found_absolute_path;
+   int    one_more_time = 1;
+   int    piece_length;
+
+   if (!symlink_path || !real_path)
+   {
+      errno = EINVAL;
+      return 0;
+   }
+
+   if (strlen(symlink_path) > FILENAME_MAX)
+   {
+      errno = ENAMETOOLONG;
+      return 0;
+   }
+
+   strcpy(path_copy, symlink_path);
+   strcpy(real_path, path_copy);
+   dir_sep_ptr = strpbrk(real_path, "/\\");
+   while (dir_sep_ptr || one_more_time)
+   {
+      if (dir_sep_ptr)
+      {
+         *dir_sep_ptr = '\0';
+         /* We have to compute it here, because real_path can change during
+            resolving and we get a nasty SIGSEGV... */
+         piece_length = dir_sep_ptr - real_path;
+      }
+      old_errno = errno;
+      link_level = 0;
+      found_absolute_path = 0;
+      do
+      {
+         bytes_copied = readlink(real_path, fn_buf, FILENAME_MAX);
+         if (bytes_copied != -1)
+         {
+            link_level++;
+            fn_buf[bytes_copied] = '\0';
+            strcpy(real_path, fn_buf);
+            /* FIXME: does absolute path check below work with chroot()? */
+            if (((bytes_copied > 2) && (real_path[1] == ':')) ||
+                ((bytes_copied > 0) && ((real_path[0] == '/') || (real_path[0] == '\\'))))
+            {
+               found_absolute_path = 1;
+            }
+         }
+      } while ((bytes_copied != -1) && (link_level <= _POSIX_LINK_MAX));
+      if (link_level > _POSIX_LINK_MAX)
+      {
+         errno = EMLINK;
+         return 0;
+      }
+      else
+         errno = old_errno;
+      if (dir_sep_ptr)
+         strcat(real_path, path_copy + piece_length);
+      strcpy(path_copy, real_path);
+      if (dir_sep_ptr)
+         dir_sep_ptr = strpbrk(found_absolute_path ?
+                               real_path           :
+                               (dir_sep_ptr + 1), "/\\");
+      one_more_time = dir_sep_ptr ? 0 : !one_more_time;
+   }
+   return 1;
+}
+
diff -u -r -N --minimal djgpp.old/src/libc/posix/unistd/xsymlink.h djgpp/src/libc/posix/unistd/xsymlink.h
--- djgpp.old/src/libc/posix/unistd/xsymlink.h	Thu Jan  1 00:00:00 1970
+++ djgpp/src/libc/posix/unistd/xsymlink.h	Wed Dec  8 19:46:30 1999
@@ -0,0 +1,18 @@
+/* Copyright (C) 1999 DJ Delorie, see COPYING.DJ for details */
+/* Symlink support by Laurynas Biveinis                      */
+
+/* Internal header containing symlink file format specifiers     */
+/* I decided not to put it in public header, because this prefix */
+/* isn't guaranteed not to change.                               */
+#ifndef XSYMLINK_H_
+#define XSYMLINK_H_
+
+/* Current prefix is for being compatible with CygWin's symlinks */
+extern char _SYMLINK_PREFIX[]; /* = "!<symlink>"; */
+unsigned    _SYMLINK_PREFIX_LEN;
+/* Symlink files have fixed length - 510 bytes. Why this value? Why not? */
+/* It is big enough to hold longest possible path                        */
+#define _SYMLINK_FILE_LEN 510
+
+#endif // #ifndef XSYMLINK_H_
+
diff -u -r -N --minimal djgpp.old/src/libc/posix/unistd/xsymlink.txh djgpp/src/libc/posix/unistd/xsymlink.txh
--- djgpp.old/src/libc/posix/unistd/xsymlink.txh	Thu Jan  1 00:00:00 1970
+++ djgpp/src/libc/posix/unistd/xsymlink.txh	Sun Dec 12 15:25:16 1999
@@ -0,0 +1,36 @@
+@node _solve_symlinks, io
+@subheading Syntax
+
+@example
+#include <libc/symlink.h>
+
+int _solve_symlinks(const char *symlink_path, char *real_path);
+@end example
+
+@subheading Description
+This function fully resolves given symlink in @var{symlink_path}---
+all path components and all symlink levels are resolved. The
+returned path in @var{real_path} is guaranteed to be symlink-clean
+and understandable by DOS. If @var{symlink_path} does not contain
+symlinks at all, it is simply copied to @var{real_path}.
+@subheading Return Value
+
+Zero in case of error (and @code{errno} set to the appropriate
+error code), non-zero in case of success.
+
+@subheading Portability
+
+@portability !ansi, !posix
+
+@subheading Example
+
+@example
+
+  #include <libc/symlink.h>
+
+  _solve_symlinks(fn, file_name);
+  printf("Filename %s is really %s\n", fn, file_name);
+
+
+@end example
+


--------------4C053DA2EC7A23114D04E754--

- Raw text -


  webmaster     delorie software   privacy  
  Copyright © 2019   by DJ Delorie     Updated Jul 2019