/*********
; Windows Interface
*********/
#define PERL_NO_GET_CONTEXT
#define WIN32_LEAN_AND_MEAN
#include "EXTERN.h"
#include "perl.h"
#include "XSUB.h"
#include "ppport.h"
#include <fcntl.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <time.h>
#include <windows.h>
#include <winioctl.h>
#ifndef 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;
ULONG Flags;
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
#ifndef IO_REPARSE_TAG_SYMLINK
#define IO_REPARSE_TAG_SYMLINK 0xA000000C
#endif
/**********
; local constants
**********/
STATIC char perr_nohash [] = "unable to create hash!";
/**********
; local functions
**********/
STATIC bool MakeSymbolicLink (WCHAR *target, WCHAR *link, DWORD attrib)
{
#if (_WIN32_WINNT >= 0x0600)
return CreateSymbolicLinkW (link, target,
attrib & FILE_ATTRIBUTE_DIRECTORY ? SYMBOLIC_LINK_FLAG_DIRECTORY : 0);
#else
SetLastError (ERROR_INVALID_FUNCTION);
return (0);
#endif
}
STATIC char *_get_DOS_time (LPFILETIME lpFTime, char *pTime)
{
SYSTEMTIME lpSTime [1];
if (!FileTimeToSystemTime (lpFTime, lpSTime))
{ strcpy (pTime, "0,0,0,0,0,0"); }
else
{
sprintf (pTime, "%d,%d,%d,%d,%d,%d", lpSTime->wSecond, lpSTime->wMinute,
lpSTime->wHour, lpSTime->wDay, lpSTime->wMonth - 1, lpSTime->wYear - 1900);
}
return pTime;
}
/**********
; Cygwin adjustments
**********/
#ifdef __CYGWIN__
#include <wchar.h>
#define win32_open_osfhandle _open_osfhandle
#define _wcsicmp wcscasecmp
extern int cygwin_attach_handle_to_fd
(char *name, int fd, HANDLE handle, int bin, int access_mode);
STATIC int _open_osfhandle (intptr_t fh, int flags)
{
int access_mode = 0;
switch (flags)
{
case O_RDONLY:
access_mode = GENERIC_READ;
break;
case O_WRONLY:
access_mode = GENERIC_WRITE;
break;
case O_RDWR:
access_mode = GENERIC_READ | GENERIC_WRITE;
break;
}
return cygwin_attach_handle_to_fd
("/dev/null", -1, (HANDLE) fh, -1, access_mode);
}
#endif
/**********
; external functions
**********/
MODULE = Win32::LongPath PACKAGE = Win32::LongPath
PROTOTYPES: ENABLE
bool
copy_file (WCHAR *from, WCHAR *to)
CODE:
RETVAL = CopyFileW (from, to, 0);
OUTPUT:
RETVAL
bool
create_directory (WCHAR *path)
CODE:
RETVAL = CreateDirectoryW (path, NULL);
OUTPUT:
RETVAL
void
create_file (WCHAR *path, long access, long dispos, int flags)
PREINIT:
PerlIO *pfh;
GV *gv;
CODE:
int fd;
HANDLE fh = CreateFileW (path, access, FILE_SHARE_READ | FILE_SHARE_WRITE,
NULL, dispos, FILE_ATTRIBUTE_NORMAL, NULL);
if (fh == INVALID_HANDLE_VALUE)
{ XSRETURN_EMPTY; }
fd = win32_open_osfhandle ((intptr_t)fh, flags);
if (fd < 0)
{
CloseHandle (fh);
XSRETURN_EMPTY;
}
char *mode;
if ((access & (GENERIC_READ | GENERIC_WRITE))
== (GENERIC_READ | GENERIC_WRITE))
{ mode = "r+"; }
else if (access & GENERIC_WRITE)
{ mode = "w"; }
else
{ mode = "r"; }
pfh = PerlIO_fdopen (fd, mode);
if (!pfh)
{
CloseHandle (fh);
XSRETURN_EMPTY;
}
char *packname = "IO::File";
gv = (GV*)SvREFCNT_inc (newGVgen (packname));
if (gv)
(void) hv_delete (GvSTASH (gv), GvNAME (gv), GvNAMELEN (gv), G_DISCARD);
if (gv && do_open (gv, "+>&", 3, FALSE, 0, 0, pfh))
{
ST(0) = sv_2mortal (newRV ((SV*)gv));
sv_bless (ST(0), gv_stashpv (packname, TRUE));
SvREFCNT_dec (gv);
}
else
{
ST(0) = &PL_sv_undef;
SvREFCNT_dec (gv);
}
bool
find_close (SV* self)
CODE:
HV* hv = (HV*)SvRV (self);
HANDLE handle = (HANDLE)SvIVx (*hv_fetchs (hv, "handle", 1));
RETVAL = FindClose (handle);
OUTPUT:
RETVAL
void
find_first_file (SV* self, WCHAR *path)
CODE:
WIN32_FIND_DATAW pinfo [1];
HANDLE handle = FindFirstFileW (path, pinfo);
HV* hv = (HV*)SvRV (self);
if (!hv_stores (hv, "handle", newSViv ((IV)handle)))
{ Perl_croak (aTHX_ perr_nohash); }
if (!hv_stores (hv, "first", newSVpvn ((char *)pinfo->cFileName,
wcslen (pinfo->cFileName) * sizeof (WCHAR))))
{ Perl_croak (aTHX_ perr_nohash); }
SV*
find_link (WCHAR *path)
CODE:
DWORD bufsize;
HANDLE fh;
/**********
; o linked file?
; o open file
; o reparse data until buffer filled
**********/
DWORD attrib = GetFileAttributesW (path);
if (attrib == INVALID_FILE_ATTRIBUTES)
{ XSRETURN_EMPTY; }
if (!(attrib & FILE_ATTRIBUTE_REPARSE_POINT))
{ XSRETURN_EMPTY; }
fh = CreateFileW (path, 0, FILE_SHARE_READ | FILE_SHARE_WRITE,
NULL, OPEN_EXISTING,
FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OPEN_REPARSE_POINT, NULL);
if (fh == INVALID_HANDLE_VALUE)
{ XSRETURN_EMPTY; }
RETVAL = NULL;
for (bufsize = sizeof (REPARSE_DATA_BUFFER) + 300; bufsize; bufsize += 300)
{
char *pnewbuf, *pbuf;
DWORD retsize;
int len = 0;
REPARSE_DATA_BUFFER *pparse;
/**********
; o create buffer
; o get data
; o error?
**********/
Newx (pbuf, bufsize, char);
pparse = (REPARSE_DATA_BUFFER *)pbuf;
if (!DeviceIoControl (fh, FSCTL_GET_REPARSE_POINT, NULL, 0, pparse,
bufsize, &retsize, NULL))
{
Safefree (pbuf);
if (GetLastError () != ERROR_MORE_DATA)
{ break; }
continue;
}
/**********
; extract link value
**********/
switch (pparse->ReparseTag)
{
case IO_REPARSE_TAG_SYMLINK:
pnewbuf = (char *)&pparse->SymbolicLinkReparseBuffer.PathBuffer
[pparse->SymbolicLinkReparseBuffer.PrintNameOffset / 2];
len = pparse->SymbolicLinkReparseBuffer.PrintNameLength;
break;
case IO_REPARSE_TAG_MOUNT_POINT:
pnewbuf = (char *)&pparse->MountPointReparseBuffer.PathBuffer
[pparse->MountPointReparseBuffer.PrintNameOffset / 2];
len = pparse->MountPointReparseBuffer.PrintNameLength;
break;
default:
pnewbuf = NULL;
}
if (pnewbuf)
{ RETVAL = newSVpvn (pnewbuf, len); }
Safefree (pbuf);
if (pnewbuf)
{ break; }
}
CloseHandle (fh);
if (!RETVAL)
{ XSRETURN_EMPTY; }
OUTPUT:
RETVAL
SV*
find_next_file (SV* self)
CODE:
WIN32_FIND_DATAW pinfo [1];
HV* hv = (HV*)SvRV (self);
HANDLE handle = (HANDLE)SvIVx (*hv_fetchs (hv, "handle", 1));
if(!FindNextFileW (handle, pinfo))
{ XSRETURN_EMPTY; }
RETVAL = newSVpvn ((char *)pinfo->cFileName,
wcslen (pinfo->cFileName) * sizeof (WCHAR));
OUTPUT:
RETVAL
DWORD
get_attribs (WCHAR *path)
CODE:
RETVAL = GetFileAttributesW (path);
if (RETVAL == INVALID_FILE_ATTRIBUTES)
{ XSRETURN_EMPTY; }
OUTPUT:
RETVAL
SV*
get_current_directory ()
CODE:
DWORD len;
char *bufptr;
len = GetCurrentDirectoryW (0, 0);
if (!len)
{ RETVAL = newSVpvn ("", 0); }
else
{
WCHAR *newptr;
Newx (bufptr, len * sizeof (WCHAR), char);
GetCurrentDirectoryW (len, (LPWSTR)bufptr);
newptr = (WCHAR *)bufptr;
RETVAL = newSVpvn ((char *)newptr, wcslen (newptr) * sizeof (WCHAR));
Safefree (bufptr);
}
OUTPUT:
RETVAL
SV*
get_long_path (WCHAR *path)
CODE:
DWORD len;
char *bufptr;
len = GetLongPathNameW (path, 0, 0);
if (!len)
{ RETVAL = newSVpvn ("", 0); }
else
{
WCHAR *newptr;
Newx (bufptr, len * sizeof (WCHAR), char);
GetLongPathNameW (path, (LPWSTR)bufptr, len);
newptr = (WCHAR *)bufptr;
RETVAL = newSVpvn ((char *)newptr, wcslen (newptr) * sizeof (WCHAR));
Safefree (bufptr);
}
OUTPUT:
RETVAL
SV*
get_short_path (WCHAR *path)
CODE:
DWORD len;
char *bufptr;
len = GetShortPathNameW (path, 0, 0);
if (!len)
{ RETVAL = newSVpvn ("", 0); }
else
{
WCHAR *newptr;
Newx (bufptr, len * sizeof (WCHAR), char);
GetShortPathNameW (path, (LPWSTR)bufptr, len);
newptr = (WCHAR *)bufptr;
RETVAL = newSVpvn ((char *)newptr, wcslen (newptr) * sizeof (WCHAR));
Safefree (bufptr);
}
OUTPUT:
RETVAL
DWORD
get_last_error ()
CODE:
RETVAL = GetLastError ();
OUTPUT:
RETVAL
void
get_stat (WCHAR *path, bool symlink)
CODE:
HANDLE fh;
BY_HANDLE_FILE_INFORMATION pfhi [1];
HV* hv;
SV* hvref;
int mode;
char sTime [40];
/**********
; open FS object and get information
**********/
DWORD flags = FILE_FLAG_BACKUP_SEMANTICS;
if (symlink)
{ flags |= FILE_FLAG_OPEN_REPARSE_POINT; }
fh = CreateFileW (path, 0, FILE_SHARE_READ | FILE_SHARE_WRITE,
NULL, OPEN_EXISTING, flags, NULL);
if (fh == INVALID_HANDLE_VALUE)
{ XSRETURN_EMPTY; }
if (!GetFileInformationByHandle (fh, pfhi))
{
CloseHandle (fh);
XSRETURN_EMPTY;
}
CloseHandle (fh);
/**********
; get attributes and store in hash
**********/
hv = newHV ();
hvref = sv_2mortal (newRV_noinc ((SV *)hv));
if (!hv_stores (hv, "dev", newSVuv (pfhi->dwVolumeSerialNumber)))
{ Perl_croak (aTHX_ perr_nohash); }
if (!hv_stores (hv, "ino", newSVuv (0)))
{ Perl_croak (aTHX_ perr_nohash); }
if (!hv_stores (hv, "attribs", newSVuv (pfhi->dwFileAttributes)))
{ Perl_croak (aTHX_ perr_nohash); }
if (pfhi->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
{ mode = S_IFDIR | S_IRWXU | S_IRWXG | S_IRWXO; }
else
{
size_t len;
mode = S_IFREG | S_IRUSR | S_IRGRP | S_IROTH;
if (!(pfhi->dwFileAttributes & FILE_ATTRIBUTE_READONLY))
{ mode |= S_IWUSR | S_IWGRP | S_IWOTH; }
len = wcslen (path);
if (path [len - 4] == '.')
{
WCHAR *ext = &path [len - 3];
if (!_wcsicmp (ext, L"BAT"))
{ mode |= S_IXUSR | S_IXGRP | S_IXOTH; }
else if (!_wcsicmp (ext, L"CMD"))
{ mode |= S_IXUSR | S_IXGRP | S_IXOTH; }
else if (!_wcsicmp (ext, L"COM"))
{ mode |= S_IXUSR | S_IXGRP | S_IXOTH; }
else if (!_wcsicmp (ext, L"EXE"))
{ mode |= S_IXUSR | S_IXGRP | S_IXOTH; }
}
}
if (!hv_stores (hv, "mode", newSVuv (mode)))
{ Perl_croak (aTHX_ perr_nohash); }
if (!hv_stores (hv, "nlink", newSVuv (1)))
{ Perl_croak (aTHX_ perr_nohash); }
if (!hv_stores (hv, "uid", newSVuv (0)))
{ Perl_croak (aTHX_ perr_nohash); }
if (!hv_stores (hv, "gid", newSVuv (0)))
{ Perl_croak (aTHX_ perr_nohash); }
if (!hv_stores (hv, "rdev", newSVuv (pfhi->dwVolumeSerialNumber)))
{ Perl_croak (aTHX_ perr_nohash); }
if (!hv_stores (hv, "atime",
newSVpv (_get_DOS_time (&pfhi->ftLastAccessTime, sTime), 0)))
{ Perl_croak (aTHX_ perr_nohash); }
if (!hv_stores (hv, "mtime",
newSVpv (_get_DOS_time (&pfhi->ftLastWriteTime, sTime), 0)))
{ Perl_croak (aTHX_ perr_nohash); }
if (!hv_stores (hv, "ctime",
newSVpv (_get_DOS_time (&pfhi->ftCreationTime, sTime), 0)))
{ Perl_croak (aTHX_ perr_nohash); }
if (mode & S_IFDIR)
{
if (!hv_stores (hv, "size_high", newSVuv (0)))
{ Perl_croak (aTHX_ perr_nohash); }
if (!hv_stores (hv, "size_low", newSVuv (0)))
{ Perl_croak (aTHX_ perr_nohash); }
}
else
{
if (!hv_stores (hv, "size_high", newSVuv (pfhi->nFileSizeHigh)))
{ Perl_croak (aTHX_ perr_nohash); }
if (!hv_stores (hv, "size_low", newSVuv (pfhi->nFileSizeLow)))
{ Perl_croak (aTHX_ perr_nohash); }
}
ST (0) = hvref;
XSRETURN (1);
void
get_vol_info (WCHAR *path)
CODE:
DWORD serial_number;
DWORD max_comp_len;
DWORD sys_flags;
WCHAR pname [MAX_PATH + 1];
HV* hv;
SV* hvref;
/**********
; get info
**********/
if (!GetVolumeInformationW (path, pname, MAX_PATH + 1,
&serial_number, &max_comp_len, &sys_flags, NULL, 0))
{ XSRETURN_EMPTY; }
/**********
; get info and store in hash
**********/
hv = newHV ();
hvref = sv_2mortal (newRV_noinc ((SV *)hv));
if (!hv_stores (hv, "serial", newSVuv (serial_number)))
{ Perl_croak (aTHX_ perr_nohash); }
if (!hv_stores (hv, "maxlen", newSVuv (max_comp_len)))
{ Perl_croak (aTHX_ perr_nohash); }
if (!hv_stores (hv, "sysflags", newSVuv (sys_flags)))
{ Perl_croak (aTHX_ perr_nohash); }
if (!hv_stores (hv, "name", newSVpvn ((char *)pname,
wcslen (pname) * sizeof(WCHAR))))
{ Perl_croak (aTHX_ perr_nohash); }
ST (0) = hvref;
XSRETURN (1);
bool
make_hlink (WCHAR *target, WCHAR *link)
CODE:
RETVAL = CreateHardLinkW (link, target, NULL);
OUTPUT:
RETVAL
bool
make_slink (WCHAR *target, WCHAR *link)
CODE:
DWORD attrib = GetFileAttributesW (target);
if (attrib == INVALID_FILE_ATTRIBUTES)
{ XSRETURN_EMPTY; }
RETVAL = MakeSymbolicLink (target, link, attrib);
OUTPUT:
RETVAL
bool
move_file (WCHAR *from, WCHAR *to)
CODE:
RETVAL = MoveFileW (from, to);
OUTPUT:
RETVAL
bool
remove_directory (WCHAR *path)
CODE:
RETVAL = RemoveDirectoryW (path);
OUTPUT:
RETVAL
bool
remove_file (WCHAR *path)
CODE:
RETVAL = DeleteFileW (path);
OUTPUT:
RETVAL
bool
set_current_directory (WCHAR *path)
CODE:
RETVAL = SetCurrentDirectoryW (path);
OUTPUT:
RETVAL
#ifndef _USE_32BIT_TIME_T
#define _USE_32BIT_TIME_T
#endif
bool
set_attribs (WCHAR *path, DWORD attribs)
CODE:
RETVAL = SetFileAttributesW (path, attribs);
OUTPUT:
RETVAL
bool
set_filetime (time_t atime, time_t mtime, WCHAR *path)
CODE:
#ifdef __CYGWIN__
SetLastError (ERROR_CALL_NOT_IMPLEMENTED);
XSRETURN_EMPTY;
#else
struct _utimbuf putb [1];
putb->actime = atime;
putb->modtime = mtime;
if (_wutime (path, putb) == -1)
{ XSRETURN_EMPTY; }
#endif
RETVAL = 1;
OUTPUT:
RETVAL
DWORD
set_last_error (long error_code)
CODE:
SetLastError (error_code);
RETVAL = error_code;
OUTPUT:
RETVAL