1a6dfe5f70959a596290e1591579d26a288a1a2f9David 'Digit' Turner/* Work around a bug of lstat on some systems
2a6dfe5f70959a596290e1591579d26a288a1a2f9David 'Digit' Turner
3a6dfe5f70959a596290e1591579d26a288a1a2f9David 'Digit' Turner   Copyright (C) 1997-1999, 2000-2006, 2008 Free Software Foundation, Inc.
4a6dfe5f70959a596290e1591579d26a288a1a2f9David 'Digit' Turner
5a6dfe5f70959a596290e1591579d26a288a1a2f9David 'Digit' Turner   This program is free software: you can redistribute it and/or modify
6a6dfe5f70959a596290e1591579d26a288a1a2f9David 'Digit' Turner   it under the terms of the GNU General Public License as published by
7a6dfe5f70959a596290e1591579d26a288a1a2f9David 'Digit' Turner   the Free Software Foundation; either version 3 of the License, or
8a6dfe5f70959a596290e1591579d26a288a1a2f9David 'Digit' Turner   (at your option) any later version.
9a6dfe5f70959a596290e1591579d26a288a1a2f9David 'Digit' Turner
10a6dfe5f70959a596290e1591579d26a288a1a2f9David 'Digit' Turner   This program is distributed in the hope that it will be useful,
11a6dfe5f70959a596290e1591579d26a288a1a2f9David 'Digit' Turner   but WITHOUT ANY WARRANTY; without even the implied warranty of
12a6dfe5f70959a596290e1591579d26a288a1a2f9David 'Digit' Turner   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13a6dfe5f70959a596290e1591579d26a288a1a2f9David 'Digit' Turner   GNU General Public License for more details.
14a6dfe5f70959a596290e1591579d26a288a1a2f9David 'Digit' Turner
15a6dfe5f70959a596290e1591579d26a288a1a2f9David 'Digit' Turner   You should have received a copy of the GNU General Public License
16a6dfe5f70959a596290e1591579d26a288a1a2f9David 'Digit' Turner   along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
17a6dfe5f70959a596290e1591579d26a288a1a2f9David 'Digit' Turner
18a6dfe5f70959a596290e1591579d26a288a1a2f9David 'Digit' Turner/* written by Jim Meyering */
19a6dfe5f70959a596290e1591579d26a288a1a2f9David 'Digit' Turner
20a6dfe5f70959a596290e1591579d26a288a1a2f9David 'Digit' Turner#include <config.h>
21a6dfe5f70959a596290e1591579d26a288a1a2f9David 'Digit' Turner
22a6dfe5f70959a596290e1591579d26a288a1a2f9David 'Digit' Turner/* Get the original definition of open.  It might be defined as a macro.  */
23a6dfe5f70959a596290e1591579d26a288a1a2f9David 'Digit' Turner#define __need_system_sys_stat_h
24a6dfe5f70959a596290e1591579d26a288a1a2f9David 'Digit' Turner#include <sys/types.h>
25a6dfe5f70959a596290e1591579d26a288a1a2f9David 'Digit' Turner#include <sys/stat.h>
26a6dfe5f70959a596290e1591579d26a288a1a2f9David 'Digit' Turner#undef __need_system_sys_stat_h
27a6dfe5f70959a596290e1591579d26a288a1a2f9David 'Digit' Turner
28a6dfe5f70959a596290e1591579d26a288a1a2f9David 'Digit' Turnerstatic inline int
29a6dfe5f70959a596290e1591579d26a288a1a2f9David 'Digit' Turnerorig_lstat (const char *filename, struct stat *buf)
30a6dfe5f70959a596290e1591579d26a288a1a2f9David 'Digit' Turner{
31a6dfe5f70959a596290e1591579d26a288a1a2f9David 'Digit' Turner  return lstat (filename, buf);
32a6dfe5f70959a596290e1591579d26a288a1a2f9David 'Digit' Turner}
33a6dfe5f70959a596290e1591579d26a288a1a2f9David 'Digit' Turner
34a6dfe5f70959a596290e1591579d26a288a1a2f9David 'Digit' Turner/* Specification.  */
35a6dfe5f70959a596290e1591579d26a288a1a2f9David 'Digit' Turner#include <sys/stat.h>
36a6dfe5f70959a596290e1591579d26a288a1a2f9David 'Digit' Turner
37a6dfe5f70959a596290e1591579d26a288a1a2f9David 'Digit' Turner#include <string.h>
38a6dfe5f70959a596290e1591579d26a288a1a2f9David 'Digit' Turner#include <errno.h>
39a6dfe5f70959a596290e1591579d26a288a1a2f9David 'Digit' Turner
40a6dfe5f70959a596290e1591579d26a288a1a2f9David 'Digit' Turner/* lstat works differently on Linux and Solaris systems.  POSIX (see
41a6dfe5f70959a596290e1591579d26a288a1a2f9David 'Digit' Turner   `pathname resolution' in the glossary) requires that programs like
42a6dfe5f70959a596290e1591579d26a288a1a2f9David 'Digit' Turner   `ls' take into consideration the fact that FILE has a trailing slash
43a6dfe5f70959a596290e1591579d26a288a1a2f9David 'Digit' Turner   when FILE is a symbolic link.  On Linux and Solaris 10 systems, the
44a6dfe5f70959a596290e1591579d26a288a1a2f9David 'Digit' Turner   lstat function already has the desired semantics (in treating
45a6dfe5f70959a596290e1591579d26a288a1a2f9David 'Digit' Turner   `lstat ("symlink/", sbuf)' just like `lstat ("symlink/.", sbuf)',
46a6dfe5f70959a596290e1591579d26a288a1a2f9David 'Digit' Turner   but on Solaris 9 and earlier it does not.
47a6dfe5f70959a596290e1591579d26a288a1a2f9David 'Digit' Turner
48a6dfe5f70959a596290e1591579d26a288a1a2f9David 'Digit' Turner   If FILE has a trailing slash and specifies a symbolic link,
49a6dfe5f70959a596290e1591579d26a288a1a2f9David 'Digit' Turner   then use stat() to get more info on the referent of FILE.
50a6dfe5f70959a596290e1591579d26a288a1a2f9David 'Digit' Turner   If the referent is a non-directory, then set errno to ENOTDIR
51a6dfe5f70959a596290e1591579d26a288a1a2f9David 'Digit' Turner   and return -1.  Otherwise, return stat's result.  */
52a6dfe5f70959a596290e1591579d26a288a1a2f9David 'Digit' Turner
53a6dfe5f70959a596290e1591579d26a288a1a2f9David 'Digit' Turnerint
54a6dfe5f70959a596290e1591579d26a288a1a2f9David 'Digit' Turnerrpl_lstat (const char *file, struct stat *sbuf)
55a6dfe5f70959a596290e1591579d26a288a1a2f9David 'Digit' Turner{
56a6dfe5f70959a596290e1591579d26a288a1a2f9David 'Digit' Turner  size_t len;
57a6dfe5f70959a596290e1591579d26a288a1a2f9David 'Digit' Turner  int lstat_result = orig_lstat (file, sbuf);
58a6dfe5f70959a596290e1591579d26a288a1a2f9David 'Digit' Turner
59a6dfe5f70959a596290e1591579d26a288a1a2f9David 'Digit' Turner  if (lstat_result != 0 || !S_ISLNK (sbuf->st_mode))
60a6dfe5f70959a596290e1591579d26a288a1a2f9David 'Digit' Turner    return lstat_result;
61a6dfe5f70959a596290e1591579d26a288a1a2f9David 'Digit' Turner
62a6dfe5f70959a596290e1591579d26a288a1a2f9David 'Digit' Turner  len = strlen (file);
63a6dfe5f70959a596290e1591579d26a288a1a2f9David 'Digit' Turner  if (len == 0 || file[len - 1] != '/')
64a6dfe5f70959a596290e1591579d26a288a1a2f9David 'Digit' Turner    return 0;
65a6dfe5f70959a596290e1591579d26a288a1a2f9David 'Digit' Turner
66a6dfe5f70959a596290e1591579d26a288a1a2f9David 'Digit' Turner  /* FILE refers to a symbolic link and the name ends with a slash.
67a6dfe5f70959a596290e1591579d26a288a1a2f9David 'Digit' Turner     Call stat() to get info about the link's referent.  */
68a6dfe5f70959a596290e1591579d26a288a1a2f9David 'Digit' Turner
69a6dfe5f70959a596290e1591579d26a288a1a2f9David 'Digit' Turner  /* If stat fails, then we do the same.  */
70a6dfe5f70959a596290e1591579d26a288a1a2f9David 'Digit' Turner  if (stat (file, sbuf) != 0)
71a6dfe5f70959a596290e1591579d26a288a1a2f9David 'Digit' Turner    return -1;
72a6dfe5f70959a596290e1591579d26a288a1a2f9David 'Digit' Turner
73a6dfe5f70959a596290e1591579d26a288a1a2f9David 'Digit' Turner  /* If FILE references a directory, return 0.  */
74a6dfe5f70959a596290e1591579d26a288a1a2f9David 'Digit' Turner  if (S_ISDIR (sbuf->st_mode))
75a6dfe5f70959a596290e1591579d26a288a1a2f9David 'Digit' Turner    return 0;
76a6dfe5f70959a596290e1591579d26a288a1a2f9David 'Digit' Turner
77a6dfe5f70959a596290e1591579d26a288a1a2f9David 'Digit' Turner  /* Here, we know stat succeeded and FILE references a non-directory.
78a6dfe5f70959a596290e1591579d26a288a1a2f9David 'Digit' Turner     But it was specified via a name including a trailing slash.
79a6dfe5f70959a596290e1591579d26a288a1a2f9David 'Digit' Turner     Fail with errno set to ENOTDIR to indicate the contradiction.  */
80a6dfe5f70959a596290e1591579d26a288a1a2f9David 'Digit' Turner  errno = ENOTDIR;
81a6dfe5f70959a596290e1591579d26a288a1a2f9David 'Digit' Turner  return -1;
82a6dfe5f70959a596290e1591579d26a288a1a2f9David 'Digit' Turner}
83