1
0
Fork 0
mirror of synced 2025-03-07 03:53:26 +01:00
wine/files/dos_fs.c
Alexandre Julliard d30dfd24d6 Release 980927
Sun Sep 27 14:25:38 1998  Petter Reinholdtsen <pere@td.org.uit.no>

	* [files/drive.c]
	Make sure GetDriveType32A() handles param NULL.  Added some
	doc on function.

Sun Sep 27 14:07:26 1998  Huw D M Davies <daviesh@abacus.physics.ox.ac.uk>

	* [controls/edit.c] [windows/win.c]
	Don't call SetWindowLong() in EDIT_WM_NCREATE.
	Fix SetWindowLong(GWL_[EX]STYLE) to work for 16bit windows. Remove
	UpdateWindow() call. 

Sun Sep 27 13:41:22 1998  Alexandre Julliard  <julliard@lrc.epfl.ch>

	* [scheduler/*.c] [server/event.c] [server/mutex.c]
	  [server/semaphore.c]
	Implemented server-side synchronisation objects.

Sun Sep 27 01:13:35 1998  Alex Priem <alexp@sci.kun.nl>

	* [dlls/comctl32/treeview.c] [include/treeview.h] [include/comctl.h]
	Treeview implementation.

	* [dlls/comctl32/trackbar.c] [include/trackbar.h] 
	Trackbar implementation.

Sat Sep 26 20:49:13 1998  Ulrich Weigand <weigand@informatik.uni-erlangen.de>

	* [if1632/thunk.c] [tools/build.c] [win32/kernel32.c]
	Bugfix: several problems with flat thunks fixed.

	* [memory/selector.c]
	Bugfix: IsBad...Ptr16 didn't work for limit_in_pages segments.

	* [scheduler/thread.c]
	Bugfix: CreateThread: Allow id parameter == NULL.

	* [objects/gdiobj.c]
	Bugfix: IsGDIObject: Return correct object type for stock objects.

	* [msdos/dpmi.c]
	Bugfix: fixed typo in INT_DoRealModeInt.

	* [msdos/int21.c]
	Bugfix: int21 READ *must* use WIN16_hread, not _hread16.

	* [if1632/kernel.spec] [if1632/dummy.c] [if1632/thunk.c]
	  [loader/ne/module.c] [scheduler/event.c] [scheduler/synchro.c]
	  [scheduler/thread.c] [win32/kernel32.c] [win32/ordinals.c]
	Added names/stubs for all undocumented KERNEL routines (Win95).
	Added the following undoc. 16-bit equivalents to Win32 routines:
	KERNEL.441-443,449-453,456-462,471-476,479-486,488.
	Added stubs for some other KERNEL routines.

	* [memory/heap.c] [memory/global.c] [include/global.h]
	Implemented Local32... 32-bit local heap routines (KERNEL.208-215, 229).

	* [miscemu/instr.c] [loader/module.c] [include/module.h]
	Implemented __GP fault handling and HasGPHandler (KERNEL.338).

	* [misc/error.c]
	Implemented LogParamErrorRegs (KERNEL.327).

	* [loader/task.c] [include/windows.h]
	Implemented GetCodeInfo (KERNEL.104).

	* [loader/task.c] [scheduler/thread.c] [include/thread.h]
	Implemented [GS]etThreadQueue and [GS]etFastQueue (KERNEL.463/4, 624/5).

	* [if1632/gdi.spec] [objects/dc.c] [objects/dib.c]
	  [objects/bitmap.c] [include/windows.h]
	Bugfix: fixed wrong parameter for CreateDIBSection16.
	Added [GS]etDIBColorTable16, stub for GetBoundsRect16.
	Partially implemented BITMAP_GetObject16 for DIBs.

	* [if1632/gdi.spec] [relay32/gdi32.spec] [objects/palette.c]
	Added some GDI stubs.

	* [if1632/Makefile.in] [if1632/display.spec] [if1632/mouse.spec]
	  [if1632/keyboard.spec] [if1632/builtin.c] [windows/keyboard.c]
	Added some stubs for Win16 drivers: KEYBOARD, MOUSE, DISPLAY.

	* [if1632/wprocs.spec] [msdos/vxd.c]
	Added some stubs for VxDs: VMM, ConfigMG, TimerAPI.

	* [msdos/int2f.c]
	Added some stubs for real-mode network drivers.

Sat Sep 26 18:18:18 1998  Marcus Meissner <marcus@jet.franken.de>

	* [configure.in]
	Merged in some more of the FreeBSD ports/emulators/wine patches. 
	(Maintainer(s) of this port: You can just submit these
	patches to Alexandre directly.)

	 * [loader/pe_image.c]
	Check filesize of image against size derived from header
	to spot truncated executeables without crashing.

	* [files/directory.c]
	Set envvar "COMSPEC". One win32(!!) program crashes without it.

	* [multimedia/mmio.c]
	Added mmioSetInfo32.

	* [include/file.h]
	Return STD_ERROR_HANDLE for AUX and PRT dos handles.

	* [loader/module.c]
	Handle executeables with spaces in their names a bit better in
	CreateProcess.

	* [relay32/msvfw32.spec][if1632/msvideo.spec][multimedia/msvideo.c][include/vfw.h]
	Started on MS Video support (can load Win32 ICMs).

	* [tools/testrun]
	A bit smarter use of ps.

	* [memory/virtual.c]
	Report PAGE_GUARDed pages as PAGE_PROTECTED (AutoCAD LT R17 fails
	without that check (since Win95 doesn't know about PAGE_GUARD)).

Sat Sep 26 15:04:05 1998  Ove Kaaven <ovek@arcticnet.no>

	* [include/miscemu.h] [if1632/builtin.c] [loader/task.c]
	  [miscemu/instr.c] [msdos/dpmi.c] [msdos/int21.c]
	  [msdos/interrupts.c] [windows/user.c]
	INT_[S|G]etHandler was renamed to INT_[S|G]etPMHandler.
	Added handlers to deal with real-mode interrupts; DOS
	programs are now able to hook real-mode interrupts.

	* [loader/dos/module.c] [msdos/dosmem.c] [msdos/int21.c]
	Moved real-mode interrupt table initialization to
	msdos/dosmem.c, and made new V86 tasks get a full copy
	of the existing "system memory" instead of almost empty
	space. Misc fixes.

	* [include/dosexe.h] [loader/dos/module.c] [msdos/dpmi.c]
	  [msdos/int2f.c]
	First shot at letting DOS programs start up DPMI (but DPMI
	is still disabled for DOS programs, for pkunzip's sake).

	* [include/debugger.h] [debugger/break.c] [debugger/dbg.y]
	  [debugger/registers.c] [debugger/memory.c] [debugger/info.c]
	  [loader/dos/dosvm.c]
	First shot at making Wine's debugger work for DOS programs.
	The -debug flag works, as do "nexti" and "stepi".

Sat Sep 26 13:13:13 1998  Juergen Schmied <juergen.schmied@metronet.de>

	* [dlls/shell32/dataobject.c]
	New classes IEnumFORMATETC implemented, IDataObject stubs.
	
	* [dlls/shell32/*.*][relay32/shell32.spec]
	Bugfixes.
	New: ICM_InsertItem(), ILCreateFromPath().
	Implemented: ILCloneFirst().
	Stubs: ILIsEqual(), ILFindChild(), SHLogILFromFSIL(),
	  PathMatchSpec(), PathIsExe().
	Changed: ILGetSize(), _ILIsDesktop(), PathCombine().

	* [include/shlobj.h]
	New SHLGUID's
	New structures: DVTARGETDEVICE32, STGMEDIUM32, FORMATETC32,
	CLIPFORMAT32.
	New interfaces: IEnumFORMATETC, IDataObject, ICommDlgBrowser
	IDockingWindowFrame, IServiceProvider.

	* [dlls/shell32/folders.c]
	Stubs for IShellLink.

	* [loader/resource.c]
	Small fixes.

	* [misc/crtdll.c][relay32/crtdll.spec]
	New __dllonexit().

	* [windows/message.c]
	SendNotifyMessageA, SendMessageCallBack32A half implemented.

	* [controls/edit.c]
	EDIT_WM_SetText set EF_UPDATE flag not for ES_MULTILINE.

	* [files/file.c]
	Handling of fileposition fixed.

Fri Sep 25 18:13:30 1998  Patrik Stridvall <ps@leissner.se>

	* [include/windows.h] [include/wintypes.h]
	  [ole/ole2nls.h] [relay32/kernel32.spec]
	Implemented EnumDateFormats and EnumTimeFormats.
	Only adds US English support.

	* [Makefile.in] [configure.in] 
	  [dlls/Makefile.in] [dlls/psapi/Makefile.in] 
	  [dlls/psapi/psapi_main.c] 
	New files to implement stubs for PSAPI.DLL (NT only).

	* [relay32/Makefile.in] [relay32/builtin32.c] 
	  [relay32/psapi.spec]
	New spec file for PSAPI.DLL (NT only).

	* [scheduler/handle.c]
	HANDLE_GetObjPtr should only interpret the pseudo handles as the
	current thread or the current process if a thread or a process is
	requested.

	* [include/winversion.h] [misc/version.c]
	Adds the global function VERSION_GetVersion() so functions can
	have different behavior depending on the -winver flag.

	* [include/oledlg.h] [ole/oledlg.c]
	Minor fixes. 

	* [windows/winproc.c]
	Minor changes.

	* [include/imm.h] [misc/imm.c]
	Now returns correct values under both Windows 95 and NT 4.0.

Thu Sep 24 22:11:44 1998  Kristian Nielsen  <kristian.nielsen@risoe.dk>

	* [configure.in] [include/acconfig.h] [include/thread.h]
	  [scheduler/sysdeps.c]
	Autoconfig test for non-reentrant libc.

Wed Sep 23 19:52:12 1998  Matthew Becker <mbecker@glasscity.net>

	* [*/*.c]
	Miscellaneous documentation updates and debugging output 
	standardizations.

	* [objects/clipping.c]
	Added ExtSelectClipRgn.

