[Top][All Lists]
[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
Re: Bug in builtin function abspath
From: |
Alessandro Vesely |
Subject: |
Re: Bug in builtin function abspath |
Date: |
Sun, 04 Jun 2006 13:50:08 +0200 |
User-agent: |
Thunderbird 1.5.0.4 (Windows/20060516) |
Eli Zaretskii wrote:
Date: Thu, 01 Jun 2006 16:02:22 +0200
From: Alessandro Vesely <address@hidden>
_fullpath is a wrapper around GetFullPathName: it possibly allocates
the buffer and maps errors to errno. Since _fullpath is not in OS/2
either, I'd use GetFullPathName directly.
I'd like to avoid too many Windows-specific APIs as much as possible.
There's no need to use _fullpath or GetFullPathName, as of now; the
patch I suggested works on Windows without any direct Win32 calls.
The questions I asked about _getdcwd was about OS/2, not Windows, and
they are only relevant if we decide that "d:./foo" is not the right
value abspath should return for d:foo arguments.
I agree avoiding w32 API in function.c is good.
I don't think "d:./foo" is the value abspath should return,
because it is not an absolute path.
[...]
Sorry, I don't understand what problem are you trying to solve here.
Please explain.
I beg your pardon for presenting rough ideas. I'm not sure what abspath
should do on win32. And what about realpath?
As I'm not sure, I cannot attach a patch here. In order to explain
what I mean, I moved abspath into a standalone program and then I
freely altered it bearing upon the mumblings in my Thursday post.
I attach that. It runs like so:
C:\ale\c>wpath .
. ==> c:/ale/c
C:\ale\c>wpath -D "d:existing dir with spaces\existing file with spaces" "d:existing
dir with spaces/existingfilewithoutspaces"
d:existing dir with spaces\existing file with spaces ==>
d:/tools/others/EXISTI~1/Existing File With Spaces
d:existing dir with spaces/existingfilewithoutspaces ==>
d:/tools/others/EXISTI~1/ExistingFileWithoutSpaces
C:\ale\c>wpath -S "d:existing dir with spaces\existing file with spaces" "d:existing
dir withspaces/existingfilewithoutspaces"
d:existing dir with spaces\existing file with spaces ==>
d:/tools/others/EXISTI~1/EXISTI~1
d:existing dir with spaces/existingfilewithoutspaces ==>
d:/tools/others/EXISTI~1/ExistingFileWithoutSpaces
C:\ale\c>wpath -s "d:existing dir with spaces\existing file with spaces" "d:existing
dir with spaces/existingfilewithoutspaces"
d:existing dir with spaces\existing file with spaces ==>
d:/tools/others/EXISTI~1/EXISTI~1
d:existing dir with spaces/existingfilewithoutspaces ==>
d:/tools/others/EXISTI~1/EXISTI~2
and also
C:\ale\c>wpath -\ wpath.c
wpath.c ==> c:\ale\c\wpath.c
C:\ale\c>wpath -j \mingw\include\w32api\w32api\w32api
\mingw\include\w32api\w32api\w32api ==> c:/uX/MinGW/include
C:\ale\c>wpath -j "drive d"
drive d ==> d:/
(where "drive d" was linked to d:\ using mountvol, and
mingw and w32api were linked using linkd from w2k resource kit)
Would any of that stuff be useful for Windows users?
Would it make sense to add Windows-specific functions to make?
In case both answers are "no" I apologize for this long post.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define WIN32_LEAN_AND_MEAN
#define _WIN32_WINNT 0x0501
#include <windows.h>
#include <Winioctl.h>
//#include <NewAPIs.h>
enum path_option_flags
{
pof_junction = 1, /* option j */
pof_short = 2, /* option s */
pof_short_only_dir = 4, /* option S (implies s) */
pof_short_only_space = 8, /* option D (implies s) */
pof_backslash = 16 /* option \ */
};
static char *windows_syserr(DWORD err)
/* result must be freed with LocalFree() */
{
LPVOID lpMsgBuf = NULL;
FormatMessage(
FORMAT_MESSAGE_ALLOCATE_BUFFER |
FORMAT_MESSAGE_FROM_SYSTEM |
FORMAT_MESSAGE_IGNORE_INSERTS,
NULL,
err,
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), /* Default language */
(LPTSTR) &lpMsgBuf,
0,
NULL
);
return lpMsgBuf;
}
#define GET_PATH_MAX 1024
#define HAVE_DOS_PATHS
#define IS_PATHSEP(c) ((c) == '/' || (c) == '\\')
#ifdef HAVE_DOS_PATHS
// was: #define IS_ABSOLUTE(n) (n[0] && n[1] == ':')
#define IS_ABSOLUTE_OR_HAS_DRIVE(n) (n[0] && n[1] == ':')
#define ROOT_LEN 3
#else
#define IS_ABSOLUTE_OR_HAS_DRIVE(n) (n[0] == '/')
#define ROOT_LEN 1
#endif
static char *starting_directory;
static void print_error(char const*what)
{
DWORD err = GetLastError();
char *errst = windows_syserr(err);
fprintf(stderr, "%s: %s [%d]\n",
what, errst? errst: "?", err);
if (errst)
LocalFree(errst);
}
static int
check_volume(char *apath, char *pathname)
{
char vol[GET_PATH_MAX];
char *volname;
size_t len, rtc;
DWORD ldrives;
len = strlen(pathname);
if (pathname[len-1] != '\\')
++len;
if ((volname = (char*)malloc(len + 1)) == NULL)
return 0;
strcpy(volname, pathname);
volname[len-1] = 0;
rtc = QueryDosDevice(volname, vol, sizeof vol);
free(volname);
if (rtc == 0)
{
print_error("cannot get drive");
return 0;
}
if ((ldrives = GetLogicalDrives()) == 0)
{
print_error("cannot get logical drives");
return 0;
}
if ((volname = strdup(vol)) == NULL)
return 0;
rtc = 'a';
while (ldrives)
{
if (ldrives & 1)
{
char root[8];
sprintf(root, "%c:", rtc);
if (QueryDosDevice(root, vol, sizeof vol) == 0)
print_error(root);
else if (strcmp(vol, volname) == 0)
break;
}
ldrives >>= 1;
++rtc;
}
if (ldrives)
sprintf(apath, "%c:/", rtc);
else
rtc = 0;
free(volname);
return rtc;
}
/*
* REPARSE_DATA_BUFFER has been moved to ntifs.h?, see
*
http://msdn.microsoft.com/library/en-us/IFSK_r/hh/IFSK_r/fileinformationstructures_4f1b658e-1833-421f-a726-448b20b1c595.xml.asp
*/
#if !defined REPARSE_DATA_BUFFER_HEADER_SIZE
typedef struct _REPARSE_DATA_BUFFER {
ULONG ReparseTag;
USHORT ReparseDataLength;
USHORT Reserved;
union {
struct {
USHORT SubstituteNameOffset;
USHORT SubstituteNameLength;
USHORT PrintNameOffset;
USHORT PrintNameLength;
WCHAR PathBuffer[1];
} SymbolicLinkReparseBuffer;
struct {
USHORT SubstituteNameOffset;
USHORT SubstituteNameLength;
USHORT PrintNameOffset;
USHORT PrintNameLength;
WCHAR PathBuffer[1];
} MountPointReparseBuffer;
struct {
UCHAR DataBuffer[1];
} GenericReparseBuffer;
};
} REPARSE_DATA_BUFFER, *PREPARSE_DATA_BUFFER;
#endif
static int
check_junction(char *apath)
{
/*
* http://msdn.microsoft.com/library/en-us/fileio/fs/reparse_points.asp
*
* Reparse points and extended attributes are mutually exclusive.
* The NTFS file system cannot create a reparse point when the
* file contains extended attributes, and it cannot create extended
* attributes on a file that contains a reparse point.
*
* Reparse point data, including the tag and optional GUID,
* cannot exceed 16 kilobytes. Setting a reparse point fails
* if the amount of data to be placed in the reparse point
* exceeds this limit.
*/
union reparse
{
REPARSE_GUID_DATA_BUFFER rgdb;
REPARSE_DATA_BUFFER rdb;
char data[GET_PATH_MAX + sizeof(REPARSE_DATA_BUFFER)];
} buf;
DWORD size;
int rtc;
HANDLE h = CreateFile(
apath, // LPCTSTR lpFileName,
STANDARD_RIGHTS_READ,//|FILE_READ_EA, // DWORD
dwDesiredAccess,
FILE_SHARE_READ, // DWORD dwShareMode,
NULL, // LPSECURITY_ATTRIBUTES lpSecurityAttributes,
OPEN_EXISTING, // DWORD
dwCreationDisposition,
FILE_ATTRIBUTE_NORMAL // DWORD
dwFlagsAndAttributes,
| FILE_FLAG_BACKUP_SEMANTICS
| FILE_FLAG_OPEN_REPARSE_POINT,
NULL); // HANDLE hTemplateFile
if (h == INVALID_HANDLE_VALUE)
{
print_error("no reparse: cannot open");
return 0;
}
rtc = DeviceIoControl(
h, // HANDLE hDevice,
FSCTL_GET_REPARSE_POINT, // DWORD
dwIoControlCode,
NULL, // LPVOID
lpInBuffer,
0, // DWORD
nInBufferSize,
&buf, // LPVOID
lpOutBuffer,
sizeof buf, // DWORD
nOutBufferSize,
&size, // LPDWORD
lpBytesReturned,
NULL); // LPOVERLAPPED
lpOverlapped
if (!rtc)
print_error("cannot devio");
CloseHandle(h);
if (rtc &&
size >= sizeof buf.rdb &&
buf.rdb.ReparseTag == IO_REPARSE_TAG_MOUNT_POINT)
{
char pathname[GET_PATH_MAX];
// #if !defined(UNICODE)
unsigned int nsize = WideCharToMultiByte(
CP_ACP, // UINT CodePage: ANSI
0, // DWORD dwFlags
(LPCWSTR)((char*)buf.rdb.MountPointReparseBuffer.PathBuffer
+
buf.rdb.MountPointReparseBuffer.SubstituteNameOffset), // LPCWSTR in
buf.rdb.MountPointReparseBuffer.SubstituteNameLength /
sizeof(WCHAR), // int cchWideChar,
pathname, // LPSTR lpMultiByteStr,
sizeof(pathname), // int cbMultiByte,
NULL, // LPCSTR lpDefaultChar,
NULL); // LPBOOL lpUsedDefaultChar
if (nsize == 0)
{
print_error("cannot convert reparse");
rtc = 0;
}
else
{
char *p = pathname;
if (nsize >= sizeof pathname)
nsize = sizeof pathname;
pathname[nsize] = 0;
if (strncmp(p, "\\??\\", 4) == 0 ||
strncmp(p, "\\\\?\\", 4) == 0)
p += 4;
if (buf.rdb.MountPointReparseBuffer.PrintNameLength ==
0)
return check_volume(apath, p);
strcpy(apath, p);
}
}
else
{
rtc = 0;
}
return rtc;
}
static int more_file_found(HANDLE h) /* helper for check_real_path below */
{
WIN32_FIND_DATA buf;
int rtc = FindNextFile(h, &buf);
if (rtc == 0 && GetLastError() != ERROR_NO_MORE_FILES)
rtc = 1;
FindClose(h);
return rtc;
}
static int
check_real_path(char *apath, char *component_start, int *check_real)
{
WIN32_FIND_DATA buf;
char *copy_name = buf.cFileName;
HANDLE h = FindFirstFile(apath, &buf);
int rtc = *check_real;
if (h == INVALID_HANDLE_VALUE || more_file_found(h))
{
*check_real = 0;
return 0;
}
if ((rtc & pof_junction) != 0 &&
(buf.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT) != 0 &&
buf.dwReserved0 == IO_REPARSE_TAG_MOUNT_POINT)
return check_junction(apath);
if ((rtc & pof_short) != 0 && buf.cAlternateFileName[0] != 0 &&
((rtc & pof_short_only_dir) == 0 ||
(buf.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0)
&&
((rtc & pof_short_only_space) == 0 ||
strchr(component_start, ' ') != NULL))
copy_name = buf.cAlternateFileName;
rtc = strlen(copy_name) != strlen(component_start);
strcpy(component_start, copy_name);
return rtc;
}
static char *
abspath (const char *name, char *apath, int check_real)
{
char *dest;
const char *start, *end, *apath_limit;
unsigned long root_len = ROOT_LEN;
if (name[0] == '\0' || apath == NULL)
return NULL;
apath_limit = apath + GET_PATH_MAX;
/* It is unlikely we would make it until here but just to make sure. */
if (!starting_directory)
return NULL;
if (!IS_ABSOLUTE_OR_HAS_DRIVE(name))
{
strcpy (apath, starting_directory);
#ifdef HAVE_DOS_PATHS
if (IS_PATHSEP(name[0]))
{
/* We have /foo, an absolute file name except for the drive
letter. Assume the missing drive letter is the current
drive, which we can get if we remove from starting_directory
everything past the root directory. */
apath[root_len] = '\0';
}
#endif
dest = strchr (apath, '\0');
}
else
{
strncpy (apath, name, root_len);
apath[root_len] = '\0';
dest = apath + root_len;
/* Get past the root in name, since we already copied it. */
name += root_len;
#ifdef HAVE_DOS_PATHS
if (!IS_PATHSEP(apath[2]))
{
/* Convert d:foo into current-or-starting-directory/foo */
int ddrive = apath[0] - starting_directory[0];
if (ddrive == 0 || ddrive == 'A' -'a' || ddrive == 'a' - 'A')
strcpy(apath, starting_directory);
else
{
char current[8];
strcpy(current, apath);
current[2] = '.';
if (GetFullPathName(current, GET_PATH_MAX, apath, &dest /* not used
*/) == 0)
return NULL;
}
dest = strchr(apath, '\0');
/* addition above skipped one character too many. */
name--;
}
#endif
}
for (start = end = name; *start != '\0'; start = end)
{
unsigned long len;
/* Skip sequence of multiple path-separators. */
while (IS_PATHSEP(*start))
++start;
/* Find end of path component. */
for (end = start; *end != '\0' && !IS_PATHSEP(*end); ++end)
;
len = end - start;
if (len == 0)
break;
else if (len == 1 && start[0] == '.')
/* nothing */;
else if (len == 2 && start[0] == '.' && start[1] == '.')
{
/* Back up to previous component, ignore if at root already. */
if (dest > apath + root_len)
for (--dest; !IS_PATHSEP(dest[-1]); --dest);
}
else
{
char *component_start;
if (!IS_PATHSEP(dest[-1]))
*dest++ = '/';
if (dest + len >= apath_limit)
return NULL;
component_start = dest;
dest = memcpy (dest, start, len);
dest += len;
*dest = '\0';
if (check_real && check_real_path(apath, component_start,
&check_real))
dest = strchr(apath, '\0');
}
}
/* Unless it is root strip trailing separator. */
if (dest > apath + root_len && IS_PATHSEP(dest[-1]))
--dest;
*dest = '\0';
return apath;
}
static void slashed(char *r, char to)
{
char *s = r, from = to == '/'? '\\' : '/';
if (r[0] == 0) return;
if (r[1] == ':' && r[0] >= 'A' && r[0] <= 'Z')
r[0] += 'c' - 'C';
while ((s = strchr(s + 1, from)) != 0)
*s = to;
}
int main(int argc, char *argv[])
{
char apath[GET_PATH_MAX], *nu;
int i = 1, check_real = 0;
if (GetFullPathName(".", sizeof apath, apath, &nu))
starting_directory = strdup(apath);
if (1 < argc && argv[1][0] == '-' && argv[1][1] != 0)
{
char *opt = &argv[1][0], ch;
while ((ch = *++opt) != 0)
switch (ch)
{
case 'j': check_real |= pof_junction; break;
case 's': check_real |= pof_short; break;
case 'S': check_real |= pof_short_only_space |
pof_short; break;
case 'D': check_real |= pof_short_only_dir |
pof_short; break;
case '\\': check_real |= pof_backslash; break;
default:
fprintf(stderr, "%c ignored (valid:
jsSD\\)\n", ch);
break;
}
i = 2;
}
for (; i < argc; ++i)
{
char *r = abspath(argv[i], apath, check_real);
if (r)
slashed(r, (check_real & pof_backslash)? '\\' : '/');
else r = "-- FAILED!! --";
printf("%s ==> %s\n", argv[i], r);
}
return 0;
}
- Re: Bug in builtin function abspath, Andreas Büning, 2006/06/01
- Message not available
- Re: Bug in builtin function abspath, Alessandro Vesely, 2006/06/01
- Re: Bug in builtin function abspath, Eli Zaretskii, 2006/06/01
- Re: Bug in builtin function abspath,
Alessandro Vesely <=
- Re: Bug in builtin function abspath, Eli Zaretskii, 2006/06/04
- Re: Bug in builtin function abspath, Alessandro Vesely, 2006/06/06
- Re: Bug in builtin function abspath, Eli Zaretskii, 2006/06/06
- Re: Bug in builtin function abspath, Earnie Boyd, 2006/06/07
- Re: Bug in builtin function abspath, Eli Zaretskii, 2006/06/07
- Re: Bug in builtin function abspath, Alessandro Vesely, 2006/06/08
- Re: Bug in builtin function abspath, Eli Zaretskii, 2006/06/09
- Re: Bug in builtin function abspath, Alessandro Vesely, 2006/06/09
- Re: Bug in builtin function abspath, Eli Zaretskii, 2006/06/09
- Re: Bug in builtin function abspath, Alessandro Vesely, 2006/06/10