2 * DOS interrupt 21h handler
4 * Copyright 1993, 1994 Erik Bos
5 * Copyright 1996 Alexandre Julliard
6 * Copyright 1997 Andreas Mohr
7 * Copyright 1998 Uwe Bonnes
8 * Copyright 1998, 1999 Ove Kaaven
9 * Copyright 2003 Thomas Mertes
11 * This library is free software; you can redistribute it and/or
12 * modify it under the terms of the GNU Lesser General Public
13 * License as published by the Free Software Foundation; either
14 * version 2.1 of the License, or (at your option) any later version.
16 * This library is distributed in the hope that it will be useful,
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
19 * Lesser General Public License for more details.
21 * You should have received a copy of the GNU Lesser General Public
22 * License along with this library; if not, write to the Free Software
23 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
27 #include "wine/port.h"
31 #ifdef HAVE_SYS_STAT_H
32 # include <sys/stat.h>
42 #include "wine/winbase16.h"
43 #include "kernel16_private.h"
47 #include "wine/unicode.h"
48 #include "wine/server.h"
49 #include "wine/debug.h"
50 #include "wine/exception.h"
52 BOOL WINAPI
VerifyConsoleIoHandle(HANDLE
);
55 * - Most of the file related functions are wrong. NT's kernel32
56 * doesn't maintain a per drive current directory, while DOS does.
57 * We should in fact keep track in here of those per drive
58 * directories, and use this info while dealing with partial paths
59 * (drive defined, but only relative paths). This could even be
60 * created as an array of CDS (there should be an entry for that in
65 * Forward declarations.
67 static BOOL
INT21_RenameFile( CONTEXT
*context
);
69 WINE_DEFAULT_DEBUG_CHANNEL(int21
);
75 * Extended Drive Parameter Block.
76 * This structure is compatible with standard DOS4+ DPB and
79 typedef struct _INT21_DPB
{
80 BYTE drive
; /* 00 drive number (0=A, ...) */
81 BYTE unit
; /* 01 unit number within device driver */
82 WORD sector_bytes
; /* 02 bytes per sector */
83 BYTE cluster_sectors
; /* 04 highest sector number within a cluster */
84 BYTE shift
; /* 05 shift count to convert clusters into sectors */
85 WORD num_reserved
; /* 06 reserved sectors at beginning of drive */
86 BYTE num_FAT
; /* 08 number of FATs */
87 WORD num_root_entries
; /* 09 number of root directory entries */
88 WORD first_data_sector
; /* 0b number of first sector containing user data */
89 WORD num_clusters1
; /* 0d highest cluster number (number of data clusters + 1) */
90 WORD sectors_per_FAT
; /* 0f number of sectors per FAT */
91 WORD first_dir_sector
; /* 11 sector number of first directory sector */
92 SEGPTR driver_header
; /* 13 address of device driver header */
93 BYTE media_ID
; /* 17 media ID byte */
94 BYTE access_flag
; /* 18 0x00 if disk accessed, 0xff if not */
95 SEGPTR next
; /* 19 pointer to next DPB */
96 WORD search_cluster1
; /* 1d cluster at which to start search for free space */
97 WORD free_clusters_lo
; /* 1f number of free clusters on drive or 0xffff if unknown */
98 WORD free_clusters_hi
; /* 21 hiword of clusters_free */
99 WORD mirroring_flags
; /* 23 active FAT/mirroring flags */
100 WORD info_sector
; /* 25 sector number of file system info sector or 0xffff for none */
101 WORD spare_boot_sector
; /* 27 sector number of backup boot sector or 0xffff for none */
102 DWORD first_cluster_sector
; /* 29 sector number of the first cluster */
103 DWORD num_clusters2
; /* 2d maximum cluster number */
104 DWORD fat_clusters
; /* 31 number of clusters occupied by FAT */
105 DWORD root_cluster
; /* 35 cluster number of start of root directory */
106 DWORD search_cluster2
; /* 39 cluster at which to start searching for free space */
111 * Structure for DOS data that can be accessed directly from applications.
112 * Real and protected mode pointers will be returned to this structure so
113 * the structure must be correctly packed.
115 typedef struct _INT21_HEAP
{
116 WORD uppercase_size
; /* Size of the following table in bytes */
117 BYTE uppercase_table
[128]; /* Uppercase equivalents of chars from 0x80 to 0xff. */
119 WORD lowercase_size
; /* Size of the following table in bytes */
120 BYTE lowercase_table
[256]; /* Lowercase equivalents of chars from 0x00 to 0xff. */
122 WORD collating_size
; /* Size of the following table in bytes */
123 BYTE collating_table
[256]; /* Values used to sort characters from 0x00 to 0xff. */
125 WORD filename_size
; /* Size of the following filename data in bytes */
126 BYTE filename_reserved1
; /* 0x01 for MS-DOS 3.30-6.00 */
127 BYTE filename_lowest
; /* Lowest permissible character value for filename */
128 BYTE filename_highest
; /* Highest permissible character value for filename */
129 BYTE filename_reserved2
; /* 0x00 for MS-DOS 3.30-6.00 */
130 BYTE filename_exclude_first
; /* First illegal character in permissible range */
131 BYTE filename_exclude_last
; /* Last illegal character in permissible range */
132 BYTE filename_reserved3
; /* 0x02 for MS-DOS 3.30-6.00 */
133 BYTE filename_illegal_size
; /* Number of terminators in the following table */
134 BYTE filename_illegal_table
[16]; /* Characters which terminate a filename */
136 WORD dbcs_size
; /* Number of valid ranges in the following table */
137 BYTE dbcs_table
[16]; /* Start/end bytes for N ranges and 00/00 as terminator */
139 BYTE misc_indos
; /* Interrupt 21 nesting flag */
140 WORD misc_selector
; /* Protected mode selector for INT21_HEAP */
141 INT21_DPB misc_dpb_list
[MAX_DOS_DRIVES
]; /* Drive parameter blocks for all drives */
149 CHAR file_extension
[3];
150 WORD current_block_number
;
151 WORD logical_record_size
;
153 WORD date_of_last_write
;
154 WORD time_of_last_write
;
157 WORD starting_cluster
;
158 WORD sequence_number
;
159 BYTE file_attributes
;
161 BYTE record_within_current_block
;
162 BYTE random_access_record_number
[4];
169 BYTE xfcb_file_attribute
;
173 /* DTA layout for FindFirst/FindNext */
176 BYTE drive
; /* 00 drive letter */
177 char mask
[11]; /* 01 search template */
178 BYTE search_attr
; /* 0c search attributes */
179 WORD count
; /* 0d entry count within directory */
180 WORD cluster
; /* 0f cluster of parent directory */
181 WCHAR
*fullPath
; /* 11 full path (was: reserved) */
182 BYTE fileattr
; /* 15 file attributes */
183 WORD filetime
; /* 16 file time */
184 WORD filedate
; /* 18 file date */
185 DWORD filesize
; /* 1a file size */
186 char filename
[13]; /* 1e file name + extension */
189 /* FCB layout for FindFirstFCB/FindNextFCB */
192 BYTE drive
; /* 00 drive letter */
193 char filename
[11]; /* 01 filename 8+3 format */
194 int count
; /* 0c entry count (was: reserved) */
195 WCHAR
*fullPath
; /* 10 full path (was: reserved) */
198 /* DOS directory entry for FindFirstFCB/FindNextFCB */
201 char filename
[11]; /* 00 filename 8+3 format */
202 BYTE fileattr
; /* 0b file attributes */
203 BYTE reserved
[10]; /* 0c reserved */
204 WORD filetime
; /* 16 file time */
205 WORD filedate
; /* 18 file date */
206 WORD cluster
; /* 1a file first cluster */
207 DWORD filesize
; /* 1c file size */
208 } DOS_DIRENTRY_LAYOUT
;
212 /* dos file attributes */
213 #define FA_NORMAL 0x00 /* Normal file, no attributes */
214 #define FA_RDONLY 0x01 /* Read only attribute */
215 #define FA_HIDDEN 0x02 /* Hidden file */
216 #define FA_SYSTEM 0x04 /* System file */
217 #define FA_LABEL 0x08 /* Volume label */
218 #define FA_DIRECTORY 0x10 /* Directory */
219 #define FA_ARCHIVE 0x20 /* Archive */
220 #define FA_UNUSED 0x40 /* Unused */
223 #define ER_NoNetwork 0x49
226 #define EC_OutOfResource 0x01
227 #define EC_Temporary 0x02
228 #define EC_AccessDenied 0x03
229 #define EC_InternalError 0x04
230 #define EC_HardwareFailure 0x05
231 #define EC_SystemFailure 0x06
232 #define EC_ProgramError 0x07
233 #define EC_NotFound 0x08
234 #define EC_MediaError 0x0b
235 #define EC_Exists 0x0c
236 #define EC_Unknown 0x0d
238 /* Suggested actions */
239 #define SA_Retry 0x01
240 #define SA_DelayedRetry 0x02
241 #define SA_Abort 0x04
242 #define SA_Ignore 0x06
243 #define SA_Ask4Retry 0x07
246 #define EL_Unknown 0x01
248 #define EL_Network 0x03
249 #define EL_Serial 0x04
250 #define EL_Memory 0x05
252 /* BIOS Keyboard Scancodes */
253 #define KEY_LEFT 0x4B
254 #define KEY_RIGHT 0x4D
256 #define KEY_DOWN 0x50
257 #define KEY_IC 0x52 /* insert char */
258 #define KEY_DC 0x53 /* delete char */
259 #define KEY_BACKSPACE 0x0E
260 #define KEY_HOME 0x47
262 #define KEY_NPAGE 0x49
263 #define KEY_PPAGE 0x51
272 void (*ioctl_handler
)(CONTEXT
*);
275 static void INT21_IoctlScsiMgrHandler( CONTEXT
* );
276 static void INT21_IoctlHPScanHandler( CONTEXT
* );
278 static struct magic_device magic_devices
[] =
280 { {'s','c','s','i','m','g','r','$',0}, NULL
, { { 0, 0 } }, INT21_IoctlScsiMgrHandler
},
281 { {'e','m','m','x','x','x','x','0',0}, NULL
, { { 0, 0 } }, EMS_Ioctl_Handler
},
282 { {'h','p','s','c','a','n',0}, NULL
, { { 0, 0 } }, INT21_IoctlHPScanHandler
},
286 /* Many calls translate a drive argument like this:
287 drive number (00h = default, 01h = A:, etc)
289 /******************************************************************
292 * Many calls translate a drive argument like this:
293 * drive number (00h = default, 01h = A:, etc)
295 static const char *INT21_DriveName(int drive
)
299 if (drive
<= 26) return wine_dbg_sprintf("%c:", 'A' + drive
- 1);
300 else return wine_dbg_sprintf( "<Bad drive: %d>", drive
);
305 /***********************************************************************
306 * INT21_GetCurrentDrive
308 * Return current drive using scheme (0=A:, 1=B:, 2=C:, ...) or
309 * MAX_DOS_DRIVES on error.
311 static BYTE
INT21_GetCurrentDrive(void)
313 WCHAR current_directory
[MAX_PATH
];
315 if (!GetCurrentDirectoryW( MAX_PATH
, current_directory
) ||
316 current_directory
[1] != ':')
318 TRACE( "Failed to get current drive.\n" );
319 return MAX_DOS_DRIVES
;
322 return toupperW( current_directory
[0] ) - 'A';
326 /***********************************************************************
329 * Convert drive number from scheme (0=default, 1=A:, 2=B:, ...) into
330 * scheme (0=A:, 1=B:, 2=C:, ...) or MAX_DOS_DRIVES on error.
332 static BYTE
INT21_MapDrive( BYTE drive
)
336 WCHAR drivespec
[] = {'A', ':', 0};
339 drivespec
[0] += drive
- 1;
340 drivetype
= GetDriveTypeW( drivespec
);
342 if (drivetype
== DRIVE_UNKNOWN
|| drivetype
== DRIVE_NO_ROOT_DIR
)
343 return MAX_DOS_DRIVES
;
348 return INT21_GetCurrentDrive();
352 /***********************************************************************
353 * INT21_SetCurrentDrive
355 * Set current drive. Uses scheme (0=A:, 1=B:, 2=C:, ...).
357 static void INT21_SetCurrentDrive( BYTE drive
)
359 WCHAR drivespec
[] = {'A', ':', 0};
361 drivespec
[0] += drive
;
363 if (!SetCurrentDirectoryW( drivespec
))
364 TRACE( "Failed to set current drive.\n" );
368 /***********************************************************************
369 * INT21_GetSystemCountryCode
371 * Return DOS country code for default system locale.
373 static WORD
INT21_GetSystemCountryCode( void )
375 return GetSystemDefaultLangID();
379 /***********************************************************************
380 * INT21_FillCountryInformation
382 * Fill 34-byte buffer with country information data using
383 * default system locale.
385 static void INT21_FillCountryInformation( BYTE
*buffer
)
387 /* 00 - WORD: date format
392 *(WORD
*)(buffer
+ 0) = 0; /* FIXME: Get from locale */
394 /* 02 - BYTE[5]: ASCIIZ currency symbol string */
395 buffer
[2] = '$'; /* FIXME: Get from locale */
398 /* 07 - BYTE[2]: ASCIIZ thousands separator */
399 buffer
[7] = 0; /* FIXME: Get from locale */
402 /* 09 - BYTE[2]: ASCIIZ decimal separator */
403 buffer
[9] = '.'; /* FIXME: Get from locale */
406 /* 11 - BYTE[2]: ASCIIZ date separator */
407 buffer
[11] = '/'; /* FIXME: Get from locale */
410 /* 13 - BYTE[2]: ASCIIZ time separator */
411 buffer
[13] = ':'; /* FIXME: Get from locale */
414 /* 15 - BYTE: Currency format
415 * bit 2 = set if currency symbol replaces decimal point
416 * bit 1 = number of spaces between value and currency symbol
417 * bit 0 = 0 if currency symbol precedes value
418 * 1 if currency symbol follows value
420 buffer
[15] = 0; /* FIXME: Get from locale */
422 /* 16 - BYTE: Number of digits after decimal in currency */
423 buffer
[16] = 0; /* FIXME: Get from locale */
425 /* 17 - BYTE: Time format
426 * bit 0 = 0 if 12-hour clock
429 buffer
[17] = 1; /* FIXME: Get from locale */
431 /* 18 - DWORD: Address of case map routine */
432 *(DWORD
*)(buffer
+ 18) = 0; /* FIXME: ptr to case map routine */
434 /* 22 - BYTE[2]: ASCIIZ data-list separator */
435 buffer
[22] = ','; /* FIXME: Get from locale */
438 /* 24 - BYTE[10]: Reserved */
439 memset( buffer
+ 24, 0, 10 );
443 /***********************************************************************
446 * Initialize DOS heap.
448 * Filename Terminator Table of w2k DE NTVDM:
449 * 16 00 01 00 FF 00 00 20-02 0E 2E 22 2F 5C 5B 5D ....... ..."/\[]
450 * 3A 7C 3C 3E 2B 3D 3B 2C-00 :|<>+=;,
452 static void INT21_FillHeap( INT21_HEAP
*heap
)
454 static const char terminators
[] = ".\"/\\[]:|<>+=;,";
460 heap
->uppercase_size
= 128;
461 for (i
= 0; i
< 128; i
++)
462 heap
->uppercase_table
[i
] = toupper( 128 + i
);
467 heap
->lowercase_size
= 256;
468 for (i
= 0; i
< 256; i
++)
469 heap
->lowercase_table
[i
] = tolower( i
);
474 heap
->collating_size
= 256;
475 for (i
= 0; i
< 256; i
++)
476 heap
->collating_table
[i
] = i
;
481 heap
->filename_size
= 8 + strlen(terminators
);
482 heap
->filename_illegal_size
= strlen(terminators
);
483 memcpy( heap
->filename_illegal_table
, terminators
, heap
->filename_illegal_size
);
485 heap
->filename_reserved1
= 0x01;
486 heap
->filename_lowest
= 0x00;
487 heap
->filename_highest
= 0xff;
488 heap
->filename_reserved2
= 0x00;
489 heap
->filename_exclude_first
= 0x00;
490 heap
->filename_exclude_last
= 0x20;
491 heap
->filename_reserved3
= 0x02;
494 * DBCS lead byte table. This table is empty.
497 memset( heap
->dbcs_table
, 0, sizeof(heap
->dbcs_table
) );
500 * Initialize InDos flag.
502 heap
->misc_indos
= 0;
505 * FIXME: Should drive parameter blocks (DPB) be
506 * initialized here and linked to DOS LOL?
511 /***********************************************************************
512 * INT21_GetHeapPointer
514 * Get pointer for DOS heap (INT21_HEAP).
515 * Creates and initializes heap on first call.
517 static INT21_HEAP
*INT21_GetHeapPointer( void )
519 static INT21_HEAP
*heap_pointer
= NULL
;
525 heap_pointer
= DOSVM_AllocDataUMB( sizeof(INT21_HEAP
), &heap_selector
);
526 heap_pointer
->misc_selector
= heap_selector
;
527 INT21_FillHeap( heap_pointer
);
534 /***********************************************************************
535 * INT21_GetHeapSelector
537 * Get segment/selector for DOS heap (INT21_HEAP).
538 * Creates and initializes heap on first call.
540 static WORD
INT21_GetHeapSelector( CONTEXT
*context
)
542 INT21_HEAP
*heap
= INT21_GetHeapPointer();
543 return heap
->misc_selector
;
547 /***********************************************************************
550 * Fill DOS heap drive parameter block for the specified drive.
551 * Return TRUE if drive was valid and there were
552 * no errors while reading drive information.
554 static BOOL
INT21_FillDrivePB( BYTE drive
)
556 WCHAR drivespec
[] = {'A', ':', 0};
557 INT21_HEAP
*heap
= INT21_GetHeapPointer();
560 DWORD cluster_sectors
;
563 DWORD total_clusters
;
565 if (drive
>= MAX_DOS_DRIVES
)
568 dpb
= &heap
->misc_dpb_list
[drive
];
569 drivespec
[0] += drive
;
570 drivetype
= GetDriveTypeW( drivespec
);
573 * FIXME: Does this check work correctly with floppy/cdrom drives?
575 if (drivetype
== DRIVE_NO_ROOT_DIR
|| drivetype
== DRIVE_UNKNOWN
)
579 * FIXME: Does this check work correctly with floppy/cdrom drives?
581 if (!GetDiskFreeSpaceW( drivespec
, &cluster_sectors
, §or_bytes
,
582 &free_clusters
, &total_clusters
))
586 * FIXME: Most of the values listed below are incorrect.
587 * All values should be validated.
592 dpb
->sector_bytes
= sector_bytes
;
593 dpb
->cluster_sectors
= cluster_sectors
- 1;
596 while (cluster_sectors
> 1)
598 cluster_sectors
/= 2;
602 dpb
->num_reserved
= 0;
604 dpb
->num_root_entries
= 2;
605 dpb
->first_data_sector
= 2;
606 dpb
->num_clusters1
= total_clusters
;
607 dpb
->sectors_per_FAT
= 1;
608 dpb
->first_dir_sector
= 1;
609 dpb
->driver_header
= 0;
610 dpb
->media_ID
= (drivetype
== DRIVE_FIXED
) ? 0xF8 : 0xF0;
611 dpb
->access_flag
= 0;
613 dpb
->search_cluster1
= 0;
614 dpb
->free_clusters_lo
= LOWORD(free_clusters
);
615 dpb
->free_clusters_hi
= HIWORD(free_clusters
);
616 dpb
->mirroring_flags
= 0;
617 dpb
->info_sector
= 0xffff;
618 dpb
->spare_boot_sector
= 0xffff;
619 dpb
->first_cluster_sector
= 0;
620 dpb
->num_clusters2
= total_clusters
;
621 dpb
->fat_clusters
= 32;
622 dpb
->root_cluster
= 0;
623 dpb
->search_cluster2
= 0;
629 /***********************************************************************
630 * INT21_GetCurrentDirectory
634 * - subfunction 0x47 of function 0x71
636 static BOOL
INT21_GetCurrentDirectory( CONTEXT
*context
, BOOL islong
)
638 char *buffer
= CTX_SEG_OFF_TO_LIN(context
, context
->SegDs
, context
->Esi
);
639 BYTE drive
= INT21_MapDrive( DL_reg(context
) );
640 WCHAR pathW
[MAX_PATH
];
641 char pathA
[MAX_PATH
];
644 TRACE( "drive %d\n", DL_reg(context
) );
646 if (drive
== MAX_DOS_DRIVES
)
648 SetLastError(ERROR_INVALID_DRIVE
);
653 * Grab current directory.
656 if (!GetCurrentDirectoryW( MAX_PATH
, pathW
)) return FALSE
;
658 if (toupperW(pathW
[0]) - 'A' != drive
|| pathW
[1] != ':')
660 /* cwd is not on the requested drive, get the environment string instead */
665 env_var
[1] = 'A' + drive
;
668 if (!GetEnvironmentVariableW( env_var
, pathW
, MAX_PATH
))
670 /* return empty path */
677 * Convert into short format.
682 DWORD result
= GetShortPathNameW( pathW
, pathW
, MAX_PATH
);
685 if (result
> MAX_PATH
)
687 WARN( "Short path too long!\n" );
688 SetLastError(ERROR_NETWORK_BUSY
); /* Internal Wine error. */
694 * The returned pathname does not include
695 * the drive letter, colon or leading backslash.
701 * FIXME: We should probably just strip host part from name...
703 FIXME( "UNC names are not supported.\n" );
704 SetLastError(ERROR_NETWORK_BUSY
); /* Internal Wine error. */
707 else if (!ptr
[0] || ptr
[1] != ':' || ptr
[2] != '\\')
709 WARN( "Path is neither UNC nor DOS path: %s\n",
710 wine_dbgstr_w(ptr
) );
711 SetLastError(ERROR_NETWORK_BUSY
); /* Internal Wine error. */
716 /* Remove drive letter, colon and leading backslash. */
721 * Convert into OEM string.
724 if (!WideCharToMultiByte(CP_OEMCP
, 0, ptr
, -1, pathA
,
725 MAX_PATH
, NULL
, NULL
))
727 WARN( "Cannot convert path!\n" );
728 SetLastError(ERROR_NETWORK_BUSY
); /* Internal Wine error. */
738 /* Undocumented success code. */
739 SET_AX( context
, 0x0100 );
741 /* Truncate buffer to 64 bytes. */
745 TRACE( "%c:=%s\n", 'A' + drive
, pathA
);
747 strcpy( buffer
, pathA
);
752 /***********************************************************************
753 * INT21_SetCurrentDirectory
757 * - subfunction 0x3b of function 0x71
759 static BOOL
INT21_SetCurrentDirectory( CONTEXT
*context
)
761 WCHAR dirW
[MAX_PATH
];
764 char *dirA
= CTX_SEG_OFF_TO_LIN(context
, context
->SegDs
, context
->Edx
);
765 BYTE drive
= INT21_GetCurrentDrive();
768 TRACE( "SET CURRENT DIRECTORY %s\n", dirA
);
770 MultiByteToWideChar(CP_OEMCP
, 0, dirA
, -1, dirW
, MAX_PATH
);
771 if (!GetFullPathNameW( dirW
, MAX_PATH
, dirW
, NULL
)) return FALSE
;
773 attr
= GetFileAttributesW( dirW
);
774 if (attr
== INVALID_FILE_ATTRIBUTES
|| !(attr
& FILE_ATTRIBUTE_DIRECTORY
))
776 SetLastError( ERROR_PATH_NOT_FOUND
);
781 env_var
[1] = dirW
[0];
784 result
= SetEnvironmentVariableW( env_var
, dirW
);
786 /* only set current directory if on the current drive */
787 if (result
&& (toupperW(dirW
[0]) - 'A' == drive
)) result
= SetCurrentDirectoryW( dirW
);
792 /***********************************************************************
793 * INT21_CreateMagicDeviceHandle
795 * Create a dummy file handle for a "magic" device.
797 static HANDLE
INT21_CreateMagicDeviceHandle( LPCWSTR name
)
799 static const WCHAR prefixW
[] = {'\\','?','?','\\','u','n','i','x'};
800 const char *dir
= wine_get_server_dir();
804 OBJECT_ATTRIBUTES attr
;
805 UNICODE_STRING nameW
;
808 len
= MultiByteToWideChar( CP_UNIXCP
, 0, dir
, -1, NULL
, 0 );
809 nameW
.Length
= sizeof(prefixW
) + (len
+ strlenW( name
)) * sizeof(WCHAR
);
810 nameW
.MaximumLength
= nameW
.Length
+ sizeof(WCHAR
);
811 if (!(nameW
.Buffer
= HeapAlloc( GetProcessHeap(), 0, nameW
.MaximumLength
)))
813 SetLastError( ERROR_NOT_ENOUGH_MEMORY
);
816 memcpy( nameW
.Buffer
, prefixW
, sizeof(prefixW
) );
817 MultiByteToWideChar( CP_UNIXCP
, 0, dir
, -1, nameW
.Buffer
+ ARRAY_SIZE(prefixW
), len
);
818 len
+= ARRAY_SIZE(prefixW
);
819 nameW
.Buffer
[len
-1] = '/';
820 strcpyW( nameW
.Buffer
+ len
, name
);
822 attr
.Length
= sizeof(attr
);
823 attr
.RootDirectory
= 0;
825 attr
.ObjectName
= &nameW
;
826 attr
.SecurityDescriptor
= NULL
;
827 attr
.SecurityQualityOfService
= NULL
;
829 status
= NtCreateFile( &ret
, GENERIC_READ
|GENERIC_WRITE
|SYNCHRONIZE
, &attr
, &io
, NULL
, 0,
830 FILE_SHARE_READ
|FILE_SHARE_WRITE
, FILE_OPEN_IF
,
831 FILE_SYNCHRONOUS_IO_ALERT
, NULL
, 0 );
835 SetLastError( RtlNtStatusToDosError(status
) );
837 RtlFreeUnicodeString( &nameW
);
842 /***********************************************************************
843 * INT21_OpenMagicDevice
845 * Open a file handle for "magic" devices like EMMXXXX0.
847 static HANDLE
INT21_OpenMagicDevice( LPCWSTR name
, DWORD access
)
853 if (name
[0] && (name
[1] == ':')) name
+= 2;
854 if ((p
= strrchrW( name
, '/' ))) name
= p
+ 1;
855 if ((p
= strrchrW( name
, '\\' ))) name
= p
+ 1;
857 for (i
= 0; i
< ARRAY_SIZE(magic_devices
); i
++)
859 int len
= strlenW( magic_devices
[i
].name
);
860 if (!strncmpiW( magic_devices
[i
].name
, name
, len
) &&
861 (!name
[len
] || name
[len
] == '.' || name
[len
] == ':')) break;
863 if (i
== ARRAY_SIZE(magic_devices
)) return 0;
865 if (!magic_devices
[i
].handle
) /* need to open it */
868 FILE_INTERNAL_INFORMATION info
;
870 if (!(handle
= INT21_CreateMagicDeviceHandle( magic_devices
[i
].name
))) return 0;
872 NtQueryInformationFile( handle
, &io
, &info
, sizeof(info
), FileInternalInformation
);
873 magic_devices
[i
].index
= info
.IndexNumber
;
874 magic_devices
[i
].handle
= handle
;
876 if (!DuplicateHandle( GetCurrentProcess(), magic_devices
[i
].handle
,
877 GetCurrentProcess(), &handle
, access
, FALSE
, 0 )) handle
= 0;
882 /***********************************************************************
890 * - subfunction 0x6c of function 0x71
892 static BOOL
INT21_CreateFile( CONTEXT
*context
,
899 char *pathA
= CTX_SEG_OFF_TO_LIN(context
, context
->SegDs
, pathSegOff
);
900 WCHAR pathW
[MAX_PATH
];
907 TRACE( "CreateFile called: function=%02x, action=%02x, access/share=%04x, "
908 "create flags=%04x, file=%s.\n",
909 AH_reg(context
), dosAction
, dosAccessShare
, CX_reg(context
), pathA
);
912 * Application tried to create/open a file whose name
913 * ends with a backslash. This is not allowed.
915 * FIXME: This needs to be validated, especially the return value.
917 if (pathA
[strlen(pathA
) - 1] == '/')
919 SetLastError( ERROR_FILE_NOT_FOUND
);
924 * Convert DOS action flags into Win32 creation disposition parameter.
929 winMode
= OPEN_EXISTING
;
932 winMode
= TRUNCATE_EXISTING
;
935 winMode
= CREATE_NEW
;
938 winMode
= OPEN_ALWAYS
;
941 winMode
= CREATE_ALWAYS
;
944 SetLastError( ERROR_INVALID_PARAMETER
);
949 * Convert DOS access/share flags into Win32 desired access parameter.
951 switch(dosAccessShare
& 0x07)
954 winAccess
= GENERIC_READ
;
957 winAccess
= GENERIC_WRITE
;
960 winAccess
= GENERIC_READ
| GENERIC_WRITE
;
964 * Read-only, do not modify file's last-access time (DOS7).
966 * FIXME: How to prevent modification of last-access time?
968 winAccess
= GENERIC_READ
;
975 * Convert DOS access/share flags into Win32 share mode parameter.
977 switch(dosAccessShare
& 0x70)
979 case OF_SHARE_EXCLUSIVE
:
982 case OF_SHARE_DENY_WRITE
:
983 winSharing
= FILE_SHARE_READ
;
985 case OF_SHARE_DENY_READ
:
986 winSharing
= FILE_SHARE_WRITE
;
988 case OF_SHARE_DENY_NONE
:
989 case OF_SHARE_COMPAT
:
991 winSharing
= FILE_SHARE_READ
| FILE_SHARE_WRITE
;
995 * FIXME: Bit (dosAccessShare & 0x80) represents inheritance.
996 * What to do with this bit?
997 * FIXME: Bits in the high byte of dosAccessShare are not supported.
998 * See both function 0x6c and subfunction 0x6c of function 0x71 for
999 * definition of these bits.
1003 * Convert DOS create attributes into Win32 flags and attributes parameter.
1005 if (winMode
== OPEN_EXISTING
|| winMode
== TRUNCATE_EXISTING
)
1011 WORD dosAttributes
= CX_reg(context
);
1013 if (dosAttributes
& FA_LABEL
)
1016 * Application tried to create volume label entry.
1017 * This is difficult to support so we do not allow it.
1019 * FIXME: If volume does not already have a label,
1020 * this function is supposed to succeed.
1022 SetLastError( ERROR_ACCESS_DENIED
);
1026 winAttributes
= dosAttributes
&
1027 (FILE_ATTRIBUTE_READONLY
| FILE_ATTRIBUTE_HIDDEN
|
1028 FILE_ATTRIBUTE_SYSTEM
| FILE_ATTRIBUTE_ARCHIVE
);
1034 MultiByteToWideChar(CP_OEMCP
, 0, pathA
, -1, pathW
, MAX_PATH
);
1036 if ((winHandle
= INT21_OpenMagicDevice( pathW
, winAccess
)))
1042 winHandle
= CreateFileW( pathW
, winAccess
, winSharing
, NULL
,
1043 winMode
, winAttributes
, 0 );
1044 /* DOS allows opening files on a CDROM R/W */
1045 if( winHandle
== INVALID_HANDLE_VALUE
&&
1046 (GetLastError() == ERROR_WRITE_PROTECT
||
1047 GetLastError() == ERROR_ACCESS_DENIED
)) {
1048 winHandle
= CreateFileW( pathW
, winAccess
& ~GENERIC_WRITE
,
1049 winSharing
, NULL
, winMode
, winAttributes
, 0 );
1052 if (winHandle
== INVALID_HANDLE_VALUE
)
1056 * Determine DOS file status.
1067 case TRUNCATE_EXISTING
:
1074 dosStatus
= (GetLastError() == ERROR_ALREADY_EXISTS
) ? 1 : 2;
1077 dosStatus
= (GetLastError() == ERROR_ALREADY_EXISTS
) ? 3 : 2;
1085 * Return DOS file handle and DOS status.
1087 SET_AX( context
, Win32HandleToDosFileHandle(winHandle
) );
1090 SET_CX( context
, dosStatus
);
1092 TRACE( "CreateFile finished: handle=%d, status=%d.\n",
1093 AX_reg(context
), dosStatus
);
1099 /***********************************************************************
1100 * INT21_GetCurrentDTA
1102 static BYTE
*INT21_GetCurrentDTA( CONTEXT
*context
)
1104 TDB
*pTask
= GlobalLock16(GetCurrentTask());
1106 /* FIXME: This assumes DTA was set correctly! */
1107 return CTX_SEG_OFF_TO_LIN( context
, SELECTOROF(pTask
->dta
),
1108 OFFSETOF(pTask
->dta
) );
1112 /***********************************************************************
1113 * INT21_OpenFileUsingFCB
1115 * Handler for function 0x0f.
1118 * DX:DX [I/O] File control block (FCB or XFCB) of unopened file
1125 * Opens a FCB file for read/write in compatibility mode. Upon calling
1126 * the FCB must have the drive_number, file_name, and file_extension
1127 * fields filled and all other bytes cleared.
1129 static void INT21_OpenFileUsingFCB( CONTEXT
*context
)
1137 BY_HANDLE_FILE_INFORMATION info
;
1140 fcb
= CTX_SEG_OFF_TO_LIN(context
, context
->SegDs
, context
->Edx
);
1141 if (fcb
->drive_number
== 0xff) {
1142 xfcb
= (struct XFCB
*) fcb
;
1143 fcb
= (struct FCB
*) xfcb
->fcb
;
1147 file_path
[0] = 'A' + INT21_MapDrive( fcb
->drive_number
);
1149 if (AL_result
== 0) {
1151 pos
= &file_path
[2];
1152 memcpy(pos
, fcb
->file_name
, 8);
1155 pos
= strchr(pos
, ' ');
1158 memcpy(pos
, fcb
->file_extension
, 3);
1161 pos
= strchr(pos
, ' ');
1164 handle
= (HANDLE
) _lopen(file_path
, OF_READWRITE
);
1165 if (handle
== INVALID_HANDLE_VALUE
) {
1166 TRACE("_lopen(\"%s\") failed: INVALID_HANDLE_VALUE\n", file_path
);
1167 AL_result
= 0xff; /* failed */
1169 hfile16
= Win32HandleToDosFileHandle(handle
);
1170 if (hfile16
== HFILE_ERROR16
) {
1171 TRACE("Win32HandleToDosFileHandle(%p) failed: HFILE_ERROR\n", handle
);
1172 CloseHandle(handle
);
1173 AL_result
= 0xff; /* failed */
1174 } else if (hfile16
> 255) {
1175 TRACE("hfile16 (=%d) larger than 255 for \"%s\"\n", hfile16
, file_path
);
1177 AL_result
= 0xff; /* failed */
1179 if (!GetFileInformationByHandle(handle
, &info
)) {
1180 TRACE("GetFileInformationByHandle(%d, %p) for \"%s\" failed\n",
1181 hfile16
, handle
, file_path
);
1183 AL_result
= 0xff; /* failed */
1185 fcb
->drive_number
= file_path
[0] - 'A' + 1;
1186 fcb
->current_block_number
= 0;
1187 fcb
->logical_record_size
= 128;
1188 fcb
->file_size
= info
.nFileSizeLow
;
1189 FileTimeToDosDateTime(&info
.ftLastWriteTime
,
1190 &fcb
->date_of_last_write
, &fcb
->time_of_last_write
);
1191 fcb
->file_number
= hfile16
;
1192 fcb
->attributes
= 0xc2;
1193 fcb
->starting_cluster
= 0; /* don't know correct init value */
1194 fcb
->sequence_number
= 0; /* don't know correct init value */
1195 fcb
->file_attributes
= info
.dwFileAttributes
;
1196 /* The following fields are not initialized */
1197 /* by the native function: */
1199 /* record_within_current_block */
1200 /* random_access_record_number */
1202 TRACE("successful opened file \"%s\" as %d (handle %p)\n",
1203 file_path
, hfile16
, handle
);
1204 AL_result
= 0x00; /* successful */
1209 SET_AL(context
, AL_result
);
1213 /***********************************************************************
1214 * INT21_CloseFileUsingFCB
1216 * Handler for function 0x10.
1219 * DX:DX [I/O] File control block (FCB or XFCB) of open file
1226 * Closes a FCB file.
1228 static void INT21_CloseFileUsingFCB( CONTEXT
*context
)
1234 fcb
= CTX_SEG_OFF_TO_LIN(context
, context
->SegDs
, context
->Edx
);
1235 if (fcb
->drive_number
== 0xff) {
1236 xfcb
= (struct XFCB
*) fcb
;
1237 fcb
= (struct FCB
*) xfcb
->fcb
;
1240 if (_lclose16((HFILE16
) fcb
->file_number
) != 0) {
1241 TRACE("_lclose16(%d) failed\n", fcb
->file_number
);
1242 AL_result
= 0xff; /* failed */
1244 TRACE("successful closed file %d\n", fcb
->file_number
);
1245 AL_result
= 0x00; /* successful */
1247 SET_AL(context
, AL_result
);
1251 /***********************************************************************
1252 * INT21_SequentialReadFromFCB
1254 * Handler for function 0x14.
1257 * DX:DX [I/O] File control block (FCB or XFCB) of open file
1261 * 1: end of file, no data read
1262 * 2: segment wrap in DTA, no data read (not returned now)
1263 * 3: end of file, partial record read
1266 * Reads a record with the size FCB->logical_record_size from the FCB
1267 * to the disk transfer area. The position of the record is specified
1268 * with FCB->current_block_number and FCB->record_within_current_block.
1269 * Then FCB->current_block_number and FCB->record_within_current_block
1270 * are updated to point to the next record. If a partial record is
1271 * read, it is filled with zeros up to the FCB->logical_record_size.
1273 static void INT21_SequentialReadFromFCB( CONTEXT
*context
)
1278 DWORD record_number
;
1280 BYTE
*disk_transfer_area
;
1284 fcb
= CTX_SEG_OFF_TO_LIN(context
, context
->SegDs
, context
->Edx
);
1285 if (fcb
->drive_number
== 0xff) {
1286 xfcb
= (struct XFCB
*) fcb
;
1287 fcb
= (struct FCB
*) xfcb
->fcb
;
1290 handle
= DosFileHandleToWin32Handle((HFILE16
) fcb
->file_number
);
1291 if (handle
== INVALID_HANDLE_VALUE
) {
1292 TRACE("DosFileHandleToWin32Handle(%d) failed: INVALID_HANDLE_VALUE\n",
1294 AL_result
= 0x01; /* end of file, no data read */
1296 record_number
= 128 * fcb
->current_block_number
+ fcb
->record_within_current_block
;
1297 position
= SetFilePointer(handle
, record_number
* fcb
->logical_record_size
, NULL
, 0);
1298 if (position
!= record_number
* fcb
->logical_record_size
) {
1299 TRACE("seek(%d, %d, 0) failed with %u\n",
1300 fcb
->file_number
, record_number
* fcb
->logical_record_size
, position
);
1301 AL_result
= 0x01; /* end of file, no data read */
1303 disk_transfer_area
= INT21_GetCurrentDTA(context
);
1304 bytes_read
= _lread((HFILE
) handle
, disk_transfer_area
, fcb
->logical_record_size
);
1305 if (bytes_read
!= fcb
->logical_record_size
) {
1306 TRACE("_lread(%d, %p, %d) failed with %d\n",
1307 fcb
->file_number
, disk_transfer_area
, fcb
->logical_record_size
, bytes_read
);
1308 if (bytes_read
== 0) {
1309 AL_result
= 0x01; /* end of file, no data read */
1311 memset(&disk_transfer_area
[bytes_read
], 0, fcb
->logical_record_size
- bytes_read
);
1312 AL_result
= 0x03; /* end of file, partial record read */
1315 TRACE("successful read %d bytes from record %d (position %u) of file %d (handle %p)\n",
1316 bytes_read
, record_number
, position
, fcb
->file_number
, handle
);
1317 AL_result
= 0x00; /* successful */
1321 if (AL_result
== 0x00 || AL_result
== 0x03) {
1322 if (fcb
->record_within_current_block
== 127) {
1323 fcb
->record_within_current_block
= 0;
1324 fcb
->current_block_number
++;
1326 fcb
->record_within_current_block
++;
1329 SET_AL(context
, AL_result
);
1333 /***********************************************************************
1334 * INT21_SequentialWriteToFCB
1336 * Handler for function 0x15.
1339 * DX:DX [I/O] File control block (FCB or XFCB) of open file
1344 * 2: segment wrap in DTA (not returned now)
1347 * Writes a record with the size FCB->logical_record_size from the disk
1348 * transfer area to the FCB. The position of the record is specified
1349 * with FCB->current_block_number and FCB->record_within_current_block.
1350 * Then FCB->current_block_number and FCB->record_within_current_block
1351 * are updated to point to the next record.
1353 static void INT21_SequentialWriteToFCB( CONTEXT
*context
)
1358 DWORD record_number
;
1360 BYTE
*disk_transfer_area
;
1364 fcb
= CTX_SEG_OFF_TO_LIN(context
, context
->SegDs
, context
->Edx
);
1365 if (fcb
->drive_number
== 0xff) {
1366 xfcb
= (struct XFCB
*) fcb
;
1367 fcb
= (struct FCB
*) xfcb
->fcb
;
1370 handle
= DosFileHandleToWin32Handle((HFILE16
) fcb
->file_number
);
1371 if (handle
== INVALID_HANDLE_VALUE
) {
1372 TRACE("DosFileHandleToWin32Handle(%d) failed: INVALID_HANDLE_VALUE\n",
1374 AL_result
= 0x01; /* disk full */
1376 record_number
= 128 * fcb
->current_block_number
+ fcb
->record_within_current_block
;
1377 position
= SetFilePointer(handle
, record_number
* fcb
->logical_record_size
, NULL
, 0);
1378 if (position
!= record_number
* fcb
->logical_record_size
) {
1379 TRACE("seek(%d, %d, 0) failed with %u\n",
1380 fcb
->file_number
, record_number
* fcb
->logical_record_size
, position
);
1381 AL_result
= 0x01; /* disk full */
1383 disk_transfer_area
= INT21_GetCurrentDTA(context
);
1384 bytes_written
= _lwrite((HFILE
) handle
, (LPCSTR
)disk_transfer_area
, fcb
->logical_record_size
);
1385 if (bytes_written
!= fcb
->logical_record_size
) {
1386 TRACE("_lwrite(%d, %p, %d) failed with %d\n",
1387 fcb
->file_number
, disk_transfer_area
, fcb
->logical_record_size
, bytes_written
);
1388 AL_result
= 0x01; /* disk full */
1390 TRACE("successful written %d bytes from record %d (position %u) of file %d (handle %p)\n",
1391 bytes_written
, record_number
, position
, fcb
->file_number
, handle
);
1392 AL_result
= 0x00; /* successful */
1396 if (AL_result
== 0x00) {
1397 if (fcb
->record_within_current_block
== 127) {
1398 fcb
->record_within_current_block
= 0;
1399 fcb
->current_block_number
++;
1401 fcb
->record_within_current_block
++;
1404 SET_AL(context
, AL_result
);
1408 /***********************************************************************
1409 * INT21_ReadRandomRecordFromFCB
1411 * Handler for function 0x21.
1414 * DX:DX [I/O] File control block (FCB or XFCB) of open file
1418 * 1: end of file, no data read
1419 * 2: segment wrap in DTA, no data read (not returned now)
1420 * 3: end of file, partial record read
1423 * Reads a record with the size FCB->logical_record_size from
1424 * the FCB to the disk transfer area. The position of the record
1425 * is specified with FCB->random_access_record_number. The
1426 * FCB->random_access_record_number is not updated. If a partial record
1427 * is read, it is filled with zeros up to the FCB->logical_record_size.
1429 static void INT21_ReadRandomRecordFromFCB( CONTEXT
*context
)
1434 DWORD record_number
;
1436 BYTE
*disk_transfer_area
;
1440 fcb
= CTX_SEG_OFF_TO_LIN(context
, context
->SegDs
, context
->Edx
);
1441 if (fcb
->drive_number
== 0xff) {
1442 xfcb
= (struct XFCB
*) fcb
;
1443 fcb
= (struct FCB
*) xfcb
->fcb
;
1446 memcpy(&record_number
, fcb
->random_access_record_number
, 4);
1447 handle
= DosFileHandleToWin32Handle((HFILE16
) fcb
->file_number
);
1448 if (handle
== INVALID_HANDLE_VALUE
) {
1449 TRACE("DosFileHandleToWin32Handle(%d) failed: INVALID_HANDLE_VALUE\n",
1451 AL_result
= 0x01; /* end of file, no data read */
1453 position
= SetFilePointer(handle
, record_number
* fcb
->logical_record_size
, NULL
, 0);
1454 if (position
!= record_number
* fcb
->logical_record_size
) {
1455 TRACE("seek(%d, %d, 0) failed with %u\n",
1456 fcb
->file_number
, record_number
* fcb
->logical_record_size
, position
);
1457 AL_result
= 0x01; /* end of file, no data read */
1459 disk_transfer_area
= INT21_GetCurrentDTA(context
);
1460 bytes_read
= _lread((HFILE
) handle
, disk_transfer_area
, fcb
->logical_record_size
);
1461 if (bytes_read
!= fcb
->logical_record_size
) {
1462 TRACE("_lread(%d, %p, %d) failed with %d\n",
1463 fcb
->file_number
, disk_transfer_area
, fcb
->logical_record_size
, bytes_read
);
1464 if (bytes_read
== 0) {
1465 AL_result
= 0x01; /* end of file, no data read */
1467 memset(&disk_transfer_area
[bytes_read
], 0, fcb
->logical_record_size
- bytes_read
);
1468 AL_result
= 0x03; /* end of file, partial record read */
1471 TRACE("successful read %d bytes from record %d (position %u) of file %d (handle %p)\n",
1472 bytes_read
, record_number
, position
, fcb
->file_number
, handle
);
1473 AL_result
= 0x00; /* successful */
1477 fcb
->current_block_number
= record_number
/ 128;
1478 fcb
->record_within_current_block
= record_number
% 128;
1479 SET_AL(context
, AL_result
);
1483 /***********************************************************************
1484 * INT21_WriteRandomRecordToFCB
1486 * Handler for function 0x22.
1489 * DX:DX [I/O] File control block (FCB or XFCB) of open file
1494 * 2: segment wrap in DTA (not returned now)
1497 * Writes a record with the size FCB->logical_record_size from
1498 * the disk transfer area to the FCB. The position of the record
1499 * is specified with FCB->random_access_record_number. The
1500 * FCB->random_access_record_number is not updated.
1502 static void INT21_WriteRandomRecordToFCB( CONTEXT
*context
)
1507 DWORD record_number
;
1509 BYTE
*disk_transfer_area
;
1513 fcb
= CTX_SEG_OFF_TO_LIN(context
, context
->SegDs
, context
->Edx
);
1514 if (fcb
->drive_number
== 0xff) {
1515 xfcb
= (struct XFCB
*) fcb
;
1516 fcb
= (struct FCB
*) xfcb
->fcb
;
1519 memcpy(&record_number
, fcb
->random_access_record_number
, 4);
1520 handle
= DosFileHandleToWin32Handle((HFILE16
) fcb
->file_number
);
1521 if (handle
== INVALID_HANDLE_VALUE
) {
1522 TRACE("DosFileHandleToWin32Handle(%d) failed: INVALID_HANDLE_VALUE\n",
1524 AL_result
= 0x01; /* disk full */
1526 position
= SetFilePointer(handle
, record_number
* fcb
->logical_record_size
, NULL
, 0);
1527 if (position
!= record_number
* fcb
->logical_record_size
) {
1528 TRACE("seek(%d, %d, 0) failed with %u\n",
1529 fcb
->file_number
, record_number
* fcb
->logical_record_size
, position
);
1530 AL_result
= 0x01; /* disk full */
1532 disk_transfer_area
= INT21_GetCurrentDTA(context
);
1533 bytes_written
= _lwrite((HFILE
) handle
, (LPCSTR
)disk_transfer_area
, fcb
->logical_record_size
);
1534 if (bytes_written
!= fcb
->logical_record_size
) {
1535 TRACE("_lwrite(%d, %p, %d) failed with %d\n",
1536 fcb
->file_number
, disk_transfer_area
, fcb
->logical_record_size
, bytes_written
);
1537 AL_result
= 0x01; /* disk full */
1539 TRACE("successful written %d bytes from record %d (position %u) of file %d (handle %p)\n",
1540 bytes_written
, record_number
, position
, fcb
->file_number
, handle
);
1541 AL_result
= 0x00; /* successful */
1545 fcb
->current_block_number
= record_number
/ 128;
1546 fcb
->record_within_current_block
= record_number
% 128;
1547 SET_AL(context
, AL_result
);
1551 /***********************************************************************
1552 * INT21_RandomBlockReadFromFCB
1554 * Handler for function 0x27.
1557 * CX [I/O] Number of records to read
1558 * DX:DX [I/O] File control block (FCB or XFCB) of open file
1562 * 1: end of file, no data read
1563 * 2: segment wrap in DTA, no data read (not returned now)
1564 * 3: end of file, partial record read
1567 * Reads several records with the size FCB->logical_record_size from
1568 * the FCB to the disk transfer area. The number of records to be
1569 * read is specified in the CX register. The position of the first
1570 * record is specified with FCB->random_access_record_number. The
1571 * FCB->random_access_record_number, the FCB->current_block_number
1572 * and FCB->record_within_current_block are updated to point to the
1573 * next record after the records read. If a partial record is read,
1574 * it is filled with zeros up to the FCB->logical_record_size. The
1575 * CX register is set to the number of successfully read records.
1577 static void INT21_RandomBlockReadFromFCB( CONTEXT
*context
)
1582 DWORD record_number
;
1584 BYTE
*disk_transfer_area
;
1585 UINT records_requested
;
1586 UINT bytes_requested
;
1591 fcb
= CTX_SEG_OFF_TO_LIN(context
, context
->SegDs
, context
->Edx
);
1592 if (fcb
->drive_number
== 0xff) {
1593 xfcb
= (struct XFCB
*) fcb
;
1594 fcb
= (struct FCB
*) xfcb
->fcb
;
1597 memcpy(&record_number
, fcb
->random_access_record_number
, 4);
1598 handle
= DosFileHandleToWin32Handle((HFILE16
) fcb
->file_number
);
1599 if (handle
== INVALID_HANDLE_VALUE
) {
1600 TRACE("DosFileHandleToWin32Handle(%d) failed: INVALID_HANDLE_VALUE\n",
1603 AL_result
= 0x01; /* end of file, no data read */
1605 position
= SetFilePointer(handle
, record_number
* fcb
->logical_record_size
, NULL
, 0);
1606 if (position
!= record_number
* fcb
->logical_record_size
) {
1607 TRACE("seek(%d, %d, 0) failed with %u\n",
1608 fcb
->file_number
, record_number
* fcb
->logical_record_size
, position
);
1610 AL_result
= 0x01; /* end of file, no data read */
1612 disk_transfer_area
= INT21_GetCurrentDTA(context
);
1613 records_requested
= CX_reg(context
);
1614 bytes_requested
= records_requested
* fcb
->logical_record_size
;
1615 bytes_read
= _lread((HFILE
) handle
, disk_transfer_area
, bytes_requested
);
1616 if (bytes_read
!= bytes_requested
) {
1617 TRACE("_lread(%d, %p, %d) failed with %d\n",
1618 fcb
->file_number
, disk_transfer_area
, bytes_requested
, bytes_read
);
1619 records_read
= bytes_read
/ fcb
->logical_record_size
;
1620 if (bytes_read
% fcb
->logical_record_size
== 0) {
1621 AL_result
= 0x01; /* end of file, no data read */
1624 memset(&disk_transfer_area
[bytes_read
], 0, records_read
* fcb
->logical_record_size
- bytes_read
);
1625 AL_result
= 0x03; /* end of file, partial record read */
1628 TRACE("successful read %d bytes from record %d (position %u) of file %d (handle %p)\n",
1629 bytes_read
, record_number
, position
, fcb
->file_number
, handle
);
1630 records_read
= records_requested
;
1631 AL_result
= 0x00; /* successful */
1635 record_number
+= records_read
;
1636 memcpy(fcb
->random_access_record_number
, &record_number
, 4);
1637 fcb
->current_block_number
= record_number
/ 128;
1638 fcb
->record_within_current_block
= record_number
% 128;
1639 SET_CX(context
, records_read
);
1640 SET_AL(context
, AL_result
);
1644 /***********************************************************************
1645 * INT21_RandomBlockWriteToFCB
1647 * Handler for function 0x28.
1650 * CX [I/O] Number of records to write
1651 * DX:DX [I/O] File control block (FCB or XFCB) of open file
1656 * 2: segment wrap in DTA (not returned now)
1659 * Writes several records with the size FCB->logical_record_size from
1660 * the disk transfer area to the FCB. The number of records to be
1661 * written is specified in the CX register. The position of the first
1662 * record is specified with FCB->random_access_record_number. The
1663 * FCB->random_access_record_number, the FCB->current_block_number
1664 * and FCB->record_within_current_block are updated to point to the
1665 * next record after the records written. The CX register is set to
1666 * the number of successfully written records.
1668 static void INT21_RandomBlockWriteToFCB( CONTEXT
*context
)
1673 DWORD record_number
;
1675 BYTE
*disk_transfer_area
;
1676 UINT records_requested
;
1677 UINT bytes_requested
;
1679 UINT records_written
;
1682 fcb
= CTX_SEG_OFF_TO_LIN(context
, context
->SegDs
, context
->Edx
);
1683 if (fcb
->drive_number
== 0xff) {
1684 xfcb
= (struct XFCB
*) fcb
;
1685 fcb
= (struct FCB
*) xfcb
->fcb
;
1688 memcpy(&record_number
, fcb
->random_access_record_number
, 4);
1689 handle
= DosFileHandleToWin32Handle((HFILE16
) fcb
->file_number
);
1690 if (handle
== INVALID_HANDLE_VALUE
) {
1691 TRACE("DosFileHandleToWin32Handle(%d) failed: INVALID_HANDLE_VALUE\n",
1693 records_written
= 0;
1694 AL_result
= 0x01; /* disk full */
1696 position
= SetFilePointer(handle
, record_number
* fcb
->logical_record_size
, NULL
, 0);
1697 if (position
!= record_number
* fcb
->logical_record_size
) {
1698 TRACE("seek(%d, %d, 0) failed with %u\n",
1699 fcb
->file_number
, record_number
* fcb
->logical_record_size
, position
);
1700 records_written
= 0;
1701 AL_result
= 0x01; /* disk full */
1703 disk_transfer_area
= INT21_GetCurrentDTA(context
);
1704 records_requested
= CX_reg(context
);
1705 bytes_requested
= records_requested
* fcb
->logical_record_size
;
1706 bytes_written
= _lwrite((HFILE
) handle
, (LPCSTR
)disk_transfer_area
, bytes_requested
);
1707 if (bytes_written
!= bytes_requested
) {
1708 TRACE("_lwrite(%d, %p, %d) failed with %d\n",
1709 fcb
->file_number
, disk_transfer_area
, bytes_requested
, bytes_written
);
1710 records_written
= bytes_written
/ fcb
->logical_record_size
;
1711 AL_result
= 0x01; /* disk full */
1713 TRACE("successful write %d bytes from record %d (position %u) of file %d (handle %p)\n",
1714 bytes_written
, record_number
, position
, fcb
->file_number
, handle
);
1715 records_written
= records_requested
;
1716 AL_result
= 0x00; /* successful */
1720 record_number
+= records_written
;
1721 memcpy(fcb
->random_access_record_number
, &record_number
, 4);
1722 fcb
->current_block_number
= record_number
/ 128;
1723 fcb
->record_within_current_block
= record_number
% 128;
1724 SET_CX(context
, records_written
);
1725 SET_AL(context
, AL_result
);
1729 /***********************************************************************
1730 * INT21_CreateDirectory
1734 * - subfunction 0x39 of function 0x71
1735 * - subfunction 0xff of function 0x43 (CL == 0x39)
1737 static BOOL
INT21_CreateDirectory( CONTEXT
*context
)
1739 WCHAR dirW
[MAX_PATH
];
1740 char *dirA
= CTX_SEG_OFF_TO_LIN(context
,
1744 TRACE( "CREATE DIRECTORY %s\n", dirA
);
1746 MultiByteToWideChar(CP_OEMCP
, 0, dirA
, -1, dirW
, MAX_PATH
);
1748 if (CreateDirectoryW(dirW
, NULL
))
1752 * FIXME: CreateDirectory's LastErrors will clash with the ones
1753 * used by DOS. AH=39 only returns 3 (path not found) and
1754 * 5 (access denied), while CreateDirectory return several
1755 * ones. Remap some of them. -Marcus
1757 switch (GetLastError())
1759 case ERROR_ALREADY_EXISTS
:
1760 case ERROR_FILENAME_EXCED_RANGE
:
1761 case ERROR_DISK_FULL
:
1762 SetLastError(ERROR_ACCESS_DENIED
);
1772 /***********************************************************************
1773 * INT21_ExtendedCountryInformation
1775 * Handler for function 0x65.
1777 static void INT21_ExtendedCountryInformation( CONTEXT
*context
)
1779 BYTE
*dataptr
= CTX_SEG_OFF_TO_LIN( context
, context
->SegEs
, context
->Edi
);
1780 BYTE buffsize
= CX_reg (context
);
1782 TRACE( "GET EXTENDED COUNTRY INFORMATION, subfunction %02x\n",
1786 * Check subfunctions that are passed country and code page.
1788 if (AL_reg(context
) >= 0x01 && AL_reg(context
) <= 0x07)
1790 WORD country
= DX_reg(context
);
1791 WORD codepage
= BX_reg(context
);
1793 if (country
!= 0xffff && country
!= INT21_GetSystemCountryCode())
1794 FIXME( "Requested info on non-default country %04x\n", country
);
1796 if (codepage
!= 0xffff && codepage
!= GetOEMCP())
1797 FIXME( "Requested info on non-default code page %04x\n", codepage
);
1800 switch (AL_reg(context
)) {
1801 case 0x00: /* SET GENERAL INTERNATIONALIZATION INFO */
1802 INT_BARF( context
, 0x21 );
1803 SET_CFLAG( context
);
1806 case 0x01: /* GET GENERAL INTERNATIONALIZATION INFO */
1807 TRACE( "Get general internationalization info\n" );
1808 dataptr
[0] = 0x01; /* Info ID */
1809 *(WORD
*)(dataptr
+1) = 38; /* Size of the following info */
1810 *(WORD
*)(dataptr
+3) = INT21_GetSystemCountryCode(); /* Country ID */
1811 *(WORD
*)(dataptr
+5) = GetOEMCP(); /* Code page */
1812 /* FIXME: fill buffer partially up to buffsize bytes*/
1813 if (buffsize
>= 0x29){
1814 INT21_FillCountryInformation( dataptr
+ 7 );
1815 SET_CX( context
, 0x29 ); /* Size of returned info */
1817 SET_CX( context
, 0x07 ); /* Size of returned info */
1821 case 0x02: /* GET POINTER TO UPPERCASE TABLE */
1822 case 0x04: /* GET POINTER TO FILENAME UPPERCASE TABLE */
1823 TRACE( "Get pointer to uppercase table\n" );
1824 dataptr
[0] = AL_reg(context
); /* Info ID */
1825 *(DWORD
*)(dataptr
+1) = MAKESEGPTR( INT21_GetHeapSelector( context
),
1826 offsetof(INT21_HEAP
, uppercase_size
) );
1827 SET_CX( context
, 5 ); /* Size of returned info */
1830 case 0x03: /* GET POINTER TO LOWERCASE TABLE */
1831 TRACE( "Get pointer to lowercase table\n" );
1832 dataptr
[0] = 0x03; /* Info ID */
1833 *(DWORD
*)(dataptr
+1) = MAKESEGPTR( INT21_GetHeapSelector( context
),
1834 offsetof(INT21_HEAP
, lowercase_size
) );
1835 SET_CX( context
, 5 ); /* Size of returned info */
1838 case 0x05: /* GET POINTER TO FILENAME TERMINATOR TABLE */
1839 TRACE("Get pointer to filename terminator table\n");
1840 dataptr
[0] = 0x05; /* Info ID */
1841 *(DWORD
*)(dataptr
+1) = MAKESEGPTR( INT21_GetHeapSelector( context
),
1842 offsetof(INT21_HEAP
, filename_size
) );
1843 SET_CX( context
, 5 ); /* Size of returned info */
1846 case 0x06: /* GET POINTER TO COLLATING SEQUENCE TABLE */
1847 TRACE("Get pointer to collating sequence table\n");
1848 dataptr
[0] = 0x06; /* Info ID */
1849 *(DWORD
*)(dataptr
+1) = MAKESEGPTR( INT21_GetHeapSelector( context
),
1850 offsetof(INT21_HEAP
, collating_size
) );
1851 SET_CX( context
, 5 ); /* Size of returned info */
1854 case 0x07: /* GET POINTER TO DBCS LEAD BYTE TABLE */
1855 TRACE("Get pointer to DBCS lead byte table\n");
1856 dataptr
[0] = 0x07; /* Info ID */
1857 *(DWORD
*)(dataptr
+1) = MAKESEGPTR( INT21_GetHeapSelector( context
),
1858 offsetof(INT21_HEAP
, dbcs_size
) );
1859 SET_CX( context
, 5 ); /* Size of returned info */
1862 case 0x20: /* CAPITALIZE CHARACTER */
1863 case 0xa0: /* CAPITALIZE FILENAME CHARACTER */
1864 TRACE("Convert char to uppercase\n");
1865 SET_DL( context
, toupper(DL_reg(context
)) );
1868 case 0x21: /* CAPITALIZE STRING */
1869 case 0xa1: /* CAPITALIZE COUNTED FILENAME STRING */
1870 TRACE("Convert string to uppercase with length\n");
1872 char *ptr
= CTX_SEG_OFF_TO_LIN( context
, context
->SegDs
,
1874 WORD len
= CX_reg(context
);
1875 while (len
--) { *ptr
= toupper(*ptr
); ptr
++; }
1879 case 0x22: /* CAPITALIZE ASCIIZ STRING */
1880 case 0xa2: /* CAPITALIZE ASCIIZ FILENAME */
1881 TRACE("Convert ASCIIZ string to uppercase\n");
1883 char *p
= CTX_SEG_OFF_TO_LIN( context
, context
->SegDs
, context
->Edx
);
1884 for ( ; *p
; p
++) *p
= toupper(*p
);
1888 case 0x23: /* DETERMINE IF CHARACTER REPRESENTS YES/NO RESPONSE */
1889 INT_BARF( context
, 0x21 );
1890 SET_CFLAG( context
);
1894 INT_BARF( context
, 0x21 );
1901 /***********************************************************************
1902 * INT21_FileAttributes
1906 * - subfunction 0x43 of function 0x71
1908 static BOOL
INT21_FileAttributes( CONTEXT
*context
,
1912 WCHAR fileW
[MAX_PATH
];
1913 char *fileA
= CTX_SEG_OFF_TO_LIN(context
,
1923 switch (subfunction
)
1925 case 0x00: /* GET FILE ATTRIBUTES */
1926 TRACE( "GET FILE ATTRIBUTES for %s\n", fileA
);
1927 len
= MultiByteToWideChar(CP_OEMCP
, 0, fileA
, -1, fileW
, MAX_PATH
);
1929 /* Winbench 96 Disk Test fails if we don't complain
1930 * about a filename that ends in \
1932 if (!len
|| (fileW
[len
-1] == '/') || (fileW
[len
-1] == '\\'))
1935 result
= GetFileAttributesW( fileW
);
1936 if (result
== INVALID_FILE_ATTRIBUTES
)
1940 SET_CX( context
, (WORD
)result
);
1942 SET_AX( context
, (WORD
)result
); /* DR DOS */
1946 case 0x01: /* SET FILE ATTRIBUTES */
1947 TRACE( "SET FILE ATTRIBUTES 0x%02x for %s\n",
1948 CX_reg(context
), fileA
);
1949 MultiByteToWideChar(CP_OEMCP
, 0, fileA
, -1, fileW
, MAX_PATH
);
1951 if (!SetFileAttributesW( fileW
, CX_reg(context
) ))
1955 case 0x02: /* GET COMPRESSED FILE SIZE */
1956 TRACE( "GET COMPRESSED FILE SIZE for %s\n", fileA
);
1957 MultiByteToWideChar(CP_OEMCP
, 0, fileA
, -1, fileW
, MAX_PATH
);
1959 result
= GetCompressedFileSizeW( fileW
, NULL
);
1960 if (result
== INVALID_FILE_SIZE
)
1964 SET_AX( context
, LOWORD(result
) );
1965 SET_DX( context
, HIWORD(result
) );
1969 case 0x03: /* SET FILE LAST-WRITTEN DATE AND TIME */
1971 INT_BARF( context
, 0x21 );
1974 TRACE( "SET FILE LAST-WRITTEN DATE AND TIME, file %s\n", fileA
);
1975 MultiByteToWideChar(CP_OEMCP
, 0, fileA
, -1, fileW
, MAX_PATH
);
1977 handle
= CreateFileW( fileW
, GENERIC_WRITE
,
1978 FILE_SHARE_READ
| FILE_SHARE_WRITE
,
1979 NULL
, OPEN_EXISTING
, 0, 0 );
1980 if (handle
== INVALID_HANDLE_VALUE
)
1983 DosDateTimeToFileTime( DI_reg(context
),
1986 status
= SetFileTime( handle
, NULL
, NULL
, &filetime
);
1988 CloseHandle( handle
);
1993 case 0x04: /* GET FILE LAST-WRITTEN DATE AND TIME */
1995 INT_BARF( context
, 0x21 );
1998 TRACE( "GET FILE LAST-WRITTEN DATE AND TIME, file %s\n", fileA
);
1999 MultiByteToWideChar(CP_OEMCP
, 0, fileA
, -1, fileW
, MAX_PATH
);
2001 handle
= CreateFileW( fileW
, GENERIC_READ
,
2002 FILE_SHARE_READ
| FILE_SHARE_WRITE
,
2003 NULL
, OPEN_EXISTING
, 0, 0 );
2004 if (handle
== INVALID_HANDLE_VALUE
)
2007 status
= GetFileTime( handle
, NULL
, NULL
, &filetime
);
2010 FileTimeToDosDateTime( &filetime
, &date
, &time
);
2011 SET_DI( context
, date
);
2012 SET_CX( context
, time
);
2015 CloseHandle( handle
);
2020 case 0x05: /* SET FILE LAST ACCESS DATE */
2022 INT_BARF( context
, 0x21 );
2025 TRACE( "SET FILE LAST ACCESS DATE, file %s\n", fileA
);
2026 MultiByteToWideChar(CP_OEMCP
, 0, fileA
, -1, fileW
, MAX_PATH
);
2028 handle
= CreateFileW( fileW
, GENERIC_WRITE
,
2029 FILE_SHARE_READ
| FILE_SHARE_WRITE
,
2030 NULL
, OPEN_EXISTING
, 0, 0 );
2031 if (handle
== INVALID_HANDLE_VALUE
)
2034 DosDateTimeToFileTime( DI_reg(context
),
2037 status
= SetFileTime( handle
, NULL
, &filetime
, NULL
);
2039 CloseHandle( handle
);
2044 case 0x06: /* GET FILE LAST ACCESS DATE */
2046 INT_BARF( context
, 0x21 );
2049 TRACE( "GET FILE LAST ACCESS DATE, file %s\n", fileA
);
2050 MultiByteToWideChar(CP_OEMCP
, 0, fileA
, -1, fileW
, MAX_PATH
);
2052 handle
= CreateFileW( fileW
, GENERIC_READ
,
2053 FILE_SHARE_READ
| FILE_SHARE_WRITE
,
2054 NULL
, OPEN_EXISTING
, 0, 0 );
2055 if (handle
== INVALID_HANDLE_VALUE
)
2058 status
= GetFileTime( handle
, NULL
, &filetime
, NULL
);
2061 FileTimeToDosDateTime( &filetime
, &date
, NULL
);
2062 SET_DI( context
, date
);
2065 CloseHandle( handle
);
2070 case 0x07: /* SET FILE CREATION DATE AND TIME */
2072 INT_BARF( context
, 0x21 );
2075 TRACE( "SET FILE CREATION DATE AND TIME, file %s\n", fileA
);
2076 MultiByteToWideChar(CP_OEMCP
, 0, fileA
, -1, fileW
, MAX_PATH
);
2078 handle
= CreateFileW( fileW
, GENERIC_WRITE
,
2079 FILE_SHARE_READ
| FILE_SHARE_WRITE
,
2080 NULL
, OPEN_EXISTING
, 0, 0 );
2081 if (handle
== INVALID_HANDLE_VALUE
)
2085 * FIXME: SI has number of 10-millisecond units past time in CX.
2087 DosDateTimeToFileTime( DI_reg(context
),
2090 status
= SetFileTime( handle
, &filetime
, NULL
, NULL
);
2092 CloseHandle( handle
);
2097 case 0x08: /* GET FILE CREATION DATE AND TIME */
2099 INT_BARF( context
, 0x21 );
2102 TRACE( "GET FILE CREATION DATE AND TIME, file %s\n", fileA
);
2103 MultiByteToWideChar(CP_OEMCP
, 0, fileA
, -1, fileW
, MAX_PATH
);
2105 handle
= CreateFileW( fileW
, GENERIC_READ
,
2106 FILE_SHARE_READ
| FILE_SHARE_WRITE
,
2107 NULL
, OPEN_EXISTING
, 0, 0 );
2108 if (handle
== INVALID_HANDLE_VALUE
)
2111 status
= GetFileTime( handle
, &filetime
, NULL
, NULL
);
2114 FileTimeToDosDateTime( &filetime
, &date
, &time
);
2115 SET_DI( context
, date
);
2116 SET_CX( context
, time
);
2118 * FIXME: SI has number of 10-millisecond units past
2121 SET_SI( context
, 0 );
2124 CloseHandle(handle
);
2129 case 0xff: /* EXTENDED-LENGTH FILENAME OPERATIONS */
2130 if (islong
|| context
->Ebp
!= 0x5053)
2131 INT_BARF( context
, 0x21 );
2134 switch(CL_reg(context
))
2137 if (!INT21_CreateDirectory( context
))
2142 if (!INT21_RenameFile( context
))
2147 INT_BARF( context
, 0x21 );
2153 INT_BARF( context
, 0x21 );
2160 /***********************************************************************
2161 * INT21_FileDateTime
2163 * Handler for function 0x57.
2165 static BOOL
INT21_FileDateTime( CONTEXT
*context
)
2167 HANDLE handle
= DosFileHandleToWin32Handle(BX_reg(context
));
2171 switch (AL_reg(context
)) {
2172 case 0x00: /* Get last-written stamp */
2173 TRACE( "GET FILE LAST-WRITTEN DATE AND TIME, handle %d\n",
2176 if (!GetFileTime( handle
, NULL
, NULL
, &filetime
))
2178 FileTimeToDosDateTime( &filetime
, &date
, &time
);
2179 SET_DX( context
, date
);
2180 SET_CX( context
, time
);
2184 case 0x01: /* Set last-written stamp */
2185 TRACE( "SET FILE LAST-WRITTEN DATE AND TIME, handle %d\n",
2188 DosDateTimeToFileTime( DX_reg(context
),
2191 if (!SetFileTime( handle
, NULL
, NULL
, &filetime
))
2196 case 0x04: /* Get last access stamp, DOS 7 */
2197 TRACE( "GET FILE LAST ACCESS DATE AND TIME, handle %d\n",
2200 if (!GetFileTime( handle
, NULL
, &filetime
, NULL
))
2202 FileTimeToDosDateTime( &filetime
, &date
, &time
);
2203 SET_DX( context
, date
);
2204 SET_CX( context
, time
);
2208 case 0x05: /* Set last access stamp, DOS 7 */
2209 TRACE( "SET FILE LAST ACCESS DATE AND TIME, handle %d\n",
2212 DosDateTimeToFileTime( DX_reg(context
),
2215 if (!SetFileTime( handle
, NULL
, &filetime
, NULL
))
2220 case 0x06: /* Get creation stamp, DOS 7 */
2221 TRACE( "GET FILE CREATION DATE AND TIME, handle %d\n",
2224 if (!GetFileTime( handle
, &filetime
, NULL
, NULL
))
2226 FileTimeToDosDateTime( &filetime
, &date
, &time
);
2227 SET_DX( context
, date
);
2228 SET_CX( context
, time
);
2230 * FIXME: SI has number of 10-millisecond units past time in CX.
2232 SET_SI( context
, 0 );
2236 case 0x07: /* Set creation stamp, DOS 7 */
2237 TRACE( "SET FILE CREATION DATE AND TIME, handle %d\n",
2241 * FIXME: SI has number of 10-millisecond units past time in CX.
2243 DosDateTimeToFileTime( DX_reg(context
),
2246 if (!SetFileTime( handle
, &filetime
, NULL
, NULL
))
2252 INT_BARF( context
, 0x21 );
2260 /***********************************************************************
2263 * Handler for functions 0x51 and 0x62.
2265 static void INT21_GetPSP( CONTEXT
*context
)
2267 TRACE( "GET CURRENT PSP ADDRESS (%02x)\n", AH_reg(context
) );
2269 SET_BX( context
, LOWORD(GetCurrentPDB16()) );
2272 static inline void setword( BYTE
*ptr
, WORD w
)
2275 ptr
[1] = (BYTE
)(w
>> 8);
2278 static void CreateBPB(int drive
, BYTE
*data
, BOOL16 limited
)
2279 /* limited == TRUE is used with INT 0x21/0x440d */
2281 /* FIXME: we're forcing some values without checking that those are valid */
2286 setword(&data
[3], 0);
2288 setword(&data
[6], 240);
2289 setword(&data
[8], 64000);
2291 setword(&data
[0x0b], 40);
2292 setword(&data
[0x0d], 56);
2293 setword(&data
[0x0f], 2);
2294 setword(&data
[0x11], 0);
2297 setword(&data
[0x1f], 800);
2299 setword(&data
[0x22], 1);
2306 setword(&data
[3], 0);
2308 setword(&data
[6], 240);
2309 setword(&data
[8], 2880);
2311 setword(&data
[0x0b], 6);
2312 setword(&data
[0x0d], 18);
2313 setword(&data
[0x0f], 2);
2314 setword(&data
[0x11], 0);
2317 setword(&data
[0x1f], 80);
2319 setword(&data
[0x22], 2);
2324 static inline DWORD
INT21_Ioctl_CylHeadSect2Lin(DWORD cyl
, WORD head
, WORD sec
, WORD cyl_cnt
,
2325 WORD head_cnt
, WORD sec_cnt
)
2327 DWORD res
= (cyl
* head_cnt
*sec_cnt
+ head
* sec_cnt
+ sec
);
2331 /***********************************************************************
2334 * Handler for block device IOCTLs.
2336 static void INT21_Ioctl_Block( CONTEXT
*context
)
2339 BYTE drive
= INT21_MapDrive( BL_reg(context
) );
2340 WCHAR drivespec
[] = {'A', ':', '\\', 0};
2343 drivespec
[0] += drive
;
2344 drivetype
= GetDriveTypeW( drivespec
);
2346 RESET_CFLAG(context
);
2347 if (drivetype
== DRIVE_UNKNOWN
|| drivetype
== DRIVE_NO_ROOT_DIR
)
2349 TRACE( "IOCTL - SUBFUNCTION %d - INVALID DRIVE %c:\n",
2350 AL_reg(context
), 'A' + drive
);
2351 SetLastError( ERROR_INVALID_DRIVE
);
2352 SET_AX( context
, ERROR_INVALID_DRIVE
);
2353 SET_CFLAG( context
);
2357 switch (AL_reg(context
))
2359 case 0x04: /* READ FROM BLOCK DEVICE CONTROL CHANNEL */
2360 case 0x05: /* WRITE TO BLOCK DEVICE CONTROL CHANNEL */
2361 INT_BARF( context
, 0x21 );
2364 case 0x08: /* CHECK IF BLOCK DEVICE REMOVABLE */
2365 TRACE( "IOCTL - CHECK IF BLOCK DEVICE REMOVABLE - %c:\n",
2368 if (drivetype
== DRIVE_REMOVABLE
)
2369 SET_AX( context
, 0 ); /* removable */
2371 SET_AX( context
, 1 ); /* not removable */
2374 case 0x09: /* CHECK IF BLOCK DEVICE REMOTE */
2375 TRACE( "IOCTL - CHECK IF BLOCK DEVICE REMOTE - %c:\n",
2378 if (drivetype
== DRIVE_REMOTE
)
2379 SET_DX( context
, (1<<9) | (1<<12) ); /* remote + no direct IO */
2380 else if (drivetype
== DRIVE_CDROM
)
2381 /* CDROM should be set to remote. If it set the app will
2382 * call int2f to check if it cdrom or remote drive. */
2383 SET_DX( context
, (1<<12) );
2384 else if (drivetype
== DRIVE_FIXED
)
2385 /* This should define if drive support 0x0d, 0x0f and 0x08
2386 * requests. The local fixed drive should do. */
2387 SET_DX( context
, (1<<11) );
2389 SET_DX( context
, 0 ); /* FIXME: use driver attr here */
2392 case 0x0d: /* GENERIC BLOCK DEVICE REQUEST */
2393 /* Get pointer to IOCTL parameter block */
2394 dataptr
= CTX_SEG_OFF_TO_LIN(context
, context
->SegDs
, context
->Edx
);
2396 switch (CX_reg(context
))
2398 case 0x0841: /* write logical device track */
2399 TRACE( "GENERIC IOCTL - Write logical device track - %c:\n",
2402 WORD head
= *(WORD
*)(dataptr
+1);
2403 WORD cyl
= *(WORD
*)(dataptr
+3);
2404 WORD sect
= *(WORD
*)(dataptr
+5);
2405 WORD nrsect
= *(WORD
*)(dataptr
+7);
2406 BYTE
*data
= CTX_SEG_OFF_TO_LIN(context
, *(WORD
*)(dataptr
+11), *(WORD
*)(dataptr
+9));
2407 WORD cyl_cnt
, head_cnt
, sec_cnt
;
2409 /* FIXME: we're faking some values here */
2415 SET_AX( context
, ERROR_WRITE_FAULT
);
2426 if (!DOSVM_RawWrite(drive
, INT21_Ioctl_CylHeadSect2Lin(cyl
, head
, sect
, cyl_cnt
, head_cnt
, sec_cnt
), nrsect
, data
, FALSE
))
2428 SET_AX( context
, ERROR_WRITE_FAULT
);
2434 case 0x084a: /* lock logical volume */
2435 TRACE( "GENERIC IOCTL - Lock logical volume, level %d mode %d - %c:\n",
2436 BH_reg(context
), DX_reg(context
), 'A' + drive
);
2439 case 0x0860: /* get device parameters */
2440 /* FIXME: we're faking some values here */
2441 /* used by w4wgrp's winfile */
2442 memset(dataptr
, 0, 0x20); /* DOS 6.22 uses 0x20 bytes */
2444 dataptr
[6] = 0; /* media type */
2447 dataptr
[1] = 0x05; /* fixed disk */
2448 setword(&dataptr
[2], 0x01); /* non removable */
2449 setword(&dataptr
[4], 0x300); /* # of cylinders */
2453 dataptr
[1] = 0x07; /* block dev, floppy */
2454 setword(&dataptr
[2], 0x02); /* removable */
2455 setword(&dataptr
[4], 80); /* # of cylinders */
2457 CreateBPB(drive
, &dataptr
[7], TRUE
);
2458 RESET_CFLAG(context
);
2461 case 0x0861: /* read logical device track */
2462 TRACE( "GENERIC IOCTL - Read logical device track - %c:\n",
2465 WORD head
= *(WORD
*)(dataptr
+1);
2466 WORD cyl
= *(WORD
*)(dataptr
+3);
2467 WORD sect
= *(WORD
*)(dataptr
+5);
2468 WORD nrsect
= *(WORD
*)(dataptr
+7);
2469 BYTE
*data
= CTX_SEG_OFF_TO_LIN(context
, *(WORD
*)(dataptr
+11), *(WORD
*)(dataptr
+9));
2470 WORD cyl_cnt
, head_cnt
, sec_cnt
;
2472 /* FIXME: we're faking some values here */
2486 if (!DOSVM_RawRead(drive
, INT21_Ioctl_CylHeadSect2Lin(cyl
, head
, sect
, cyl_cnt
, head_cnt
, sec_cnt
), nrsect
, data
, FALSE
))
2488 SET_AX( context
, ERROR_READ_FAULT
);
2494 case 0x0866: /* get volume serial number */
2496 WCHAR label
[12],fsname
[9];
2498 TRACE( "GENERIC IOCTL - Get media id - %c:\n",
2501 GetVolumeInformationW(drivespec
, label
, 12, &serial
, NULL
, NULL
, fsname
, 9);
2502 *(WORD
*)dataptr
= 0;
2503 memcpy(dataptr
+2,&serial
,4);
2504 WideCharToMultiByte(CP_OEMCP
, 0, label
, 11, (LPSTR
)dataptr
+ 6, 11, NULL
, NULL
);
2505 WideCharToMultiByte(CP_OEMCP
, 0, fsname
, 8, (LPSTR
)dataptr
+ 17, 8, NULL
, NULL
);
2509 case 0x086a: /* unlock logical volume */
2510 TRACE( "GENERIC IOCTL - Logical volume unlocked - %c:\n",
2514 case 0x086f: /* get drive map information */
2515 memset(dataptr
+1, '\0', dataptr
[0]-1);
2516 dataptr
[1] = dataptr
[0];
2517 dataptr
[2] = 0x07; /* protected mode driver; no eject; no notification */
2518 dataptr
[3] = 0xFF; /* no physical drive */
2522 /* Trial and error implementation */
2523 SET_AX( context
, drivetype
== DRIVE_UNKNOWN
? 0x0f : 0x01 );
2524 SET_CFLAG(context
); /* Seems to be set all the time */
2528 INT_BARF( context
, 0x21 );
2532 case 0x0e: /* GET LOGICAL DRIVE MAP */
2533 TRACE( "IOCTL - GET LOGICAL DRIVE MAP - %c:\n",
2535 /* FIXME: this is not correct if drive has mappings */
2536 SET_AL( context
, 0 ); /* drive has no mapping */
2539 case 0x0f: /* SET LOGICAL DRIVE MAP */
2540 TRACE("IOCTL - SET LOGICAL DRIVE MAP for drive %s\n",
2541 INT21_DriveName( BL_reg(context
)));
2542 /* FIXME: as of today, we don't support logical drive mapping... */
2543 SET_AL( context
, 0 );
2546 case 0x11: /* QUERY GENERIC IOCTL CAPABILITY */
2548 INT_BARF( context
, 0x21 );
2553 /***********************************************************************
2554 * INT21_IoctlScsiMgrHandler
2556 * IOCTL handler for the SCSIMGR device.
2558 static void INT21_IoctlScsiMgrHandler( CONTEXT
*context
)
2560 switch (AL_reg(context
))
2562 case 0x00: /* GET DEVICE INFORMATION */
2563 SET_DX( context
, 0xc0c0 );
2566 case 0x0a: /* CHECK IF HANDLE IS REMOTE */
2567 SET_DX( context
, 0 );
2570 case 0x01: /* SET DEVICE INFORMATION */
2571 case 0x02: /* READ FROM CHARACTER DEVICE CONTROL CHANNEL */
2572 case 0x03: /* WRITE TO CHARACTER DEVICE CONTROL CHANNEL */
2573 case 0x06: /* GET INPUT STATUS */
2574 case 0x07: /* GET OUTPUT STATUS */
2575 case 0x0c: /* GENERIC CHARACTER DEVICE REQUEST */
2576 case 0x10: /* QUERY GENERIC IOCTL CAPABILITY */
2578 INT_BARF( context
, 0x21 );
2584 /***********************************************************************
2585 * INT21_IoctlHPScanHandler
2587 * IOCTL handler for the HPSCAN device.
2589 static void INT21_IoctlHPScanHandler( CONTEXT
*context
)
2591 switch (AL_reg(context
))
2593 case 0x00: /* GET DEVICE INFORMATION */
2594 SET_DX( context
, 0xc0c0 );
2597 case 0x0a: /* CHECK IF HANDLE IS REMOTE */
2598 SET_DX( context
, 0 );
2601 case 0x01: /* SET DEVICE INFORMATION */
2602 case 0x02: /* READ FROM CHARACTER DEVICE CONTROL CHANNEL */
2603 case 0x03: /* WRITE TO CHARACTER DEVICE CONTROL CHANNEL */
2604 case 0x06: /* GET INPUT STATUS */
2605 case 0x07: /* GET OUTPUT STATUS */
2606 case 0x0c: /* GENERIC CHARACTER DEVICE REQUEST */
2607 case 0x10: /* QUERY GENERIC IOCTL CAPABILITY */
2609 INT_BARF( context
, 0x21 );
2615 /***********************************************************************
2618 * Handler for character device IOCTLs.
2620 static void INT21_Ioctl_Char( CONTEXT
*context
)
2623 BOOL IsConsoleIOHandle
= FALSE
;
2625 FILE_INTERNAL_INFORMATION info
;
2626 HANDLE handle
= DosFileHandleToWin32Handle(BX_reg(context
));
2628 status
= NtQueryInformationFile( handle
, &io
, &info
, sizeof(info
), FileInternalInformation
);
2631 if( VerifyConsoleIoHandle( handle
))
2632 IsConsoleIOHandle
= TRUE
;
2634 SET_AX( context
, RtlNtStatusToDosError(status
) );
2635 SET_CFLAG( context
);
2640 for (i
= 0; i
< ARRAY_SIZE(magic_devices
); i
++)
2642 if (!magic_devices
[i
].handle
) continue;
2643 if (magic_devices
[i
].index
.QuadPart
== info
.IndexNumber
.QuadPart
)
2646 magic_devices
[i
].ioctl_handler( context
);
2652 /* no magic device found, do default handling */
2654 switch (AL_reg(context
))
2656 case 0x00: /* GET DEVICE INFORMATION */
2657 TRACE( "IOCTL - GET DEVICE INFORMATION - %d\n", BX_reg(context
) );
2658 if (IsConsoleIOHandle
|| GetFileType(handle
) == FILE_TYPE_CHAR
)
2661 * Returns attribute word in DX:
2662 * Bit 14 - Device driver can process IOCTL requests.
2663 * Bit 13 - Output until busy supported.
2664 * Bit 11 - Driver supports OPEN/CLOSE calls.
2666 * Bit 7 - Set (indicates device).
2667 * Bit 6 - EOF on input.
2668 * Bit 5 - Raw (binary) mode.
2669 * Bit 4 - Device is special (uses int29).
2670 * Bit 3 - Clock device.
2671 * Bit 2 - NUL device.
2672 * Bit 1 - Console output device.
2673 * Bit 0 - Console input device.
2675 SET_DX( context
, IsConsoleIOHandle
? 0x80c3 : 0x80c0 /* FIXME */ );
2680 * Returns attribute word in DX:
2681 * Bit 15 - File is remote.
2682 * Bit 14 - Don't set file date/time on closing.
2683 * Bit 11 - Media not removable.
2684 * Bit 8 - Generate int24 if no disk space on write
2685 * or read past end of file
2686 * Bit 7 - Clear (indicates file).
2687 * Bit 6 - File has not been written.
2688 * Bit 5..0 - Drive number (0=A:,...)
2690 * FIXME: Should check if file is on remote or removable drive.
2691 * FIXME: Should use drive file is located on (and not current).
2693 SET_DX( context
, 0x0140 + INT21_GetCurrentDrive() );
2697 case 0x0a: /* CHECK IF HANDLE IS REMOTE */
2698 TRACE( "IOCTL - CHECK IF HANDLE IS REMOTE - %d\n", BX_reg(context
) );
2700 * Returns attribute word in DX:
2701 * Bit 15 - Set if remote.
2702 * Bit 14 - Set if date/time not set on close.
2704 * FIXME: Should check if file is on remote drive.
2706 SET_DX( context
, 0 );
2709 case 0x01: /* SET DEVICE INFORMATION */
2710 case 0x02: /* READ FROM CHARACTER DEVICE CONTROL CHANNEL */
2711 case 0x03: /* WRITE TO CHARACTER DEVICE CONTROL CHANNEL */
2712 case 0x06: /* GET INPUT STATUS */
2713 case 0x07: /* GET OUTPUT STATUS */
2714 case 0x0c: /* GENERIC CHARACTER DEVICE REQUEST */
2715 case 0x10: /* QUERY GENERIC IOCTL CAPABILITY */
2717 INT_BARF( context
, 0x21 );
2723 /***********************************************************************
2726 * Handler for function 0x44.
2728 static void INT21_Ioctl( CONTEXT
*context
)
2730 switch (AL_reg(context
))
2736 INT21_Ioctl_Char( context
);
2741 INT21_Ioctl_Block( context
);
2746 INT21_Ioctl_Char( context
);
2751 INT21_Ioctl_Block( context
);
2755 INT21_Ioctl_Char( context
);
2758 case 0x0b: /* SET SHARING RETRY COUNT */
2759 TRACE( "SET SHARING RETRY COUNT: Pause %d, retries %d.\n",
2760 CX_reg(context
), DX_reg(context
) );
2761 if (!CX_reg(context
))
2763 SET_AX( context
, 1 );
2764 SET_CFLAG( context
);
2768 RESET_CFLAG( context
);
2773 INT21_Ioctl_Char( context
);
2779 INT21_Ioctl_Block( context
);
2783 INT21_Ioctl_Char( context
);
2787 INT21_Ioctl_Block( context
);
2790 case 0x12: /* DR DOS - DETERMINE DOS TYPE (OBSOLETE FUNCTION) */
2791 TRACE( "DR DOS - DETERMINE DOS TYPE (OBSOLETE FUNCTION)\n" );
2792 SET_CFLAG(context
); /* Error / This is not DR DOS. */
2793 SET_AX( context
, 0x0001 ); /* Invalid function */
2796 case 0x52: /* DR DOS - DETERMINE DOS TYPE */
2797 TRACE( "DR DOS - DETERMINE DOS TYPE\n" );
2798 SET_CFLAG(context
); /* Error / This is not DR DOS. */
2799 SET_AX( context
, 0x0001 ); /* Invalid function */
2802 case 0xe0: /* Sun PC-NFS API */
2803 TRACE( "Sun PC-NFS API\n" );
2808 INT_BARF( context
, 0x21 );
2813 /***********************************************************************
2816 * Handler for function 0x73.
2818 static BOOL
INT21_Fat32( CONTEXT
*context
)
2820 switch (AL_reg(context
))
2822 case 0x02: /* FAT32 - GET EXTENDED DPB */
2824 BYTE drive
= INT21_MapDrive( DL_reg(context
) );
2825 WORD
*ptr
= CTX_SEG_OFF_TO_LIN(context
,
2826 context
->SegEs
, context
->Edi
);
2827 INT21_DPB
*target
= (INT21_DPB
*)(ptr
+ 1);
2830 TRACE( "FAT32 - GET EXTENDED DPB %d\n", DL_reg(context
) );
2832 if ( CX_reg(context
) < sizeof(INT21_DPB
) + 2 || *ptr
< sizeof(INT21_DPB
) )
2834 SetLastError( ERROR_BAD_LENGTH
);
2838 if ( !INT21_FillDrivePB( drive
) )
2840 SetLastError( ERROR_INVALID_DRIVE
);
2844 source
= &INT21_GetHeapPointer()->misc_dpb_list
[drive
];
2846 *ptr
= sizeof(INT21_DPB
);
2849 if (LOWORD(context
->Esi
) != 0xF1A6)
2851 target
->driver_header
= 0;
2856 FIXME( "Caller requested driver and next DPB pointers!\n" );
2861 case 0x03: /* FAT32 - GET EXTENDED FREE SPACE ON DRIVE */
2863 WCHAR dirW
[MAX_PATH
];
2864 char *dirA
= CTX_SEG_OFF_TO_LIN( context
,
2865 context
->SegDs
, context
->Edx
);
2866 BYTE
*data
= CTX_SEG_OFF_TO_LIN( context
,
2867 context
->SegEs
, context
->Edi
);
2868 DWORD cluster_sectors
;
2870 DWORD free_clusters
;
2871 DWORD total_clusters
;
2873 TRACE( "FAT32 - GET EXTENDED FREE SPACE ON DRIVE %s\n", dirA
);
2874 MultiByteToWideChar(CP_OEMCP
, 0, dirA
, -1, dirW
, MAX_PATH
);
2876 if (CX_reg(context
) < 44)
2878 SetLastError( ERROR_BAD_LENGTH
);
2882 if (!GetDiskFreeSpaceW( dirW
, &cluster_sectors
, §or_bytes
,
2883 &free_clusters
, &total_clusters
))
2886 *(WORD
*) (data
+ 0) = 44; /* size of structure */
2887 *(WORD
*) (data
+ 2) = 0; /* version */
2888 *(DWORD
*)(data
+ 4) = cluster_sectors
;
2889 *(DWORD
*)(data
+ 8) = sector_bytes
;
2890 *(DWORD
*)(data
+ 12) = free_clusters
;
2891 *(DWORD
*)(data
+ 16) = total_clusters
;
2894 * Below we have free/total sectors and
2895 * free/total allocation units without adjustment
2896 * for compression. We fake both using cluster information.
2898 *(DWORD
*)(data
+ 20) = free_clusters
* cluster_sectors
;
2899 *(DWORD
*)(data
+ 24) = total_clusters
* cluster_sectors
;
2900 *(DWORD
*)(data
+ 28) = free_clusters
;
2901 *(DWORD
*)(data
+ 32) = total_clusters
;
2904 * Between (data + 36) and (data + 43) there
2905 * are eight reserved bytes.
2911 INT_BARF( context
, 0x21 );
2917 static void INT21_ConvertFindDataWtoA(WIN32_FIND_DATAA
*dataA
,
2918 const WIN32_FIND_DATAW
*dataW
)
2920 dataA
->dwFileAttributes
= dataW
->dwFileAttributes
;
2921 dataA
->ftCreationTime
= dataW
->ftCreationTime
;
2922 dataA
->ftLastAccessTime
= dataW
->ftLastAccessTime
;
2923 dataA
->ftLastWriteTime
= dataW
->ftLastWriteTime
;
2924 dataA
->nFileSizeHigh
= dataW
->nFileSizeHigh
;
2925 dataA
->nFileSizeLow
= dataW
->nFileSizeLow
;
2926 WideCharToMultiByte( CP_OEMCP
, 0, dataW
->cFileName
, -1,
2927 dataA
->cFileName
, sizeof(dataA
->cFileName
), NULL
, NULL
);
2928 WideCharToMultiByte( CP_OEMCP
, 0, dataW
->cAlternateFileName
, -1,
2929 dataA
->cAlternateFileName
, sizeof(dataA
->cAlternateFileName
), NULL
, NULL
);
2932 /***********************************************************************
2933 * INT21_LongFilename
2935 * Handler for function 0x71.
2937 static void INT21_LongFilename( CONTEXT
*context
)
2939 BOOL bSetDOSExtendedError
= FALSE
;
2940 WCHAR pathW
[MAX_PATH
];
2943 switch (AL_reg(context
))
2945 case 0x0d: /* RESET DRIVE */
2946 INT_BARF( context
, 0x21 );
2949 case 0x39: /* LONG FILENAME - MAKE DIRECTORY */
2950 if (!INT21_CreateDirectory( context
))
2951 bSetDOSExtendedError
= TRUE
;
2954 case 0x3a: /* LONG FILENAME - REMOVE DIRECTORY */
2955 pathA
= CTX_SEG_OFF_TO_LIN(context
, context
->SegDs
, context
->Edx
);
2957 TRACE( "LONG FILENAME - REMOVE DIRECTORY %s\n", pathA
);
2958 MultiByteToWideChar(CP_OEMCP
, 0, pathA
, -1, pathW
, MAX_PATH
);
2959 if (!RemoveDirectoryW( pathW
)) bSetDOSExtendedError
= TRUE
;
2962 case 0x3b: /* LONG FILENAME - CHANGE DIRECTORY */
2963 if (!INT21_SetCurrentDirectory( context
))
2964 bSetDOSExtendedError
= TRUE
;
2967 case 0x41: /* LONG FILENAME - DELETE FILE */
2968 pathA
= CTX_SEG_OFF_TO_LIN(context
, context
->SegDs
, context
->Edx
);
2970 TRACE( "LONG FILENAME - DELETE FILE %s\n", pathA
);
2971 MultiByteToWideChar(CP_OEMCP
, 0, pathA
, -1, pathW
, MAX_PATH
);
2973 if (!DeleteFileW( pathW
)) bSetDOSExtendedError
= TRUE
;
2976 case 0x43: /* LONG FILENAME - EXTENDED GET/SET FILE ATTRIBUTES */
2977 if (!INT21_FileAttributes( context
, BL_reg(context
), TRUE
))
2978 bSetDOSExtendedError
= TRUE
;
2981 case 0x47: /* LONG FILENAME - GET CURRENT DIRECTORY */
2982 if (!INT21_GetCurrentDirectory( context
, TRUE
))
2983 bSetDOSExtendedError
= TRUE
;
2986 case 0x4e: /* LONG FILENAME - FIND FIRST MATCHING FILE */
2990 WIN32_FIND_DATAW dataW
;
2991 WIN32_FIND_DATAA
* dataA
;
2993 pathA
= CTX_SEG_OFF_TO_LIN(context
, context
->SegDs
,context
->Edx
);
2994 TRACE(" LONG FILENAME - FIND FIRST MATCHING FILE for %s\n", pathA
);
2996 MultiByteToWideChar(CP_OEMCP
, 0, pathA
, -1, pathW
, MAX_PATH
);
2997 handle
= FindFirstFileW(pathW
, &dataW
);
2999 dataA
= CTX_SEG_OFF_TO_LIN(context
, context
->SegEs
, context
->Edi
);
3000 if (handle
!= INVALID_HANDLE_VALUE
&&
3001 (h16
= GlobalAlloc16(GMEM_MOVEABLE
, sizeof(handle
))))
3003 HANDLE
* ptr
= GlobalLock16( h16
);
3005 GlobalUnlock16( h16
);
3006 SET_AX( context
, h16
);
3007 INT21_ConvertFindDataWtoA(dataA
, &dataW
);
3011 if (handle
!= INVALID_HANDLE_VALUE
) FindClose(handle
);
3012 SET_AX( context
, INVALID_HANDLE_VALUE16
);
3013 bSetDOSExtendedError
= TRUE
;
3018 case 0x4f: /* LONG FILENAME - FIND NEXT MATCHING FILE */
3020 HGLOBAL16 h16
= BX_reg(context
);
3022 WIN32_FIND_DATAW dataW
;
3023 WIN32_FIND_DATAA
* dataA
;
3025 TRACE("LONG FILENAME - FIND NEXT MATCHING FILE for handle %d\n",
3028 dataA
= CTX_SEG_OFF_TO_LIN(context
, context
->SegEs
, context
->Edi
);
3030 if (h16
!= INVALID_HANDLE_VALUE16
&& (ptr
= GlobalLock16( h16
)))
3032 if (!FindNextFileW(*ptr
, &dataW
)) bSetDOSExtendedError
= TRUE
;
3033 else INT21_ConvertFindDataWtoA(dataA
, &dataW
);
3034 GlobalUnlock16( h16
);
3038 SetLastError( ERROR_INVALID_HANDLE
);
3039 bSetDOSExtendedError
= TRUE
;
3044 case 0x56: /* LONG FILENAME - RENAME FILE */
3045 if (!INT21_RenameFile(context
))
3046 bSetDOSExtendedError
= TRUE
;
3049 case 0x60: /* LONG FILENAME - CONVERT PATH */
3051 WCHAR res
[MAX_PATH
];
3053 switch (CL_reg(context
))
3055 case 0x00: /* "truename" - Canonicalize path */
3057 * FIXME: This is not 100% equal to 0x01 case,
3058 * if you fix this, fix int21 subfunction 0x60, too.
3061 case 0x01: /* Get short filename or path */
3062 MultiByteToWideChar(CP_OEMCP
, 0, CTX_SEG_OFF_TO_LIN(context
, context
->SegDs
, context
->Esi
), -1, pathW
, MAX_PATH
);
3063 if (!GetShortPathNameW(pathW
, res
, 67))
3064 bSetDOSExtendedError
= TRUE
;
3067 SET_AX( context
, 0 );
3068 WideCharToMultiByte(CP_OEMCP
, 0, res
, -1,
3069 CTX_SEG_OFF_TO_LIN(context
, context
->SegEs
, context
->Edi
),
3074 case 0x02: /* Get canonical long filename or path */
3075 MultiByteToWideChar(CP_OEMCP
, 0, CTX_SEG_OFF_TO_LIN(context
, context
->SegDs
, context
->Esi
), -1, pathW
, MAX_PATH
);
3076 if (!GetFullPathNameW(pathW
, 128, res
, NULL
))
3077 bSetDOSExtendedError
= TRUE
;
3080 SET_AX( context
, 0 );
3081 WideCharToMultiByte(CP_OEMCP
, 0, res
, -1,
3082 CTX_SEG_OFF_TO_LIN(context
, context
->SegEs
, context
->Edi
),
3087 FIXME("Unimplemented long file name function:\n");
3088 INT_BARF( context
, 0x21 );
3090 SET_AL( context
, 0 );
3096 case 0x6c: /* LONG FILENAME - CREATE OR OPEN FILE */
3097 if (!INT21_CreateFile( context
, context
->Esi
, TRUE
,
3098 BX_reg(context
), DL_reg(context
) ))
3099 bSetDOSExtendedError
= TRUE
;
3102 case 0xa0: /* LONG FILENAME - GET VOLUME INFORMATION */
3104 DWORD filename_len
, flags
;
3107 pathA
= CTX_SEG_OFF_TO_LIN(context
, context
->SegDs
,context
->Edx
);
3109 TRACE("LONG FILENAME - GET VOLUME INFORMATION for drive having root dir '%s'.\n", pathA
);
3110 SET_AX( context
, 0 );
3111 MultiByteToWideChar(CP_OEMCP
, 0, pathA
, -1, pathW
, MAX_PATH
);
3112 if (!GetVolumeInformationW( pathW
, NULL
, 0, NULL
, &filename_len
,
3115 INT_BARF( context
, 0x21 );
3119 SET_BX( context
, flags
| 0x4000 ); /* support for LFN functions */
3120 SET_CX( context
, filename_len
);
3121 SET_DX( context
, MAX_PATH
); /* FIXME: which len if DRIVE_SHORT_NAMES ? */
3122 WideCharToMultiByte(CP_OEMCP
, 0, dstW
, -1,
3123 CTX_SEG_OFF_TO_LIN(context
, context
->SegEs
, context
->Edi
),
3128 case 0xa1: /* LONG FILENAME - "FindClose" - TERMINATE DIRECTORY SEARCH */
3130 HGLOBAL16 h16
= BX_reg(context
);
3133 TRACE("LONG FILENAME - FINDCLOSE for handle %d\n",
3135 if (h16
!= INVALID_HANDLE_VALUE16
&& (ptr
= GlobalLock16( h16
)))
3137 if (!FindClose( *ptr
)) bSetDOSExtendedError
= TRUE
;
3138 GlobalUnlock16( h16
);
3139 GlobalFree16( h16
);
3143 SetLastError( ERROR_INVALID_HANDLE
);
3144 bSetDOSExtendedError
= TRUE
;
3149 case 0xa6: /* LONG FILENAME - GET FILE INFO BY HANDLE */
3151 HANDLE handle
= DosFileHandleToWin32Handle(BX_reg(context
));
3152 BY_HANDLE_FILE_INFORMATION
*info
=
3153 CTX_SEG_OFF_TO_LIN(context
, context
->SegDs
, context
->Edx
);
3155 TRACE( "LONG FILENAME - GET FILE INFO BY HANDLE\n" );
3157 if (!GetFileInformationByHandle(handle
, info
))
3158 bSetDOSExtendedError
= TRUE
;
3162 case 0xa7: /* LONG FILENAME - CONVERT TIME */
3163 switch (BL_reg(context
))
3165 case 0x00: /* FILE TIME TO DOS TIME */
3168 FILETIME
*filetime
= CTX_SEG_OFF_TO_LIN(context
,
3172 TRACE( "LONG FILENAME - FILE TIME TO DOS TIME\n" );
3174 FileTimeToDosDateTime( filetime
, &date
, &time
);
3176 SET_DX( context
, date
);
3177 SET_CX( context
, time
);
3180 * FIXME: BH has number of 10-millisecond units
3183 SET_BH( context
, 0 );
3187 case 0x01: /* DOS TIME TO FILE TIME */
3189 FILETIME
*filetime
= CTX_SEG_OFF_TO_LIN(context
,
3193 TRACE( "LONG FILENAME - DOS TIME TO FILE TIME\n" );
3196 * FIXME: BH has number of 10-millisecond units
3199 DosDateTimeToFileTime( DX_reg(context
), CX_reg(context
),
3205 INT_BARF( context
, 0x21 );
3210 case 0xa8: /* LONG FILENAME - GENERATE SHORT FILENAME */
3211 case 0xa9: /* LONG FILENAME - SERVER CREATE OR OPEN FILE */
3212 case 0xaa: /* LONG FILENAME - SUBST */
3214 FIXME("Unimplemented long file name function:\n");
3215 INT_BARF( context
, 0x21 );
3217 SET_AL( context
, 0 );
3221 if (bSetDOSExtendedError
)
3223 SET_AX( context
, GetLastError() );
3224 SET_CFLAG( context
);
3229 /***********************************************************************
3234 * - subfunction 0x56 of function 0x71
3235 * - subfunction 0xff of function 0x43 (CL == 0x56)
3237 static BOOL
INT21_RenameFile( CONTEXT
*context
)
3239 WCHAR fromW
[MAX_PATH
];
3240 WCHAR toW
[MAX_PATH
];
3241 char *fromA
= CTX_SEG_OFF_TO_LIN(context
,
3242 context
->SegDs
,context
->Edx
);
3243 char *toA
= CTX_SEG_OFF_TO_LIN(context
,
3244 context
->SegEs
,context
->Edi
);
3246 TRACE( "RENAME FILE %s to %s\n", fromA
, toA
);
3247 MultiByteToWideChar(CP_OEMCP
, 0, fromA
, -1, fromW
, MAX_PATH
);
3248 MultiByteToWideChar(CP_OEMCP
, 0, toA
, -1, toW
, MAX_PATH
);
3250 return MoveFileW( fromW
, toW
);
3254 /***********************************************************************
3260 static BOOL
INT21_NetworkFunc (CONTEXT
*context
)
3262 switch (AL_reg(context
))
3264 case 0x00: /* Get machine name. */
3266 WCHAR dstW
[MAX_COMPUTERNAME_LENGTH
+ 1];
3267 DWORD s
= ARRAY_SIZE(dstW
);
3270 char *dst
= CTX_SEG_OFF_TO_LIN (context
,context
->SegDs
,context
->Edx
);
3271 TRACE("getting machine name to %p\n", dst
);
3272 if (!GetComputerNameW(dstW
, &s
) ||
3273 !WideCharToMultiByte(CP_OEMCP
, 0, dstW
, -1, dst
, 16, NULL
, NULL
))
3276 SetLastError( ER_NoNetwork
);
3279 for (len
= strlen(dst
); len
< 15; len
++) dst
[len
] = ' ';
3281 SET_CH( context
, 1 ); /* Valid */
3282 SET_CL( context
, 1 ); /* NETbios number??? */
3283 TRACE("returning %s\n", debugstr_an(dst
, 16));
3288 SetLastError( ER_NoNetwork
);
3293 /******************************************************************
3294 * INT21_GetDiskSerialNumber
3297 static int INT21_GetDiskSerialNumber( CONTEXT
*context
)
3299 BYTE
*dataptr
= CTX_SEG_OFF_TO_LIN(context
, context
->SegDs
, context
->Edx
);
3300 WCHAR path
[] = {'A',':','\\',0}, label
[11];
3303 path
[0] += INT21_MapDrive(BL_reg(context
));
3304 if (!GetVolumeInformationW( path
, label
, 11, &serial
, NULL
, NULL
, NULL
, 0))
3306 SetLastError( ERROR_INVALID_DRIVE
);
3310 *(WORD
*)dataptr
= 0;
3311 memcpy(dataptr
+ 2, &serial
, sizeof(DWORD
));
3312 WideCharToMultiByte(CP_OEMCP
, 0, label
, 11, (LPSTR
)dataptr
+ 6, 11, NULL
, NULL
);
3313 memcpy(dataptr
+ 17, "FAT16 ", 8);
3318 /******************************************************************
3319 * INT21_SetDiskSerialNumber
3322 static BOOL
INT21_SetDiskSerialNumber( CONTEXT
*context
)
3325 BYTE
*dataptr
= CTX_SEG_OFF_TO_LIN(context
, context
->SegDs
, context
->Edx
);
3326 int drive
= INT21_MapDrive(BL_reg(context
));
3328 if (!is_valid_drive(drive
))
3330 SetLastError( ERROR_INVALID_DRIVE
);
3334 DRIVE_SetSerialNumber( drive
, *(DWORD
*)(dataptr
+ 2) );
3337 FIXME("Setting drive serial number is no longer supported\n");
3338 SetLastError( ERROR_NOT_SUPPORTED
);
3344 /******************************************************************
3345 * INT21_GetFreeDiskSpace
3348 static BOOL
INT21_GetFreeDiskSpace( CONTEXT
*context
)
3350 DWORD cluster_sectors
, sector_bytes
, free_clusters
, total_clusters
;
3351 WCHAR root
[] = {'A',':','\\',0};
3352 const DWORD max_clusters
= 0x3d83;
3353 const DWORD max_sectors_per_cluster
= 0x7f;
3354 const DWORD max_bytes_per_sector
= 0x200;
3356 root
[0] += INT21_MapDrive(DL_reg(context
));
3357 if (!GetDiskFreeSpaceW( root
, &cluster_sectors
, §or_bytes
,
3358 &free_clusters
, &total_clusters
))
3361 /* Some old win31 apps (Lotus SmartSuite 5.1) crap out if there's too
3362 * much disk space, so Windows XP seems to apply the following limits:
3363 * cluster_sectors <= 0x7f
3364 * sector size <= 0x200
3365 * clusters <= 0x3D83
3366 * This means total reported space is limited to about 1GB.
3369 /* Make sure bytes-per-sector is in [, max] */
3370 while (sector_bytes
> max_bytes_per_sector
) {
3372 free_clusters
<<= 1;
3373 total_clusters
<<= 1;
3375 /* Then make sure sectors-per-cluster is in [max/2, max]. */
3376 while (cluster_sectors
<= max_sectors_per_cluster
/2) {
3377 cluster_sectors
<<= 1;
3378 free_clusters
>>= 1;
3379 total_clusters
>>= 1;
3381 while (cluster_sectors
> max_sectors_per_cluster
) {
3382 cluster_sectors
>>= 1;
3383 free_clusters
<<= 1;
3384 total_clusters
<<= 1;
3387 /* scale up sectors_per_cluster to exactly max_sectors_per_cluster.
3388 * We could skip this, but that would impose an artificially low
3389 * limit on reported disk space.
3390 * To avoid overflow, first apply a preliminary cap on sector count;
3391 * this will not affect the correctness of the final result,
3392 * because if the preliminary cap hits, the final one will, too.
3394 if (total_clusters
> 4 * max_clusters
)
3395 total_clusters
= 4 * max_clusters
;
3396 if (free_clusters
> 4 * max_clusters
)
3397 free_clusters
= 4 * max_clusters
;
3398 if (cluster_sectors
< max_sectors_per_cluster
) {
3399 free_clusters
*= cluster_sectors
;
3400 free_clusters
/= max_sectors_per_cluster
;
3401 total_clusters
*= cluster_sectors
;
3402 total_clusters
/= max_sectors_per_cluster
;
3403 cluster_sectors
= max_sectors_per_cluster
;
3406 /* Finally, apply real cluster count cap. */
3407 if (total_clusters
> max_clusters
)
3408 total_clusters
= max_clusters
;
3409 if (free_clusters
> max_clusters
)
3410 free_clusters
= max_clusters
;
3412 SET_AX( context
, cluster_sectors
);
3413 SET_BX( context
, free_clusters
);
3414 SET_CX( context
, sector_bytes
);
3415 SET_DX( context
, total_clusters
);
3419 /******************************************************************
3420 * INT21_GetDriveAllocInfo
3423 static BOOL
INT21_GetDriveAllocInfo( CONTEXT
*context
, BYTE drive
)
3427 drive
= INT21_MapDrive( drive
);
3428 if (!INT21_FillDrivePB( drive
)) return FALSE
;
3429 dpb
= &(INT21_GetHeapPointer()->misc_dpb_list
[drive
]);
3430 SET_AL( context
, dpb
->cluster_sectors
+ 1 );
3431 SET_CX( context
, dpb
->sector_bytes
);
3432 SET_DX( context
, dpb
->num_clusters1
);
3434 context
->SegDs
= INT21_GetHeapSelector( context
);
3435 SET_BX( context
, offsetof( INT21_HEAP
, misc_dpb_list
[drive
].media_ID
) );
3439 /***********************************************************************
3440 * INT21_GetExtendedError
3442 static void INT21_GetExtendedError( CONTEXT
*context
)
3444 BYTE
class, action
, locus
;
3445 WORD error
= GetLastError();
3450 class = action
= locus
= 0;
3452 case ERROR_DIR_NOT_EMPTY
:
3457 case ERROR_ACCESS_DENIED
:
3458 class = EC_AccessDenied
;
3462 case ERROR_CANNOT_MAKE
:
3463 class = EC_AccessDenied
;
3467 case ERROR_DISK_FULL
:
3468 case ERROR_HANDLE_DISK_FULL
:
3469 class = EC_MediaError
;
3473 case ERROR_FILE_EXISTS
:
3474 case ERROR_ALREADY_EXISTS
:
3479 case ERROR_FILE_NOT_FOUND
:
3480 case ERROR_PATH_NOT_FOUND
:
3481 case ERROR_INVALID_NAME
:
3482 class = EC_NotFound
;
3486 case ERROR_GEN_FAILURE
:
3487 class = EC_SystemFailure
;
3491 case ERROR_INVALID_DRIVE
:
3492 class = EC_MediaError
;
3496 case ERROR_INVALID_HANDLE
:
3497 class = EC_ProgramError
;
3501 case ERROR_LOCK_VIOLATION
:
3502 class = EC_AccessDenied
;
3506 case ERROR_NO_MORE_FILES
:
3507 class = EC_MediaError
;
3512 class = EC_NotFound
;
3516 case ERROR_NOT_ENOUGH_MEMORY
:
3517 class = EC_OutOfResource
;
3522 class = EC_NotFound
;
3526 case ERROR_SHARING_VIOLATION
:
3527 class = EC_Temporary
;
3531 case ERROR_TOO_MANY_OPEN_FILES
:
3532 class = EC_ProgramError
;
3537 FIXME("Unknown error %d\n", error
);
3538 class = EC_SystemFailure
;
3543 TRACE("GET EXTENDED ERROR code 0x%02x class 0x%02x action 0x%02x locus %02x\n",
3544 error
, class, action
, locus
);
3545 SET_AX( context
, error
);
3546 SET_BH( context
, class );
3547 SET_BL( context
, action
);
3548 SET_CH( context
, locus
);
3551 static BOOL
INT21_CreateTempFile( CONTEXT
*context
)
3553 static int counter
= 0;
3554 char *name
= CTX_SEG_OFF_TO_LIN(context
, context
->SegDs
, context
->Edx
);
3555 char *p
= name
+ strlen(name
);
3557 /* despite what Ralf Brown says, some programs seem to call without
3558 * ending backslash (DOS accepts that, so we accept it too) */
3559 if ((p
== name
) || (p
[-1] != '\\')) *p
++ = '\\';
3563 sprintf( p
, "wine%04x.%03d", (int)getpid(), counter
);
3564 counter
= (counter
+ 1) % 1000;
3567 Win32HandleToDosFileHandle(
3568 CreateFileA( name
, GENERIC_READ
| GENERIC_WRITE
,
3569 FILE_SHARE_READ
| FILE_SHARE_WRITE
, NULL
,
3570 CREATE_NEW
, 0, 0 ) ) );
3571 if (AX_reg(context
) != HFILE_ERROR16
)
3573 TRACE("created %s\n", name
);
3576 if (GetLastError() != ERROR_FILE_EXISTS
) return FALSE
;
3580 /***********************************************************************
3581 * DOSFS_ToDosFCBFormat
3583 * Convert a file name to DOS FCB format (8+3 chars, padded with blanks),
3584 * expanding wild cards and converting to upper-case in the process.
3585 * File name can be terminated by '\0', '\\' or '/'.
3586 * Return FALSE if the name is not a valid DOS name.
3587 * 'buffer' must be at least 12 characters long.
3589 /* Chars we don't want to see in DOS file names */
3590 static BOOL
INT21_ToDosFCBFormat( LPCWSTR name
, LPWSTR buffer
)
3592 static const WCHAR invalid_chars
[] = {'*','?','<','>','|','\\','"','+','=',',',';','[',']',' ','\345',0};
3596 /* Check for "." and ".." */
3601 for(i
= 1; i
< 11; i
++) buffer
[i
] = ' ';
3608 return (!*p
|| (*p
== '/') || (*p
== '\\'));
3611 for (i
= 0; i
< 8; i
++)
3628 if (strchrW( invalid_chars
, *p
)) return FALSE
;
3629 buffer
[i
] = toupperW(*p
);
3637 /* Skip all chars after wildcard up to first dot */
3638 while (*p
&& (*p
!= '/') && (*p
!= '\\') && (*p
!= '.')) p
++;
3642 /* Check if name too long */
3643 if (*p
&& (*p
!= '/') && (*p
!= '\\') && (*p
!= '.')) return FALSE
;
3645 if (*p
== '.') p
++; /* Skip dot */
3647 for (i
= 8; i
< 11; i
++)
3657 return FALSE
; /* Second extension not allowed */
3665 if (strchrW( invalid_chars
, *p
)) return FALSE
;
3666 buffer
[i
] = toupperW(*p
);
3673 /* at most 3 character of the extension are processed
3674 * is something behind this ?
3676 while (*p
== '*' || *p
== ' ') p
++; /* skip wildcards and spaces */
3677 return (!*p
|| (*p
== '/') || (*p
== '\\'));
3680 static HANDLE INT21_FindHandle
;
3681 static const WCHAR
*INT21_FindPath
; /* will point to current dta->fullPath search */
3683 /******************************************************************
3686 static BOOL
INT21_FindFirst( CONTEXT
*context
)
3690 FINDFILE_DTA
*dta
= (FINDFILE_DTA
*)INT21_GetCurrentDTA(context
);
3691 WCHAR maskW
[12], pathW
[MAX_PATH
];
3692 static const WCHAR wildcardW
[] = {'*','.','*',0};
3694 path
= CTX_SEG_OFF_TO_LIN(context
, context
->SegDs
, context
->Edx
);
3695 MultiByteToWideChar(CP_OEMCP
, 0, path
, -1, pathW
, MAX_PATH
);
3697 p
= strrchrW( pathW
, '\\');
3698 q
= strrchrW( pathW
, '/');
3702 if (pathW
[0] && pathW
[1] == ':') p
= pathW
+ 2;
3707 /* Note: terminating NULL in dta->mask overwrites dta->search_attr
3708 * (doesn't matter as it is set below anyway)
3710 if (!INT21_ToDosFCBFormat( p
, maskW
))
3712 SetLastError( ERROR_FILE_NOT_FOUND
);
3713 SET_AX( context
, ERROR_FILE_NOT_FOUND
);
3717 WideCharToMultiByte(CP_OEMCP
, 0, maskW
, 12, dta
->mask
, sizeof(dta
->mask
), NULL
, NULL
);
3719 dta
->fullPath
= HeapAlloc( GetProcessHeap(), 0, sizeof(wildcardW
) + (p
- pathW
)*sizeof(WCHAR
) );
3720 memcpy( dta
->fullPath
, pathW
, (p
- pathW
) * sizeof(WCHAR
) );
3721 memcpy( dta
->fullPath
+ (p
- pathW
), wildcardW
, sizeof(wildcardW
) );
3722 /* we must have a fully qualified file name in dta->fullPath
3723 * (we could have a UNC path, but this would lead to some errors later on)
3725 dta
->drive
= toupperW(dta
->fullPath
[0]) - 'A';
3727 dta
->search_attr
= CL_reg(context
);
3731 /******************************************************************
3734 * Check is a short path name (DTA unicode) matches a mask (FCB ansi)
3736 static BOOL
match_short(LPCWSTR shortW
, LPCSTR maskA
)
3738 WCHAR mask
[11], file
[12];
3741 if (!INT21_ToDosFCBFormat( shortW
, file
)) return FALSE
;
3742 MultiByteToWideChar(CP_OEMCP
, 0, maskA
, 11, mask
, 11);
3743 for (i
= 0; i
< 11; i
++)
3744 if (mask
[i
] != '?' && mask
[i
] != file
[i
]) return FALSE
;
3748 static unsigned INT21_FindHelper(LPCWSTR fullPath
, unsigned drive
, unsigned count
,
3749 LPCSTR mask
, unsigned search_attr
,
3750 WIN32_FIND_DATAW
* entry
)
3754 if ((search_attr
& ~(FA_UNUSED
| FA_ARCHIVE
| FA_RDONLY
)) == FA_LABEL
)
3756 WCHAR path
[] = {' ',':','\\',0};
3758 if (count
) return 0;
3759 path
[0] = drive
+ 'A';
3760 if (!GetVolumeInformationW(path
, entry
->cAlternateFileName
, 13, NULL
, NULL
, NULL
, NULL
, 0)) return 0;
3761 if (!entry
->cAlternateFileName
[0]) return 0;
3762 RtlSecondsSince1970ToTime( 0, (LARGE_INTEGER
*)&entry
->ftCreationTime
);
3763 RtlSecondsSince1970ToTime( 0, (LARGE_INTEGER
*)&entry
->ftLastAccessTime
);
3764 RtlSecondsSince1970ToTime( 0, (LARGE_INTEGER
*)&entry
->ftLastWriteTime
);
3765 entry
->dwFileAttributes
= FA_LABEL
;
3766 entry
->nFileSizeHigh
= entry
->nFileSizeLow
= 0;
3767 TRACE("returning %s as label\n", debugstr_w(entry
->cAlternateFileName
));
3771 if (!INT21_FindHandle
|| INT21_FindPath
!= fullPath
|| count
== 0)
3773 if (INT21_FindHandle
) FindClose(INT21_FindHandle
);
3774 INT21_FindHandle
= FindFirstFileW(fullPath
, entry
);
3775 if (INT21_FindHandle
== INVALID_HANDLE_VALUE
)
3777 INT21_FindHandle
= 0;
3780 INT21_FindPath
= fullPath
;
3781 /* we need to resync search */
3786 while (ncalls
-- != 0)
3788 if (!FindNextFileW(INT21_FindHandle
, entry
))
3790 FindClose(INT21_FindHandle
); INT21_FindHandle
= 0;
3794 while (count
< 0xffff)
3797 /* Check the file attributes, and path */
3798 if (!(entry
->dwFileAttributes
& ~search_attr
) &&
3799 match_short(entry
->cAlternateFileName
[0] ? entry
->cAlternateFileName
: entry
->cFileName
,
3804 if (!FindNextFileW(INT21_FindHandle
, entry
))
3806 FindClose(INT21_FindHandle
); INT21_FindHandle
= 0;
3810 WARN("Too many directory entries in %s\n", debugstr_w(fullPath
) );
3814 /******************************************************************
3817 static BOOL
INT21_FindNext( CONTEXT
*context
)
3819 FINDFILE_DTA
*dta
= (FINDFILE_DTA
*)INT21_GetCurrentDTA(context
);
3820 DWORD attr
= dta
->search_attr
| FA_UNUSED
| FA_ARCHIVE
| FA_RDONLY
;
3821 WIN32_FIND_DATAW entry
;
3824 if (!dta
->fullPath
) return FALSE
;
3826 n
= INT21_FindHelper(dta
->fullPath
, dta
->drive
, dta
->count
,
3827 dta
->mask
, attr
, &entry
);
3830 dta
->fileattr
= entry
.dwFileAttributes
;
3831 dta
->filesize
= entry
.nFileSizeLow
;
3832 FileTimeToDosDateTime( &entry
.ftLastWriteTime
, &dta
->filedate
, &dta
->filetime
);
3833 if (entry
.cAlternateFileName
[0])
3834 WideCharToMultiByte(CP_OEMCP
, 0, entry
.cAlternateFileName
, -1,
3835 dta
->filename
, 13, NULL
, NULL
);
3837 WideCharToMultiByte(CP_OEMCP
, 0, entry
.cFileName
, -1, dta
->filename
, 13, NULL
, NULL
);
3839 if (!memchr(dta
->mask
,'?',11))
3841 /* wildcardless search, release resources in case no findnext will
3842 * be issued, and as a workaround in case file creation messes up
3843 * findnext, as sometimes happens with pkunzip
3845 HeapFree( GetProcessHeap(), 0, dta
->fullPath
);
3846 INT21_FindPath
= dta
->fullPath
= NULL
;
3851 HeapFree( GetProcessHeap(), 0, dta
->fullPath
);
3852 INT21_FindPath
= dta
->fullPath
= NULL
;
3856 /* microsoft's programmers should be shot for using CP/M style int21
3857 calls in Windows for Workgroup's winfile.exe */
3859 /******************************************************************
3860 * INT21_FindFirstFCB
3863 static BOOL
INT21_FindFirstFCB( CONTEXT
*context
)
3865 BYTE
*fcb
= CTX_SEG_OFF_TO_LIN(context
, context
->SegDs
, context
->Edx
);
3868 WCHAR p
[] = {' ',':',};
3870 if (*fcb
== 0xff) pFCB
= (FINDFILE_FCB
*)(fcb
+ 7);
3871 else pFCB
= (FINDFILE_FCB
*)fcb
;
3872 drive
= INT21_MapDrive( pFCB
->drive
);
3873 if (drive
== MAX_DOS_DRIVES
) return FALSE
;
3876 pFCB
->fullPath
= HeapAlloc(GetProcessHeap(), 0, MAX_PATH
* sizeof(WCHAR
));
3877 if (!pFCB
->fullPath
) return FALSE
;
3878 GetLongPathNameW(p
, pFCB
->fullPath
, MAX_PATH
);
3883 /******************************************************************
3887 static BOOL
INT21_FindNextFCB( CONTEXT
*context
)
3889 BYTE
*fcb
= CTX_SEG_OFF_TO_LIN(context
, context
->SegDs
, context
->Edx
);
3891 LPBYTE pResult
= INT21_GetCurrentDTA(context
);
3892 DOS_DIRENTRY_LAYOUT
*ddl
;
3893 WIN32_FIND_DATAW entry
;
3898 if (*fcb
== 0xff) /* extended FCB ? */
3901 pFCB
= (FINDFILE_FCB
*)(fcb
+ 7);
3906 pFCB
= (FINDFILE_FCB
*)fcb
;
3909 if (!pFCB
->fullPath
) return FALSE
;
3910 n
= INT21_FindHelper(pFCB
->fullPath
, INT21_MapDrive( pFCB
->drive
),
3911 pFCB
->count
, pFCB
->filename
, attr
, &entry
);
3914 HeapFree( GetProcessHeap(), 0, pFCB
->fullPath
);
3915 INT21_FindPath
= pFCB
->fullPath
= NULL
;
3922 /* place extended FCB header before pResult if called with extended FCB */
3924 pResult
+= 6; /* leave reserved field behind */
3925 *pResult
++ = entry
.dwFileAttributes
;
3927 *pResult
++ = INT21_MapDrive( pFCB
->drive
); /* DOS_DIRENTRY_LAYOUT after current drive number */
3928 ddl
= (DOS_DIRENTRY_LAYOUT
*)pResult
;
3929 ddl
->fileattr
= entry
.dwFileAttributes
;
3930 ddl
->cluster
= 0; /* what else? */
3931 ddl
->filesize
= entry
.nFileSizeLow
;
3932 memset( ddl
->reserved
, 0, sizeof(ddl
->reserved
) );
3933 FileTimeToDosDateTime( &entry
.ftLastWriteTime
,
3934 &ddl
->filedate
, &ddl
->filetime
);
3936 /* Convert file name to FCB format */
3937 if (entry
.cAlternateFileName
[0])
3938 INT21_ToDosFCBFormat( entry
.cAlternateFileName
, nameW
);
3940 INT21_ToDosFCBFormat( entry
.cFileName
, nameW
);
3941 WideCharToMultiByte(CP_OEMCP
, 0, nameW
, 11, ddl
->filename
, 11, NULL
, NULL
);
3946 /******************************************************************
3947 * INT21_ParseFileNameIntoFCB
3950 static void INT21_ParseFileNameIntoFCB( CONTEXT
*context
)
3953 CTX_SEG_OFF_TO_LIN(context
, context
->SegDs
, context
->Esi
);
3955 CTX_SEG_OFF_TO_LIN(context
, context
->SegEs
, context
->Edi
);
3959 INT buffer_len
, len
;
3961 SET_AL( context
, 0xff ); /* failed */
3963 TRACE("filename: '%s'\n", filename
);
3966 while (*s
&& (*s
!= ' ') && (*s
!= '\r') && (*s
!= '\n'))
3970 buffer_len
= MultiByteToWideChar(CP_OEMCP
, 0, filename
, len
, NULL
, 0);
3971 buffer
= HeapAlloc( GetProcessHeap(), 0, (buffer_len
+ 1) * sizeof(WCHAR
));
3972 len
= MultiByteToWideChar(CP_OEMCP
, 0, filename
, len
, buffer
, buffer_len
);
3974 INT21_ToDosFCBFormat(buffer
, fcbW
);
3975 HeapFree(GetProcessHeap(), 0, buffer
);
3976 WideCharToMultiByte(CP_OEMCP
, 0, fcbW
, 12, fcb
+ 1, 12, NULL
, NULL
);
3978 TRACE("FCB: '%s'\n", fcb
+ 1);
3980 SET_AL( context
, ((strchr(filename
, '*')) || (strchr(filename
, '$'))) != 0 );
3982 /* point DS:SI to first unparsed character */
3983 SET_SI( context
, context
->Esi
+ (int)s
- (int)filename
);
3986 static BOOL
INT21_Dup2(HFILE16 hFile1
, HFILE16 hFile2
)
3988 HFILE16 res
= HFILE_ERROR16
;
3989 HANDLE handle
, new_handle
;
3990 #define DOS_TABLE_SIZE 256
3991 DWORD map
[DOS_TABLE_SIZE
/ 32];
3994 handle
= DosFileHandleToWin32Handle(hFile1
);
3995 if (handle
== INVALID_HANDLE_VALUE
)
3999 /* now loop to allocate the same one... */
4000 memset(map
, 0, sizeof(map
));
4001 for (i
= 0; i
< DOS_TABLE_SIZE
; i
++)
4003 if (!DuplicateHandle(GetCurrentProcess(), handle
,
4004 GetCurrentProcess(), &new_handle
,
4005 0, FALSE
, DUPLICATE_SAME_ACCESS
))
4007 res
= HFILE_ERROR16
;
4010 res
= Win32HandleToDosFileHandle(new_handle
);
4011 if (res
== HFILE_ERROR16
|| res
== hFile2
) break;
4012 map
[res
/ 32] |= 1 << (res
% 32);
4014 /* clean up the allocated slots */
4015 for (i
= 0; i
< DOS_TABLE_SIZE
; i
++)
4017 if (map
[i
/ 32] & (1 << (i
% 32)))
4018 _lclose16((HFILE16
)i
);
4020 return res
== hFile2
;
4024 /***********************************************************************
4025 * DOSVM_Int21Handler
4027 * Interrupt 0x21 handler.
4029 void WINAPI
DOSVM_Int21Handler( CONTEXT
*context
)
4031 BOOL bSetDOSExtendedError
= FALSE
;
4033 TRACE( "AX=%04x BX=%04x CX=%04x DX=%04x "
4034 "SI=%04x DI=%04x DS=%04x ES=%04x EFL=%08x\n",
4035 AX_reg(context
), BX_reg(context
),
4036 CX_reg(context
), DX_reg(context
),
4037 SI_reg(context
), DI_reg(context
),
4038 (WORD
)context
->SegDs
, (WORD
)context
->SegEs
,
4042 * Extended error is used by (at least) functions 0x2f to 0x62.
4043 * Function 0x59 returns extended error and error should not
4044 * be cleared before handling the function.
4046 if (AH_reg(context
) >= 0x2f && AH_reg(context
) != 0x59)
4049 RESET_CFLAG(context
); /* Not sure if this is a good idea. */
4051 switch(AH_reg(context
))
4053 case 0x00: /* TERMINATE PROGRAM */
4054 TRACE("TERMINATE PROGRAM\n");
4058 case 0x01: /* READ CHARACTER FROM STANDARD INPUT, WITH ECHO */
4059 TRACE("DIRECT CHARACTER INPUT WITH ECHO - not supported\n");
4062 case 0x02: /* WRITE CHARACTER TO STANDARD OUTPUT */
4063 TRACE("Write Character to Standard Output\n");
4066 case 0x03: /* READ CHARACTER FROM STDAUX */
4067 case 0x04: /* WRITE CHARACTER TO STDAUX */
4068 case 0x05: /* WRITE CHARACTER TO PRINTER */
4069 INT_BARF( context
, 0x21 );
4072 case 0x06: /* DIRECT CONSOLE IN/OUTPUT */
4073 if (DL_reg(context
) == 0xff)
4075 TRACE("Direct Console Input\n");
4077 /* no character available */
4078 SET_AL( context
, 0 );
4079 SET_ZFLAG( context
);
4083 TRACE("Direct Console Output\n");
4085 * At least DOS versions 2.1-7.0 return character
4086 * that was written in AL register.
4088 SET_AL( context
, DL_reg(context
) );
4092 case 0x07: /* DIRECT CHARACTER INPUT WITHOUT ECHO */
4093 TRACE("DIRECT CHARACTER INPUT WITHOUT ECHO - not supported\n");
4094 SET_AL( context
, 0 );
4097 case 0x08: /* CHARACTER INPUT WITHOUT ECHO */
4098 TRACE("CHARACTER INPUT WITHOUT ECHO - not supported\n");
4099 SET_AL( context
, 0 );
4102 case 0x09: /* WRITE STRING TO STANDARD OUTPUT */
4103 TRACE("WRITE '$'-terminated string from %04X:%04X to stdout\n",
4104 context
->SegDs
, DX_reg(context
) );
4106 LPSTR data
= CTX_SEG_OFF_TO_LIN( context
,
4107 context
->SegDs
, context
->Edx
);
4111 * Do NOT use strchr() to calculate the string length,
4112 * as '\0' is valid string content, too!
4113 * Maybe we should check for non-'$' strings, but DOS doesn't.
4115 while (*p
!= '$') p
++;
4117 WriteFile( DosFileHandleToWin32Handle(1), data
, p
- data
, &w
, NULL
);
4118 SET_AL( context
, '$' ); /* yes, '$' (0x24) gets returned in AL */
4122 case 0x0a: /* BUFFERED INPUT */
4123 TRACE( "BUFFERED INPUT - not supported\n" );
4126 case 0x0b: /* GET STDIN STATUS */
4127 TRACE( "GET STDIN STATUS - not supported\n" );
4128 SET_AL( context
, 0 ); /* no character available */
4131 case 0x0c: /* FLUSH BUFFER AND READ STANDARD INPUT */
4133 BYTE al
= AL_reg(context
); /* Input function to execute after flush. */
4135 TRACE( "FLUSH BUFFER AND READ STANDARD INPUT - 0x%02x\n", al
);
4137 /* FIXME: buffers are not flushed */
4140 * If AL is one of 0x01, 0x06, 0x07, 0x08, or 0x0a,
4141 * int21 function identified by AL will be called.
4143 if(al
== 0x01 || al
== 0x06 || al
== 0x07 || al
== 0x08 || al
== 0x0a)
4145 SET_AH( context
, al
);
4146 DOSVM_Int21Handler( context
);
4151 case 0x0d: /* DISK BUFFER FLUSH */
4152 TRACE("DISK BUFFER FLUSH ignored\n");
4155 case 0x0e: /* SELECT DEFAULT DRIVE */
4156 TRACE( "SELECT DEFAULT DRIVE - %c:\n", 'A' + DL_reg(context
) );
4157 INT21_SetCurrentDrive( DL_reg(context
) );
4158 SET_AL( context
, MAX_DOS_DRIVES
);
4161 case 0x0f: /* OPEN FILE USING FCB */
4162 INT21_OpenFileUsingFCB( context
);
4165 case 0x10: /* CLOSE FILE USING FCB */
4166 INT21_CloseFileUsingFCB( context
);
4169 case 0x11: /* FIND FIRST MATCHING FILE USING FCB */
4170 TRACE("FIND FIRST MATCHING FILE USING FCB %p\n",
4171 CTX_SEG_OFF_TO_LIN(context
, context
->SegDs
, context
->Edx
));
4172 if (!INT21_FindFirstFCB(context
))
4174 SET_AL( context
, 0xff );
4177 /* else fall through */
4179 case 0x12: /* FIND NEXT MATCHING FILE USING FCB */
4180 SET_AL( context
, INT21_FindNextFCB(context
) ? 0x00 : 0xff );
4183 case 0x13: /* DELETE FILE USING FCB */
4184 INT_BARF( context
, 0x21 );
4187 case 0x14: /* SEQUENTIAL READ FROM FCB FILE */
4188 INT21_SequentialReadFromFCB( context
);
4191 case 0x15: /* SEQUENTIAL WRITE TO FCB FILE */
4192 INT21_SequentialWriteToFCB( context
);
4195 case 0x16: /* CREATE OR TRUNCATE FILE USING FCB */
4196 case 0x17: /* RENAME FILE USING FCB */
4197 INT_BARF( context
, 0x21 );
4200 case 0x18: /* NULL FUNCTION FOR CP/M COMPATIBILITY */
4201 SET_AL( context
, 0 );
4204 case 0x19: /* GET CURRENT DEFAULT DRIVE */
4205 SET_AL( context
, INT21_GetCurrentDrive() );
4206 TRACE( "GET CURRENT DRIVE -> %c:\n", 'A' + AL_reg( context
) );
4209 case 0x1a: /* SET DISK TRANSFER AREA ADDRESS */
4210 TRACE( "SET DISK TRANSFER AREA ADDRESS %04X:%04X\n",
4211 context
->SegDs
, DX_reg(context
) );
4213 TDB
*task
= GlobalLock16( GetCurrentTask() );
4214 task
->dta
= MAKESEGPTR( context
->SegDs
, DX_reg(context
) );
4218 case 0x1b: /* GET ALLOCATION INFORMATION FOR DEFAULT DRIVE */
4219 if (!INT21_GetDriveAllocInfo(context
, 0))
4220 SET_AX( context
, 0xffff );
4223 case 0x1c: /* GET ALLOCATION INFORMATION FOR SPECIFIC DRIVE */
4224 if (!INT21_GetDriveAllocInfo(context
, DL_reg(context
)))
4225 SET_AX( context
, 0xffff );
4228 case 0x1d: /* NULL FUNCTION FOR CP/M COMPATIBILITY */
4229 case 0x1e: /* NULL FUNCTION FOR CP/M COMPATIBILITY */
4230 SET_AL( context
, 0 );
4233 case 0x1f: /* GET DRIVE PARAMETER BLOCK FOR DEFAULT DRIVE */
4235 BYTE drive
= INT21_MapDrive( 0 );
4236 TRACE( "GET DPB FOR DEFAULT DRIVE\n" );
4238 if (INT21_FillDrivePB( drive
))
4240 SET_AL( context
, 0x00 ); /* success */
4241 SET_BX( context
, offsetof( INT21_HEAP
, misc_dpb_list
[drive
] ) );
4242 context
->SegDs
= INT21_GetHeapSelector( context
);
4246 SET_AL( context
, 0xff ); /* invalid or network drive */
4251 case 0x20: /* NULL FUNCTION FOR CP/M COMPATIBILITY */
4252 SET_AL( context
, 0 );
4255 case 0x21: /* READ RANDOM RECORD FROM FCB FILE */
4256 INT21_ReadRandomRecordFromFCB( context
);
4259 case 0x22: /* WRITE RANDOM RECORD TO FCB FILE */
4260 INT21_WriteRandomRecordToFCB( context
);
4263 case 0x23: /* GET FILE SIZE FOR FCB */
4264 case 0x24: /* SET RANDOM RECORD NUMBER FOR FCB */
4265 INT_BARF( context
, 0x21 );
4268 case 0x25: /* SET INTERRUPT VECTOR */
4269 TRACE("SET INTERRUPT VECTOR 0x%02x\n",AL_reg(context
));
4271 FARPROC16 ptr
= (FARPROC16
)MAKESEGPTR( context
->SegDs
, DX_reg(context
) );
4272 DOSVM_SetPMHandler16( AL_reg(context
), ptr
);
4276 case 0x26: /* CREATE NEW PROGRAM SEGMENT PREFIX */
4277 INT_BARF( context
, 0x21 );
4280 case 0x27: /* RANDOM BLOCK READ FROM FCB FILE */
4281 INT21_RandomBlockReadFromFCB( context
);
4284 case 0x28: /* RANDOM BLOCK WRITE TO FCB FILE */
4285 INT21_RandomBlockWriteToFCB( context
);
4288 case 0x29: /* PARSE FILENAME INTO FCB */
4289 INT21_ParseFileNameIntoFCB(context
);
4292 case 0x2a: /* GET SYSTEM DATE */
4293 TRACE( "GET SYSTEM DATE\n" );
4296 GetLocalTime( &systime
);
4297 SET_CX( context
, systime
.wYear
);
4298 SET_DH( context
, systime
.wMonth
);
4299 SET_DL( context
, systime
.wDay
);
4300 SET_AL( context
, systime
.wDayOfWeek
);
4304 case 0x2b: /* SET SYSTEM DATE */
4305 TRACE( "SET SYSTEM DATE\n" );
4307 WORD year
= CX_reg(context
);
4308 BYTE month
= DH_reg(context
);
4309 BYTE day
= DL_reg(context
);
4311 if (year
>= 1980 && year
<= 2099 &&
4312 month
>= 1 && month
<= 12 &&
4313 day
>= 1 && day
<= 31)
4315 FIXME( "SetSystemDate(%02d/%02d/%04d): not allowed\n",
4317 SET_AL( context
, 0 ); /* Let's pretend we succeeded */
4321 SET_AL( context
, 0xff ); /* invalid date */
4322 TRACE( "SetSystemDate(%02d/%02d/%04d): invalid date\n",
4328 case 0x2c: /* GET SYSTEM TIME */
4329 TRACE( "GET SYSTEM TIME\n" );
4332 GetLocalTime( &systime
);
4333 SET_CH( context
, systime
.wHour
);
4334 SET_CL( context
, systime
.wMinute
);
4335 SET_DH( context
, systime
.wSecond
);
4336 SET_DL( context
, systime
.wMilliseconds
/ 10 );
4340 case 0x2d: /* SET SYSTEM TIME */
4341 if( CH_reg(context
) >= 24 || CL_reg(context
) >= 60 || DH_reg(context
) >= 60 || DL_reg(context
) >= 100 ) {
4342 TRACE("SetSystemTime(%02d:%02d:%02d.%02d): wrong time\n",
4343 CH_reg(context
), CL_reg(context
),
4344 DH_reg(context
), DL_reg(context
) );
4345 SET_AL( context
, 0xFF );
4349 FIXME("SetSystemTime(%02d:%02d:%02d.%02d): not allowed\n",
4350 CH_reg(context
), CL_reg(context
),
4351 DH_reg(context
), DL_reg(context
) );
4352 SET_AL( context
, 0 ); /* Let's pretend we succeeded */
4356 case 0x2e: /* SET VERIFY FLAG */
4357 TRACE("SET VERIFY FLAG ignored\n");
4358 /* we cannot change the behaviour anyway, so just ignore it */
4361 case 0x2f: /* GET DISK TRANSFER AREA ADDRESS */
4362 TRACE( "GET DISK TRANSFER AREA ADDRESS\n" );
4364 TDB
*task
= GlobalLock16( GetCurrentTask() );
4365 context
->SegEs
= SELECTOROF( task
->dta
);
4366 SET_BX( context
, OFFSETOF( task
->dta
) );
4370 case 0x30: /* GET DOS VERSION */
4371 TRACE( "GET DOS VERSION - %s requested\n",
4372 (AL_reg(context
) == 0x00) ? "OEM number" : "version flag" );
4374 if (AL_reg(context
) == 0x00)
4375 SET_BH( context
, 0xff ); /* OEM number => undefined */
4377 SET_BH( context
, 0x08 ); /* version flag => DOS is in ROM */
4379 SET_AL( context
, HIBYTE(HIWORD(GetVersion16())) ); /* major version */
4380 SET_AH( context
, LOBYTE(HIWORD(GetVersion16())) ); /* minor version */
4382 SET_BL( context
, 0x12 ); /* 0x123456 is 24-bit Wine's serial # */
4383 SET_CX( context
, 0x3456 );
4386 case 0x31: /* TERMINATE AND STAY RESIDENT */
4387 FIXME("TERMINATE AND STAY RESIDENT stub\n");
4390 case 0x32: /* GET DOS DRIVE PARAMETER BLOCK FOR SPECIFIC DRIVE */
4392 BYTE drive
= INT21_MapDrive( DL_reg(context
) );
4393 TRACE( "GET DPB FOR SPECIFIC DRIVE %d\n", DL_reg(context
) );
4395 if (INT21_FillDrivePB( drive
))
4397 SET_AL( context
, 0x00 ); /* success */
4398 SET_BX( context
, offsetof( INT21_HEAP
, misc_dpb_list
[drive
] ) );
4399 context
->SegDs
= INT21_GetHeapSelector( context
);
4403 SET_AL( context
, 0xff ); /* invalid or network drive */
4408 case 0x33: /* MULTIPLEXED */
4409 switch (AL_reg(context
))
4411 case 0x00: /* GET CURRENT EXTENDED BREAK STATE */
4412 TRACE("GET CURRENT EXTENDED BREAK STATE\n");
4413 SET_DL( context
, brk_flag
);
4416 case 0x01: /* SET EXTENDED BREAK STATE */
4417 TRACE("SET CURRENT EXTENDED BREAK STATE\n");
4418 brk_flag
= (DL_reg(context
) > 0) ? 1 : 0;
4421 case 0x02: /* GET AND SET EXTENDED CONTROL-BREAK CHECKING STATE*/
4422 TRACE("GET AND SET EXTENDED CONTROL-BREAK CHECKING STATE\n");
4423 /* ugly coding in order to stay reentrant */
4424 if (DL_reg(context
))
4426 SET_DL( context
, brk_flag
);
4431 SET_DL( context
, brk_flag
);
4436 case 0x05: /* GET BOOT DRIVE */
4437 TRACE("GET BOOT DRIVE\n");
4438 SET_DL( context
, 3 );
4439 /* c: is Wine's bootdrive (a: is 1)*/
4442 case 0x06: /* GET TRUE VERSION NUMBER */
4443 TRACE("GET TRUE VERSION NUMBER\n");
4444 SET_BL( context
, HIBYTE(HIWORD(GetVersion16())) ); /* major */
4445 SET_BH( context
, LOBYTE(HIWORD(GetVersion16())) ); /* minor */
4446 SET_DL( context
, 0x00 ); /* revision */
4447 SET_DH( context
, 0x08 ); /* DOS is in ROM */
4451 INT_BARF( context
, 0x21 );
4456 case 0x34: /* GET ADDRESS OF INDOS FLAG */
4457 TRACE( "GET ADDRESS OF INDOS FLAG\n" );
4458 context
->SegEs
= INT21_GetHeapSelector( context
);
4459 SET_BX( context
, offsetof(INT21_HEAP
, misc_indos
) );
4462 case 0x35: /* GET INTERRUPT VECTOR */
4463 TRACE("GET INTERRUPT VECTOR 0x%02x\n",AL_reg(context
));
4465 FARPROC16 addr
= DOSVM_GetPMHandler16( AL_reg(context
) );
4466 context
->SegEs
= SELECTOROF(addr
);
4467 SET_BX( context
, OFFSETOF(addr
) );
4471 case 0x36: /* GET FREE DISK SPACE */
4472 TRACE("GET FREE DISK SPACE FOR DRIVE %s (limited to about 1GB)\n",
4473 INT21_DriveName( DL_reg(context
) ));
4474 if (!INT21_GetFreeDiskSpace(context
)) SET_AX( context
, 0xffff );
4477 case 0x37: /* SWITCHAR */
4479 switch (AL_reg(context
))
4481 case 0x00: /* "SWITCHAR" - GET SWITCH CHARACTER */
4482 TRACE( "SWITCHAR - GET SWITCH CHARACTER\n" );
4483 SET_AL( context
, 0x00 ); /* success*/
4484 SET_DL( context
, '/' );
4486 case 0x01: /*"SWITCHAR" - SET SWITCH CHARACTER*/
4487 FIXME( "SWITCHAR - SET SWITCH CHARACTER: %c\n",
4489 SET_AL( context
, 0x00 ); /* success*/
4492 INT_BARF( context
, 0x21 );
4498 case 0x38: /* GET COUNTRY-SPECIFIC INFORMATION */
4499 TRACE( "GET COUNTRY-SPECIFIC INFORMATION\n" );
4500 if (AL_reg(context
))
4502 WORD country
= AL_reg(context
);
4503 if (country
== 0xff)
4504 country
= BX_reg(context
);
4505 if (country
!= INT21_GetSystemCountryCode()) {
4506 FIXME( "Requested info on non-default country %04x\n", country
);
4511 if(AX_reg(context
) != 2 )
4513 INT21_FillCountryInformation( CTX_SEG_OFF_TO_LIN(context
,
4516 SET_AX( context
, INT21_GetSystemCountryCode() );
4517 SET_BX( context
, INT21_GetSystemCountryCode() );
4518 RESET_CFLAG(context
);
4522 case 0x39: /* "MKDIR" - CREATE SUBDIRECTORY */
4523 if (!INT21_CreateDirectory( context
))
4524 bSetDOSExtendedError
= TRUE
;
4526 RESET_CFLAG(context
);
4529 case 0x3a: /* "RMDIR" - REMOVE DIRECTORY */
4531 WCHAR dirW
[MAX_PATH
];
4532 char *dirA
= CTX_SEG_OFF_TO_LIN(context
,
4533 context
->SegDs
, context
->Edx
);
4535 TRACE( "REMOVE DIRECTORY %s\n", dirA
);
4537 MultiByteToWideChar(CP_OEMCP
, 0, dirA
, -1, dirW
, MAX_PATH
);
4539 if (!RemoveDirectoryW( dirW
))
4540 bSetDOSExtendedError
= TRUE
;
4542 RESET_CFLAG(context
);
4546 case 0x3b: /* "CHDIR" - SET CURRENT DIRECTORY */
4547 if (!INT21_SetCurrentDirectory( context
))
4548 bSetDOSExtendedError
= TRUE
;
4550 RESET_CFLAG(context
);
4553 case 0x3c: /* "CREAT" - CREATE OR TRUNCATE FILE */
4554 if (!INT21_CreateFile( context
, context
->Edx
, FALSE
,
4555 OF_READWRITE
| OF_SHARE_COMPAT
, 0x12 ))
4556 bSetDOSExtendedError
= TRUE
;
4558 RESET_CFLAG(context
);
4561 case 0x3d: /* "OPEN" - OPEN EXISTING FILE */
4562 if (!INT21_CreateFile( context
, context
->Edx
, FALSE
,
4563 AL_reg(context
), 0x01 ))
4564 bSetDOSExtendedError
= TRUE
;
4566 RESET_CFLAG(context
);
4569 case 0x3e: /* "CLOSE" - CLOSE FILE */
4570 TRACE( "CLOSE handle %d\n", BX_reg(context
) );
4571 if (_lclose16( BX_reg(context
) ) == HFILE_ERROR16
)
4572 bSetDOSExtendedError
= TRUE
;
4574 RESET_CFLAG(context
);
4577 case 0x3f: /* "READ" - READ FROM FILE OR DEVICE */
4578 TRACE( "READ from %d to %04X:%04X for %d bytes\n",
4585 WORD count
= CX_reg(context
);
4586 BYTE
*buffer
= CTX_SEG_OFF_TO_LIN( context
,
4590 /* Some programs pass a count larger than the allocated buffer */
4591 DWORD maxcount
= GetSelectorLimit16( context
->SegDs
) - DX_reg(context
) + 1;
4592 if (count
> maxcount
) count
= maxcount
;
4595 * FIXME: Reading from console (BX=1) in DOS mode
4596 * does not work as it is supposed to work.
4599 RESET_CFLAG(context
); /* set if error */
4600 if (ReadFile( DosFileHandleToWin32Handle(BX_reg(context
)), buffer
, count
, &result
, NULL
))
4601 SET_AX( context
, (WORD
)result
);
4603 bSetDOSExtendedError
= TRUE
;
4607 case 0x40: /* "WRITE" - WRITE TO FILE OR DEVICE */
4608 TRACE( "WRITE from %04X:%04X to handle %d for %d byte\n",
4609 context
->SegDs
, DX_reg(context
),
4610 BX_reg(context
), CX_reg(context
) );
4612 char *ptr
= CTX_SEG_OFF_TO_LIN(context
, context
->SegDs
, context
->Edx
);
4613 HFILE handle
= (HFILE
)DosFileHandleToWin32Handle(BX_reg(context
));
4614 LONG result
= _hwrite( handle
, ptr
, CX_reg(context
) );
4615 if (result
== HFILE_ERROR
)
4616 bSetDOSExtendedError
= TRUE
;
4619 SET_AX( context
, (WORD
)result
);
4620 RESET_CFLAG(context
);
4625 case 0x41: /* "UNLINK" - DELETE FILE */
4627 WCHAR fileW
[MAX_PATH
];
4628 char *fileA
= CTX_SEG_OFF_TO_LIN(context
,
4632 TRACE( "UNLINK %s\n", fileA
);
4633 MultiByteToWideChar(CP_OEMCP
, 0, fileA
, -1, fileW
, MAX_PATH
);
4635 if (!DeleteFileW( fileW
))
4636 bSetDOSExtendedError
= TRUE
;
4638 RESET_CFLAG(context
);
4642 case 0x42: /* "LSEEK" - SET CURRENT FILE POSITION */
4643 TRACE( "LSEEK handle %d offset %d from %s\n",
4645 MAKELONG( DX_reg(context
), CX_reg(context
) ),
4646 (AL_reg(context
) == 0) ?
4647 "start of file" : ((AL_reg(context
) == 1) ?
4648 "current file position" : "end of file") );
4650 HANDLE handle
= DosFileHandleToWin32Handle(BX_reg(context
));
4651 LONG offset
= MAKELONG( DX_reg(context
), CX_reg(context
) );
4652 DWORD status
= SetFilePointer( handle
, offset
,
4653 NULL
, AL_reg(context
) );
4654 if (status
== INVALID_SET_FILE_POINTER
)
4655 bSetDOSExtendedError
= TRUE
;
4658 SET_AX( context
, LOWORD(status
) );
4659 SET_DX( context
, HIWORD(status
) );
4660 RESET_CFLAG(context
);
4665 case 0x43: /* FILE ATTRIBUTES */
4666 if (!INT21_FileAttributes( context
, AL_reg(context
), FALSE
))
4667 bSetDOSExtendedError
= TRUE
;
4669 RESET_CFLAG(context
);
4672 case 0x44: /* IOCTL */
4673 INT21_Ioctl( context
);
4676 case 0x45: /* "DUP" - DUPLICATE FILE HANDLE */
4677 TRACE( "DUPLICATE FILE HANDLE %d\n", BX_reg(context
) );
4680 HFILE handle16
= HFILE_ERROR
;
4682 if (DuplicateHandle( GetCurrentProcess(),
4683 DosFileHandleToWin32Handle(BX_reg(context
)),
4684 GetCurrentProcess(),
4686 0, TRUE
, DUPLICATE_SAME_ACCESS
))
4687 handle16
= Win32HandleToDosFileHandle(handle32
);
4689 if (handle16
== HFILE_ERROR
)
4690 bSetDOSExtendedError
= TRUE
;
4693 SET_AX( context
, handle16
);
4694 RESET_CFLAG(context
);
4699 case 0x46: /* "DUP2", "FORCEDUP" - FORCE DUPLICATE FILE HANDLE */
4700 TRACE( "FORCEDUP - FORCE DUPLICATE FILE HANDLE %d to %d\n",
4701 BX_reg(context
), CX_reg(context
) );
4702 if (!INT21_Dup2(BX_reg(context
), CX_reg(context
)))
4703 bSetDOSExtendedError
= TRUE
;
4705 RESET_CFLAG(context
);
4708 case 0x47: /* "CWD" - GET CURRENT DIRECTORY */
4709 if (!INT21_GetCurrentDirectory( context
, FALSE
))
4710 bSetDOSExtendedError
= TRUE
;
4712 RESET_CFLAG(context
);
4715 case 0x48: /* ALLOCATE MEMORY */
4716 TRACE( "ALLOCATE MEMORY for %d paragraphs\n", BX_reg(context
) );
4718 DWORD bytes
= (DWORD
)BX_reg(context
) << 4;
4719 DWORD rv
= GlobalDOSAlloc16( bytes
);
4720 WORD selector
= LOWORD( rv
);
4724 SET_AX( context
, selector
);
4725 RESET_CFLAG(context
);
4730 SET_AX( context
, 0x0008 ); /* insufficient memory */
4731 SET_BX( context
, DOSMEM_Available() >> 4 );
4736 case 0x49: /* FREE MEMORY */
4737 TRACE( "FREE MEMORY segment %04X\n", context
->SegEs
);
4739 BOOL ok
= !GlobalDOSFree16( context
->SegEs
);
4741 /* If we don't reset ES_reg, we will fail in the relay code */
4742 if (ok
) context
->SegEs
= 0;
4745 TRACE("FREE MEMORY failed\n");
4747 SET_AX( context
, 0x0009 ); /* memory block address invalid */
4752 case 0x4a: /* RESIZE MEMORY BLOCK */
4753 TRACE( "RESIZE MEMORY segment %04X to %d paragraphs\n",
4754 context
->SegEs
, BX_reg(context
) );
4756 FIXME( "Resize memory block - unsupported under Win16\n" );
4761 case 0x4b: /* "EXEC" - LOAD AND/OR EXECUTE PROGRAM */
4763 char *program
= CTX_SEG_OFF_TO_LIN(context
, context
->SegDs
, context
->Edx
);
4764 HINSTANCE16 instance
;
4766 TRACE( "EXEC %s\n", program
);
4768 RESET_CFLAG(context
);
4769 instance
= WinExec16( program
, SW_NORMAL
);
4772 SET_CFLAG( context
);
4773 SET_AX( context
, instance
);
4778 case 0x4c: /* "EXIT" - TERMINATE WITH RETURN CODE */
4779 TRACE( "EXIT with return code %d\n", AL_reg(context
) );
4780 DOSVM_Exit( AL_reg(context
) );
4783 case 0x4d: /* GET RETURN CODE */
4784 TRACE("GET RETURN CODE (ERRORLEVEL)\n");
4785 SET_AX( context
, DOSVM_retval
);
4789 case 0x4e: /* "FINDFIRST" - FIND FIRST MATCHING FILE */
4790 TRACE("FINDFIRST mask 0x%04x spec %s\n",CX_reg(context
),
4791 (LPCSTR
)CTX_SEG_OFF_TO_LIN(context
, context
->SegDs
, context
->Edx
));
4792 if (!INT21_FindFirst(context
)) break;
4795 case 0x4f: /* "FINDNEXT" - FIND NEXT MATCHING FILE */
4796 TRACE("FINDNEXT\n");
4797 if (!INT21_FindNext(context
))
4799 SetLastError( ERROR_NO_MORE_FILES
);
4800 SET_AX( context
, ERROR_NO_MORE_FILES
);
4803 else SET_AX( context
, 0 ); /* OK */
4806 case 0x50: /* SET CURRENT PROCESS ID (SET PSP ADDRESS) */
4807 TRACE("SET CURRENT PROCESS ID (SET PSP ADDRESS)\n");
4808 DOSVM_psp
= BX_reg(context
);
4811 case 0x51: /* GET PSP ADDRESS */
4812 INT21_GetPSP( context
);
4815 case 0x52: /* "SYSVARS" - GET LIST OF LISTS */
4816 TRACE("Get List of Lists - not supported\n");
4818 SET_BX( context
, 0 );
4821 case 0x54: /* Get Verify Flag */
4822 TRACE("Get Verify Flag - Not Supported\n");
4823 SET_AL( context
, 0x00 ); /* pretend we can tell. 00h = off 01h = on */
4826 case 0x56: /* "RENAME" - RENAME FILE */
4827 if (!INT21_RenameFile( context
))
4828 bSetDOSExtendedError
= TRUE
;
4830 RESET_CFLAG(context
);
4833 case 0x57: /* FILE DATE AND TIME */
4834 if (!INT21_FileDateTime( context
))
4835 bSetDOSExtendedError
= TRUE
;
4837 RESET_CFLAG(context
);
4840 case 0x58: /* GET OR SET MEMORY ALLOCATION STRATEGY */
4841 switch (AL_reg(context
))
4843 case 0x00: /* GET MEMORY ALLOCATION STRATEGY */
4844 TRACE( "GET MEMORY ALLOCATION STRATEGY\n" );
4845 SET_AX( context
, 0 ); /* low memory first fit */
4848 case 0x01: /* SET ALLOCATION STRATEGY */
4849 TRACE( "SET MEMORY ALLOCATION STRATEGY to %d - ignored\n",
4853 case 0x02: /* GET UMB LINK STATE */
4854 TRACE( "GET UMB LINK STATE\n" );
4855 SET_AL( context
, 0 ); /* UMBs not part of DOS memory chain */
4858 case 0x03: /* SET UMB LINK STATE */
4859 TRACE( "SET UMB LINK STATE to %d - ignored\n",
4864 INT_BARF( context
, 0x21 );
4868 case 0x59: /* GET EXTENDED ERROR INFO */
4869 INT21_GetExtendedError( context
);
4872 case 0x5a: /* CREATE TEMPORARY FILE */
4873 TRACE("CREATE TEMPORARY FILE\n");
4874 bSetDOSExtendedError
= !INT21_CreateTempFile(context
);
4877 case 0x5b: /* CREATE NEW FILE */
4878 if (!INT21_CreateFile( context
, context
->Edx
, FALSE
,
4879 OF_READWRITE
| OF_SHARE_COMPAT
, 0x10 ))
4880 bSetDOSExtendedError
= TRUE
;
4882 RESET_CFLAG(context
);
4885 case 0x5c: /* "FLOCK" - RECORD LOCKING */
4887 DWORD offset
= MAKELONG(DX_reg(context
), CX_reg(context
));
4888 DWORD length
= MAKELONG(DI_reg(context
), SI_reg(context
));
4889 HANDLE handle
= DosFileHandleToWin32Handle(BX_reg(context
));
4891 RESET_CFLAG(context
);
4892 switch (AL_reg(context
))
4894 case 0x00: /* LOCK */
4895 TRACE( "lock handle %d offset %d length %d\n",
4896 BX_reg(context
), offset
, length
);
4897 if (!LockFile( handle
, offset
, 0, length
, 0 ))
4898 bSetDOSExtendedError
= TRUE
;
4901 case 0x01: /* UNLOCK */
4902 TRACE( "unlock handle %d offset %d length %d\n",
4903 BX_reg(context
), offset
, length
);
4904 if (!UnlockFile( handle
, offset
, 0, length
, 0 ))
4905 bSetDOSExtendedError
= TRUE
;
4909 INT_BARF( context
, 0x21 );
4914 case 0x5d: /* NETWORK 5D */
4915 FIXME( "Network function 5D not implemented.\n" );
4916 SetLastError( ER_NoNetwork
);
4917 bSetDOSExtendedError
= TRUE
;
4920 case 0x5e: /* NETWORK 5E */
4921 bSetDOSExtendedError
= INT21_NetworkFunc( context
);
4924 case 0x5f: /* NETWORK 5F */
4925 /* FIXME: supporting this would need to 1:
4926 * - implement per drive current directory (as kernel32 doesn't)
4927 * - assign enabled/disabled flag on a per drive basis
4929 /* network software not installed */
4930 TRACE("NETWORK function AX=%04x not implemented\n",AX_reg(context
));
4931 SetLastError( ER_NoNetwork
);
4932 bSetDOSExtendedError
= TRUE
;
4935 case 0x60: /* "TRUENAME" - CANONICALIZE FILENAME OR PATH */
4937 WCHAR pathW
[MAX_PATH
], res
[MAX_PATH
];
4938 /* FIXME: likely to be broken */
4940 TRACE("TRUENAME %s\n",
4941 (LPCSTR
)CTX_SEG_OFF_TO_LIN(context
, context
->SegDs
,context
->Esi
));
4942 MultiByteToWideChar(CP_OEMCP
, 0, CTX_SEG_OFF_TO_LIN(context
, context
->SegDs
, context
->Esi
), -1, pathW
, MAX_PATH
);
4943 if (!GetFullPathNameW( pathW
, 128, res
, NULL
))
4944 bSetDOSExtendedError
= TRUE
;
4947 SET_AX( context
, 0 );
4948 WideCharToMultiByte(CP_OEMCP
, 0, res
, -1,
4949 CTX_SEG_OFF_TO_LIN(context
, context
->SegEs
, context
->Edi
),
4955 case 0x61: /* UNUSED */
4956 SET_AL( context
, 0 );
4959 case 0x62: /* GET PSP ADDRESS */
4960 INT21_GetPSP( context
);
4963 case 0x63: /* MISC. LANGUAGE SUPPORT */
4964 switch (AL_reg(context
)) {
4965 case 0x00: /* GET DOUBLE BYTE CHARACTER SET LEAD-BYTE TABLE */
4966 TRACE( "GET DOUBLE BYTE CHARACTER SET LEAD-BYTE TABLE\n" );
4967 context
->SegDs
= INT21_GetHeapSelector( context
);
4968 SET_SI( context
, offsetof(INT21_HEAP
, dbcs_table
) );
4969 SET_AL( context
, 0 ); /* success */
4974 case 0x64: /* OS/2 DOS BOX */
4975 INT_BARF( context
, 0x21 );
4979 case 0x65: /* EXTENDED COUNTRY INFORMATION */
4980 INT21_ExtendedCountryInformation( context
);
4983 case 0x66: /* GLOBAL CODE PAGE TABLE */
4984 switch (AL_reg(context
))
4987 TRACE( "GET GLOBAL CODE PAGE TABLE\n" );
4988 SET_BX( context
, GetOEMCP() );
4989 SET_DX( context
, GetOEMCP() );
4992 FIXME( "SET GLOBAL CODE PAGE TABLE, active %d, system %d - ignored\n",
4993 BX_reg(context
), DX_reg(context
) );
4998 case 0x67: /* SET HANDLE COUNT */
4999 TRACE( "SET HANDLE COUNT to %d\n", BX_reg(context
) );
5000 if (SetHandleCount( BX_reg(context
) ) < BX_reg(context
) )
5001 bSetDOSExtendedError
= TRUE
;
5004 case 0x68: /* "FFLUSH" - COMMIT FILE */
5005 TRACE( "FFLUSH - handle %d\n", BX_reg(context
) );
5006 if (!FlushFileBuffers( DosFileHandleToWin32Handle(BX_reg(context
)) ) &&
5007 GetLastError() != ERROR_ACCESS_DENIED
)
5008 bSetDOSExtendedError
= TRUE
;
5011 case 0x69: /* DISK SERIAL NUMBER */
5012 switch (AL_reg(context
))
5015 TRACE("GET DISK SERIAL NUMBER for drive %s\n",
5016 INT21_DriveName(BL_reg(context
)));
5017 if (!INT21_GetDiskSerialNumber(context
)) bSetDOSExtendedError
= TRUE
;
5018 else SET_AX( context
, 0 );
5022 TRACE("SET DISK SERIAL NUMBER for drive %s\n",
5023 INT21_DriveName(BL_reg(context
)));
5024 if (!INT21_SetDiskSerialNumber(context
)) bSetDOSExtendedError
= TRUE
;
5025 else SET_AX( context
, 1 );
5030 case 0x6a: /* COMMIT FILE */
5031 TRACE( "COMMIT FILE - handle %d\n", BX_reg(context
) );
5032 if (!FlushFileBuffers( DosFileHandleToWin32Handle(BX_reg(context
)) ) &&
5033 GetLastError() != ERROR_ACCESS_DENIED
)
5034 bSetDOSExtendedError
= TRUE
;
5037 case 0x6b: /* NULL FUNCTION FOR CP/M COMPATIBILITY */
5038 SET_AL( context
, 0 );
5041 case 0x6c: /* EXTENDED OPEN/CREATE */
5042 if (!INT21_CreateFile( context
, context
->Esi
, TRUE
,
5043 BX_reg(context
), DL_reg(context
) ))
5044 bSetDOSExtendedError
= TRUE
;
5047 case 0x70: /* MSDOS 7 - GET/SET INTERNATIONALIZATION INFORMATION */
5048 FIXME( "MS-DOS 7 - GET/SET INTERNATIONALIZATION INFORMATION\n" );
5049 SET_CFLAG( context
);
5050 SET_AL( context
, 0 );
5053 case 0x71: /* MSDOS 7 - LONG FILENAME FUNCTIONS */
5054 INT21_LongFilename( context
);
5057 case 0x73: /* MSDOS7 - FAT32 */
5058 RESET_CFLAG( context
);
5059 if (!INT21_Fat32( context
))
5060 bSetDOSExtendedError
= TRUE
;
5063 case 0xdc: /* CONNECTION SERVICES - GET CONNECTION NUMBER */
5064 TRACE( "CONNECTION SERVICES - GET CONNECTION NUMBER - ignored\n" );
5067 case 0xea: /* NOVELL NETWARE - RETURN SHELL VERSION */
5068 TRACE( "NOVELL NETWARE - RETURN SHELL VERSION - ignored\n" );
5071 case 0xff: /* DOS32 EXTENDER (DOS/4GW) - API */
5072 /* we don't implement a DOS32 extender */
5073 TRACE( "DOS32 EXTENDER API - ignored\n" );
5077 INT_BARF( context
, 0x21 );
5080 } /* END OF SWITCH */
5082 /* Set general error condition. */
5083 if (bSetDOSExtendedError
)
5085 SET_AX( context
, GetLastError() );
5086 SET_CFLAG( context
);
5089 /* Print error code if carry flag is set. */
5090 if (context
->EFlags
& 0x0001)
5091 TRACE("failed, error %d\n", GetLastError() );
5093 TRACE( "returning: AX=%04x BX=%04x CX=%04x DX=%04x "
5094 "SI=%04x DI=%04x DS=%04x ES=%04x EFL=%08x\n",
5095 AX_reg(context
), BX_reg(context
),
5096 CX_reg(context
), DX_reg(context
),
5097 SI_reg(context
), DI_reg(context
),
5098 (WORD
)context
->SegDs
, (WORD
)context
->SegEs
,