Wed Sep 23 00:03:28 EDT 1998  Pete Ratzlaff <pratzlaff@cfa.harvard.edu>

	* [include/windows.h] [if1632/user.spec] [relay32/user32.spec]
	  [windows/keyboard.c]
	Added, marginally implemented, GetKeyboardLayoutName().
	Only returns US English keyboard name.

Tue Sep 22 16:32:41 1998  Marcel Baur <mbaur@iiic.ethz.ch>

	* [programs/control/*]
	New Winelib application.

Mon Sep 21 00:29:18 1998  Peter Hunnisett <hunnise@nortel.ca>

	* [include/dplay.h][multimedia/dplay.c][ole/compobj.c]
	Added all DirectPlayLobby interfaces and enhanced DirectPlay
	and DirectPlayLobby support. Still not all that much. Useful
	enough if you just need to start a program, don't try any
	real dplay/lobby stuff.

	* [documentation/status/directplay]
	Added a very little bit.

	* [graphics/ddraw.c]
	- Call to SetWindowLong32A wasn't working because there was no
	  memory set aside when the window class was registered.
	- Fixed some xlib reference counting and change the behaviour
	  of DirectDrawSurface3_SetPalette to mimic observed behaviour
	  (palette is associated will all backbuffers)
	- Also stored all palette colour fields and spit back our saved
	  colour fields rather than query X for them.
	- Added plenty of AddRef and Release traces.
	- Added Xlib support for using -desktop option.
	- Fixed Xlib message handling. Messages weren't being passed to
	  the application. Fixes mouse movements in some xlib DDraw games.
	- Added a few stubs.

	* [windows/win.c][include/winerror.h]
	Fixed up some error handling in WIN_SetWindowLong. SetLastError
	wasn't being used. Could cause problems with 0 return codes.
	Added new error in winerror (1400).

	* [AUTHORS] [include/authors.h]
	Added myself as a Wine author.

Sun Sep 20 21:22:44 1998  Alexander Larsson  <alla@lysator.liu.se>

	* [loader/module.c]
	Changed GetModuleFileName32A so that is returns the
	long version of the filename. Note that just the name
	is long, not the directories.

Sat Sep 19 20:05:30 1998 Per Ångström <pang@mind.nu> 

	* [controls/menu.c]
	Made a couple of fixes to make life easier for applications that alter
	their menus at runtime.

	* [windows/defdlg.c]
	Removed the cast of the return value from dialog procedures to a 16-bit
	bool. The return value needs to retain all its 32 bits, since it is not 
	always a bool, such as when responding to the WM_NCHITTEST message.

Fri Sep 18 11:30:38 1998  Sergey Turchanov <turchanov@usa.net>

	* [loader/resource.c]
	Fixed very funny bug (though gravely affecting further excecution)
	with FindResource[Ex]32 functions.

	* [include/multimon.h] [windows/multimon.c] [relay32/user32.spec]
	  [include/windows.h] [windows/sysmetrics.c]
	Default implementation for Multimonitor API.

	* [include/windows.h] [windows/winpos.c]
	Fixed incorrect declaration (and behaviour) of GetWindowRect32.

Wed Sep 16 10:21:15 1998  Gerard Patel <G.Patel@Wanadoo.fr>

	* [controls/edit.c]
	Fixed EDIT_EM_GetLine to use correctly length of lines.

Tue Sep 15 20:40:16 1998  Eric Kohl <ekohl@abo.rhein-zeitung.de>

	* [misc/tweak.c][include/tweak.h][controls/menu.c]
	Replaced the tweak graphic routines by calls to DrawEdge32().

	* [misc/tweak.c][include/tweak.h][documentation/win95look]
	  [wine.ini][*/*]
	Changed "look and feel" selection. Allows Win3.1, Win95 and
	Win98 (no GUI code implemented) look and feel.

	* [dlls/comctl32/header.c][include/header.h][include/commctrl.h]
	Started callback item support and did some minor improvements.

	* [dlls/comctl32/imagelist.c]
	Fixed bug in transparent image display.
	ImageList_GetIcon is still buggy :-(

	* [dlls/comctl32/toolbar.c]
	Fixed button drawing (partial hack).

	* [dlls/comctl32/commctrl.c]
	Fixed MenuHelp().

	* [controls/button.c]
	Added 3d effect for groupbox.

	* [windows/msgbox.c]
	Added font support for message boxes.

	* [windows/nonclient.c]
	Fixed window moving bug.

	* [dlls/comctl32/*.c]
	Various improvements.

	* [dlls/comctl32/listview.c][dlls/comctl32/rebar.c]
	  [include/commctrl.h]
	More messages.

	* [windows/syscolor.c][include/windows.h]
	Introduced new Win98 system colors.

Tue Sep 15 18:29:45 1998 Wesley Filardo <eightknots@aol.com>

	* [files/profile.c]
	Added support in PROFILE_LoadWineIni for -config option

	* [misc/main.c] [include/options.h]
	Added -config option.

Tue Sep 15 18:22:26 1998  Petter Reinholdtsen <pere@td.org.uit.no>

	* [documentation/Makefile.in]
	Make sure directory exists before installing into it.

Tue Sep 15 01:47:33 1998  Pablo Saratxaga <pablo.sarachaga@ping.be>

	* [ole/nls/*] [ole/ole2nls.c] [include/winnls.h]
	Fixed a few errors and completed some NLS files.

Mon Sep 14 01:23:45 1998  Joseph Pranevich <knight@baltimore.wwaves.com>

	* [include/miscemu.h] [msdos/interrupts.c]
	Removed a compilation warning, added INT 25 to the list of interrupts
	callable from DOS applications, added a debug message when unsupported
	interrupts are used.

Sun Sep 13 19:55:22 1998  Lawson Whitney <lawson_whitney@juno.com>

	* [if1632/relay.c]
	CallProcEx32W should not reverse arguments.

Sun Aug 17 21:18:12 1998  Eric Pouech  <eric.pouech@lemel.fr>

	* [multimedia/midi.c] [multimedia/init.c] [multimedia/mmsys.c] 
	  [include/multimedia.h] [include/mmsystem.h] 
	  [multimedia/Makefile.in] [multimedia/midipatch.c]
	  [if1632/multimedia.spec]
	Made MIDI input and output functional on OSS capable systems.

	* [multimedia/timer.c]
	Changes to trigger callbacks at the accurate pace even when
	fake timers are used.
1998-09-27 18:28:36 +00:00

1764 lines
53 KiB
C

/*
* DOS file system functions
*
* Copyright 1993 Erik Bos
* Copyright 1996 Alexandre Julliard
*/
#include "config.h"
#include <sys/types.h>
#include <ctype.h>
#include <dirent.h>
#include <errno.h>
#include <sys/errno.h>
#include <fcntl.h>
#include <string.h>
#include <stdlib.h>
#include <sys/stat.h>
#include <sys/ioctl.h>
#include <time.h>
#include <unistd.h>
#include "windows.h"
#include "winerror.h"
#include "drive.h"
#include "file.h"
#include "heap.h"
#include "msdos.h"
#include "syslevel.h"
#include "debug.h"
/* Define the VFAT ioctl to get both short and long file names */
/* FIXME: is it possible to get this to work on other systems? */
#ifdef linux
#define VFAT_IOCTL_READDIR_BOTH _IOR('r', 1, long)
/* We want the real kernel dirent structure, not the libc one */
typedef struct
{
long d_ino;
long d_off;
unsigned short d_reclen;
char d_name[256];
} KERNEL_DIRENT;
#else /* linux */
#undef VFAT_IOCTL_READDIR_BOTH /* just in case... */
#endif /* linux */
/* Chars we don't want to see in DOS file names */
#define INVALID_DOS_CHARS "*?<>|\"+=,;[] \345"
static const DOS_DEVICE DOSFS_Devices[] =
/* name, device flags (see Int 21/AX=0x4400) */
{
{ "CON", 0xc0d3 },
{ "PRN", 0xa0c0 },
{ "NUL", 0x80c4 },
{ "AUX", 0x80c0 },
{ "LPT1", 0xa0c0 },
{ "LPT2", 0xa0c0 },
{ "LPT3", 0xa0c0 },
{ "LPT4", 0xc0d3 },
{ "COM1", 0x80c0 },
{ "COM2", 0x80c0 },
{ "COM3", 0x80c0 },
{ "COM4", 0x80c0 },
{ "SCSIMGR$", 0xc0c0 },
{ "HPSCAN", 0xc0c0 }
};
#define GET_DRIVE(path) \
(((path)[1] == ':') ? toupper((path)[0]) - 'A' : DOSFS_CurDrive)
/* DOS extended error status */
WORD DOS_ExtendedError;
BYTE DOS_ErrorClass;
BYTE DOS_ErrorAction;
BYTE DOS_ErrorLocus;
/* Directory info for DOSFS_ReadDir */
typedef struct
{
DIR *dir;
#ifdef VFAT_IOCTL_READDIR_BOTH
int fd;
char short_name[12];
KERNEL_DIRENT dirent[2];
#endif
} DOS_DIR;
/* Info structure for FindFirstFile handle */
typedef struct
{
LPSTR path;
LPSTR long_mask;
LPSTR short_mask;
BYTE attr;
int drive;
int cur_pos;
DOS_DIR *dir;
} FIND_FIRST_INFO;
/***********************************************************************
* DOSFS_ValidDOSName
*
* Return 1 if Unix file 'name' is also a valid MS-DOS name
* (i.e. contains only valid DOS chars, lower-case only, fits in 8.3 format).
* File name can be terminated by '\0', '\\' or '/'.
*/
static int DOSFS_ValidDOSName( const char *name, int ignore_case )
{
static const char invalid_chars[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" INVALID_DOS_CHARS;
const char *p = name;
const char *invalid = ignore_case ? (invalid_chars + 26) : invalid_chars;
int len = 0;
if (*p == '.')
{
/* Check for "." and ".." */
p++;
if (*p == '.') p++;
/* All other names beginning with '.' are invalid */
return (IS_END_OF_NAME(*p));
}
while (!IS_END_OF_NAME(*p))
{
if (strchr( invalid, *p )) return 0; /* Invalid char */
if (*p == '.') break; /* Start of the extension */
if (++len > 8) return 0; /* Name too long */
p++;
}
if (*p != '.') return 1; /* End of name */
p++;
if (IS_END_OF_NAME(*p)) return 0; /* Empty extension not allowed */
len = 0;
while (!IS_END_OF_NAME(*p))
{
if (strchr( invalid, *p )) return 0; /* Invalid char */
if (*p == '.') return 0; /* Second extension not allowed */
if (++len > 3) return 0; /* Extension too long */
p++;
}
return 1;
}
/***********************************************************************
* DOSFS_ToDosFCBFormat
*
* Convert a file name to DOS FCB format (8+3 chars, padded with blanks),
* expanding wild cards and converting to upper-case in the process.
* File name can be terminated by '\0', '\\' or '/'.
* Return FALSE if the name is not a valid DOS name.
* 'buffer' must be at least 12 characters long.
*/
BOOL32 DOSFS_ToDosFCBFormat( LPCSTR name, LPSTR buffer )
{
static const char invalid_chars[] = INVALID_DOS_CHARS;
const char *p = name;
int i;
/* Check for "." and ".." */
if (*p == '.')
{
p++;
strcpy( buffer, ". " );
if (*p == '.')
{
buffer[1] = '.';
p++;
}
return (!*p || (*p == '/') || (*p == '\\'));
}
for (i = 0; i < 8; i++)
{
switch(*p)
{
case '\0':
case '\\':
case '/':
case '.':
buffer[i] = ' ';
break;
case '?':
p++;
/* fall through */
case '*':
buffer[i] = '?';
break;
default:
if (strchr( invalid_chars, *p )) return FALSE;
buffer[i] = toupper(*p);
p++;
break;
}
}
if (*p == '*')
{
/* Skip all chars after wildcard up to first dot */
while (*p && (*p != '/') && (*p != '\\') && (*p != '.')) p++;
}
else
{
/* Check if name too long */
if (*p && (*p != '/') && (*p != '\\') && (*p != '.')) return FALSE;
}
if (*p == '.') p++; /* Skip dot */
for (i = 8; i < 11; i++)
{
switch(*p)
{
case '\0':
case '\\':
case '/':
buffer[i] = ' ';
break;
case '.':
return FALSE; /* Second extension not allowed */
case '?':
p++;
/* fall through */
case '*':
buffer[i] = '?';
break;
default:
if (strchr( invalid_chars, *p )) return FALSE;
buffer[i] = toupper(*p);
p++;
break;
}
}
buffer[11] = '\0';
return TRUE;
}
/***********************************************************************
* DOSFS_ToDosDTAFormat
*
* Convert a file name from FCB to DTA format (name.ext, null-terminated)
* converting to upper-case in the process.
* File name can be terminated by '\0', '\\' or '/'.
* 'buffer' must be at least 13 characters long.
*/
static void DOSFS_ToDosDTAFormat( LPCSTR name, LPSTR buffer )
{
char *p;
memcpy( buffer, name, 8 );
for (p = buffer + 8; (p > buffer) && (p[-1] == ' '); p--);
*p++ = '.';
memcpy( p, name + 8, 3 );
for (p += 3; p[-1] == ' '; p--);
if (p[-1] == '.') p--;
*p = '\0';
}
/***********************************************************************
* DOSFS_MatchShort
*
* Check a DOS file name against a mask (both in FCB format).
*/
static int DOSFS_MatchShort( const char *mask, const char *name )
{
int i;
for (i = 11; i > 0; i--, mask++, name++)
if ((*mask != '?') && (*mask != *name)) return 0;
return 1;
}
/***********************************************************************
* DOSFS_MatchLong
*
* Check a long file name against a mask.
*/
static int DOSFS_MatchLong( const char *mask, const char *name,
int case_sensitive )
{
if (!strcmp( mask, "*.*" )) return 1;
while (*name && *mask)
{
if (*mask == '*')
{
mask++;
while (*mask == '*') mask++; /* Skip consecutive '*' */
if (!*mask) return 1;
if (case_sensitive) while (*name && (*name != *mask)) name++;
else while (*name && (toupper(*name) != toupper(*mask))) name++;
if (!*name) return 0;
}
else if (*mask != '?')
{
if (case_sensitive)
{
if (*mask != *name) return 0;
}
else if (toupper(*mask) != toupper(*name)) return 0;
}
mask++;
name++;
}
return (!*name && !*mask);
}
/***********************************************************************
* DOSFS_OpenDir
*/
static DOS_DIR *DOSFS_OpenDir( LPCSTR path )
{
DOS_DIR *dir = HeapAlloc( SystemHeap, 0, sizeof(*dir) );
if (!dir)
{
DOS_ERROR( ER_OutOfMemory, EC_OutOfResource, SA_Abort, EL_Memory );
return NULL;
}
/* Treat empty path as root directory. This simplifies path split into
directory and mask in several other places */
if (!*path) path = "/";
#ifdef VFAT_IOCTL_READDIR_BOTH
/* Check if the VFAT ioctl is supported on this directory */
if ((dir->fd = open( path, O_RDONLY )) != -1)
{
if (ioctl( dir->fd, VFAT_IOCTL_READDIR_BOTH, (long)dir->dirent ) == -1)
{
close( dir->fd );
dir->fd = -1;
}
else
{
/* Set the file pointer back at the start of the directory */
lseek( dir->fd, 0, SEEK_SET );
dir->dir = NULL;
return dir;
}
}
#endif /* VFAT_IOCTL_READDIR_BOTH */
/* Now use the standard opendir/readdir interface */
if (!(dir->dir = opendir( path )))
{
HeapFree( SystemHeap, 0, dir );
return NULL;
}
return dir;
}
/***********************************************************************
* DOSFS_CloseDir
*/
static void DOSFS_CloseDir( DOS_DIR *dir )
{
#ifdef VFAT_IOCTL_READDIR_BOTH
if (dir->fd != -1) close( dir->fd );
#endif /* VFAT_IOCTL_READDIR_BOTH */
if (dir->dir) closedir( dir->dir );
HeapFree( SystemHeap, 0, dir );
}
/***********************************************************************
* DOSFS_ReadDir
*/
static BOOL32 DOSFS_ReadDir( DOS_DIR *dir, LPCSTR *long_name,
LPCSTR *short_name )
{
struct dirent *dirent;
#ifdef VFAT_IOCTL_READDIR_BOTH
if (dir->fd != -1)
{
if (ioctl( dir->fd, VFAT_IOCTL_READDIR_BOTH, (long)dir->dirent ) != -1) {
if (!dir->dirent[0].d_reclen) return FALSE;
if (!DOSFS_ToDosFCBFormat( dir->dirent[0].d_name, dir->short_name ))
dir->short_name[0] = '\0';
*short_name = dir->short_name;
if (dir->dirent[1].d_name[0]) *long_name = dir->dirent[1].d_name;
else *long_name = dir->dirent[0].d_name;
return TRUE;
}
}
#endif /* VFAT_IOCTL_READDIR_BOTH */
if (!(dirent = readdir( dir->dir ))) return FALSE;
*long_name = dirent->d_name;
*short_name = NULL;
return TRUE;
}
/***********************************************************************
* DOSFS_Hash
*
* Transform a Unix file name into a hashed DOS name. If the name is a valid
* DOS name, it is converted to upper-case; otherwise it is replaced by a
* hashed version that fits in 8.3 format.
* File name can be terminated by '\0', '\\' or '/'.
* 'buffer' must be at least 13 characters long.
*/
static void DOSFS_Hash( LPCSTR name, LPSTR buffer, BOOL32 dir_format,
BOOL32 ignore_case )
{
static const char invalid_chars[] = INVALID_DOS_CHARS "~.";
static const char hash_chars[32] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ012345";
const char *p, *ext;
char *dst;
unsigned short hash;
int i;
if (dir_format) strcpy( buffer, " " );
if (DOSFS_ValidDOSName( name, ignore_case ))
{
/* Check for '.' and '..' */
if (*name == '.')
{
buffer[0] = '.';
if (!dir_format) buffer[1] = buffer[2] = '\0';
if (name[1] == '.') buffer[1] = '.';
return;
}
/* Simply copy the name, converting to uppercase */
for (dst = buffer; !IS_END_OF_NAME(*name) && (*name != '.'); name++)
*dst++ = toupper(*name);
if (*name == '.')
{
if (dir_format) dst = buffer + 8;
else *dst++ = '.';
for (name++; !IS_END_OF_NAME(*name); name++)
*dst++ = toupper(*name);
}
if (!dir_format) *dst = '\0';
return;
}
/* Compute the hash code of the file name */
/* If you know something about hash functions, feel free to */
/* insert a better algorithm here... */
if (ignore_case)
{
for (p = name, hash = 0xbeef; !IS_END_OF_NAME(p[1]); p++)
hash = (hash<<3) ^ (hash>>5) ^ tolower(*p) ^ (tolower(p[1]) << 8);
hash = (hash<<3) ^ (hash>>5) ^ tolower(*p); /* Last character*/
}
else
{
for (p = name, hash = 0xbeef; !IS_END_OF_NAME(p[1]); p++)
hash = (hash << 3) ^ (hash >> 5) ^ *p ^ (p[1] << 8);
hash = (hash << 3) ^ (hash >> 5) ^ *p; /* Last character */
}
/* Find last dot for start of the extension */
for (p = name+1, ext = NULL; !IS_END_OF_NAME(*p); p++)
if (*p == '.') ext = p;
if (ext && IS_END_OF_NAME(ext[1]))
ext = NULL; /* Empty extension ignored */
/* Copy first 4 chars, replacing invalid chars with '_' */
for (i = 4, p = name, dst = buffer; i > 0; i--, p++)
{
if (IS_END_OF_NAME(*p) || (p == ext)) break;
*dst++ = strchr( invalid_chars, *p ) ? '_' : toupper(*p);
}
/* Pad to 5 chars with '~' */
while (i-- >= 0) *dst++ = '~';
/* Insert hash code converted to 3 ASCII chars */
*dst++ = hash_chars[(hash >> 10) & 0x1f];
*dst++ = hash_chars[(hash >> 5) & 0x1f];
*dst++ = hash_chars[hash & 0x1f];
/* Copy the first 3 chars of the extension (if any) */
if (ext)
{
if (!dir_format) *dst++ = '.';
for (i = 3, ext++; (i > 0) && !IS_END_OF_NAME(*ext); i--, ext++)
*dst++ = strchr( invalid_chars, *ext ) ? '_' : toupper(*ext);
}
if (!dir_format) *dst = '\0';
}
/***********************************************************************
* DOSFS_FindUnixName
*
* Find the Unix file name in a given directory that corresponds to
* a file name (either in Unix or DOS format).
* File name can be terminated by '\0', '\\' or '/'.
* Return TRUE if OK, FALSE if no file name matches.
*
* 'long_buf' must be at least 'long_len' characters long. If the long name
* turns out to be larger than that, the function returns FALSE.
* 'short_buf' must be at least 13 characters long.
*/
BOOL32 DOSFS_FindUnixName( LPCSTR path, LPCSTR name, LPSTR long_buf,
INT32 long_len, LPSTR short_buf, BOOL32 ignore_case)
{
DOS_DIR *dir;
LPCSTR long_name, short_name;
char dos_name[12], tmp_buf[13];
BOOL32 ret;
const char *p = strchr( name, '/' );
int len = p ? (int)(p - name) : strlen(name);
if ((p = strchr( name, '\\' ))) len = MIN( (int)(p - name), len );
if (long_len < len + 1) return FALSE;
TRACE(dosfs, "%s,%s\n", path, name );
if (!DOSFS_ToDosFCBFormat( name, dos_name )) dos_name[0] = '\0';
if (!(dir = DOSFS_OpenDir( path )))
{
WARN(dosfs, "(%s,%s): can't open dir: %s\n",
path, name, strerror(errno) );
return FALSE;
}
while ((ret = DOSFS_ReadDir( dir, &long_name, &short_name )))
{
/* Check against Unix name */
if (len == strlen(long_name))
{
if (!ignore_case)
{
if (!lstrncmp32A( long_name, name, len )) break;
}
else
{
if (!lstrncmpi32A( long_name, name, len )) break;
}
}
if (dos_name[0])
{
/* Check against hashed DOS name */
if (!short_name)
{
DOSFS_Hash( long_name, tmp_buf, TRUE, ignore_case );
short_name = tmp_buf;
}
if (!strcmp( dos_name, short_name )) break;
}
}
if (ret)
{
if (long_buf) strcpy( long_buf, long_name );
if (short_buf)
{
if (short_name)
DOSFS_ToDosDTAFormat( short_name, short_buf );
else
DOSFS_Hash( long_name, short_buf, FALSE, ignore_case );
}
TRACE(dosfs, "(%s,%s) -> %s (%s)\n",
path, name, long_name, short_buf ? short_buf : "***");
}
else
WARN(dosfs, "'%s' not found in '%s'\n", name, path);
DOSFS_CloseDir( dir );
return ret;
}
/***********************************************************************
* DOSFS_GetDevice
*
* Check if a DOS file name represents a DOS device and return the device.
*/
const DOS_DEVICE *DOSFS_GetDevice( const char *name )
{
int i;
const char *p;
if (!name) return NULL; /* if FILE_DupUnixHandle was used */
if (name[0] && (name[1] == ':')) name += 2;
if ((p = strrchr( name, '/' ))) name = p + 1;
if ((p = strrchr( name, '\\' ))) name = p + 1;
for (i = 0; i < sizeof(DOSFS_Devices)/sizeof(DOSFS_Devices[0]); i++)
{
const char *dev = DOSFS_Devices[i].name;
if (!lstrncmpi32A( dev, name, strlen(dev) ))
{
p = name + strlen( dev );
if (!*p || (*p == '.')) return &DOSFS_Devices[i];
}
}
return NULL;
}
/***********************************************************************
* DOSFS_OpenDevice
*
* Open a DOS device. This might not map 1:1 into the UNIX device concept.
*/
HFILE32 DOSFS_OpenDevice( const char *name, int unixmode )
{
int i;
const char *p;
FILE_OBJECT *file;
HFILE32 handle;
if (!name) return (HFILE32)NULL; /* if FILE_DupUnixHandle was used */
if (name[0] && (name[1] == ':')) name += 2;
if ((p = strrchr( name, '/' ))) name = p + 1;
if ((p = strrchr( name, '\\' ))) name = p + 1;
for (i = 0; i < sizeof(DOSFS_Devices)/sizeof(DOSFS_Devices[0]); i++)
{
const char *dev = DOSFS_Devices[i].name;
if (!lstrncmpi32A( dev, name, strlen(dev) ))
{
p = name + strlen( dev );
if (!*p || (*p == '.')) {
/* got it */
if (!strcmp(DOSFS_Devices[i].name,"NUL"))
return FILE_OpenUnixFile("/dev/null",unixmode);
if (!strcmp(DOSFS_Devices[i].name,"CON")) {
HFILE32 to_dup;
HFILE32 handle;
switch (unixmode) {
case O_RDONLY:
to_dup = GetStdHandle( STD_INPUT_HANDLE );
break;
case O_WRONLY:
to_dup = GetStdHandle( STD_OUTPUT_HANDLE );
break;
default:
FIXME(dosfs,"can't open CON read/write\n");
return HFILE_ERROR32;
break;
}
if (!DuplicateHandle( GetCurrentProcess(), to_dup, GetCurrentProcess(),
&handle, 0, FALSE, DUPLICATE_SAME_ACCESS ))
handle = HFILE_ERROR32;
return handle;
}
if (!strcmp(DOSFS_Devices[i].name,"SCSIMGR$")) {
if ((handle = FILE_Alloc( &file )) == INVALID_HANDLE_VALUE32)
return HFILE_ERROR32;
else {
file->unix_name = HEAP_strdupA( SystemHeap, 0, name );
return handle;
}
}
if (!strcmp(DOSFS_Devices[i].name,"HPSCAN")) {
if ((handle = FILE_Alloc( &file )) == INVALID_HANDLE_VALUE32)
return HFILE_ERROR32;
else {
file->unix_name = HEAP_strdupA( SystemHeap, 0, name );
return handle;
}
}
FIXME(dosfs,"device open %s not supported (yet)\n",DOSFS_Devices[i].name);
return HFILE_ERROR32;
}
}
}
return HFILE_ERROR32;
}
/***********************************************************************
* DOSFS_GetPathDrive
*
* Get the drive specified by a given path name (DOS or Unix format).
*/
static int DOSFS_GetPathDrive( const char **name )
{
int drive;
const char *p = *name;
if (*p && (p[1] == ':'))
{
drive = toupper(*p) - 'A';
*name += 2;
}
else if (*p == '/') /* Absolute Unix path? */
{
if ((drive = DRIVE_FindDriveRoot( name )) == -1)
{
MSG("Warning: %s not accessible from a DOS drive\n", *name );
/* Assume it really was a DOS name */
drive = DRIVE_GetCurrentDrive();
}
}
else drive = DRIVE_GetCurrentDrive();
if (!DRIVE_IsValid(drive))
{
DOS_ERROR( ER_InvalidDrive, EC_MediaError, SA_Abort, EL_Disk );
return -1;
}
return drive;
}
/***********************************************************************
* DOSFS_GetFullName
*
* Convert a file name (DOS or mixed DOS/Unix format) to a valid
* Unix name / short DOS name pair.
* Return FALSE if one of the path components does not exist. The last path
* component is only checked if 'check_last' is non-zero.
* The buffers pointed to by 'long_buf' and 'short_buf' must be
* at least MAX_PATHNAME_LEN long.
*/
BOOL32 DOSFS_GetFullName( LPCSTR name, BOOL32 check_last, DOS_FULL_NAME *full )
{
BOOL32 found;
UINT32 flags;
char *p_l, *p_s, *root;
TRACE(dosfs, "%s (last=%d)\n",
name, check_last );
if ((full->drive = DOSFS_GetPathDrive( &name )) == -1) return FALSE;
flags = DRIVE_GetFlags( full->drive );
lstrcpyn32A( full->long_name, DRIVE_GetRoot( full->drive ),
sizeof(full->long_name) );
if (full->long_name[1]) root = full->long_name + strlen(full->long_name);
else root = full->long_name; /* root directory */
strcpy( full->short_name, "A:\\" );
full->short_name[0] += full->drive;
if ((*name == '\\') || (*name == '/')) /* Absolute path */
{
while ((*name == '\\') || (*name == '/')) name++;
}
else /* Relative path */
{
lstrcpyn32A( root + 1, DRIVE_GetUnixCwd( full->drive ),
sizeof(full->long_name) - (root - full->long_name) - 1 );
if (root[1]) *root = '/';
lstrcpyn32A( full->short_name + 3, DRIVE_GetDosCwd( full->drive ),
sizeof(full->short_name) - 3 );
}
p_l = full->long_name[1] ? full->long_name + strlen(full->long_name)
: full->long_name;
p_s = full->short_name[3] ? full->short_name + strlen(full->short_name)
: full->short_name + 2;
found = TRUE;
while (*name && found)
{
/* Check for '.' and '..' */
if (*name == '.')
{
if (IS_END_OF_NAME(name[1]))
{
name++;
while ((*name == '\\') || (*name == '/')) name++;
continue;
}
else if ((name[1] == '.') && IS_END_OF_NAME(name[2]))
{
name += 2;
while ((*name == '\\') || (*name == '/')) name++;
while ((p_l > root) && (*p_l != '/')) p_l--;
while ((p_s > full->short_name + 2) && (*p_s != '\\')) p_s--;
*p_l = *p_s = '\0'; /* Remove trailing separator */
continue;
}
}
/* Make sure buffers are large enough */
if ((p_s >= full->short_name + sizeof(full->short_name) - 14) ||
(p_l >= full->long_name + sizeof(full->long_name) - 1))
{
DOS_ERROR( ER_PathNotFound, EC_NotFound, SA_Abort, EL_Disk );
return FALSE;
}
/* Get the long and short name matching the file name */
if ((found = DOSFS_FindUnixName( full->long_name, name, p_l + 1,
sizeof(full->long_name) - (p_l - full->long_name) - 1,
p_s + 1, !(flags & DRIVE_CASE_SENSITIVE) )))
{
*p_l++ = '/';
p_l += strlen(p_l);
*p_s++ = '\\';
p_s += strlen(p_s);
while (!IS_END_OF_NAME(*name)) name++;
}
else if (!check_last)
{
*p_l++ = '/';
*p_s++ = '\\';
while (!IS_END_OF_NAME(*name) &&
(p_s < full->short_name + sizeof(full->short_name) - 1) &&
(p_l < full->long_name + sizeof(full->long_name) - 1))
{
*p_s++ = tolower(*name);
/* If the drive is case-sensitive we want to create new */
/* files in lower-case otherwise we can't reopen them */
/* under the same short name. */
if (flags & DRIVE_CASE_SENSITIVE) *p_l++ = tolower(*name);
else *p_l++ = *name;
name++;
}
*p_l = *p_s = '\0';
}
while ((*name == '\\') || (*name == '/')) name++;
}
if (!found)
{
if (check_last)
{
DOS_ERROR( ER_FileNotFound, EC_NotFound, SA_Abort, EL_Disk );
return FALSE;
}
if (*name) /* Not last */
{
DOS_ERROR( ER_PathNotFound, EC_NotFound, SA_Abort, EL_Disk );
return FALSE;
}
}
if (!full->long_name[0]) strcpy( full->long_name, "/" );
if (!full->short_name[2]) strcpy( full->short_name + 2, "\\" );
TRACE(dosfs, "returning %s = %s\n",
full->long_name, full->short_name );
return TRUE;
}
/***********************************************************************
* GetShortPathName32A (KERNEL32.271)
*/
DWORD WINAPI GetShortPathName32A( LPCSTR longpath, LPSTR shortpath,
DWORD shortlen )
{
DOS_FULL_NAME full_name;
/* FIXME: is it correct to always return a fully qualified short path? */
if (!DOSFS_GetFullName( longpath, TRUE, &full_name )) return 0;
lstrcpyn32A( shortpath, full_name.short_name, shortlen );
return strlen( full_name.short_name );
}
/***********************************************************************
* GetShortPathName32W (KERNEL32.272)
*/
DWORD WINAPI GetShortPathName32W( LPCWSTR longpath, LPWSTR shortpath,
DWORD shortlen )
{
DOS_FULL_NAME full_name;
DWORD ret = 0;
LPSTR longpathA = HEAP_strdupWtoA( GetProcessHeap(), 0, longpath );
/* FIXME: is it correct to always return a fully qualified short path? */
if (DOSFS_GetFullName( longpathA, TRUE, &full_name ))
{
ret = strlen( full_name.short_name );
lstrcpynAtoW( shortpath, full_name.short_name, shortlen );
}
HeapFree( GetProcessHeap(), 0, longpathA );
return ret;
}
/***********************************************************************
* GetLongPathName32A (KERNEL32.xxx)
*/
DWORD WINAPI GetLongPathName32A( LPCSTR shortpath, LPSTR longpath,
DWORD longlen )
{
DOS_FULL_NAME full_name;
/* FIXME: Is it correct to return a UNIX style path here? */
if (!DOSFS_GetFullName( shortpath, TRUE, &full_name )) return 0;
lstrcpyn32A( longpath, full_name.long_name, longlen );
return strlen( full_name.long_name );
}
/***********************************************************************
* GetLongPathName32W (KERNEL32.269)
*/
DWORD WINAPI GetLongPathName32W( LPCWSTR shortpath, LPWSTR longpath,
DWORD longlen )
{
DOS_FULL_NAME full_name;
DWORD ret = 0;
LPSTR shortpathA = HEAP_strdupWtoA( GetProcessHeap(), 0, shortpath );
/* FIXME: is it correct to always return a fully qualified short path? */
if (DOSFS_GetFullName( shortpathA, TRUE, &full_name ))
{
ret = strlen( full_name.short_name );
lstrcpynAtoW( longpath, full_name.long_name, longlen );
}
HeapFree( GetProcessHeap(), 0, shortpathA );
return ret;
}
/***********************************************************************
* DOSFS_DoGetFullPathName
*
* Implementation of GetFullPathName32A/W.
*/
static DWORD DOSFS_DoGetFullPathName( LPCSTR name, DWORD len, LPSTR result,
BOOL32 unicode )
{
char buffer[MAX_PATHNAME_LEN];
int drive;
char *p;
TRACE(dosfs, "converting %s\n", name );
if (!name || !result) return 0;
if ((drive = DOSFS_GetPathDrive( &name )) == -1) return 0;
p = buffer;
*p++ = 'A' + drive;
*p++ = ':';
if (IS_END_OF_NAME(*name) && (*name)) /* Absolute path */
{
while ((*name == '\\') || (*name == '/')) name++;
}
else /* Relative path or empty path */
{
*p++ = '\\';
lstrcpyn32A( p, DRIVE_GetDosCwd(drive), sizeof(buffer) - 3 );
if (*p) p += strlen(p); else p--;
}
if (!*name) /* empty path */
*p++ = '\\';
*p = '\0';
while (*name)
{
if (*name == '.')
{
if (IS_END_OF_NAME(name[1]))
{
name++;
while ((*name == '\\') || (*name == '/')) name++;
continue;
}
else if ((name[1] == '.') && IS_END_OF_NAME(name[2]))
{
name += 2;
while ((*name == '\\') || (*name == '/')) name++;
while ((p > buffer + 2) && (*p != '\\')) p--;
*p = '\0'; /* Remove trailing separator */
continue;
}
}
if (p >= buffer + sizeof(buffer) - 1)
{
DOS_ERROR( ER_PathNotFound, EC_NotFound, SA_Abort, EL_Disk );
return 0;
}
*p++ = '\\';
while (!IS_END_OF_NAME(*name) && (p < buffer + sizeof(buffer) - 1))
*p++ = *name++;
*p = '\0';
while ((*name == '\\') || (*name == '/')) name++;
}
if (!buffer[2])
{
buffer[2] = '\\';
buffer[3] = '\0';
}
if (!(DRIVE_GetFlags(drive) & DRIVE_CASE_PRESERVING))
CharUpper32A( buffer );
if (unicode) lstrcpynAtoW( (LPWSTR)result, buffer, len );
else lstrcpyn32A( result, buffer, len );
TRACE(dosfs, "returning %s\n", buffer );
return strlen(buffer);
}
/***********************************************************************
* GetFullPathName32A (KERNEL32.272)
*/
DWORD WINAPI GetFullPathName32A( LPCSTR name, DWORD len, LPSTR buffer,
LPSTR *lastpart )
{
DWORD ret = DOSFS_DoGetFullPathName( name, len, buffer, FALSE );
if (ret && lastpart)
{
LPSTR p = buffer + strlen(buffer);
while ((p > buffer + 2) && (*p != '\\')) p--;
*lastpart = p + 1;
}
return ret;
}
/***********************************************************************
* GetFullPathName32W (KERNEL32.273)
*/
DWORD WINAPI GetFullPathName32W( LPCWSTR name, DWORD len, LPWSTR buffer,
LPWSTR *lastpart )
{
LPSTR nameA = HEAP_strdupWtoA( GetProcessHeap(), 0, name );
DWORD ret = DOSFS_DoGetFullPathName( nameA, len, (LPSTR)buffer, TRUE );
HeapFree( GetProcessHeap(), 0, nameA );
if (ret && lastpart)
{
LPWSTR p = buffer + lstrlen32W(buffer);
while ((p > buffer + 2) && (*p != '\\')) p--;
*lastpart = p + 1;
}
return ret;
}
/***********************************************************************
* DOSFS_FindNextEx
*/
static int DOSFS_FindNextEx( FIND_FIRST_INFO *info, WIN32_FIND_DATA32A *entry )
{
BYTE attr = info->attr | FA_UNUSED | FA_ARCHIVE | FA_RDONLY;
UINT32 flags = DRIVE_GetFlags( info->drive );
char *p, buffer[MAX_PATHNAME_LEN];
const char *drive_path;
int drive_root;
LPCSTR long_name, short_name;
BY_HANDLE_FILE_INFORMATION fileinfo;
char dos_name[13];
if ((info->attr & ~(FA_UNUSED | FA_ARCHIVE | FA_RDONLY)) == FA_LABEL)
{
if (info->cur_pos) return 0;
entry->dwFileAttributes = FILE_ATTRIBUTE_LABEL;
DOSFS_UnixTimeToFileTime( (time_t)0, &entry->ftCreationTime, 0 );
DOSFS_UnixTimeToFileTime( (time_t)0, &entry->ftLastAccessTime, 0 );
DOSFS_UnixTimeToFileTime( (time_t)0, &entry->ftLastWriteTime, 0 );
entry->nFileSizeHigh = 0;
entry->nFileSizeLow = 0;
entry->dwReserved0 = 0;
entry->dwReserved1 = 0;
DOSFS_ToDosDTAFormat( DRIVE_GetLabel( info->drive ), entry->cFileName );
strcpy( entry->cAlternateFileName, entry->cFileName );
info->cur_pos++;
return 1;
}
drive_path = info->path + strlen(DRIVE_GetRoot( info->drive ));
while ((*drive_path == '/') || (*drive_path == '\\')) drive_path++;
drive_root = !*drive_path;
lstrcpyn32A( buffer, info->path, sizeof(buffer) - 1 );
strcat( buffer, "/" );
p = buffer + strlen(buffer);
while (DOSFS_ReadDir( info->dir, &long_name, &short_name ))
{
info->cur_pos++;
/* Don't return '.' and '..' in the root of the drive */
if (drive_root && (long_name[0] == '.') &&
(!long_name[1] || ((long_name[1] == '.') && !long_name[2])))
continue;
/* Check the long mask */
if (info->long_mask)
{
if (!DOSFS_MatchLong( info->long_mask, long_name,
flags & DRIVE_CASE_SENSITIVE )) continue;
}
/* Check the short mask */
if (info->short_mask)
{
if (!short_name)
{
DOSFS_Hash( long_name, dos_name, TRUE,
!(flags & DRIVE_CASE_SENSITIVE) );
short_name = dos_name;
}
if (!DOSFS_MatchShort( info->short_mask, short_name )) continue;
}
/* Check the file attributes */
lstrcpyn32A( p, long_name, sizeof(buffer) - (int)(p - buffer) );
if (!FILE_Stat( buffer, &fileinfo ))
{
WARN(dosfs, "can't stat %s\n", buffer);
continue;
}
if (fileinfo.dwFileAttributes & ~attr) continue;
/* We now have a matching entry; fill the result and return */
entry->dwFileAttributes = fileinfo.dwFileAttributes;
entry->ftCreationTime = fileinfo.ftCreationTime;
entry->ftLastAccessTime = fileinfo.ftLastAccessTime;
entry->ftLastWriteTime = fileinfo.ftLastWriteTime;
entry->nFileSizeHigh = fileinfo.nFileSizeHigh;
entry->nFileSizeLow = fileinfo.nFileSizeLow;
if (short_name)
DOSFS_ToDosDTAFormat( short_name, entry->cAlternateFileName );
else
DOSFS_Hash( long_name, entry->cAlternateFileName, FALSE,
!(flags & DRIVE_CASE_SENSITIVE) );
lstrcpyn32A( entry->cFileName, long_name, sizeof(entry->cFileName) );
if (!(flags & DRIVE_CASE_PRESERVING)) CharLower32A( entry->cFileName );
TRACE(dosfs, "returning %s (%s) %02lx %ld\n",
entry->cFileName, entry->cAlternateFileName,
entry->dwFileAttributes, entry->nFileSizeLow );
return 1;
}
return 0; /* End of directory */
}
/***********************************************************************
* DOSFS_FindNext
*
* Find the next matching file. Return the number of entries read to find
* the matching one, or 0 if no more entries.
* 'short_mask' is the 8.3 mask (in FCB format), 'long_mask' is the long
* file name mask. Either or both can be NULL.
*
* NOTE: This is supposed to be only called by the int21 emulation
* routines. Thus, we should own the Win16Mutex anyway.
* Nevertheless, we explicitly enter it to ensure the static
* directory cache is protected.
*/
int DOSFS_FindNext( const char *path, const char *short_mask,
const char *long_mask, int drive, BYTE attr,
int skip, WIN32_FIND_DATA32A *entry )
{
static FIND_FIRST_INFO info = { NULL };
LPCSTR short_name, long_name;
int count;
SYSLEVEL_EnterWin16Lock();
/* Check the cached directory */
if (!(info.dir && info.path == path && info.short_mask == short_mask
&& info.long_mask == long_mask && info.drive == drive
&& info.attr == attr && info.cur_pos <= skip))
{
/* Not in the cache, open it anew */
if (info.dir) DOSFS_CloseDir( info.dir );
info.path = (LPSTR)path;
info.long_mask = (LPSTR)long_mask;
info.short_mask = (LPSTR)short_mask;
info.attr = attr;
info.drive = drive;
info.cur_pos = 0;
info.dir = DOSFS_OpenDir( info.path );
}
/* Skip to desired position */
while (info.cur_pos < skip)
if (info.dir && DOSFS_ReadDir( info.dir, &long_name, &short_name ))
info.cur_pos++;
else
break;
if (info.dir && info.cur_pos == skip && DOSFS_FindNextEx( &info, entry ))
count = info.cur_pos - skip;
else
count = 0;
if (!count)
{
if (info.dir) DOSFS_CloseDir( info.dir );
memset( &info, '\0', sizeof(info) );
}
SYSLEVEL_LeaveWin16Lock();
return count;
}
/*************************************************************************
* FindFirstFile16 (KERNEL.413)
*/
HANDLE16 WINAPI FindFirstFile16( LPCSTR path, WIN32_FIND_DATA32A *data )
{
DOS_FULL_NAME full_name;
HGLOBAL16 handle;
FIND_FIRST_INFO *info;
if (!path) return 0;
if (!DOSFS_GetFullName( path, FALSE, &full_name ))
return INVALID_HANDLE_VALUE16;
if (!(handle = GlobalAlloc16( GMEM_MOVEABLE, sizeof(FIND_FIRST_INFO) )))
return INVALID_HANDLE_VALUE16;
info = (FIND_FIRST_INFO *)GlobalLock16( handle );
info->path = HEAP_strdupA( SystemHeap, 0, full_name.long_name );
info->long_mask = strrchr( info->path, '/' );
*(info->long_mask++) = '\0';
info->short_mask = NULL;
info->attr = 0xff;
if (path[0] && (path[1] == ':')) info->drive = toupper(*path) - 'A';
else info->drive = DRIVE_GetCurrentDrive();
info->cur_pos = 0;
info->dir = DOSFS_OpenDir( info->path );
GlobalUnlock16( handle );
if (!FindNextFile16( handle, data ))
{
FindClose16( handle );
DOS_ERROR( ER_NoMoreFiles, EC_MediaError, SA_Abort, EL_Disk );
return INVALID_HANDLE_VALUE16;
}
return handle;
}
/*************************************************************************
* FindFirstFile32A (KERNEL32.123)
*/
HANDLE32 WINAPI FindFirstFile32A( LPCSTR path, WIN32_FIND_DATA32A *data )
{
HANDLE32 handle = FindFirstFile16( path, data );
if (handle == INVALID_HANDLE_VALUE16) return INVALID_HANDLE_VALUE32;
return handle;
}
/*************************************************************************
* FindFirstFile32W (KERNEL32.124)
*/
HANDLE32 WINAPI FindFirstFile32W( LPCWSTR path, WIN32_FIND_DATA32W *data )
{
WIN32_FIND_DATA32A dataA;
LPSTR pathA = HEAP_strdupWtoA( GetProcessHeap(), 0, path );
HANDLE32 handle = FindFirstFile32A( pathA, &dataA );
HeapFree( GetProcessHeap(), 0, pathA );
if (handle != INVALID_HANDLE_VALUE32)
{
data->dwFileAttributes = dataA.dwFileAttributes;
data->ftCreationTime = dataA.ftCreationTime;
data->ftLastAccessTime = dataA.ftLastAccessTime;
data->ftLastWriteTime = dataA.ftLastWriteTime;
data->nFileSizeHigh = dataA.nFileSizeHigh;
data->nFileSizeLow = dataA.nFileSizeLow;
lstrcpyAtoW( data->cFileName, dataA.cFileName );
lstrcpyAtoW( data->cAlternateFileName, dataA.cAlternateFileName );
}
return handle;
}
/*************************************************************************
* FindNextFile16 (KERNEL.414)
*/
BOOL16 WINAPI FindNextFile16( HANDLE16 handle, WIN32_FIND_DATA32A *data )
{
FIND_FIRST_INFO *info;
if (!(info = (FIND_FIRST_INFO *)GlobalLock16( handle )))
{
DOS_ERROR( ER_InvalidHandle, EC_ProgramError, SA_Abort, EL_Disk );
return FALSE;
}
GlobalUnlock16( handle );
if (!info->path || !info->dir)
{
DOS_ERROR( ER_NoMoreFiles, EC_MediaError, SA_Abort, EL_Disk );
return FALSE;
}
if (!DOSFS_FindNextEx( info, data ))
{
DOSFS_CloseDir( info->dir ); info->dir = NULL;
HeapFree( SystemHeap, 0, info->path );
info->path = info->long_mask = NULL;
DOS_ERROR( ER_NoMoreFiles, EC_MediaError, SA_Abort, EL_Disk );
return FALSE;
}
return TRUE;
}
/*************************************************************************
* FindNextFile32A (KERNEL32.126)
*/
BOOL32 WINAPI FindNextFile32A( HANDLE32 handle, WIN32_FIND_DATA32A *data )
{
return FindNextFile16( handle, data );
}
/*************************************************************************
* FindNextFile32W (KERNEL32.127)
*/
BOOL32 WINAPI FindNextFile32W( HANDLE32 handle, WIN32_FIND_DATA32W *data )
{
WIN32_FIND_DATA32A dataA;
if (!FindNextFile32A( handle, &dataA )) return FALSE;
data->dwFileAttributes = dataA.dwFileAttributes;
data->ftCreationTime = dataA.ftCreationTime;
data->ftLastAccessTime = dataA.ftLastAccessTime;
data->ftLastWriteTime = dataA.ftLastWriteTime;
data->nFileSizeHigh = dataA.nFileSizeHigh;
data->nFileSizeLow = dataA.nFileSizeLow;
lstrcpyAtoW( data->cFileName, dataA.cFileName );
lstrcpyAtoW( data->cAlternateFileName, dataA.cAlternateFileName );
return TRUE;
}
/*************************************************************************
* FindClose16 (KERNEL.415)
*/
BOOL16 WINAPI FindClose16( HANDLE16 handle )
{
FIND_FIRST_INFO *info;
if ((handle == INVALID_HANDLE_VALUE16) ||
!(info = (FIND_FIRST_INFO *)GlobalLock16( handle )))
{
DOS_ERROR( ER_InvalidHandle, EC_ProgramError, SA_Abort, EL_Disk );
return FALSE;
}
if (info->dir) DOSFS_CloseDir( info->dir );
if (info->path) HeapFree( SystemHeap, 0, info->path );
GlobalUnlock16( handle );
GlobalFree16( handle );
return TRUE;
}
/*************************************************************************
* FindClose32 (KERNEL32.119)
*/
BOOL32 WINAPI FindClose32( HANDLE32 handle )
{
return FindClose16( (HANDLE16)handle );
}
/***********************************************************************
* DOSFS_UnixTimeToFileTime
*
* Convert a Unix time to FILETIME format.
* The FILETIME structure is a 64-bit value representing the number of
* 100-nanosecond intervals since January 1, 1601, 0:00.
* 'remainder' is the nonnegative number of 100-ns intervals
* corresponding to the time fraction smaller than 1 second that
* couldn't be stored in the time_t value.
*/
void DOSFS_UnixTimeToFileTime( time_t unix_time, FILETIME *filetime,
DWORD remainder )
{
/* NOTES:
CONSTANTS:
The time difference between 1 January 1601, 00:00:00 and
1 January 1970, 00:00:00 is 369 years, plus the leap years
from 1604 to 1968, excluding 1700, 1800, 1900.
This makes (1968 - 1600) / 4 - 3 = 89 leap days, and a total
of 134774 days.
Any day in that period had 24 * 60 * 60 = 86400 seconds.
The time difference is 134774 * 86400 * 10000000, which can be written
116444736000000000
27111902 * 2^32 + 3577643008
413 * 2^48 + 45534 * 2^32 + 54590 * 2^16 + 32768
If you find that these constants are buggy, please change them in all
instances in both conversion functions.
VERSIONS:
There are two versions, one of them uses long long variables and
is presumably faster but not ISO C. The other one uses standard C
data types and operations but relies on the assumption that negative
numbers are stored as 2's complement (-1 is 0xffff....). If this
assumption is violated, dates before 1970 will not convert correctly.
This should however work on any reasonable architecture where WINE
will run.
DETAILS:
Take care not to remove the casts. I have tested these functions
(in both versions) for a lot of numbers. I would be interested in
results on other compilers than GCC.
The operations have been designed to account for the possibility
of 64-bit time_t in future UNICES. Even the versions without
internal long long numbers will work if time_t only is 64 bit.
A 32-bit shift, which was necessary for that operation, turned out
not to work correctly in GCC, besides giving the warning. So I
used a double 16-bit shift instead. Numbers are in the ISO version
represented by three limbs, the most significant with 32 bit, the
other two with 16 bit each.
As the modulo-operator % is not well-defined for negative numbers,
negative divisors have been avoided in DOSFS_FileTimeToUnixTime.
There might be quicker ways to do this in C. Certainly so in
assembler.
Claus Fischer, fischer@iue.tuwien.ac.at
*/
#if (SIZEOF_LONG_LONG >= 8)
# define USE_LONG_LONG 1
#else
# define USE_LONG_LONG 0
#endif
#if USE_LONG_LONG /* gcc supports long long type */
long long int t = unix_time;
t *= 10000000;
t += 116444736000000000LL;
t += remainder;
filetime->dwLowDateTime = (UINT32)t;
filetime->dwHighDateTime = (UINT32)(t >> 32);
#else /* ISO version */
UINT32 a0; /* 16 bit, low bits */
UINT32 a1; /* 16 bit, medium bits */
UINT32 a2; /* 32 bit, high bits */
/* Copy the unix time to a2/a1/a0 */
a0 = unix_time & 0xffff;
a1 = (unix_time >> 16) & 0xffff;
/* This is obsolete if unix_time is only 32 bits, but it does not hurt.
Do not replace this by >> 32, it gives a compiler warning and it does
not work. */
a2 = (unix_time >= 0 ? (unix_time >> 16) >> 16 :
~((~unix_time >> 16) >> 16));
/* Multiply a by 10000000 (a = a2/a1/a0)
Split the factor into 10000 * 1000 which are both less than 0xffff. */
a0 *= 10000;
a1 = a1 * 10000 + (a0 >> 16);
a2 = a2 * 10000 + (a1 >> 16);
a0 &= 0xffff;
a1 &= 0xffff;
a0 *= 1000;
a1 = a1 * 1000 + (a0 >> 16);
a2 = a2 * 1000 + (a1 >> 16);
a0 &= 0xffff;
a1 &= 0xffff;
/* Add the time difference and the remainder */
a0 += 32768 + (remainder & 0xffff);
a1 += 54590 + (remainder >> 16 ) + (a0 >> 16);
a2 += 27111902 + (a1 >> 16);
a0 &= 0xffff;
a1 &= 0xffff;
/* Set filetime */
filetime->dwLowDateTime = (a1 << 16) + a0;
filetime->dwHighDateTime = a2;
#endif
}
/***********************************************************************
* DOSFS_FileTimeToUnixTime
*
* Convert a FILETIME format to Unix time.
* If not NULL, 'remainder' contains the fractional part of the filetime,
* in the range of [0..9999999] (even if time_t is negative).
*/
time_t DOSFS_FileTimeToUnixTime( const FILETIME *filetime, DWORD *remainder )
{
/* Read the comment in the function DOSFS_UnixTimeToFileTime. */
#if USE_LONG_LONG
long long int t = filetime->dwHighDateTime;
t <<= 32;
t += (UINT32)filetime->dwLowDateTime;
t -= 116444736000000000LL;
if (t < 0)
{
if (remainder) *remainder = 9999999 - (-t - 1) % 10000000;
return -1 - ((-t - 1) / 10000000);
}
else
{
if (remainder) *remainder = t % 10000000;
return t / 10000000;
}
#else /* ISO version */
UINT32 a0; /* 16 bit, low bits */
UINT32 a1; /* 16 bit, medium bits */
UINT32 a2; /* 32 bit, high bits */
UINT32 r; /* remainder of division */
unsigned int carry; /* carry bit for subtraction */
int negative; /* whether a represents a negative value */
/* Copy the time values to a2/a1/a0 */
a2 = (UINT32)filetime->dwHighDateTime;
a1 = ((UINT32)filetime->dwLowDateTime ) >> 16;
a0 = ((UINT32)filetime->dwLowDateTime ) & 0xffff;
/* Subtract the time difference */
if (a0 >= 32768 ) a0 -= 32768 , carry = 0;
else a0 += (1 << 16) - 32768 , carry = 1;
if (a1 >= 54590 + carry) a1 -= 54590 + carry, carry = 0;
else a1 += (1 << 16) - 54590 - carry, carry = 1;
a2 -= 27111902 + carry;
/* If a is negative, replace a by (-1-a) */
negative = (a2 >= ((UINT32)1) << 31);
if (negative)
{
/* Set a to -a - 1 (a is a2/a1/a0) */
a0 = 0xffff - a0;
a1 = 0xffff - a1;
a2 = ~a2;
}
/* Divide a by 10000000 (a = a2/a1/a0), put the rest into r.
Split the divisor into 10000 * 1000 which are both less than 0xffff. */
a1 += (a2 % 10000) << 16;
a2 /= 10000;
a0 += (a1 % 10000) << 16;
a1 /= 10000;
r = a0 % 10000;
a0 /= 10000;
a1 += (a2 % 1000) << 16;
a2 /= 1000;
a0 += (a1 % 1000) << 16;
a1 /= 1000;
r += (a0 % 1000) * 10000;
a0 /= 1000;
/* If a was negative, replace a by (-1-a) and r by (9999999 - r) */
if (negative)
{
/* Set a to -a - 1 (a is a2/a1/a0) */
a0 = 0xffff - a0;
a1 = 0xffff - a1;
a2 = ~a2;
r = 9999999 - r;
}
if (remainder) *remainder = r;
/* Do not replace this by << 32, it gives a compiler warning and it does
not work. */
return ((((time_t)a2) << 16) << 16) + (a1 << 16) + a0;
#endif
}
/***********************************************************************
* DosDateTimeToFileTime (KERNEL32.76)
*/
BOOL32 WINAPI DosDateTimeToFileTime( WORD fatdate, WORD fattime, LPFILETIME ft)
{
struct tm newtm;
newtm.tm_sec = (fattime & 0x1f) * 2;
newtm.tm_min = (fattime >> 5) & 0x3f;
newtm.tm_hour = (fattime >> 11);
newtm.tm_mday = (fatdate & 0x1f);
newtm.tm_mon = ((fatdate >> 5) & 0x0f) - 1;
newtm.tm_year = (fatdate >> 9) + 80;
DOSFS_UnixTimeToFileTime( mktime( &newtm ), ft, 0 );
return TRUE;
}
/***********************************************************************
* FileTimeToDosDateTime (KERNEL32.111)
*/
BOOL32 WINAPI FileTimeToDosDateTime( const FILETIME *ft, LPWORD fatdate,
LPWORD fattime )
{
time_t unixtime = DOSFS_FileTimeToUnixTime( ft, NULL );
struct tm *tm = localtime( &unixtime );
if (fattime)
*fattime = (tm->tm_hour << 11) + (tm->tm_min << 5) + (tm->tm_sec / 2);
if (fatdate)
*fatdate = ((tm->tm_year - 80) << 9) + ((tm->tm_mon + 1) << 5)
+ tm->tm_mday;
return TRUE;
}
/***********************************************************************
* LocalFileTimeToFileTime (KERNEL32.373)
*/
BOOL32 WINAPI LocalFileTimeToFileTime( const FILETIME *localft,
LPFILETIME utcft )
{
struct tm *xtm;
DWORD remainder;
/* convert from local to UTC. Perhaps not correct. FIXME */
time_t unixtime = DOSFS_FileTimeToUnixTime( localft, &remainder );
xtm = gmtime( &unixtime );
DOSFS_UnixTimeToFileTime( mktime(xtm), utcft, remainder );
return TRUE;
}
/***********************************************************************
* FileTimeToLocalFileTime (KERNEL32.112)
*/
BOOL32 WINAPI FileTimeToLocalFileTime( const FILETIME *utcft,
LPFILETIME localft )
{
DWORD remainder;
/* convert from UTC to local. Perhaps not correct. FIXME */
time_t unixtime = DOSFS_FileTimeToUnixTime( utcft, &remainder );
#ifdef HAVE_TIMEGM
struct tm *xtm = localtime( &unixtime );
time_t localtime;
localtime = timegm(xtm);
DOSFS_UnixTimeToFileTime( localtime, localft, remainder );
#else
struct tm *xtm,*gtm;
time_t time1,time2;
xtm = localtime( &unixtime );
gtm = gmtime( &unixtime );
time1 = mktime(xtm);
time2 = mktime(gtm);
DOSFS_UnixTimeToFileTime( 2*time1-time2, localft, remainder );
#endif
return TRUE;
}
/***********************************************************************
* FileTimeToSystemTime (KERNEL32.113)
*/
BOOL32 WINAPI FileTimeToSystemTime( const FILETIME *ft, LPSYSTEMTIME syst )
{
struct tm *xtm;
DWORD remainder;
time_t xtime = DOSFS_FileTimeToUnixTime( ft, &remainder );
xtm = gmtime(&xtime);
syst->wYear = xtm->tm_year+1900;
syst->wMonth = xtm->tm_mon + 1;
syst->wDayOfWeek = xtm->tm_wday;
syst->wDay = xtm->tm_mday;
syst->wHour = xtm->tm_hour;
syst->wMinute = xtm->tm_min;
syst->wSecond = xtm->tm_sec;
syst->wMilliseconds = remainder / 10000;
return TRUE;
}
/***********************************************************************
* QueryDosDeviceA (KERNEL32.413)
*
* returns array of strings terminated by \0, terminated by \0
*/
DWORD WINAPI QueryDosDevice32A(LPCSTR devname,LPSTR target,DWORD bufsize)
{
LPSTR s;
char buffer[200];
TRACE(dosfs,"(%s,...)\n",devname?devname:"<null>");
if (!devname) {
/* return known MSDOS devices */
lstrcpy32A(buffer,"CON COM1 COM2 LPT1 NUL ");
while ((s=strchr(buffer,' ')))
*s='\0';
lstrcpyn32A(target,buffer,bufsize);
return strlen(buffer);
}
lstrcpy32A(buffer,"\\DEV\\");
lstrcat32A(buffer,devname);
if ((s=strchr(buffer,':'))) *s='\0';
lstrcpyn32A(target,buffer,bufsize);
return strlen(buffer);
}
/***********************************************************************
* QueryDosDeviceW (KERNEL32.414)
*
* returns array of strings terminated by \0, terminated by \0
*/
DWORD WINAPI QueryDosDevice32W(LPCWSTR devname,LPWSTR target,DWORD bufsize)
{
LPSTR devnameA = devname?HEAP_strdupWtoA(GetProcessHeap(),0,devname):NULL;
LPSTR targetA = (LPSTR)HEAP_xalloc(GetProcessHeap(),0,bufsize);
DWORD ret = QueryDosDevice32A(devnameA,targetA,bufsize);
lstrcpynAtoW(target,targetA,bufsize);
if (devnameA) HeapFree(GetProcessHeap(),0,devnameA);
if (targetA) HeapFree(GetProcessHeap(),0,targetA);
return ret;
}
/***********************************************************************
* SystemTimeToFileTime (KERNEL32.526)
*/
BOOL32 WINAPI SystemTimeToFileTime( const SYSTEMTIME *syst, LPFILETIME ft )
{
#ifdef HAVE_TIMEGM
struct tm xtm;
time_t utctime;
#else
struct tm xtm,*local_tm,*utc_tm;
time_t localtim,utctime;
#endif
xtm.tm_year = syst->wYear-1900;
xtm.tm_mon = syst->wMonth - 1;
xtm.tm_wday = syst->wDayOfWeek;
xtm.tm_mday = syst->wDay;
xtm.tm_hour = syst->wHour;
xtm.tm_min = syst->wMinute;
xtm.tm_sec = syst->wSecond; /* this is UTC */
xtm.tm_isdst = -1;
#ifdef HAVE_TIMEGM
utctime = timegm(&xtm);
DOSFS_UnixTimeToFileTime( utctime, ft,
syst->wMilliseconds * 10000 );
#else
localtim = mktime(&xtm); /* now we've got local time */
local_tm = localtime(&localtim);
utc_tm = gmtime(&localtim);
utctime = mktime(utc_tm);
DOSFS_UnixTimeToFileTime( 2*localtim -utctime, ft,
syst->wMilliseconds * 10000 );
#endif
return TRUE;
}