winedos: Open files with write access on read only media.
[wine] / dlls / winedos / int21.c
1 /*
2  * DOS interrupt 21h handler
3  *
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
10  *
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.
15  *
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.
20  *
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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
24  */
25
26 #include "config.h"
27 #include "wine/port.h"
28
29 #include <stdarg.h>
30 #include <stdio.h>
31 #ifdef HAVE_SYS_STAT_H
32 # include <sys/stat.h>
33 #endif
34 #ifdef HAVE_UNISTD_H
35 # include <unistd.h>
36 #endif
37
38 #include "windef.h"
39 #include "winbase.h"
40 #include "winreg.h"
41 #include "winternl.h"
42 #include "wine/winbase16.h"
43 #include "dosexe.h"
44 #include "winerror.h"
45 #include "winuser.h"
46 #include "wine/unicode.h"
47 #include "wine/server.h"
48 #include "wine/debug.h"
49 #include "wine/exception.h"
50
51 BOOL    WINAPI VerifyConsoleIoHandle(HANDLE);
52 /*
53  * Note:
54  * - Most of the file related functions are wrong. NT's kernel32
55  *   doesn't maintain a per drive current directory, while DOS does. 
56  *   We should in fact keep track in here of those per drive
57  *   directories, and use this info while dealing with partial paths
58  *   (drive defined, but only relative paths). This could even be
59  *   created as an array of CDS (there should be an entry for that in
60  *   the LOL)
61  */
62
63 /*
64  * Forward declarations.
65  */
66 static BOOL INT21_RenameFile( CONTEXT86 *context );
67
68 WINE_DEFAULT_DEBUG_CHANNEL(int21);
69
70
71 #include "pshpack1.h"
72
73 /*
74  * Extended Drive Parameter Block.
75  * This structure is compatible with standard DOS4+ DPB and 
76  * extended DOS7 DPB.
77  */
78 typedef struct _INT21_DPB {
79     BYTE   drive;                /* 00 drive number (0=A, ...) */
80     BYTE   unit;                 /* 01 unit number within device driver */
81     WORD   sector_bytes;         /* 02 bytes per sector */
82     BYTE   cluster_sectors;      /* 04 highest sector number within a cluster */
83     BYTE   shift;                /* 05 shift count to convert clusters into sectors */
84     WORD   num_reserved;         /* 06 reserved sectors at beginning of drive */
85     BYTE   num_FAT;              /* 08 number of FATs */
86     WORD   num_root_entries;     /* 09 number of root directory entries */
87     WORD   first_data_sector;    /* 0b number of first sector containing user data */
88     WORD   num_clusters1;        /* 0d highest cluster number (number of data clusters + 1) */
89     WORD   sectors_per_FAT;      /* 0f number of sectors per FAT */
90     WORD   first_dir_sector;     /* 11 sector number of first directory sector */
91     SEGPTR driver_header;        /* 13 address of device driver header */
92     BYTE   media_ID;             /* 17 media ID byte */
93     BYTE   access_flag;          /* 18 0x00 if disk accessed, 0xff if not */
94     SEGPTR next;                 /* 19 pointer to next DPB */
95     WORD   search_cluster1;      /* 1d cluster at which to start search for free space */
96     WORD   free_clusters_lo;     /* 1f number of free clusters on drive or 0xffff if unknown */
97     WORD   free_clusters_hi;     /* 21 hiword of clusters_free */
98     WORD   mirroring_flags;      /* 23 active FAT/mirroring flags */
99     WORD   info_sector;          /* 25 sector number of file system info sector or 0xffff for none */
100     WORD   spare_boot_sector;    /* 27 sector number of backup boot sector or 0xffff for none */
101     DWORD  first_cluster_sector; /* 29 sector number of the first cluster */
102     DWORD  num_clusters2;        /* 2d maximum cluster number */
103     DWORD  fat_clusters;         /* 31 number of clusters occupied by FAT */
104     DWORD  root_cluster;         /* 35 cluster number of start of root directory */
105     DWORD  search_cluster2;      /* 39 cluster at which to start searching for free space */
106 } INT21_DPB;
107
108
109 /*
110  * Structure for DOS data that can be accessed directly from applications.
111  * Real and protected mode pointers will be returned to this structure so
112  * the structure must be correctly packed.
113  */
114 typedef struct _INT21_HEAP {
115     WORD uppercase_size;             /* Size of the following table in bytes */
116     BYTE uppercase_table[128];       /* Uppercase equivalents of chars from 0x80 to 0xff. */
117
118     WORD lowercase_size;             /* Size of the following table in bytes */
119     BYTE lowercase_table[256];       /* Lowercase equivalents of chars from 0x00 to 0xff. */
120
121     WORD collating_size;             /* Size of the following table in bytes */
122     BYTE collating_table[256];       /* Values used to sort characters from 0x00 to 0xff. */
123
124     WORD filename_size;              /* Size of the following filename data in bytes */
125     BYTE filename_reserved1;         /* 0x01 for MS-DOS 3.30-6.00 */
126     BYTE filename_lowest;            /* Lowest permissible character value for filename */
127     BYTE filename_highest;           /* Highest permissible character value for filename */
128     BYTE filename_reserved2;         /* 0x00 for MS-DOS 3.30-6.00 */
129     BYTE filename_exclude_first;     /* First illegal character in permissible range */
130     BYTE filename_exclude_last;      /* Last illegal character in permissible range */
131     BYTE filename_reserved3;         /* 0x02 for MS-DOS 3.30-6.00 */
132     BYTE filename_illegal_size;      /* Number of terminators in the following table */
133     BYTE filename_illegal_table[16]; /* Characters which terminate a filename */
134
135     WORD dbcs_size;                  /* Number of valid ranges in the following table */
136     BYTE dbcs_table[16];             /* Start/end bytes for N ranges and 00/00 as terminator */
137
138     BYTE      misc_indos;                    /* Interrupt 21 nesting flag */
139     WORD      misc_segment;                  /* Real mode segment for INT21_HEAP */
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 */
142
143 } INT21_HEAP;
144
145
146 struct FCB {
147     BYTE  drive_number;
148     CHAR  file_name[8];
149     CHAR  file_extension[3];
150     WORD  current_block_number;
151     WORD  logical_record_size;
152     DWORD file_size;
153     WORD  date_of_last_write;
154     WORD  time_of_last_write;
155     BYTE  file_number;
156     BYTE  attributes;
157     WORD  starting_cluster;
158     WORD  sequence_number;
159     BYTE  file_attributes;
160     BYTE  unused;
161     BYTE  record_within_current_block;
162     BYTE  random_access_record_number[4];
163 };
164
165
166 struct XFCB {
167     BYTE  xfcb_signature;
168     BYTE  reserved[5];
169     BYTE  xfcb_file_attribute;
170     BYTE  fcb[37];
171 };
172
173 /* DTA layout for FindFirst/FindNext */
174 typedef struct
175 {
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 */
187 } FINDFILE_DTA;
188
189 /* FCB layout for FindFirstFCB/FindNextFCB */
190 typedef struct
191 {
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) */
196 } FINDFILE_FCB;
197
198 /* DOS directory entry for FindFirstFCB/FindNextFCB */
199 typedef struct
200 {
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;
209
210 #include "poppack.h"
211
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 */
221
222 /* Error codes */
223 #define ER_NoNetwork         0x49
224
225 /* Error classes */
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
237
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
244
245 /* Error locus */
246 #define EL_Unknown           0x01
247 #define EL_Disk              0x02
248 #define EL_Network           0x03
249 #define EL_Serial            0x04
250 #define EL_Memory            0x05
251
252 /* BIOS Keyboard Scancodes */
253 #define KEY_LEFT        0x4B
254 #define KEY_RIGHT       0x4D
255 #define KEY_UP          0x48
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
261 #define KEY_END         0x4F
262 #define KEY_NPAGE       0x49
263 #define KEY_PPAGE       0x51
264
265
266 struct magic_device
267 {
268     WCHAR  name[10];
269     HANDLE handle;
270     dev_t  dev;
271     ino_t  ino;
272     void (*ioctl_handler)(CONTEXT86 *);
273 };
274
275 static void INT21_IoctlScsiMgrHandler( CONTEXT86 * );
276 static void INT21_IoctlEMSHandler( CONTEXT86 * );
277 static void INT21_IoctlHPScanHandler( CONTEXT86 * );
278
279 static struct magic_device magic_devices[] =
280 {
281     { {'s','c','s','i','m','g','r','$',0}, NULL, 0, 0, INT21_IoctlScsiMgrHandler },
282     { {'e','m','m','x','x','x','x','0',0}, NULL, 0, 0, INT21_IoctlEMSHandler },
283     { {'h','p','s','c','a','n',0},         NULL, 0, 0, INT21_IoctlHPScanHandler },
284 };
285
286 #define NB_MAGIC_DEVICES  (sizeof(magic_devices)/sizeof(magic_devices[0]))
287
288
289 /* Many calls translate a drive argument like this:
290    drive number (00h = default, 01h = A:, etc)
291    */
292 /******************************************************************
293  *              INT21_DriveName
294  *
295  * Many calls translate a drive argument like this:
296  * drive number (00h = default, 01h = A:, etc)
297  */
298 static const char *INT21_DriveName(int drive)
299 {
300     if (drive > 0) 
301     {
302         if (drive <= 26) return wine_dbg_sprintf("%c:", 'A' + drive - 1);
303         else return wine_dbg_sprintf( "<Bad drive: %d>", drive);
304     }
305     return "default";
306 }
307
308 /***********************************************************************
309  *           INT21_GetCurrentDrive
310  *
311  * Return current drive using scheme (0=A:, 1=B:, 2=C:, ...) or
312  * MAX_DOS_DRIVES on error.
313  */
314 static BYTE INT21_GetCurrentDrive(void)
315 {
316     WCHAR current_directory[MAX_PATH];
317
318     if (!GetCurrentDirectoryW( MAX_PATH, current_directory ) ||
319         current_directory[1] != ':')
320     {
321         TRACE( "Failed to get current drive.\n" );
322         return MAX_DOS_DRIVES;
323     }
324
325     return toupperW( current_directory[0] ) - 'A';
326 }
327
328
329 /***********************************************************************
330  *           INT21_MapDrive
331  *
332  * Convert drive number from scheme (0=default, 1=A:, 2=B:, ...) into
333  * scheme (0=A:, 1=B:, 2=C:, ...) or MAX_DOS_DRIVES on error.
334  */
335 static BYTE INT21_MapDrive( BYTE drive )
336 {
337     if (drive)
338     {
339         WCHAR drivespec[3] = {'A', ':', 0};
340         UINT  drivetype;
341
342         drivespec[0] += drive - 1;
343         drivetype = GetDriveTypeW( drivespec );
344
345         if (drivetype == DRIVE_UNKNOWN || drivetype == DRIVE_NO_ROOT_DIR)
346             return MAX_DOS_DRIVES;
347
348         return drive - 1;
349     }
350
351     return INT21_GetCurrentDrive();
352 }
353
354
355 /***********************************************************************
356  *           INT21_SetCurrentDrive
357  *
358  * Set current drive. Uses scheme (0=A:, 1=B:, 2=C:, ...).
359  */
360 static void INT21_SetCurrentDrive( BYTE drive )
361 {
362     WCHAR drivespec[3] = {'A', ':', 0};
363
364     drivespec[0] += drive;
365
366     if (!SetCurrentDirectoryW( drivespec ))
367         TRACE( "Failed to set current drive.\n" );
368 }
369
370
371 /***********************************************************************
372  *           INT21_ReadChar
373  *
374  * Reads a character from the standard input.
375  * Extended keycodes will be returned as two separate characters.
376  */
377 static BOOL INT21_ReadChar( BYTE *input, CONTEXT86 *waitctx )
378 {
379     static BYTE pending_scan = 0;
380
381     if (pending_scan)
382     {
383         if (input)
384             *input = pending_scan;
385         if (waitctx)
386             pending_scan = 0;
387         return TRUE;
388     }
389     else
390     {
391         BYTE ascii;
392         BYTE scan;
393         if (!DOSVM_Int16ReadChar( &ascii, &scan, waitctx ))
394             return FALSE;
395
396         if (input)
397             *input = ascii;
398         if (waitctx && !ascii)
399             pending_scan = scan;
400         return TRUE;
401     }
402 }
403
404
405 /***********************************************************************
406  *           INT21_GetSystemCountryCode
407  *
408  * Return DOS country code for default system locale.
409  */
410 static WORD INT21_GetSystemCountryCode( void )
411 {
412     /*
413      * FIXME: Determine country code. We should probably use
414      *        DOSCONF structure for that.
415      */
416     return GetSystemDefaultLangID();
417 }
418
419
420 /***********************************************************************
421  *           INT21_FillCountryInformation
422  *
423  * Fill 34-byte buffer with country information data using
424  * default system locale.
425  */
426 static void INT21_FillCountryInformation( BYTE *buffer )
427 {
428     /* 00 - WORD: date format
429      *          00 = mm/dd/yy
430      *          01 = dd/mm/yy
431      *          02 = yy/mm/dd
432      */
433     *(WORD*)(buffer + 0) = 0; /* FIXME: Get from locale */
434
435     /* 02 - BYTE[5]: ASCIIZ currency symbol string */
436     buffer[2] = '$'; /* FIXME: Get from locale */
437     buffer[3] = 0;
438
439     /* 07 - BYTE[2]: ASCIIZ thousands separator */
440     buffer[7] = 0; /* FIXME: Get from locale */
441     buffer[8] = 0;
442
443     /* 09 - BYTE[2]: ASCIIZ decimal separator */
444     buffer[9]  = '.'; /* FIXME: Get from locale */
445     buffer[10] = 0;
446
447     /* 11 - BYTE[2]: ASCIIZ date separator */
448     buffer[11] = '/'; /* FIXME: Get from locale */
449     buffer[12] = 0;
450
451     /* 13 - BYTE[2]: ASCIIZ time separator */
452     buffer[13] = ':'; /* FIXME: Get from locale */
453     buffer[14] = 0;
454
455     /* 15 - BYTE: Currency format
456      *          bit 2 = set if currency symbol replaces decimal point
457      *          bit 1 = number of spaces between value and currency symbol
458      *          bit 0 = 0 if currency symbol precedes value
459      *                  1 if currency symbol follows value
460      */
461     buffer[15] = 0; /* FIXME: Get from locale */
462
463     /* 16 - BYTE: Number of digits after decimal in currency */
464     buffer[16] = 0; /* FIXME: Get from locale */
465
466     /* 17 - BYTE: Time format
467      *          bit 0 = 0 if 12-hour clock
468      *                  1 if 24-hour clock
469      */
470     buffer[17] = 1; /* FIXME: Get from locale */
471
472     /* 18 - DWORD: Address of case map routine */
473     *(DWORD*)(buffer + 18) = 0; /* FIXME: ptr to case map routine */
474
475     /* 22 - BYTE[2]: ASCIIZ data-list separator */
476     buffer[22] = ','; /* FIXME: Get from locale */
477     buffer[23] = 0;
478
479     /* 24 - BYTE[10]: Reserved */
480     memset( buffer + 24, 0, 10 );
481 }
482
483
484 /***********************************************************************
485  *           INT21_FillHeap
486  *
487  * Initialize DOS heap.
488  *
489  * Filename Terminator Table of w2k DE NTVDM:
490  * 16 00 01 00 FF 00 00 20-02 0E 2E 22 2F 5C 5B 5D  ....... ..."/\[]
491  * 3A 7C 3C 3E 2B 3D 3B 2C-00                       :|<>+=;,
492  */
493 static void INT21_FillHeap( INT21_HEAP *heap )
494 {
495     static const char terminators[] = ".\"/\\[]:|<>+=;,";
496     int i;
497
498     /*
499      * Uppercase table.
500      */
501     heap->uppercase_size = 128;
502     for (i = 0; i < 128; i++) 
503         heap->uppercase_table[i] = toupper( 128 + i );
504
505     /*
506      * Lowercase table.
507      */
508     heap->lowercase_size = 256;
509     for (i = 0; i < 256; i++) 
510         heap->lowercase_table[i] = tolower( i );
511     
512     /*
513      * Collating table.
514      */
515     heap->collating_size = 256;
516     for (i = 0; i < 256; i++) 
517         heap->collating_table[i] = i;
518
519     /*
520      * Filename table.
521      */
522     heap->filename_size = 8 + strlen(terminators);
523     heap->filename_illegal_size = strlen(terminators);
524     memcpy( heap->filename_illegal_table, terminators, heap->filename_illegal_size );
525
526     heap->filename_reserved1 = 0x01;
527     heap->filename_lowest = 0x00;
528     heap->filename_highest = 0xff;
529     heap->filename_reserved2 = 0x00;    
530     heap->filename_exclude_first = 0x00;
531     heap->filename_exclude_last = 0x20;
532     heap->filename_reserved3 = 0x02;
533
534     /*
535      * DBCS lead byte table. This table is empty.
536      */
537     heap->dbcs_size = 0;
538     memset( heap->dbcs_table, 0, sizeof(heap->dbcs_table) );
539
540     /*
541      * Initialize InDos flag.
542      */
543     heap->misc_indos = 0;
544
545     /*
546      * FIXME: Should drive parameter blocks (DPB) be
547      *        initialized here and linked to DOS LOL?
548      */
549 }
550
551
552 /***********************************************************************
553  *           INT21_GetHeapPointer
554  *
555  * Get pointer for DOS heap (INT21_HEAP).
556  * Creates and initializes heap on first call.
557  */
558 static INT21_HEAP *INT21_GetHeapPointer( void )
559 {
560     static INT21_HEAP *heap_pointer = NULL;
561
562     if (!heap_pointer)
563     {
564         WORD heap_segment;
565         WORD heap_selector;
566
567         heap_pointer = DOSVM_AllocDataUMB( sizeof(INT21_HEAP), 
568                                            &heap_segment,
569                                            &heap_selector );
570
571         heap_pointer->misc_segment  = heap_segment;
572         heap_pointer->misc_selector = heap_selector;
573         INT21_FillHeap( heap_pointer );
574     }
575
576     return heap_pointer;
577 }
578
579
580 /***********************************************************************
581  *           INT21_GetHeapSelector
582  *
583  * Get segment/selector for DOS heap (INT21_HEAP).
584  * Creates and initializes heap on first call.
585  */
586 static WORD INT21_GetHeapSelector( CONTEXT86 *context )
587 {
588     INT21_HEAP *heap = INT21_GetHeapPointer();
589
590     if (!ISV86(context) && DOSVM_IsWin16())
591         return heap->misc_selector;
592     else
593         return heap->misc_segment;
594 }
595
596
597 /***********************************************************************
598  *           INT21_FillDrivePB
599  *
600  * Fill DOS heap drive parameter block for the specified drive.
601  * Return TRUE if drive was valid and there were
602  * no errors while reading drive information.
603  */
604 static BOOL INT21_FillDrivePB( BYTE drive )
605 {
606     WCHAR       drivespec[3] = {'A', ':', 0};
607     INT21_HEAP *heap = INT21_GetHeapPointer();
608     INT21_DPB  *dpb;
609     UINT        drivetype;
610     DWORD       cluster_sectors;
611     DWORD       sector_bytes;
612     DWORD       free_clusters;
613     DWORD       total_clusters;
614
615     if (drive >= MAX_DOS_DRIVES)
616         return FALSE;
617
618     dpb = &heap->misc_dpb_list[drive];
619     drivespec[0] += drive;
620     drivetype = GetDriveTypeW( drivespec );
621
622     /*
623      * FIXME: Does this check work correctly with floppy/cdrom drives?
624      */
625     if (drivetype == DRIVE_NO_ROOT_DIR || drivetype == DRIVE_UNKNOWN)
626         return FALSE;
627
628     /*
629      * FIXME: Does this check work correctly with floppy/cdrom drives?
630      */
631     if (!GetDiskFreeSpaceW( drivespec, &cluster_sectors, &sector_bytes,
632                             &free_clusters, &total_clusters ))
633         return FALSE;
634
635     /*
636      * FIXME: Most of the values listed below are incorrect.
637      *        All values should be validated.
638      */
639  
640     dpb->drive           = drive;
641     dpb->unit            = 0;
642     dpb->sector_bytes    = sector_bytes;
643     dpb->cluster_sectors = cluster_sectors - 1;
644
645     dpb->shift = 0;
646     while (cluster_sectors > 1)
647     {
648         cluster_sectors /= 2;
649         dpb->shift++;
650     }
651
652     dpb->num_reserved         = 0;
653     dpb->num_FAT              = 1;
654     dpb->num_root_entries     = 2;
655     dpb->first_data_sector    = 2;
656     dpb->num_clusters1        = total_clusters;
657     dpb->sectors_per_FAT      = 1;
658     dpb->first_dir_sector     = 1;
659     dpb->driver_header        = 0;
660     dpb->media_ID             = (drivetype == DRIVE_FIXED) ? 0xF8 : 0xF0;
661     dpb->access_flag          = 0;
662     dpb->next                 = 0;
663     dpb->search_cluster1      = 0;
664     dpb->free_clusters_lo     = LOWORD(free_clusters);
665     dpb->free_clusters_hi     = HIWORD(free_clusters);
666     dpb->mirroring_flags      = 0;
667     dpb->info_sector          = 0xffff;
668     dpb->spare_boot_sector    = 0xffff;
669     dpb->first_cluster_sector = 0;
670     dpb->num_clusters2        = total_clusters;
671     dpb->fat_clusters         = 32;
672     dpb->root_cluster         = 0;
673     dpb->search_cluster2      = 0;
674
675     return TRUE;
676 }
677
678
679 /***********************************************************************
680  *           INT21_GetCurrentDirectory
681  *
682  * Handler for:
683  * - function 0x47
684  * - subfunction 0x47 of function 0x71
685  */
686 static BOOL INT21_GetCurrentDirectory( CONTEXT86 *context, BOOL islong )
687 {
688     char  *buffer = CTX_SEG_OFF_TO_LIN(context, context->SegDs, context->Esi);
689     BYTE   drive = INT21_MapDrive( DL_reg(context) );
690     WCHAR  pathW[MAX_PATH];
691     char   pathA[MAX_PATH];
692     WCHAR *ptr = pathW;
693
694     TRACE( "drive %d\n", DL_reg(context) );
695
696     if (drive == MAX_DOS_DRIVES)
697     {
698         SetLastError(ERROR_INVALID_DRIVE);
699         return FALSE;
700     }
701     
702     /*
703      * Grab current directory.
704      */
705
706     if (!GetCurrentDirectoryW( MAX_PATH, pathW )) return FALSE;
707
708     if (toupperW(pathW[0]) - 'A' != drive || pathW[1] != ':')
709     {
710         /* cwd is not on the requested drive, get the environment string instead */
711
712         WCHAR env_var[4];
713
714         env_var[0] = '=';
715         env_var[1] = 'A' + drive;
716         env_var[2] = ':';
717         env_var[3] = 0;
718         if (!GetEnvironmentVariableW( env_var, pathW, MAX_PATH ))
719         {
720             /* return empty path */
721             buffer[0] = 0;
722             return TRUE;
723         }
724     }
725
726     /*
727      * Convert into short format.
728      */
729
730     if (!islong)
731     {
732         DWORD result = GetShortPathNameW( pathW, pathW, MAX_PATH );
733         if (!result)
734             return FALSE;
735         if (result > MAX_PATH)
736         {
737             WARN( "Short path too long!\n" );
738             SetLastError(ERROR_NETWORK_BUSY); /* Internal Wine error. */
739             return FALSE;
740         }
741     }
742
743     /*
744      * The returned pathname does not include 
745      * the drive letter, colon or leading backslash.
746      */
747
748     if (ptr[0] == '\\')
749     {
750         /*
751          * FIXME: We should probably just strip host part from name...
752          */
753         FIXME( "UNC names are not supported.\n" );
754         SetLastError(ERROR_NETWORK_BUSY); /* Internal Wine error. */
755         return FALSE;
756     }
757     else if (!ptr[0] || ptr[1] != ':' || ptr[2] != '\\')
758     {
759         WARN( "Path is neither UNC nor DOS path: %s\n",
760               wine_dbgstr_w(ptr) );
761         SetLastError(ERROR_NETWORK_BUSY); /* Internal Wine error. */
762         return FALSE;
763     }
764     else
765     {
766         /* Remove drive letter, colon and leading backslash. */
767         ptr += 3;
768     }
769
770     /*
771      * Convert into OEM string.
772      */
773     
774     if (!WideCharToMultiByte(CP_OEMCP, 0, ptr, -1, pathA, 
775                              MAX_PATH, NULL, NULL))
776     {
777         WARN( "Cannot convert path!\n" );
778         SetLastError(ERROR_NETWORK_BUSY); /* Internal Wine error. */
779         return FALSE;
780     }
781
782     /*
783      * Success.
784      */
785
786     if (!islong)
787     {
788         /* Undocumented success code. */
789         SET_AX( context, 0x0100 );
790         
791         /* Truncate buffer to 64 bytes. */
792         pathA[63] = 0;
793     }
794
795     TRACE( "%c:=%s\n", 'A' + drive, pathA );
796
797     strcpy( buffer, pathA );
798     return TRUE;
799 }
800
801
802 /***********************************************************************
803  *           INT21_SetCurrentDirectory
804  *
805  * Handler for:
806  * - function 0x3b
807  * - subfunction 0x3b of function 0x71
808  */
809 static BOOL INT21_SetCurrentDirectory( CONTEXT86 *context )
810 {
811     WCHAR dirW[MAX_PATH];
812     WCHAR env_var[4];
813     DWORD attr;
814     char *dirA = CTX_SEG_OFF_TO_LIN(context, context->SegDs, context->Edx);
815     BYTE  drive = INT21_GetCurrentDrive();
816     BOOL  result;
817
818     TRACE( "SET CURRENT DIRECTORY %s\n", dirA );
819
820     MultiByteToWideChar(CP_OEMCP, 0, dirA, -1, dirW, MAX_PATH);
821     if (!GetFullPathNameW( dirW, MAX_PATH, dirW, NULL )) return FALSE;
822
823     attr = GetFileAttributesW( dirW );
824     if (attr == INVALID_FILE_ATTRIBUTES || !(attr & FILE_ATTRIBUTE_DIRECTORY))
825     {
826         SetLastError( ERROR_PATH_NOT_FOUND );
827         return FALSE;
828     }
829
830     env_var[0] = '=';
831     env_var[1] = dirW[0];
832     env_var[2] = ':';
833     env_var[3] = 0;
834     result = SetEnvironmentVariableW( env_var, dirW );
835
836     /* only set current directory if on the current drive */
837     if (result && (toupperW(dirW[0]) - 'A' == drive)) result = SetCurrentDirectoryW( dirW );
838
839     return result;
840 }
841
842 /***********************************************************************
843  *           INT21_CreateMagicDeviceHandle
844  *
845  * Create a dummy file handle for a "magic" device.
846  */
847 static HANDLE INT21_CreateMagicDeviceHandle( LPCWSTR name )
848 {
849     const char *dir = wine_get_server_dir();
850     int len;
851     HANDLE ret;
852     NTSTATUS status;
853     OBJECT_ATTRIBUTES attr;
854     UNICODE_STRING nameW;
855     IO_STATUS_BLOCK io;
856
857     len = MultiByteToWideChar( CP_UNIXCP, 0, dir, -1, NULL, 0 );
858     nameW.Length = (len + 1 + strlenW( name )) * sizeof(WCHAR);
859     nameW.MaximumLength = nameW.Length + sizeof(WCHAR);
860     if (!(nameW.Buffer = HeapAlloc( GetProcessHeap(), 0, nameW.Length )))
861     {
862         SetLastError( ERROR_NOT_ENOUGH_MEMORY );
863         return 0;
864     }
865     MultiByteToWideChar( CP_UNIXCP, 0, dir, -1, nameW.Buffer, len );
866     nameW.Buffer[len-1] = '/';
867     strcpyW( nameW.Buffer + len, name );
868
869     attr.Length = sizeof(attr);
870     attr.RootDirectory = 0;
871     attr.Attributes = 0;
872     attr.ObjectName = &nameW;
873     attr.SecurityDescriptor = NULL;
874     attr.SecurityQualityOfService = NULL;
875
876     status = NtCreateFile( &ret, GENERIC_READ|GENERIC_WRITE, &attr, &io, NULL, 0,
877                            FILE_SHARE_READ|FILE_SHARE_WRITE, FILE_OPEN_IF,
878                            FILE_SYNCHRONOUS_IO_ALERT, NULL, 0 );
879     if (status)
880     {
881         ret = 0;
882         SetLastError( RtlNtStatusToDosError(status) );
883     }
884     RtlFreeUnicodeString( &nameW );
885     return ret;
886 }
887
888
889 /***********************************************************************
890  *           INT21_OpenMagicDevice
891  *
892  * Open a file handle for "magic" devices like EMMXXXX0.
893  */
894 static HANDLE INT21_OpenMagicDevice( LPCWSTR name, DWORD access )
895 {
896     unsigned int i;
897     const WCHAR *p;
898     HANDLE handle;
899
900     if (name[0] && (name[1] == ':')) name += 2;
901     if ((p = strrchrW( name, '/' ))) name = p + 1;
902     if ((p = strrchrW( name, '\\' ))) name = p + 1;
903
904     for (i = 0; i < NB_MAGIC_DEVICES; i++)
905     {
906         int len = strlenW( magic_devices[i].name );
907         if (!strncmpiW( magic_devices[i].name, name, len ) &&
908             (!name[len] || name[len] == '.' || name[len] == ':')) break;
909     }
910     if (i == NB_MAGIC_DEVICES) return 0;
911
912     if (!magic_devices[i].handle) /* need to open it */
913     {
914         int fd;
915         struct stat st;
916
917         if (!(handle = INT21_CreateMagicDeviceHandle( magic_devices[i].name ))) return 0;
918         wine_server_handle_to_fd( handle, 0, &fd, NULL );
919         fstat( fd, &st );
920         wine_server_release_fd( handle, fd );
921         magic_devices[i].dev = st.st_dev;
922         magic_devices[i].ino = st.st_ino;
923         magic_devices[i].handle = handle;
924     }
925     if (!DuplicateHandle( GetCurrentProcess(), magic_devices[i].handle,
926                           GetCurrentProcess(), &handle, access, FALSE, 0 )) handle = 0;
927     return handle;
928 }
929
930
931 /***********************************************************************
932  *           INT21_CreateFile
933  *
934  * Handler for:
935  * - function 0x3c
936  * - function 0x3d
937  * - function 0x5b
938  * - function 0x6c
939  * - subfunction 0x6c of function 0x71
940  */
941 static BOOL INT21_CreateFile( CONTEXT86 *context, 
942                               DWORD      pathSegOff,
943                               BOOL       returnStatus,
944                               WORD       dosAccessShare,
945                               BYTE       dosAction )
946 {
947     WORD   dosStatus;
948     char  *pathA = CTX_SEG_OFF_TO_LIN(context, context->SegDs, pathSegOff);
949     WCHAR  pathW[MAX_PATH];   
950     DWORD  winAccess;
951     DWORD  winAttributes;
952     HANDLE winHandle;
953     DWORD  winMode;
954     DWORD  winSharing;
955
956     TRACE( "CreateFile called: function=%02x, action=%02x, access/share=%04x, "
957            "create flags=%04x, file=%s.\n",
958            AH_reg(context), dosAction, dosAccessShare, CX_reg(context), pathA );
959
960     /*
961      * Application tried to create/open a file whose name 
962      * ends with a backslash. This is not allowed.
963      *
964      * FIXME: This needs to be validated, especially the return value.
965      */
966     if (pathA[strlen(pathA) - 1] == '/')
967     {
968         SetLastError( ERROR_FILE_NOT_FOUND );
969         return FALSE;
970     }
971
972     /*
973      * Convert DOS action flags into Win32 creation disposition parameter.
974      */ 
975     switch(dosAction)
976     {
977     case 0x01:
978         winMode = OPEN_EXISTING;
979         break;
980     case 0x02:
981         winMode = TRUNCATE_EXISTING;
982         break;
983     case 0x10:
984         winMode = CREATE_NEW;
985         break;
986     case 0x11:
987         winMode = OPEN_ALWAYS;
988         break;
989     case 0x12:
990         winMode = CREATE_ALWAYS;
991         break;
992     default:
993         SetLastError( ERROR_INVALID_PARAMETER );
994         return FALSE;
995     }
996
997     /*
998      * Convert DOS access/share flags into Win32 desired access parameter.
999      */ 
1000     switch(dosAccessShare & 0x07)
1001     {
1002     case OF_READ:
1003         winAccess = GENERIC_READ;
1004         break;
1005     case OF_WRITE:
1006         winAccess = GENERIC_WRITE;
1007         break;
1008     case OF_READWRITE:
1009         winAccess = GENERIC_READ | GENERIC_WRITE;
1010         break;
1011     case 0x04:
1012         /*
1013          * Read-only, do not modify file's last-access time (DOS7).
1014          *
1015          * FIXME: How to prevent modification of last-access time?
1016          */
1017         winAccess = GENERIC_READ;
1018         break;
1019     default:
1020         winAccess = 0;
1021     }
1022
1023     /*
1024      * Convert DOS access/share flags into Win32 share mode parameter.
1025      */ 
1026     switch(dosAccessShare & 0x70)
1027     {
1028     case OF_SHARE_EXCLUSIVE:  
1029         winSharing = 0; 
1030         break;
1031     case OF_SHARE_DENY_WRITE: 
1032         winSharing = FILE_SHARE_READ; 
1033         break;
1034     case OF_SHARE_DENY_READ:  
1035         winSharing = FILE_SHARE_WRITE; 
1036         break;
1037     case OF_SHARE_DENY_NONE:
1038     case OF_SHARE_COMPAT:
1039     default:
1040         winSharing = FILE_SHARE_READ | FILE_SHARE_WRITE;
1041     }
1042
1043     /*
1044      * FIXME: Bit (dosAccessShare & 0x80) represents inheritance.
1045      *        What to do with this bit?
1046      * FIXME: Bits in the high byte of dosAccessShare are not supported.
1047      *        See both function 0x6c and subfunction 0x6c of function 0x71 for
1048      *        definition of these bits.
1049      */
1050
1051     /*
1052      * Convert DOS create attributes into Win32 flags and attributes parameter.
1053      */
1054     if (winMode == OPEN_EXISTING || winMode == TRUNCATE_EXISTING)
1055     {
1056         winAttributes = 0;
1057     }
1058     else
1059     {        
1060         WORD dosAttributes = CX_reg(context);
1061
1062         if (dosAttributes & FA_LABEL)
1063         {
1064             /*
1065              * Application tried to create volume label entry.
1066              * This is difficult to support so we do not allow it.
1067              *
1068              * FIXME: If volume does not already have a label, 
1069              *        this function is supposed to succeed.
1070              */
1071             SetLastError( ERROR_ACCESS_DENIED );
1072             return TRUE;
1073         }
1074
1075         winAttributes = dosAttributes & 
1076             (FILE_ATTRIBUTE_READONLY | FILE_ATTRIBUTE_HIDDEN | 
1077              FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_ARCHIVE);
1078     }
1079
1080     /*
1081      * Open the file.
1082      */
1083     MultiByteToWideChar(CP_OEMCP, 0, pathA, -1, pathW, MAX_PATH);
1084
1085     if ((winHandle = INT21_OpenMagicDevice( pathW, winAccess )))
1086     {
1087         dosStatus = 1;
1088     }
1089     else
1090     {
1091         winHandle = CreateFileW( pathW, winAccess, winSharing, NULL,
1092                                  winMode, winAttributes, 0 );
1093         /* DOS allows to open files on a CDROM R/W */
1094         if( winHandle == INVALID_HANDLE_VALUE &&
1095                 GetLastError()== ERROR_WRITE_PROTECT) {
1096             winHandle = CreateFileW( pathW, winAccess & ~GENERIC_WRITE,
1097                     winSharing, NULL, winMode, winAttributes, 0 );
1098         }
1099
1100         if (winHandle == INVALID_HANDLE_VALUE)
1101             return FALSE;
1102
1103         /*
1104          * Determine DOS file status.
1105          *
1106          * 1 = file opened
1107          * 2 = file created
1108          * 3 = file replaced
1109          */
1110         switch(winMode)
1111         {
1112         case OPEN_EXISTING:
1113             dosStatus = 1;
1114             break;
1115         case TRUNCATE_EXISTING:
1116             dosStatus = 3;
1117             break;
1118         case CREATE_NEW:
1119             dosStatus = 2;
1120             break;
1121         case OPEN_ALWAYS:
1122             dosStatus = (GetLastError() == ERROR_ALREADY_EXISTS) ? 1 : 2;
1123             break;
1124         case CREATE_ALWAYS:
1125             dosStatus = (GetLastError() == ERROR_ALREADY_EXISTS) ? 3 : 2;
1126             break;
1127         default:
1128             dosStatus = 0;
1129         }
1130     }
1131
1132     /*
1133      * Return DOS file handle and DOS status.
1134      */
1135     SET_AX( context, Win32HandleToDosFileHandle(winHandle) );
1136
1137     if (returnStatus)
1138         SET_CX( context, dosStatus );
1139
1140     TRACE( "CreateFile finished: handle=%d, status=%d.\n", 
1141            AX_reg(context), dosStatus );
1142
1143     return TRUE;
1144 }
1145
1146
1147 /***********************************************************************
1148  *           INT21_BufferedInput
1149  *
1150  * Handler for function 0x0a and reading from console using
1151  * function 0x3f.
1152  *
1153  * Reads a string of characters from standard input until
1154  * enter key is pressed. Returns either number of characters 
1155  * read from console including terminating CR or 
1156  * zero if capacity was zero.
1157  */
1158 static WORD INT21_BufferedInput( CONTEXT86 *context, BYTE *ptr, WORD capacity )
1159 {
1160     BYTE length = 0;
1161
1162     /*
1163      * Return immediately if capacity is zero.
1164      */
1165     if (capacity == 0)
1166         return 0;
1167
1168     while(TRUE)
1169     {
1170         BYTE ascii;
1171         BYTE scan;
1172
1173         DOSVM_Int16ReadChar( &ascii, &scan, context );
1174
1175         if (ascii == '\r' || ascii == '\n')
1176         {
1177             ptr[length] = '\r';
1178             return length + 1;
1179         }
1180
1181         /*
1182          * DOS handles only backspace and KEY_LEFT
1183          *        perhaps we should do more
1184          */
1185         if (ascii == '\b' || scan == KEY_LEFT)
1186         {
1187             if (length==0) continue;
1188             DOSVM_PutChar( '\b' );
1189             length--;
1190             continue;
1191         }
1192
1193         /*
1194          * If the buffer becomes filled to within one byte of
1195          * capacity, DOS rejects all further characters up to,
1196          * but not including, the terminating carriage return.
1197          */
1198         if (ascii != 0 && length < capacity-1)
1199         {
1200             DOSVM_PutChar( ascii );
1201             ptr[length] = ascii;
1202             length++;
1203         }
1204     }
1205 }
1206
1207
1208 /***********************************************************************
1209  *           INT21_GetCurrentDTA
1210  */
1211 static BYTE *INT21_GetCurrentDTA( CONTEXT86 *context )
1212 {
1213     TDB *pTask = GlobalLock16(GetCurrentTask());
1214
1215     /* FIXME: This assumes DTA was set correctly! */
1216     return (BYTE *)CTX_SEG_OFF_TO_LIN( context, SELECTOROF(pTask->dta),
1217                                                 (DWORD)OFFSETOF(pTask->dta) );
1218 }
1219
1220
1221 /***********************************************************************
1222  *           INT21_OpenFileUsingFCB
1223  *
1224  * Handler for function 0x0f.
1225  *
1226  * PARAMS
1227  *  DX:DX [I/O] File control block (FCB or XFCB) of unopened file
1228  *
1229  * RETURNS (in AL)
1230  *  0x00: successful
1231  *  0xff: failed
1232  *
1233  * NOTES
1234  *  Opens a FCB file for read/write in compatibility mode. Upon calling
1235  *  the FCB must have the drive_number, file_name, and file_extension
1236  *  fields filled and all other bytes cleared.
1237  */
1238 static void INT21_OpenFileUsingFCB( CONTEXT86 *context )
1239 {
1240     struct FCB *fcb;
1241     struct XFCB *xfcb;
1242     char file_path[16];
1243     char *pos;
1244     HANDLE handle;
1245     HFILE16 hfile16;
1246     BY_HANDLE_FILE_INFORMATION info;
1247     BYTE AL_result;
1248
1249     fcb = CTX_SEG_OFF_TO_LIN(context, context->SegDs, context->Edx);
1250     if (fcb->drive_number == 0xff) {
1251         xfcb = (struct XFCB *) fcb;
1252         fcb = (struct FCB *) xfcb->fcb;
1253     } /* if */
1254
1255     AL_result = 0;
1256     file_path[0] = 'A' + INT21_MapDrive( fcb->drive_number );
1257
1258     if (AL_result == 0) {
1259         file_path[1] = ':';
1260         pos = &file_path[2];
1261         memcpy(pos, fcb->file_name, 8);
1262         pos[8] = ' ';
1263         pos[9] = '\0';
1264         pos = strchr(pos, ' ');
1265         *pos = '.';
1266         pos++;
1267         memcpy(pos, fcb->file_extension, 3);
1268         pos[3] = ' ';
1269         pos[4] = '\0';
1270         pos = strchr(pos, ' ');
1271         *pos = '\0';
1272
1273         handle = (HANDLE) _lopen(file_path, OF_READWRITE);
1274         if (handle == INVALID_HANDLE_VALUE) {
1275             TRACE("_lopen(\"%s\") failed: INVALID_HANDLE_VALUE\n", file_path);
1276             AL_result = 0xff; /* failed */
1277         } else {
1278             hfile16 = Win32HandleToDosFileHandle(handle);
1279             if (hfile16 == HFILE_ERROR16) {
1280                 TRACE("Win32HandleToDosFileHandle(%p) failed: HFILE_ERROR\n", handle);
1281                 CloseHandle(handle);
1282                 AL_result = 0xff; /* failed */
1283             } else if (hfile16 > 255) {
1284                 TRACE("hfile16 (=%d) larger than 255 for \"%s\"\n", hfile16, file_path);
1285                 _lclose16(hfile16);
1286                 AL_result = 0xff; /* failed */
1287             } else {
1288                 if (!GetFileInformationByHandle(handle, &info)) {
1289                     TRACE("GetFileInformationByHandle(%d, %p) for \"%s\" failed\n",
1290                           hfile16, handle, file_path);
1291                     _lclose16(hfile16);
1292                     AL_result = 0xff; /* failed */
1293                 } else {
1294                     fcb->drive_number = file_path[0] - 'A' + 1;
1295                     fcb->current_block_number = 0;
1296                     fcb->logical_record_size = 128;
1297                     fcb->file_size = info.nFileSizeLow;
1298                     FileTimeToDosDateTime(&info.ftLastWriteTime,
1299                         &fcb->date_of_last_write, &fcb->time_of_last_write);
1300                     fcb->file_number = hfile16;
1301                     fcb->attributes = 0xc2;
1302                     fcb->starting_cluster = 0; /* don't know correct init value */
1303                     fcb->sequence_number = 0; /* don't know correct init value */
1304                     fcb->file_attributes = info.dwFileAttributes;
1305                     /* The following fields are not initialized */
1306                     /* by the native function: */
1307                     /* unused */
1308                     /* record_within_current_block */
1309                     /* random_access_record_number */
1310
1311                     TRACE("successful opened file \"%s\" as %d (handle %p)\n",
1312                           file_path, hfile16, handle);
1313                     AL_result = 0x00; /* successful */
1314                 } /* if */
1315             } /* if */
1316         } /* if */
1317     } /* if */
1318     SET_AL(context, AL_result);
1319 }
1320
1321
1322 /***********************************************************************
1323  *           INT21_CloseFileUsingFCB
1324  *
1325  * Handler for function 0x10.
1326  *
1327  * PARAMS
1328  *  DX:DX [I/O] File control block (FCB or XFCB) of open file
1329  *
1330  * RETURNS (in AL)
1331  *  0x00: successful
1332  *  0xff: failed
1333  *
1334  * NOTES
1335  *  Closes a FCB file.
1336  */
1337 static void INT21_CloseFileUsingFCB( CONTEXT86 *context )
1338 {
1339     struct FCB *fcb;
1340     struct XFCB *xfcb;
1341     BYTE AL_result;
1342
1343     fcb = CTX_SEG_OFF_TO_LIN(context, context->SegDs, context->Edx);
1344     if (fcb->drive_number == 0xff) {
1345         xfcb = (struct XFCB *) fcb;
1346         fcb = (struct FCB *) xfcb->fcb;
1347     } /* if */
1348
1349     if (_lclose16((HFILE16) fcb->file_number) != 0) {
1350         TRACE("_lclose16(%d) failed\n", fcb->file_number);
1351         AL_result = 0xff; /* failed */
1352     } else {
1353         TRACE("successful closed file %d\n", fcb->file_number);
1354         AL_result = 0x00; /* successful */
1355     } /* if */
1356     SET_AL(context, AL_result);
1357 }
1358
1359
1360 /***********************************************************************
1361  *           INT21_SequentialReadFromFCB
1362  *
1363  * Handler for function 0x14.
1364  *
1365  * PARAMS
1366  *  DX:DX [I/O] File control block (FCB or XFCB) of open file
1367  *
1368  * RETURNS (in AL)
1369  *  0: successful
1370  *  1: end of file, no data read
1371  *  2: segment wrap in DTA, no data read (not returned now)
1372  *  3: end of file, partial record read
1373  *
1374  * NOTES
1375  *  Reads a record with the size FCB->logical_record_size from the FCB
1376  *  to the disk transfer area. The position of the record is specified
1377  *  with FCB->current_block_number and FCB->record_within_current_block.
1378  *  Then FCB->current_block_number and FCB->record_within_current_block
1379  *  are updated to point to the next record. If a partial record is
1380  *  read, it is filled with zeros up to the FCB->logical_record_size.
1381  */
1382 static void INT21_SequentialReadFromFCB( CONTEXT86 *context )
1383 {
1384     struct FCB *fcb;
1385     struct XFCB *xfcb;
1386     HANDLE handle;
1387     DWORD record_number;
1388     long position;
1389     BYTE *disk_transfer_area;
1390     UINT bytes_read;
1391     BYTE AL_result;
1392
1393     fcb = CTX_SEG_OFF_TO_LIN(context, context->SegDs, context->Edx);
1394     if (fcb->drive_number == 0xff) {
1395         xfcb = (struct XFCB *) fcb;
1396         fcb = (struct FCB *) xfcb->fcb;
1397     } /* if */
1398
1399     handle = DosFileHandleToWin32Handle((HFILE16) fcb->file_number);
1400     if (handle == INVALID_HANDLE_VALUE) {
1401         TRACE("DosFileHandleToWin32Handle(%d) failed: INVALID_HANDLE_VALUE\n",
1402             fcb->file_number);
1403         AL_result = 0x01; /* end of file, no data read */
1404     } else {
1405         record_number = 128 * fcb->current_block_number + fcb->record_within_current_block;
1406         position = SetFilePointer(handle, record_number * fcb->logical_record_size, NULL, 0);
1407         if (position != record_number * fcb->logical_record_size) {
1408             TRACE("seek(%d, %ld, 0) failed with %ld\n",
1409                   fcb->file_number, record_number * fcb->logical_record_size, position);
1410             AL_result = 0x01; /* end of file, no data read */
1411         } else {
1412             disk_transfer_area = INT21_GetCurrentDTA(context);
1413             bytes_read = _lread((HFILE) handle, disk_transfer_area, fcb->logical_record_size);
1414             if (bytes_read != fcb->logical_record_size) {
1415                 TRACE("_lread(%d, %p, %d) failed with %d\n",
1416                       fcb->file_number, disk_transfer_area, fcb->logical_record_size, bytes_read);
1417                 if (bytes_read == 0) {
1418                     AL_result = 0x01; /* end of file, no data read */
1419                 } else {
1420                     memset(&disk_transfer_area[bytes_read], 0, fcb->logical_record_size - bytes_read);
1421                     AL_result = 0x03; /* end of file, partial record read */
1422                 } /* if */
1423             } else {
1424                 TRACE("successful read %d bytes from record %ld (position %ld) of file %d (handle %p)\n",
1425                     bytes_read, record_number, position, fcb->file_number, handle);
1426                 AL_result = 0x00; /* successful */
1427             } /* if */
1428         } /* if */
1429     } /* if */
1430     if (AL_result == 0x00 || AL_result == 0x03) {
1431         if (fcb->record_within_current_block == 127) {
1432             fcb->record_within_current_block = 0;
1433             fcb->current_block_number++;
1434         } else {
1435             fcb->record_within_current_block++;
1436         } /* if */
1437     } /* if */
1438     SET_AL(context, AL_result);
1439 }
1440
1441
1442 /***********************************************************************
1443  *           INT21_SequentialWriteToFCB
1444  *
1445  * Handler for function 0x15.
1446  *
1447  * PARAMS
1448  *  DX:DX [I/O] File control block (FCB or XFCB) of open file
1449  *
1450  * RETURNS (in AL)
1451  *  0: successful
1452  *  1: disk full
1453  *  2: segment wrap in DTA (not returned now)
1454  *
1455  * NOTES
1456  *  Writes a record with the size FCB->logical_record_size from the disk
1457  *  transfer area to the FCB. The position of the record is specified
1458  *  with FCB->current_block_number and FCB->record_within_current_block.
1459  *  Then FCB->current_block_number and FCB->record_within_current_block
1460  *  are updated to point to the next record. 
1461  */
1462 static void INT21_SequentialWriteToFCB( CONTEXT86 *context )
1463 {
1464     struct FCB *fcb;
1465     struct XFCB *xfcb;
1466     HANDLE handle;
1467     DWORD record_number;
1468     long position;
1469     BYTE *disk_transfer_area;
1470     UINT bytes_written;
1471     BYTE AL_result;
1472
1473     fcb = CTX_SEG_OFF_TO_LIN(context, context->SegDs, context->Edx);
1474     if (fcb->drive_number == 0xff) {
1475         xfcb = (struct XFCB *) fcb;
1476         fcb = (struct FCB *) xfcb->fcb;
1477     } /* if */
1478
1479     handle = DosFileHandleToWin32Handle((HFILE16) fcb->file_number);
1480     if (handle == INVALID_HANDLE_VALUE) {
1481         TRACE("DosFileHandleToWin32Handle(%d) failed: INVALID_HANDLE_VALUE\n",
1482             fcb->file_number);
1483         AL_result = 0x01; /* disk full */
1484     } else {
1485         record_number = 128 * fcb->current_block_number + fcb->record_within_current_block;
1486         position = SetFilePointer(handle, record_number * fcb->logical_record_size, NULL, 0);
1487         if (position != record_number * fcb->logical_record_size) {
1488             TRACE("seek(%d, %ld, 0) failed with %ld\n",
1489                   fcb->file_number, record_number * fcb->logical_record_size, position);
1490             AL_result = 0x01; /* disk full */
1491         } else {
1492             disk_transfer_area = INT21_GetCurrentDTA(context);
1493             bytes_written = _lwrite((HFILE) handle, (LPCSTR)disk_transfer_area, fcb->logical_record_size);
1494             if (bytes_written != fcb->logical_record_size) {
1495                 TRACE("_lwrite(%d, %p, %d) failed with %d\n",
1496                       fcb->file_number, disk_transfer_area, fcb->logical_record_size, bytes_written);
1497                 AL_result = 0x01; /* disk full */
1498             } else {
1499                 TRACE("successful written %d bytes from record %ld (position %ld) of file %d (handle %p)\n",
1500                     bytes_written, record_number, position, fcb->file_number, handle);
1501                 AL_result = 0x00; /* successful */
1502             } /* if */
1503         } /* if */
1504     } /* if */
1505     if (AL_result == 0x00) {
1506         if (fcb->record_within_current_block == 127) {
1507             fcb->record_within_current_block = 0;
1508             fcb->current_block_number++;
1509         } else {
1510             fcb->record_within_current_block++;
1511         } /* if */
1512     } /* if */
1513     SET_AL(context, AL_result);
1514 }
1515
1516
1517 /***********************************************************************
1518  *           INT21_ReadRandomRecordFromFCB
1519  *
1520  * Handler for function 0x21.
1521  *
1522  * PARAMS
1523  *  DX:DX [I/O] File control block (FCB or XFCB) of open file
1524  *
1525  * RETURNS (in AL)
1526  *  0: successful
1527  *  1: end of file, no data read
1528  *  2: segment wrap in DTA, no data read (not returned now)
1529  *  3: end of file, partial record read
1530  *
1531  * NOTES
1532  *  Reads a record with the size FCB->logical_record_size from
1533  *  the FCB to the disk transfer area. The position of the record
1534  *  is specified with FCB->random_access_record_number. The
1535  *  FCB->random_access_record_number is not updated. If a partial record
1536  *  is read, it is filled with zeros up to the FCB->logical_record_size.
1537  */
1538 static void INT21_ReadRandomRecordFromFCB( CONTEXT86 *context )
1539 {
1540     struct FCB *fcb;
1541     struct XFCB *xfcb;
1542     HANDLE handle;
1543     DWORD record_number;
1544     long position;
1545     BYTE *disk_transfer_area;
1546     UINT bytes_read;
1547     BYTE AL_result;
1548
1549     fcb = CTX_SEG_OFF_TO_LIN(context, context->SegDs, context->Edx);
1550     if (fcb->drive_number == 0xff) {
1551         xfcb = (struct XFCB *) fcb;
1552         fcb = (struct FCB *) xfcb->fcb;
1553     } /* if */
1554
1555     memcpy(&record_number, fcb->random_access_record_number, 4);
1556     handle = DosFileHandleToWin32Handle((HFILE16) fcb->file_number);
1557     if (handle == INVALID_HANDLE_VALUE) {
1558         TRACE("DosFileHandleToWin32Handle(%d) failed: INVALID_HANDLE_VALUE\n",
1559             fcb->file_number);
1560         AL_result = 0x01; /* end of file, no data read */
1561     } else {
1562         position = SetFilePointer(handle, record_number * fcb->logical_record_size, NULL, 0);
1563         if (position != record_number * fcb->logical_record_size) {
1564             TRACE("seek(%d, %ld, 0) failed with %ld\n",
1565                   fcb->file_number, record_number * fcb->logical_record_size, position);
1566             AL_result = 0x01; /* end of file, no data read */
1567         } else {
1568             disk_transfer_area = INT21_GetCurrentDTA(context);
1569             bytes_read = _lread((HFILE) handle, disk_transfer_area, fcb->logical_record_size);
1570             if (bytes_read != fcb->logical_record_size) {
1571                 TRACE("_lread(%d, %p, %d) failed with %d\n",
1572                       fcb->file_number, disk_transfer_area, fcb->logical_record_size, bytes_read);
1573                 if (bytes_read == 0) {
1574                     AL_result = 0x01; /* end of file, no data read */
1575                 } else {
1576                     memset(&disk_transfer_area[bytes_read], 0, fcb->logical_record_size - bytes_read);
1577                     AL_result = 0x03; /* end of file, partial record read */
1578                 } /* if */
1579             } else {
1580                 TRACE("successful read %d bytes from record %ld (position %ld) of file %d (handle %p)\n",
1581                     bytes_read, record_number, position, fcb->file_number, handle);
1582                 AL_result = 0x00; /* successful */
1583             } /* if */
1584         } /* if */
1585     } /* if */
1586     fcb->current_block_number = record_number / 128;
1587     fcb->record_within_current_block = record_number % 128;
1588     SET_AL(context, AL_result);
1589 }
1590
1591
1592 /***********************************************************************
1593  *           INT21_WriteRandomRecordToFCB
1594  *
1595  * Handler for function 0x22.
1596  *
1597  * PARAMS
1598  *  DX:DX [I/O] File control block (FCB or XFCB) of open file
1599  *
1600  * RETURNS (in AL)
1601  *  0: successful
1602  *  1: disk full
1603  *  2: segment wrap in DTA (not returned now)
1604  *
1605  * NOTES
1606  *  Writes a record with the size FCB->logical_record_size from
1607  *  the disk transfer area to the FCB. The position of the record
1608  *  is specified with FCB->random_access_record_number. The
1609  *  FCB->random_access_record_number is not updated.
1610  */
1611 static void INT21_WriteRandomRecordToFCB( CONTEXT86 *context )
1612 {
1613     struct FCB *fcb;
1614     struct XFCB *xfcb;
1615     HANDLE handle;
1616     DWORD record_number;
1617     long position;
1618     BYTE *disk_transfer_area;
1619     UINT bytes_written;
1620     BYTE AL_result;
1621
1622     fcb = CTX_SEG_OFF_TO_LIN(context, context->SegDs, context->Edx);
1623     if (fcb->drive_number == 0xff) {
1624         xfcb = (struct XFCB *) fcb;
1625         fcb = (struct FCB *) xfcb->fcb;
1626     } /* if */
1627
1628     memcpy(&record_number, fcb->random_access_record_number, 4);
1629     handle = DosFileHandleToWin32Handle((HFILE16) fcb->file_number);
1630     if (handle == INVALID_HANDLE_VALUE) {
1631         TRACE("DosFileHandleToWin32Handle(%d) failed: INVALID_HANDLE_VALUE\n",
1632             fcb->file_number);
1633         AL_result = 0x01; /* disk full */
1634     } else {
1635         position = SetFilePointer(handle, record_number * fcb->logical_record_size, NULL, 0);
1636         if (position != record_number * fcb->logical_record_size) {
1637             TRACE("seek(%d, %ld, 0) failed with %ld\n",
1638                   fcb->file_number, record_number * fcb->logical_record_size, position);
1639             AL_result = 0x01; /* disk full */
1640         } else {
1641             disk_transfer_area = INT21_GetCurrentDTA(context);
1642             bytes_written = _lwrite((HFILE) handle, (LPCSTR)disk_transfer_area, fcb->logical_record_size);
1643             if (bytes_written != fcb->logical_record_size) {
1644                 TRACE("_lwrite(%d, %p, %d) failed with %d\n",
1645                       fcb->file_number, disk_transfer_area, fcb->logical_record_size, bytes_written);
1646                 AL_result = 0x01; /* disk full */
1647             } else {
1648                 TRACE("successful written %d bytes from record %ld (position %ld) of file %d (handle %p)\n",
1649                     bytes_written, record_number, position, fcb->file_number, handle);
1650                 AL_result = 0x00; /* successful */
1651             } /* if */
1652         } /* if */
1653     } /* if */
1654     fcb->current_block_number = record_number / 128;
1655     fcb->record_within_current_block = record_number % 128;
1656     SET_AL(context, AL_result);
1657 }
1658
1659
1660 /***********************************************************************
1661  *           INT21_RandomBlockReadFromFCB
1662  *
1663  * Handler for function 0x27.
1664  *
1665  * PARAMS
1666  *  CX    [I/O] Number of records to read
1667  *  DX:DX [I/O] File control block (FCB or XFCB) of open file
1668  *
1669  * RETURNS (in AL)
1670  *  0: successful
1671  *  1: end of file, no data read
1672  *  2: segment wrap in DTA, no data read (not returned now)
1673  *  3: end of file, partial record read
1674  *
1675  * NOTES
1676  *  Reads several records with the size FCB->logical_record_size from
1677  *  the FCB to the disk transfer area. The number of records to be
1678  *  read is specified in the CX register. The position of the first
1679  *  record is specified with FCB->random_access_record_number. The
1680  *  FCB->random_access_record_number, the FCB->current_block_number
1681  *  and FCB->record_within_current_block are updated to point to the
1682  *  next record after the records read. If a partial record is read,
1683  *  it is filled with zeros up to the FCB->logical_record_size. The
1684  *  CX register is set to the number of successfully read records.
1685  */
1686 static void INT21_RandomBlockReadFromFCB( CONTEXT86 *context )
1687 {
1688     struct FCB *fcb;
1689     struct XFCB *xfcb;
1690     HANDLE handle;
1691     DWORD record_number;
1692     long position;
1693     BYTE *disk_transfer_area;
1694     UINT records_requested;
1695     UINT bytes_requested;
1696     UINT bytes_read;
1697     UINT records_read;
1698     BYTE AL_result;
1699
1700     fcb = CTX_SEG_OFF_TO_LIN(context, context->SegDs, context->Edx);
1701     if (fcb->drive_number == 0xff) {
1702         xfcb = (struct XFCB *) fcb;
1703         fcb = (struct FCB *) xfcb->fcb;
1704     } /* if */
1705
1706     memcpy(&record_number, fcb->random_access_record_number, 4);
1707     handle = DosFileHandleToWin32Handle((HFILE16) fcb->file_number);
1708     if (handle == INVALID_HANDLE_VALUE) {
1709         TRACE("DosFileHandleToWin32Handle(%d) failed: INVALID_HANDLE_VALUE\n",
1710             fcb->file_number);
1711         records_read = 0;
1712         AL_result = 0x01; /* end of file, no data read */
1713     } else {
1714         position = SetFilePointer(handle, record_number * fcb->logical_record_size, NULL, 0);
1715         if (position != record_number * fcb->logical_record_size) {
1716             TRACE("seek(%d, %ld, 0) failed with %ld\n",
1717                   fcb->file_number, record_number * fcb->logical_record_size, position);
1718             records_read = 0;
1719             AL_result = 0x01; /* end of file, no data read */
1720         } else {
1721             disk_transfer_area = INT21_GetCurrentDTA(context);
1722             records_requested = CX_reg(context);
1723             bytes_requested = (UINT) records_requested * fcb->logical_record_size;
1724             bytes_read = _lread((HFILE) handle, disk_transfer_area, bytes_requested);
1725             if (bytes_read != bytes_requested) {
1726                 TRACE("_lread(%d, %p, %d) failed with %d\n",
1727                       fcb->file_number, disk_transfer_area, bytes_requested, bytes_read);
1728                 records_read = bytes_read / fcb->logical_record_size;
1729                 if (bytes_read % fcb->logical_record_size == 0) {
1730                     AL_result = 0x01; /* end of file, no data read */
1731                 } else {
1732                     records_read++;
1733                     memset(&disk_transfer_area[bytes_read], 0, records_read * fcb->logical_record_size - bytes_read);
1734                     AL_result = 0x03; /* end of file, partial record read */
1735                 } /* if */
1736             } else {
1737                 TRACE("successful read %d bytes from record %ld (position %ld) of file %d (handle %p)\n",
1738                     bytes_read, record_number, position, fcb->file_number, handle);
1739                 records_read = records_requested;
1740                 AL_result = 0x00; /* successful */
1741             } /* if */
1742         } /* if */
1743     } /* if */
1744     record_number += records_read;
1745     memcpy(fcb->random_access_record_number, &record_number, 4);
1746     fcb->current_block_number = record_number / 128;
1747     fcb->record_within_current_block = record_number % 128;
1748     SET_CX(context, records_read);
1749     SET_AL(context, AL_result);
1750 }
1751
1752
1753 /***********************************************************************
1754  *           INT21_RandomBlockWriteToFCB
1755  *
1756  * Handler for function 0x28.
1757  *
1758  * PARAMS
1759  *  CX    [I/O] Number of records to write
1760  *  DX:DX [I/O] File control block (FCB or XFCB) of open file
1761  *
1762  * RETURNS (in AL)
1763  *  0: successful
1764  *  1: disk full
1765  *  2: segment wrap in DTA (not returned now)
1766  *
1767  * NOTES
1768  *  Writes several records with the size FCB->logical_record_size from
1769  *  the disk transfer area to the FCB. The number of records to be
1770  *  written is specified in the CX register. The position of the first
1771  *  record is specified with FCB->random_access_record_number. The
1772  *  FCB->random_access_record_number, the FCB->current_block_number
1773  *  and FCB->record_within_current_block are updated to point to the
1774  *  next record after the records written. The CX register is set to
1775  *  the number of successfully written records.
1776  */
1777 static void INT21_RandomBlockWriteToFCB( CONTEXT86 *context )
1778 {
1779     struct FCB *fcb;
1780     struct XFCB *xfcb;
1781     HANDLE handle;
1782     DWORD record_number;
1783     long position;
1784     BYTE *disk_transfer_area;
1785     UINT records_requested;
1786     UINT bytes_requested;
1787     UINT bytes_written;
1788     UINT records_written;
1789     BYTE AL_result;
1790
1791     fcb = CTX_SEG_OFF_TO_LIN(context, context->SegDs, context->Edx);
1792     if (fcb->drive_number == 0xff) {
1793         xfcb = (struct XFCB *) fcb;
1794         fcb = (struct FCB *) xfcb->fcb;
1795     } /* if */
1796
1797     memcpy(&record_number, fcb->random_access_record_number, 4);
1798     handle = DosFileHandleToWin32Handle((HFILE16) fcb->file_number);
1799     if (handle == INVALID_HANDLE_VALUE) {
1800         TRACE("DosFileHandleToWin32Handle(%d) failed: INVALID_HANDLE_VALUE\n",
1801             fcb->file_number);
1802         records_written = 0;
1803         AL_result = 0x01; /* disk full */
1804     } else {
1805         position = SetFilePointer(handle, record_number * fcb->logical_record_size, NULL, 0);
1806         if (position != record_number * fcb->logical_record_size) {
1807             TRACE("seek(%d, %ld, 0) failed with %ld\n",
1808                   fcb->file_number, record_number * fcb->logical_record_size, position);
1809             records_written = 0;
1810             AL_result = 0x01; /* disk full */
1811         } else {
1812             disk_transfer_area = INT21_GetCurrentDTA(context);
1813             records_requested = CX_reg(context);
1814             bytes_requested = (UINT) records_requested * fcb->logical_record_size;
1815             bytes_written = _lwrite((HFILE) handle, (LPCSTR)disk_transfer_area, bytes_requested);
1816             if (bytes_written != bytes_requested) {
1817                 TRACE("_lwrite(%d, %p, %d) failed with %d\n",
1818                       fcb->file_number, disk_transfer_area, bytes_requested, bytes_written);
1819                 records_written = bytes_written / fcb->logical_record_size;
1820                 AL_result = 0x01; /* disk full */
1821             } else {
1822                 TRACE("successful write %d bytes from record %ld (position %ld) of file %d (handle %p)\n",
1823                     bytes_written, record_number, position, fcb->file_number, handle);
1824                 records_written = records_requested;
1825                 AL_result = 0x00; /* successful */
1826             } /* if */
1827         } /* if */
1828     } /* if */
1829     record_number += records_written;
1830     memcpy(fcb->random_access_record_number, &record_number, 4);
1831     fcb->current_block_number = record_number / 128;
1832     fcb->record_within_current_block = record_number % 128;
1833     SET_CX(context, records_written);
1834     SET_AL(context, AL_result);
1835 }
1836
1837
1838 /***********************************************************************
1839  *           INT21_CreateDirectory
1840  *
1841  * Handler for:
1842  * - function 0x39
1843  * - subfunction 0x39 of function 0x71
1844  * - subfunction 0xff of function 0x43 (CL == 0x39)
1845  */
1846 static BOOL INT21_CreateDirectory( CONTEXT86 *context )
1847 {
1848     WCHAR dirW[MAX_PATH];
1849     char *dirA = CTX_SEG_OFF_TO_LIN(context,
1850                                     context->SegDs, 
1851                                     context->Edx);
1852
1853     TRACE( "CREATE DIRECTORY %s\n", dirA );
1854
1855     MultiByteToWideChar(CP_OEMCP, 0, dirA, -1, dirW, MAX_PATH);
1856
1857     if (CreateDirectoryW(dirW, NULL))
1858         return TRUE;
1859
1860     /*
1861      * FIXME: CreateDirectory's LastErrors will clash with the ones
1862      *        used by DOS. AH=39 only returns 3 (path not found) and 
1863      *        5 (access denied), while CreateDirectory return several
1864      *        ones. Remap some of them. -Marcus
1865      */
1866     switch (GetLastError()) 
1867     {
1868     case ERROR_ALREADY_EXISTS:
1869     case ERROR_FILENAME_EXCED_RANGE:
1870     case ERROR_DISK_FULL:
1871         SetLastError(ERROR_ACCESS_DENIED);
1872         break;
1873     default: 
1874         break;
1875     }
1876
1877     return FALSE;
1878 }
1879
1880
1881 /***********************************************************************
1882  *           INT21_ExtendedCountryInformation
1883  *
1884  * Handler for function 0x65.
1885  */
1886 static void INT21_ExtendedCountryInformation( CONTEXT86 *context )
1887 {
1888     BYTE *dataptr = CTX_SEG_OFF_TO_LIN( context, context->SegEs, context->Edi );
1889     BYTE buffsize = CX_reg (context);
1890     
1891     TRACE( "GET EXTENDED COUNTRY INFORMATION, subfunction %02x\n",
1892            AL_reg(context) );
1893
1894     /*
1895      * Check subfunctions that are passed country and code page.
1896      */
1897     if (AL_reg(context) >= 0x01 && AL_reg(context) <= 0x07)
1898     {
1899         WORD country = DX_reg(context);
1900         WORD codepage = BX_reg(context);
1901
1902         if (country != 0xffff && country != INT21_GetSystemCountryCode())
1903             FIXME( "Requested info on non-default country %04x\n", country );
1904
1905         if (codepage != 0xffff && codepage != GetOEMCP())
1906             FIXME( "Requested info on non-default code page %04x\n", codepage );
1907     }
1908
1909     switch (AL_reg(context)) {
1910     case 0x00: /* SET GENERAL INTERNATIONALIZATION INFO */
1911         INT_BARF( context, 0x21 );
1912         SET_CFLAG( context );
1913         break;
1914
1915     case 0x01: /* GET GENERAL INTERNATIONALIZATION INFO */
1916         TRACE( "Get general internationalization info\n" );
1917         dataptr[0] = 0x01; /* Info ID */
1918         *(WORD*)(dataptr+1) = 38; /* Size of the following info */
1919         *(WORD*)(dataptr+3) = INT21_GetSystemCountryCode(); /* Country ID */
1920         *(WORD*)(dataptr+5) = GetOEMCP(); /* Code page */
1921         /* FIXME: fill buffer partially up to buffsize bytes*/
1922         if (buffsize >= 0x29){
1923             INT21_FillCountryInformation( dataptr + 7 );
1924             SET_CX( context, 0x29 ); /* Size of returned info */
1925         }else{
1926             SET_CX( context, 0x07 ); /* Size of returned info */        
1927         }
1928         break;
1929         
1930     case 0x02: /* GET POINTER TO UPPERCASE TABLE */
1931     case 0x04: /* GET POINTER TO FILENAME UPPERCASE TABLE */
1932         TRACE( "Get pointer to uppercase table\n" );
1933         dataptr[0] = AL_reg(context); /* Info ID */
1934         *(DWORD*)(dataptr+1) = MAKESEGPTR( INT21_GetHeapSelector( context ),
1935                                            offsetof(INT21_HEAP, uppercase_size) );
1936         SET_CX( context, 5 ); /* Size of returned info */
1937         break;
1938
1939     case 0x03: /* GET POINTER TO LOWERCASE TABLE */
1940         TRACE( "Get pointer to lowercase table\n" );
1941         dataptr[0] = 0x03; /* Info ID */
1942         *(DWORD*)(dataptr+1) = MAKESEGPTR( INT21_GetHeapSelector( context ),
1943                                            offsetof(INT21_HEAP, lowercase_size) );
1944         SET_CX( context, 5 ); /* Size of returned info */
1945         break;
1946
1947     case 0x05: /* GET POINTER TO FILENAME TERMINATOR TABLE */
1948         TRACE("Get pointer to filename terminator table\n");
1949         dataptr[0] = 0x05; /* Info ID */
1950         *(DWORD*)(dataptr+1) = MAKESEGPTR( INT21_GetHeapSelector( context ),
1951                                            offsetof(INT21_HEAP, filename_size) );
1952         SET_CX( context, 5 ); /* Size of returned info */
1953         break;
1954
1955     case 0x06: /* GET POINTER TO COLLATING SEQUENCE TABLE */
1956         TRACE("Get pointer to collating sequence table\n");
1957         dataptr[0] = 0x06; /* Info ID */
1958         *(DWORD*)(dataptr+1) = MAKESEGPTR( INT21_GetHeapSelector( context ),
1959                                            offsetof(INT21_HEAP, collating_size) );
1960         SET_CX( context, 5 ); /* Size of returned info */
1961         break;
1962
1963     case 0x07: /* GET POINTER TO DBCS LEAD BYTE TABLE */
1964         TRACE("Get pointer to DBCS lead byte table\n");
1965         dataptr[0] = 0x07; /* Info ID */
1966         *(DWORD*)(dataptr+1) = MAKESEGPTR( INT21_GetHeapSelector( context ),
1967                                            offsetof(INT21_HEAP, dbcs_size) );
1968         SET_CX( context, 5 ); /* Size of returned info */
1969         break;
1970
1971     case 0x20: /* CAPITALIZE CHARACTER */
1972     case 0xa0: /* CAPITALIZE FILENAME CHARACTER */
1973         TRACE("Convert char to uppercase\n");
1974         SET_DL( context, toupper(DL_reg(context)) );
1975         break;
1976
1977     case 0x21: /* CAPITALIZE STRING */
1978     case 0xa1: /* CAPITALIZE COUNTED FILENAME STRING */
1979         TRACE("Convert string to uppercase with length\n");
1980         {
1981             char *ptr = (char *)CTX_SEG_OFF_TO_LIN( context,
1982                                                     context->SegDs,
1983                                                     context->Edx );
1984             WORD len = CX_reg(context);
1985             while (len--) { *ptr = toupper(*ptr); ptr++; }
1986         }
1987         break;
1988
1989     case 0x22: /* CAPITALIZE ASCIIZ STRING */
1990     case 0xa2: /* CAPITALIZE ASCIIZ FILENAME */
1991         TRACE("Convert ASCIIZ string to uppercase\n");
1992         _strupr( (LPSTR)CTX_SEG_OFF_TO_LIN(context, context->SegDs, context->Edx) );
1993         break;
1994
1995     case 0x23: /* DETERMINE IF CHARACTER REPRESENTS YES/NO RESPONSE */
1996         INT_BARF( context, 0x21 );
1997         SET_CFLAG( context );
1998         break;
1999
2000     default:
2001         INT_BARF( context, 0x21 );
2002         SET_CFLAG(context);
2003         break;
2004     }
2005 }
2006
2007
2008 /***********************************************************************
2009  *           INT21_FileAttributes
2010  *
2011  * Handler for:
2012  * - function 0x43
2013  * - subfunction 0x43 of function 0x71
2014  */
2015 static BOOL INT21_FileAttributes( CONTEXT86 *context, 
2016                                   BYTE       subfunction,
2017                                   BOOL       islong )
2018 {
2019     WCHAR fileW[MAX_PATH];
2020     char *fileA = CTX_SEG_OFF_TO_LIN(context, 
2021                                      context->SegDs, 
2022                                      context->Edx);
2023     HANDLE   handle;
2024     BOOL     status;
2025     FILETIME filetime;
2026     DWORD    result;
2027     WORD     date, time;
2028
2029     switch (subfunction)
2030     {
2031     case 0x00: /* GET FILE ATTRIBUTES */
2032         TRACE( "GET FILE ATTRIBUTES for %s\n", fileA );
2033         MultiByteToWideChar(CP_OEMCP, 0, fileA, -1, fileW, MAX_PATH);
2034
2035         result = GetFileAttributesW( fileW );
2036         if (result == INVALID_FILE_ATTRIBUTES)
2037             return FALSE;
2038         else
2039         {
2040             SET_CX( context, (WORD)result );
2041             if (!islong)
2042                 SET_AX( context, (WORD)result ); /* DR DOS */
2043         }
2044         break;
2045
2046     case 0x01: /* SET FILE ATTRIBUTES */
2047         TRACE( "SET FILE ATTRIBUTES 0x%02x for %s\n", 
2048                CX_reg(context), fileA );
2049         MultiByteToWideChar(CP_OEMCP, 0, fileA, -1, fileW, MAX_PATH);
2050
2051         if (!SetFileAttributesW( fileW, CX_reg(context) ))
2052             return FALSE;
2053         break;
2054
2055     case 0x02: /* GET COMPRESSED FILE SIZE */
2056         TRACE( "GET COMPRESSED FILE SIZE for %s\n", fileA );
2057         MultiByteToWideChar(CP_OEMCP, 0, fileA, -1, fileW, MAX_PATH);
2058
2059         result = GetCompressedFileSizeW( fileW, NULL );
2060         if (result == INVALID_FILE_SIZE)
2061             return FALSE;
2062         else
2063         {
2064             SET_AX( context, LOWORD(result) );
2065             SET_DX( context, HIWORD(result) );
2066         }
2067         break;
2068
2069     case 0x03: /* SET FILE LAST-WRITTEN DATE AND TIME */
2070         if (!islong)
2071             INT_BARF( context, 0x21 );
2072         else
2073         {
2074             TRACE( "SET FILE LAST-WRITTEN DATE AND TIME, file %s\n", fileA );
2075             MultiByteToWideChar(CP_OEMCP, 0, fileA, -1, fileW, MAX_PATH);
2076
2077             handle = CreateFileW( fileW, GENERIC_WRITE, 
2078                                   FILE_SHARE_READ | FILE_SHARE_WRITE, 
2079                                   NULL, OPEN_EXISTING, 0, 0 );
2080             if (handle == INVALID_HANDLE_VALUE)
2081                 return FALSE;
2082
2083             DosDateTimeToFileTime( DI_reg(context), 
2084                                    CX_reg(context),
2085                                    &filetime );
2086             status = SetFileTime( handle, NULL, NULL, &filetime );
2087
2088             CloseHandle( handle );
2089             return status;
2090         }
2091         break;
2092
2093     case 0x04: /* GET FILE LAST-WRITTEN DATE AND TIME */
2094         if (!islong)
2095             INT_BARF( context, 0x21 );
2096         else
2097         {
2098             TRACE( "GET FILE LAST-WRITTEN DATE AND TIME, file %s\n", fileA );
2099             MultiByteToWideChar(CP_OEMCP, 0, fileA, -1, fileW, MAX_PATH);
2100
2101             handle = CreateFileW( fileW, GENERIC_READ, 
2102                                   FILE_SHARE_READ | FILE_SHARE_WRITE, 
2103                                   NULL, OPEN_EXISTING, 0, 0 );
2104             if (handle == INVALID_HANDLE_VALUE)
2105                 return FALSE;
2106
2107             status = GetFileTime( handle, NULL, NULL, &filetime );
2108             if (status)
2109             {
2110                 FileTimeToDosDateTime( &filetime, &date, &time );
2111                 SET_DI( context, date );
2112                 SET_CX( context, time );
2113             }
2114
2115             CloseHandle( handle );
2116             return status;
2117         }
2118         break;
2119
2120     case 0x05: /* SET FILE LAST ACCESS DATE */
2121         if (!islong)
2122             INT_BARF( context, 0x21 );
2123         else
2124         {
2125             TRACE( "SET FILE LAST ACCESS DATE, file %s\n", fileA );
2126             MultiByteToWideChar(CP_OEMCP, 0, fileA, -1, fileW, MAX_PATH);
2127
2128             handle = CreateFileW( fileW, GENERIC_WRITE, 
2129                                   FILE_SHARE_READ | FILE_SHARE_WRITE, 
2130                                   NULL, OPEN_EXISTING, 0, 0 );
2131             if (handle == INVALID_HANDLE_VALUE)
2132                 return FALSE;
2133
2134             DosDateTimeToFileTime( DI_reg(context), 
2135                                    0,
2136                                    &filetime );
2137             status = SetFileTime( handle, NULL, &filetime, NULL );
2138
2139             CloseHandle( handle );
2140             return status;
2141         }
2142         break;
2143
2144     case 0x06: /* GET FILE LAST ACCESS DATE */
2145         if (!islong)
2146             INT_BARF( context, 0x21 );
2147         else
2148         {
2149             TRACE( "GET FILE LAST ACCESS DATE, file %s\n", fileA );
2150             MultiByteToWideChar(CP_OEMCP, 0, fileA, -1, fileW, MAX_PATH);
2151
2152             handle = CreateFileW( fileW, GENERIC_READ, 
2153                                   FILE_SHARE_READ | FILE_SHARE_WRITE, 
2154                                   NULL, OPEN_EXISTING, 0, 0 );
2155             if (handle == INVALID_HANDLE_VALUE)
2156                 return FALSE;
2157
2158             status = GetFileTime( handle, NULL, &filetime, NULL );
2159             if (status)
2160             {
2161                 FileTimeToDosDateTime( &filetime, &date, NULL );
2162                 SET_DI( context, date );
2163             }
2164
2165             CloseHandle( handle );
2166             return status;
2167         }
2168         break;
2169
2170     case 0x07: /* SET FILE CREATION DATE AND TIME */
2171         if (!islong)
2172             INT_BARF( context, 0x21 );
2173         else
2174         {
2175             TRACE( "SET FILE CREATION DATE AND TIME, file %s\n", fileA );
2176
2177             handle = CreateFileW( fileW, GENERIC_WRITE,
2178                                   FILE_SHARE_READ | FILE_SHARE_WRITE, 
2179                                   NULL, OPEN_EXISTING, 0, 0 );
2180             if (handle == INVALID_HANDLE_VALUE)
2181                 return FALSE;
2182             
2183             /*
2184              * FIXME: SI has number of 10-millisecond units past time in CX.
2185              */
2186             DosDateTimeToFileTime( DI_reg(context),
2187                                    CX_reg(context),
2188                                    &filetime );
2189             status = SetFileTime( handle, &filetime, NULL, NULL );
2190
2191             CloseHandle( handle );
2192             return status;
2193         }
2194         break;
2195
2196     case 0x08: /* GET FILE CREATION DATE AND TIME */
2197         if (!islong)
2198             INT_BARF( context, 0x21 );
2199         else
2200         {
2201             TRACE( "GET FILE CREATION DATE AND TIME, handle %d\n",
2202                    BX_reg(context) );
2203
2204             handle = CreateFileW( fileW, GENERIC_READ, 
2205                                   FILE_SHARE_READ | FILE_SHARE_WRITE, 
2206                                   NULL, OPEN_EXISTING, 0, 0 );
2207             if (handle == INVALID_HANDLE_VALUE)
2208                 return FALSE;
2209
2210             status = GetFileTime( handle, &filetime, NULL, NULL );
2211             if (status)
2212             {            
2213                 FileTimeToDosDateTime( &filetime, &date, &time );
2214                 SET_DI( context, date );
2215                 SET_CX( context, time );
2216                 /*
2217                  * FIXME: SI has number of 10-millisecond units past 
2218                  *        time in CX.
2219                  */
2220                 SET_SI( context, 0 );
2221             }
2222
2223             CloseHandle(handle);
2224             return status;
2225         }
2226         break;
2227
2228     case 0xff: /* EXTENDED-LENGTH FILENAME OPERATIONS */
2229         if (islong || context->Ebp != 0x5053)
2230             INT_BARF( context, 0x21 );
2231         else
2232         {
2233             switch(CL_reg(context))
2234             {
2235             case 0x39:
2236                 if (!INT21_CreateDirectory( context ))
2237                     return FALSE;
2238                 break;
2239
2240             case 0x56:
2241                 if (!INT21_RenameFile( context ))
2242                     return FALSE;
2243                 break;
2244
2245             default:
2246                 INT_BARF( context, 0x21 );
2247             }
2248         }
2249         break;
2250
2251     default:
2252         INT_BARF( context, 0x21 );
2253     }
2254
2255     return TRUE;
2256 }
2257
2258
2259 /***********************************************************************
2260  *           INT21_FileDateTime
2261  *
2262  * Handler for function 0x57.
2263  */
2264 static BOOL INT21_FileDateTime( CONTEXT86 *context )
2265 {
2266     HANDLE   handle = DosFileHandleToWin32Handle(BX_reg(context));
2267     FILETIME filetime;
2268     WORD     date, time;
2269
2270     switch (AL_reg(context)) {
2271     case 0x00:  /* Get last-written stamp */
2272         TRACE( "GET FILE LAST-WRITTEN DATE AND TIME, handle %d\n",
2273                BX_reg(context) );
2274         {
2275             if (!GetFileTime( handle, NULL, NULL, &filetime ))
2276                 return FALSE;
2277             FileTimeToDosDateTime( &filetime, &date, &time );
2278             SET_DX( context, date );
2279             SET_CX( context, time );
2280             break;
2281         }
2282
2283     case 0x01:  /* Set last-written stamp */
2284         TRACE( "SET FILE LAST-WRITTEN DATE AND TIME, handle %d\n",
2285                BX_reg(context) );
2286         {
2287             DosDateTimeToFileTime( DX_reg(context), 
2288                                    CX_reg(context),
2289                                    &filetime );
2290             if (!SetFileTime( handle, NULL, NULL, &filetime ))
2291                 return FALSE;
2292             break;
2293         }
2294
2295     case 0x04:  /* Get last access stamp, DOS 7 */
2296         TRACE( "GET FILE LAST ACCESS DATE AND TIME, handle %d\n",
2297                BX_reg(context) );
2298         {
2299             if (!GetFileTime( handle, NULL, &filetime, NULL ))
2300                 return FALSE;
2301             FileTimeToDosDateTime( &filetime, &date, &time );
2302             SET_DX( context, date );
2303             SET_CX( context, time );
2304             break;
2305         }
2306
2307     case 0x05:  /* Set last access stamp, DOS 7 */
2308         TRACE( "SET FILE LAST ACCESS DATE AND TIME, handle %d\n",
2309                BX_reg(context) );
2310         {
2311             DosDateTimeToFileTime( DX_reg(context), 
2312                                    CX_reg(context),
2313                                    &filetime );
2314             if (!SetFileTime( handle, NULL, &filetime, NULL ))
2315                 return FALSE;
2316             break;
2317         }
2318
2319     case 0x06:  /* Get creation stamp, DOS 7 */
2320         TRACE( "GET FILE CREATION DATE AND TIME, handle %d\n",
2321                BX_reg(context) );
2322         {
2323             if (!GetFileTime( handle, &filetime, NULL, NULL ))
2324                 return FALSE;
2325             FileTimeToDosDateTime( &filetime, &date, &time );
2326             SET_DX( context, date );
2327             SET_CX( context, time );
2328             /*
2329              * FIXME: SI has number of 10-millisecond units past time in CX.
2330              */
2331             SET_SI( context, 0 );
2332             break;
2333         }
2334
2335     case 0x07:  /* Set creation stamp, DOS 7 */
2336         TRACE( "SET FILE CREATION DATE AND TIME, handle %d\n",
2337                BX_reg(context) );
2338         {
2339             /*
2340              * FIXME: SI has number of 10-millisecond units past time in CX.
2341              */
2342             DosDateTimeToFileTime( DX_reg(context), 
2343                                    CX_reg(context),
2344                                    &filetime );
2345             if (!SetFileTime( handle, &filetime, NULL, NULL ))
2346                 return FALSE;
2347             break;
2348         }
2349
2350     default:
2351         INT_BARF( context, 0x21 );
2352         break;
2353     }
2354
2355     return TRUE;
2356 }
2357
2358
2359 /***********************************************************************
2360  *           INT21_GetPSP
2361  *
2362  * Handler for functions 0x51 and 0x62.
2363  */
2364 static void INT21_GetPSP( CONTEXT86 *context )
2365 {
2366     TRACE( "GET CURRENT PSP ADDRESS (%02x)\n", AH_reg(context) );
2367
2368     /*
2369      * FIXME: should we return the original DOS PSP upon
2370      *        Windows startup ? 
2371      */
2372     if (!ISV86(context) && DOSVM_IsWin16())
2373         SET_BX( context, LOWORD(GetCurrentPDB16()) );
2374     else
2375         SET_BX( context, DOSVM_psp );
2376 }
2377
2378 static inline void setword( BYTE *ptr, WORD w )
2379 {
2380     ptr[0] = (BYTE)w;
2381     ptr[1] = (BYTE)(w >> 8);
2382 }
2383
2384 static void CreateBPB(int drive, BYTE *data, BOOL16 limited)
2385 /* limited == TRUE is used with INT 0x21/0x440d */
2386 {
2387     /* FIXME: we're forcing some values without checking that those are valid */
2388     if (drive > 1) 
2389     {
2390         setword(data, 512);
2391         data[2] = 2;
2392         setword(&data[3], 0);
2393         data[5] = 2;
2394         setword(&data[6], 240);
2395         setword(&data[8], 64000);
2396         data[0x0a] = 0xf8;
2397         setword(&data[0x0b], 40);
2398         setword(&data[0x0d], 56);
2399         setword(&data[0x0f], 2);
2400         setword(&data[0x11], 0);
2401         if (!limited) 
2402         {
2403             setword(&data[0x1f], 800);
2404             data[0x21] = 5;
2405             setword(&data[0x22], 1);
2406         }
2407     }
2408     else
2409     { /* 1.44mb */
2410         setword(data, 512);
2411         data[2] = 2;
2412         setword(&data[3], 0);
2413         data[5] = 2;
2414         setword(&data[6], 240);
2415         setword(&data[8], 2880);
2416         data[0x0a] = 0xf8;
2417         setword(&data[0x0b], 6);
2418         setword(&data[0x0d], 18);
2419         setword(&data[0x0f], 2);
2420         setword(&data[0x11], 0);
2421         if (!limited) 
2422         {
2423             setword(&data[0x1f], 80);
2424             data[0x21] = 7;
2425             setword(&data[0x22], 2);
2426         }
2427     }
2428 }
2429
2430 inline DWORD INT21_Ioctl_CylHeadSect2Lin(DWORD cyl, WORD head, WORD sec, WORD cyl_cnt, WORD head_cnt, WORD sec_cnt)
2431 {
2432     DWORD res = (cyl * head_cnt*sec_cnt + head * sec_cnt + sec);
2433     return res;
2434 }
2435
2436 /***********************************************************************
2437  *           INT21_Ioctl_Block
2438  *
2439  * Handler for block device IOCTLs.
2440  */
2441 static void INT21_Ioctl_Block( CONTEXT86 *context )
2442 {
2443     BYTE *dataptr;
2444     BYTE  drive = INT21_MapDrive( BL_reg(context) );
2445     WCHAR drivespec[4] = {'A', ':', '\\', 0};
2446     UINT  drivetype;
2447
2448     drivespec[0] += drive;
2449     drivetype = GetDriveTypeW( drivespec );
2450
2451     RESET_CFLAG(context);
2452     if (drivetype == DRIVE_UNKNOWN || drivetype == DRIVE_NO_ROOT_DIR)
2453     {
2454         TRACE( "IOCTL - SUBFUNCTION %d - INVALID DRIVE %c:\n", 
2455                AL_reg(context), 'A' + drive );
2456         SetLastError( ERROR_INVALID_DRIVE );
2457         SET_AX( context, ERROR_INVALID_DRIVE );
2458         SET_CFLAG( context );
2459         return;
2460     }
2461
2462     switch (AL_reg(context))
2463     {
2464     case 0x04: /* READ FROM BLOCK DEVICE CONTROL CHANNEL */
2465     case 0x05: /* WRITE TO BLOCK DEVICE CONTROL CHANNEL */
2466         INT_BARF( context, 0x21 );
2467         break;
2468
2469     case 0x08: /* CHECK IF BLOCK DEVICE REMOVABLE */
2470         TRACE( "IOCTL - CHECK IF BLOCK DEVICE REMOVABLE - %c:\n",
2471                'A' + drive );
2472
2473         if (drivetype == DRIVE_REMOVABLE)
2474             SET_AX( context, 0 ); /* removable */
2475         else
2476             SET_AX( context, 1 ); /* not removable */
2477         break;
2478
2479     case 0x09: /* CHECK IF BLOCK DEVICE REMOTE */
2480         TRACE( "IOCTL - CHECK IF BLOCK DEVICE REMOTE - %c:\n",
2481                'A' + drive );
2482
2483         if (drivetype == DRIVE_REMOTE)
2484             SET_DX( context, (1<<9) | (1<<12) ); /* remote + no direct IO */
2485         else
2486             SET_DX( context, 0 ); /* FIXME: use driver attr here */
2487         break;
2488
2489     case 0x0d: /* GENERIC BLOCK DEVICE REQUEST */
2490         /* Get pointer to IOCTL parameter block */
2491         dataptr = CTX_SEG_OFF_TO_LIN(context, context->SegDs, context->Edx);
2492
2493         switch (CX_reg(context))
2494         {
2495         case 0x0841: /* write logical device track */
2496             TRACE( "GENERIC IOCTL - Write logical device track - %c:\n",
2497                    'A' + drive);
2498             {
2499                 WORD head   = *(WORD *)(dataptr+1);
2500                 WORD cyl    = *(WORD *)(dataptr+3);
2501                 WORD sect   = *(WORD *)(dataptr+5);
2502                 WORD nrsect = *(WORD *)(dataptr+7);
2503                 BYTE *data  = CTX_SEG_OFF_TO_LIN(context, *(WORD *)(dataptr+11), *(WORD *)(dataptr+9));
2504                 WORD cyl_cnt, head_cnt, sec_cnt;
2505
2506                 /* FIXME: we're faking some values here */
2507                 if (drive > 1)
2508                 {
2509                     /* cyl_cnt = 0x300;
2510                     head_cnt = 16;
2511                     sec_cnt = 255; */
2512                     SET_AX( context, ERROR_WRITE_FAULT );
2513                     SET_CFLAG(context);
2514                     break;
2515                 }
2516                 else
2517                 { /* floppy */
2518                     cyl_cnt = 80;
2519                     head_cnt = 2;
2520                     sec_cnt = 18;
2521                 }
2522
2523                 if (!DOSVM_RawWrite(drive, INT21_Ioctl_CylHeadSect2Lin(cyl, head, sect, cyl_cnt, head_cnt, sec_cnt), nrsect, data, FALSE))
2524                 {
2525                     SET_AX( context, ERROR_WRITE_FAULT );
2526                     SET_CFLAG(context);
2527                 }
2528             }
2529             break;
2530
2531         case 0x084a: /* lock logical volume */
2532             TRACE( "GENERIC IOCTL - Lock logical volume, level %d mode %d - %c:\n",
2533                    BH_reg(context), DX_reg(context), 'A' + drive );
2534             break;
2535
2536         case 0x0860: /* get device parameters */
2537             /* FIXME: we're faking some values here */
2538             /* used by w4wgrp's winfile */
2539             memset(dataptr, 0, 0x20); /* DOS 6.22 uses 0x20 bytes */
2540             dataptr[0] = 0x04;
2541             dataptr[6] = 0; /* media type */
2542             if (drive > 1)
2543             {
2544                 dataptr[1] = 0x05; /* fixed disk */
2545                 setword(&dataptr[2], 0x01); /* non removable */
2546                 setword(&dataptr[4], 0x300); /* # of cylinders */
2547             }
2548             else
2549             {
2550                 dataptr[1] = 0x07; /* block dev, floppy */
2551                 setword(&dataptr[2], 0x02); /* removable */
2552                 setword(&dataptr[4], 80); /* # of cylinders */
2553             }
2554             CreateBPB(drive, &dataptr[7], TRUE);
2555             RESET_CFLAG(context);
2556             break;
2557
2558         case 0x0861: /* read logical device track */
2559             TRACE( "GENERIC IOCTL - Read logical device track - %c:\n",
2560                    'A' + drive);
2561             {
2562                 WORD head   = *(WORD *)(dataptr+1);
2563                 WORD cyl    = *(WORD *)(dataptr+3);
2564                 WORD sect   = *(WORD *)(dataptr+5);
2565                 WORD nrsect = *(WORD *)(dataptr+7);
2566                 BYTE *data  = CTX_SEG_OFF_TO_LIN(context, *(WORD *)(dataptr+11), *(WORD *)(dataptr+9));
2567                 WORD cyl_cnt, head_cnt, sec_cnt;
2568
2569                 /* FIXME: we're faking some values here */
2570                 if (drive > 1)
2571                 {
2572                     cyl_cnt = 0x300;
2573                     head_cnt = 16;
2574                     sec_cnt = 255;
2575                 }
2576                 else
2577                 { /* floppy */
2578                     cyl_cnt = 80;
2579                     head_cnt = 2;
2580                     sec_cnt = 18;
2581                 }
2582
2583                 if (!DOSVM_RawRead(drive, INT21_Ioctl_CylHeadSect2Lin(cyl, head, sect, cyl_cnt, head_cnt, sec_cnt), nrsect, data, FALSE))
2584                 {
2585                     SET_AX( context, ERROR_READ_FAULT );
2586                     SET_CFLAG(context);
2587                 }
2588             }
2589             break;
2590
2591         case 0x0866: /* get volume serial number */
2592             {
2593                 WCHAR   label[12],fsname[9];
2594                 DWORD   serial;
2595
2596                 drivespec[0] += drive;
2597                 GetVolumeInformationW(drivespec, label, 12, &serial, NULL, NULL, fsname, 9);
2598                 *(WORD*)dataptr = 0;
2599                 memcpy(dataptr+2,&serial,4);
2600                 WideCharToMultiByte(CP_OEMCP, 0, label, 11, (LPSTR)dataptr + 6, 11, NULL, NULL);
2601                 WideCharToMultiByte(CP_OEMCP, 0, fsname, 8, (LPSTR)dataptr + 17, 8, NULL, NULL);
2602             }
2603             break;
2604
2605         case 0x086a: /* unlock logical volume */
2606             TRACE( "GENERIC IOCTL - Logical volume unlocked - %c:\n", 
2607                    'A' + drive );
2608             break;
2609
2610         case 0x086f: /* get drive map information */
2611             memset(dataptr+1, '\0', dataptr[0]-1);
2612             dataptr[1] = dataptr[0];
2613             dataptr[2] = 0x07; /* protected mode driver; no eject; no notification */
2614             dataptr[3] = 0xFF; /* no physical drive */
2615             break;
2616
2617         case 0x0872:
2618             /* Trial and error implementation */
2619             SET_AX( context, drivetype == DRIVE_UNKNOWN ? 0x0f : 0x01 );
2620             SET_CFLAG(context); /* Seems to be set all the time */
2621             break;
2622
2623         default:
2624             INT_BARF( context, 0x21 );            
2625         }
2626         break;
2627
2628     case 0x0e: /* GET LOGICAL DRIVE MAP */
2629         TRACE( "IOCTL - GET LOGICAL DRIVE MAP - %c:\n",
2630                'A' + drive );
2631         /* FIXME: this is not correct if drive has mappings */
2632         SET_AL( context, 0 ); /* drive has no mapping */
2633         break;
2634
2635     case 0x0f: /* SET LOGICAL DRIVE MAP */
2636         TRACE("IOCTL - SET LOGICAL DRIVE MAP for drive %s\n",
2637               INT21_DriveName( BL_reg(context)));
2638         /* FIXME: as of today, we don't support logical drive mapping... */
2639         SET_AL( context, 0 );
2640         break;
2641
2642     case 0x11: /* QUERY GENERIC IOCTL CAPABILITY */
2643     default:
2644         INT_BARF( context, 0x21 );
2645     }
2646 }
2647
2648
2649 /***********************************************************************
2650  *           INT21_IoctlScsiMgrHandler
2651  *
2652  * IOCTL handler for the SCSIMGR device.
2653  */
2654 static void INT21_IoctlScsiMgrHandler( CONTEXT86 *context )
2655 {
2656     switch (AL_reg(context))
2657     {
2658     case 0x00: /* GET DEVICE INFORMATION */
2659         SET_DX( context, 0xc0c0 );
2660         break;
2661
2662     case 0x02: /* READ FROM CHARACTER DEVICE CONTROL CHANNEL */
2663         DOSVM_ASPIHandler(context);
2664         break;
2665
2666     case 0x0a: /* CHECK IF HANDLE IS REMOTE */
2667         SET_DX( context, 0 );
2668         break;
2669
2670     case 0x01: /* SET DEVICE INFORMATION */
2671     case 0x03: /* WRITE TO CHARACTER DEVICE CONTROL CHANNEL */
2672     case 0x06: /* GET INPUT STATUS */
2673     case 0x07: /* GET OUTPUT STATUS */
2674     case 0x0c: /* GENERIC CHARACTER DEVICE REQUEST */
2675     case 0x10: /* QUERY GENERIC IOCTL CAPABILITY */
2676     default:
2677         INT_BARF( context, 0x21 );
2678         break;
2679     }
2680 }
2681
2682
2683 /***********************************************************************
2684  *           INT21_IoctlEMSHandler
2685  *
2686  * IOCTL handler for the EMXXXX0 device.
2687  */
2688 static void INT21_IoctlEMSHandler( CONTEXT86 *context )
2689 {
2690     EMS_Ioctl_Handler(context);
2691 }
2692
2693
2694 /***********************************************************************
2695  *           INT21_IoctlHPScanHandler
2696  *
2697  * IOCTL handler for the HPSCAN device.
2698  */
2699 static void INT21_IoctlHPScanHandler( CONTEXT86 *context )
2700 {
2701     switch (AL_reg(context))
2702     {
2703     case 0x00: /* GET DEVICE INFORMATION */
2704         SET_DX( context, 0xc0c0 );
2705         break;
2706
2707     case 0x0a: /* CHECK IF HANDLE IS REMOTE */
2708         SET_DX( context, 0 );
2709         break;
2710
2711     case 0x01: /* SET DEVICE INFORMATION */
2712     case 0x02: /* READ FROM CHARACTER DEVICE CONTROL CHANNEL */
2713     case 0x03: /* WRITE TO CHARACTER DEVICE CONTROL CHANNEL */
2714     case 0x06: /* GET INPUT STATUS */
2715     case 0x07: /* GET OUTPUT STATUS */
2716     case 0x0c: /* GENERIC CHARACTER DEVICE REQUEST */
2717     case 0x10: /* QUERY GENERIC IOCTL CAPABILITY */
2718     default:
2719         INT_BARF( context, 0x21 );
2720         break;
2721     }
2722 }
2723
2724
2725 /***********************************************************************
2726  *           INT21_Ioctl_Char
2727  *
2728  * Handler for character device IOCTLs.
2729  */
2730 static void INT21_Ioctl_Char( CONTEXT86 *context )
2731 {
2732     struct stat st;
2733     int status, i, fd;
2734     int IsConsoleIOHandle = 0;
2735     HANDLE handle = DosFileHandleToWin32Handle(BX_reg(context));
2736
2737     status = wine_server_handle_to_fd( handle, 0, &fd, NULL );
2738     if (status)
2739     {
2740         if( VerifyConsoleIoHandle( handle))
2741             IsConsoleIOHandle = 1;
2742         else {
2743             SET_AX( context, RtlNtStatusToDosError(status) );
2744             SET_CFLAG( context );
2745             return;
2746         }
2747     } else {
2748         fstat( fd, &st );
2749         IsConsoleIOHandle = isatty( fd);
2750         wine_server_release_fd( handle, fd );
2751         for (i = 0; i < NB_MAGIC_DEVICES; i++)
2752         {
2753             if (!magic_devices[i].handle) continue;
2754             if (magic_devices[i].dev == st.st_dev &&
2755                     magic_devices[i].ino == st.st_ino)
2756             {
2757                 /* found it */
2758                 magic_devices[i].ioctl_handler( context );
2759                 return;
2760             }
2761         }
2762     }
2763
2764     /* no magic device found, do default handling */
2765
2766     switch (AL_reg(context))
2767     {
2768     case 0x00: /* GET DEVICE INFORMATION */
2769         TRACE( "IOCTL - GET DEVICE INFORMATION - %d\n", BX_reg(context) );
2770         if (IsConsoleIOHandle || S_ISCHR(st.st_mode))
2771         {
2772             /*
2773              * Returns attribute word in DX: 
2774              *   Bit 14 - Device driver can process IOCTL requests.
2775              *   Bit 13 - Output until busy supported.
2776              *   Bit 11 - Driver supports OPEN/CLOSE calls.
2777              *   Bit  8 - Unknown.
2778              *   Bit  7 - Set (indicates device).
2779              *   Bit  6 - EOF on input.
2780              *   Bit  5 - Raw (binary) mode.
2781              *   Bit  4 - Device is special (uses int29).
2782              *   Bit  3 - Clock device.
2783              *   Bit  2 - NUL device.
2784              *   Bit  1 - Console output device.
2785              *   Bit  0 - Console input device.
2786              */
2787             SET_DX( context, IsConsoleIOHandle ? 0x80c3 : 0x80c0 /* FIXME */ );
2788         }
2789         else
2790         {
2791             /*
2792              * Returns attribute word in DX: 
2793              *   Bit 15    - File is remote.
2794              *   Bit 14    - Don't set file date/time on closing.
2795              *   Bit 11    - Media not removable.
2796              *   Bit  8    - Generate int24 if no disk space on write 
2797              *               or read past end of file
2798              *   Bit  7    - Clear (indicates file).
2799              *   Bit  6    - File has not been written.
2800              *   Bit  5..0 - Drive number (0=A:,...)
2801              *
2802              * FIXME: Should check if file is on remote or removable drive.
2803              * FIXME: Should use drive file is located on (and not current).
2804              */
2805             SET_DX( context, 0x0140 + INT21_GetCurrentDrive() );
2806         }
2807         break;
2808
2809     case 0x0a: /* CHECK IF HANDLE IS REMOTE */
2810         TRACE( "IOCTL - CHECK IF HANDLE IS REMOTE - %d\n", BX_reg(context) );
2811         /*
2812          * Returns attribute word in DX:
2813          *   Bit 15 - Set if remote.
2814          *   Bit 14 - Set if date/time not set on close.
2815          *
2816          * FIXME: Should check if file is on remote drive.
2817          */
2818         SET_DX( context, 0 );
2819         break;
2820
2821     case 0x01: /* SET DEVICE INFORMATION */
2822     case 0x02: /* READ FROM CHARACTER DEVICE CONTROL CHANNEL */
2823     case 0x03: /* WRITE TO CHARACTER DEVICE CONTROL CHANNEL */
2824     case 0x06: /* GET INPUT STATUS */
2825     case 0x07: /* GET OUTPUT STATUS */
2826     case 0x0c: /* GENERIC CHARACTER DEVICE REQUEST */
2827     case 0x10: /* QUERY GENERIC IOCTL CAPABILITY */
2828     default:
2829         INT_BARF( context, 0x21 );
2830         break;
2831     }
2832 }
2833
2834
2835 /***********************************************************************
2836  *           INT21_Ioctl
2837  *
2838  * Handler for function 0x44.
2839  */
2840 static void INT21_Ioctl( CONTEXT86 *context )
2841 {
2842     switch (AL_reg(context))
2843     {
2844     case 0x00:
2845     case 0x01:
2846     case 0x02:
2847     case 0x03:
2848         INT21_Ioctl_Char( context );
2849         break;
2850
2851     case 0x04:
2852     case 0x05:
2853         INT21_Ioctl_Block( context );
2854         break;
2855
2856     case 0x06:
2857     case 0x07:
2858         INT21_Ioctl_Char( context );
2859         break;
2860
2861     case 0x08:
2862     case 0x09:
2863         INT21_Ioctl_Block( context );
2864         break;
2865
2866     case 0x0a:
2867         INT21_Ioctl_Char( context );
2868         break;
2869
2870     case 0x0b: /* SET SHARING RETRY COUNT */
2871         TRACE( "SET SHARING RETRY COUNT: Pause %d, retries %d.\n",
2872                CX_reg(context), DX_reg(context) );
2873         if (!CX_reg(context))
2874         {
2875             SET_AX( context, 1 );
2876             SET_CFLAG( context );
2877         }
2878         else
2879         {
2880             DOSDEV_SetSharingRetry( CX_reg(context), DX_reg(context) );
2881             RESET_CFLAG( context );
2882         }
2883         break;
2884
2885     case 0x0c:
2886         INT21_Ioctl_Char( context );
2887         break;
2888
2889     case 0x0d:
2890     case 0x0e:
2891     case 0x0f:
2892         INT21_Ioctl_Block( context );
2893         break;
2894
2895     case 0x10:
2896         INT21_Ioctl_Char( context );
2897         break;
2898
2899     case 0x11:
2900         INT21_Ioctl_Block( context );
2901         break;
2902
2903     case 0x12: /*  DR DOS - DETERMINE DOS TYPE (OBSOLETE FUNCTION) */
2904         TRACE( "DR DOS - DETERMINE DOS TYPE (OBSOLETE FUNCTION)\n" );
2905         SET_CFLAG(context);        /* Error / This is not DR DOS. */
2906         SET_AX( context, 0x0001 ); /* Invalid function */
2907         break;
2908
2909     case 0x52: /* DR DOS - DETERMINE DOS TYPE */
2910         TRACE( "DR DOS - DETERMINE DOS TYPE\n" );
2911         SET_CFLAG(context);        /* Error / This is not DR DOS. */
2912         SET_AX( context, 0x0001 ); /* Invalid function */
2913         break;
2914
2915     case 0xe0:  /* Sun PC-NFS API */
2916         TRACE( "Sun PC-NFS API\n" );
2917         /* not installed */
2918         break;
2919
2920     default:
2921         INT_BARF( context, 0x21 );
2922     }
2923 }
2924
2925
2926 /***********************************************************************
2927  *           INT21_Fat32
2928  *
2929  * Handler for function 0x73.
2930  */
2931 static BOOL INT21_Fat32( CONTEXT86 *context )
2932 {
2933     switch (AL_reg(context))
2934     {
2935     case 0x02: /* FAT32 - GET EXTENDED DPB */
2936         {
2937             BYTE drive = INT21_MapDrive( DL_reg(context) );
2938             WORD *ptr = CTX_SEG_OFF_TO_LIN(context, 
2939                                            context->SegEs, context->Edi);
2940             INT21_DPB *target = (INT21_DPB*)(ptr + 1);
2941             INT21_DPB *source;
2942
2943             TRACE( "FAT32 - GET EXTENDED DPB %d\n", DL_reg(context) );
2944
2945             if ( CX_reg(context) < sizeof(INT21_DPB) + 2 || *ptr < sizeof(INT21_DPB) )
2946             {
2947                 SetLastError( ERROR_BAD_LENGTH );
2948                 return FALSE;
2949             }
2950
2951             if ( !INT21_FillDrivePB( drive ) )
2952             {
2953                 SetLastError( ERROR_INVALID_DRIVE );
2954                 return FALSE;
2955             }
2956
2957             source = &INT21_GetHeapPointer()->misc_dpb_list[drive];
2958
2959             *ptr = sizeof(INT21_DPB);
2960             memcpy( target, source, sizeof(INT21_DPB));
2961
2962             if (LOWORD(context->Esi) != 0xF1A6)
2963             {
2964                 target->driver_header = 0;
2965                 target->next          = 0;
2966             }
2967             else
2968             {
2969                 FIXME( "Caller requested driver and next DPB pointers!\n" );
2970             }
2971         }
2972         break;
2973
2974     case 0x03: /* FAT32 - GET EXTENDED FREE SPACE ON DRIVE */
2975         {
2976             WCHAR dirW[MAX_PATH];
2977             char *dirA = CTX_SEG_OFF_TO_LIN( context,
2978                                              context->SegDs, context->Edx );
2979             BYTE *data = CTX_SEG_OFF_TO_LIN( context, 
2980                                              context->SegEs, context->Edi );
2981             DWORD cluster_sectors;
2982             DWORD sector_bytes;
2983             DWORD free_clusters;
2984             DWORD total_clusters;
2985
2986             TRACE( "FAT32 - GET EXTENDED FREE SPACE ON DRIVE %s\n", dirA );
2987             MultiByteToWideChar(CP_OEMCP, 0, dirA, -1, dirW, MAX_PATH);
2988
2989             if (CX_reg(context) < 44)
2990             {
2991                 SetLastError( ERROR_BAD_LENGTH );
2992                 return FALSE;
2993             }
2994
2995             if (!GetDiskFreeSpaceW( dirW, &cluster_sectors, &sector_bytes,
2996                                     &free_clusters, &total_clusters ))
2997                 return FALSE;
2998
2999             *(WORD*) (data +  0) = 44; /* size of structure */
3000             *(WORD*) (data +  2) = 0;  /* version */
3001             *(DWORD*)(data +  4) = cluster_sectors;
3002             *(DWORD*)(data +  8) = sector_bytes;
3003             *(DWORD*)(data + 12) = free_clusters;
3004             *(DWORD*)(data + 16) = total_clusters;
3005
3006             /*
3007              * Below we have free/total sectors and
3008              * free/total allocation units without adjustment
3009              * for compression. We fake both using cluster information.
3010              */
3011             *(DWORD*)(data + 20) = free_clusters * cluster_sectors;
3012             *(DWORD*)(data + 24) = total_clusters * cluster_sectors;
3013             *(DWORD*)(data + 28) = free_clusters;
3014             *(DWORD*)(data + 32) = total_clusters;
3015             
3016             /*
3017              * Between (data + 36) and (data + 43) there
3018              * are eight reserved bytes.
3019              */
3020         }
3021         break;
3022
3023     default:
3024         INT_BARF( context, 0x21 );
3025     }
3026
3027     return TRUE;
3028 }
3029
3030 static void INT21_ConvertFindDataWtoA(WIN32_FIND_DATAA *dataA,
3031                                       const WIN32_FIND_DATAW *dataW)
3032 {
3033     dataA->dwFileAttributes = dataW->dwFileAttributes;
3034     dataA->ftCreationTime   = dataW->ftCreationTime;
3035     dataA->ftLastAccessTime = dataW->ftLastAccessTime;
3036     dataA->ftLastWriteTime  = dataW->ftLastWriteTime;
3037     dataA->nFileSizeHigh    = dataW->nFileSizeHigh;
3038     dataA->nFileSizeLow     = dataW->nFileSizeLow;
3039     WideCharToMultiByte( CP_OEMCP, 0, dataW->cFileName, -1,
3040                          dataA->cFileName, sizeof(dataA->cFileName), NULL, NULL );
3041     WideCharToMultiByte( CP_OEMCP, 0, dataW->cAlternateFileName, -1,
3042                          dataA->cAlternateFileName, sizeof(dataA->cAlternateFileName), NULL, NULL );
3043 }
3044
3045 /***********************************************************************
3046  *           INT21_LongFilename
3047  *
3048  * Handler for function 0x71.
3049  */
3050 static void INT21_LongFilename( CONTEXT86 *context )
3051 {
3052     BOOL bSetDOSExtendedError = FALSE;
3053     WCHAR pathW[MAX_PATH];
3054     char* pathA;
3055
3056     if (HIBYTE(HIWORD(GetVersion16())) < 0x07)
3057     {
3058         TRACE( "LONG FILENAME - functions supported only under DOS7\n" );
3059         SET_CFLAG( context );
3060         SET_AL( context, 0 );
3061         return;
3062     }
3063
3064     switch (AL_reg(context))
3065     {
3066     case 0x0d: /* RESET DRIVE */
3067         INT_BARF( context, 0x21 );
3068         break;
3069
3070     case 0x39: /* LONG FILENAME - MAKE DIRECTORY */
3071         if (!INT21_CreateDirectory( context ))
3072             bSetDOSExtendedError = TRUE;
3073         break;
3074
3075     case 0x3a: /* LONG FILENAME - REMOVE DIRECTORY */
3076         pathA = CTX_SEG_OFF_TO_LIN(context, context->SegDs, context->Edx);
3077
3078         TRACE( "LONG FILENAME - REMOVE DIRECTORY %s\n", pathA);
3079         MultiByteToWideChar(CP_OEMCP, 0, pathA, -1, pathW, MAX_PATH);
3080         if (!RemoveDirectoryW( pathW )) bSetDOSExtendedError = TRUE;
3081         break;
3082
3083     case 0x3b: /* LONG FILENAME - CHANGE DIRECTORY */
3084         if (!INT21_SetCurrentDirectory( context ))
3085             bSetDOSExtendedError = TRUE;
3086         break;
3087
3088     case 0x41: /* LONG FILENAME - DELETE FILE */
3089         pathA = CTX_SEG_OFF_TO_LIN(context, context->SegDs, context->Edx);
3090
3091         TRACE( "LONG FILENAME - DELETE FILE %s\n", pathA );
3092         MultiByteToWideChar(CP_OEMCP, 0, pathA, -1, pathW, MAX_PATH);
3093
3094         if (!DeleteFileW( pathW )) bSetDOSExtendedError = TRUE;
3095         break;
3096
3097     case 0x43: /* LONG FILENAME - EXTENDED GET/SET FILE ATTRIBUTES */
3098         if (!INT21_FileAttributes( context, BL_reg(context), TRUE ))
3099             bSetDOSExtendedError = TRUE;
3100         break;
3101
3102     case 0x47: /* LONG FILENAME - GET CURRENT DIRECTORY */
3103         if (!INT21_GetCurrentDirectory( context, TRUE ))
3104             bSetDOSExtendedError = TRUE;
3105         break;
3106
3107     case 0x4e: /* LONG FILENAME - FIND FIRST MATCHING FILE */
3108         {
3109             HANDLE              handle;
3110             HGLOBAL16           h16;
3111             WIN32_FIND_DATAW    dataW;
3112             WIN32_FIND_DATAA*   dataA;
3113
3114             pathA = CTX_SEG_OFF_TO_LIN(context, context->SegDs,context->Edx);
3115             TRACE(" LONG FILENAME - FIND FIRST MATCHING FILE for %s\n", pathA);
3116
3117             MultiByteToWideChar(CP_OEMCP, 0, pathA, -1, pathW, MAX_PATH);
3118             handle = FindFirstFileW(pathW, &dataW);
3119             
3120             dataA = (WIN32_FIND_DATAA *)CTX_SEG_OFF_TO_LIN(context, context->SegEs,
3121                                                            context->Edi);
3122             if (handle != INVALID_HANDLE_VALUE && 
3123                 (h16 = GlobalAlloc16(GMEM_MOVEABLE, sizeof(handle))))
3124             {
3125                 HANDLE* ptr = GlobalLock16( h16 );
3126                 *ptr = handle;
3127                 GlobalUnlock16( h16 );
3128                 SET_AX( context, h16 );
3129                 INT21_ConvertFindDataWtoA(dataA, &dataW);
3130             }
3131             else
3132             {           
3133                 if (handle != INVALID_HANDLE_VALUE) FindClose(handle);
3134                 SET_AX( context, INVALID_HANDLE_VALUE16);
3135                 bSetDOSExtendedError = TRUE;
3136             }
3137         }
3138         break;
3139
3140     case 0x4f: /* LONG FILENAME - FIND NEXT MATCHING FILE */
3141         {
3142             HGLOBAL16           h16 = BX_reg(context);
3143             HANDLE*             ptr;
3144             WIN32_FIND_DATAW    dataW;
3145             WIN32_FIND_DATAA*   dataA;
3146
3147             TRACE("LONG FILENAME - FIND NEXT MATCHING FILE for handle %d\n",
3148                   BX_reg(context));
3149
3150             dataA = (WIN32_FIND_DATAA *)CTX_SEG_OFF_TO_LIN(context, context->SegEs,
3151                                                            context->Edi);
3152
3153             if (h16 != INVALID_HANDLE_VALUE16 && (ptr = GlobalLock16( h16 )))
3154             {
3155                 if (!FindNextFileW(*ptr, &dataW)) bSetDOSExtendedError = TRUE;
3156                 else INT21_ConvertFindDataWtoA(dataA, &dataW);
3157                 GlobalUnlock16( h16 );
3158             }
3159             else
3160             {
3161                 SetLastError( ERROR_INVALID_HANDLE );
3162                 bSetDOSExtendedError = TRUE;
3163             }
3164         }
3165         break;
3166
3167     case 0x56: /* LONG FILENAME - RENAME FILE */
3168         if (!INT21_RenameFile(context))
3169             bSetDOSExtendedError = TRUE;
3170         break;
3171
3172     case 0x60: /* LONG FILENAME - CONVERT PATH */
3173         {
3174             WCHAR   res[MAX_PATH];
3175
3176             switch (CL_reg(context))
3177             {
3178             case 0x00:  /* "truename" - Canonicalize path */
3179                 /* 
3180                  * FIXME: This is not 100% equal to 0x01 case, 
3181                  *        if you fix this, fix int21 subfunction 0x60, too.
3182                  */
3183
3184             case 0x01:  /* Get short filename or path */
3185                 MultiByteToWideChar(CP_OEMCP, 0, CTX_SEG_OFF_TO_LIN(context, context->SegDs, context->Esi), -1, pathW, MAX_PATH);
3186                 if (!GetShortPathNameW(pathW, res, 67))
3187                     bSetDOSExtendedError = TRUE;
3188                 else
3189                 {
3190                     SET_AX( context, 0 );
3191                     WideCharToMultiByte(CP_OEMCP, 0, res, -1, 
3192                                         CTX_SEG_OFF_TO_LIN(context, context->SegEs, context->Edi), 
3193                                         67, NULL, NULL);
3194                 }
3195                 break;
3196             
3197             case 0x02:  /* Get canonical long filename or path */
3198                 MultiByteToWideChar(CP_OEMCP, 0, CTX_SEG_OFF_TO_LIN(context, context->SegDs, context->Esi), -1, pathW, MAX_PATH);
3199                 if (!GetFullPathNameW(pathW, 128, res, NULL))
3200                     bSetDOSExtendedError = TRUE;
3201                 else
3202                 {
3203                     SET_AX( context, 0 );
3204                     WideCharToMultiByte(CP_OEMCP, 0, res, -1, 
3205                                         CTX_SEG_OFF_TO_LIN(context, context->SegEs, context->Edi), 
3206                                         128, NULL, NULL);
3207                 }
3208                 break;
3209             default:
3210                 FIXME("Unimplemented long file name function:\n");
3211                 INT_BARF( context, 0x21 );
3212                 SET_CFLAG(context);
3213                 SET_AL( context, 0 );
3214                 break;
3215             }
3216         }
3217         break;
3218
3219     case 0x6c: /* LONG FILENAME - CREATE OR OPEN FILE */
3220         if (!INT21_CreateFile( context, context->Esi, TRUE,
3221                                BX_reg(context), DL_reg(context) ))
3222             bSetDOSExtendedError = TRUE;
3223         break;
3224
3225     case 0xa0: /* LONG FILENAME - GET VOLUME INFORMATION */
3226         {
3227             DWORD filename_len, flags;
3228             WCHAR dstW[8];
3229
3230             pathA = CTX_SEG_OFF_TO_LIN(context, context->SegDs,context->Edx);
3231
3232             TRACE("LONG FILENAME - GET VOLUME INFORMATION for drive having root dir '%s'.\n", pathA);
3233             SET_AX( context, 0 );
3234             MultiByteToWideChar(CP_OEMCP, 0, pathA, -1, pathW, MAX_PATH);
3235             if (!GetVolumeInformationW( pathW, NULL, 0, NULL, &filename_len,
3236                                         &flags, dstW, 8 ))
3237             {
3238                 INT_BARF( context, 0x21 );
3239                 SET_CFLAG(context);
3240                 break;
3241             }
3242             SET_BX( context, flags | 0x4000 ); /* support for LFN functions */
3243             SET_CX( context, filename_len );
3244             SET_DX( context, MAX_PATH ); /* FIXME: which len if DRIVE_SHORT_NAMES ? */
3245             WideCharToMultiByte(CP_OEMCP, 0, dstW, -1, 
3246                                 CTX_SEG_OFF_TO_LIN(context, context->SegEs, context->Edi), 
3247                                 8, NULL, NULL);
3248         }
3249         break;
3250
3251     case 0xa1: /* LONG FILENAME - "FindClose" - TERMINATE DIRECTORY SEARCH */
3252         {
3253             HGLOBAL16 h16 = BX_reg(context);
3254             HANDLE* ptr;
3255
3256             TRACE("LONG FILENAME - FINDCLOSE for handle %d\n",
3257                   BX_reg(context));
3258             if (h16 != INVALID_HANDLE_VALUE16 && (ptr = GlobalLock16( h16 )))
3259             {
3260                 if (!FindClose( *ptr )) bSetDOSExtendedError = TRUE;
3261                 GlobalUnlock16( h16 );
3262                 GlobalFree16( h16 );
3263             }
3264             else
3265             {
3266                 SetLastError( ERROR_INVALID_HANDLE );
3267                 bSetDOSExtendedError = TRUE;
3268             }
3269         }
3270         break;
3271           
3272     case 0xa6: /* LONG FILENAME - GET FILE INFO BY HANDLE */
3273         {
3274             HANDLE handle = DosFileHandleToWin32Handle(BX_reg(context));
3275             BY_HANDLE_FILE_INFORMATION *info =
3276                 CTX_SEG_OFF_TO_LIN(context, context->SegDs, context->Edx);
3277             
3278             TRACE( "LONG FILENAME - GET FILE INFO BY HANDLE\n" );
3279             
3280             if (!GetFileInformationByHandle(handle, info))
3281                 bSetDOSExtendedError = TRUE;
3282         }
3283         break;
3284
3285     case 0xa7: /* LONG FILENAME - CONVERT TIME */
3286         switch (BL_reg(context))
3287         {
3288         case 0x00: /* FILE TIME TO DOS TIME */
3289             {
3290                 WORD      date, time;
3291                 FILETIME *filetime = CTX_SEG_OFF_TO_LIN(context,
3292                                                         context->SegDs,
3293                                                         context->Esi);
3294
3295                 TRACE( "LONG FILENAME - FILE TIME TO DOS TIME\n" );
3296
3297                 FileTimeToDosDateTime( filetime, &date, &time );
3298
3299                 SET_DX( context, date );
3300                 SET_CX( context, time );
3301
3302                 /*
3303                  * FIXME: BH has number of 10-millisecond units 
3304                  * past time in CX.
3305                  */
3306                 SET_BH( context, 0 );
3307             }
3308             break;
3309
3310         case 0x01: /* DOS TIME TO FILE TIME */
3311             {
3312                 FILETIME *filetime = CTX_SEG_OFF_TO_LIN(context,
3313                                                         context->SegEs,
3314                                                         context->Edi);
3315
3316                 TRACE( "LONG FILENAME - DOS TIME TO FILE TIME\n" );
3317
3318                 /*
3319                  * FIXME: BH has number of 10-millisecond units 
3320                  * past time in CX.
3321                  */
3322                 DosDateTimeToFileTime( DX_reg(context), CX_reg(context),
3323                                        filetime );
3324             }
3325             break;
3326
3327         default:
3328             INT_BARF( context, 0x21 );
3329             break;
3330         }
3331         break;
3332
3333     case 0xa8: /* LONG FILENAME - GENERATE SHORT FILENAME */
3334     case 0xa9: /* LONG FILENAME - SERVER CREATE OR OPEN FILE */
3335     case 0xaa: /* LONG FILENAME - SUBST */
3336     default:
3337         FIXME("Unimplemented long file name function:\n");
3338         INT_BARF( context, 0x21 );
3339         SET_CFLAG(context);
3340         SET_AL( context, 0 );
3341         break;
3342     }
3343
3344     if (bSetDOSExtendedError)
3345     {
3346         SET_AX( context, GetLastError() );
3347         SET_CFLAG( context );
3348     }
3349 }
3350
3351
3352 /***********************************************************************
3353  *           INT21_RenameFile
3354  *
3355  * Handler for:
3356  * - function 0x56
3357  * - subfunction 0x56 of function 0x71
3358  * - subfunction 0xff of function 0x43 (CL == 0x56)
3359  */
3360 static BOOL INT21_RenameFile( CONTEXT86 *context )
3361 {
3362     WCHAR fromW[MAX_PATH];
3363     WCHAR toW[MAX_PATH];
3364     char *fromA = CTX_SEG_OFF_TO_LIN(context, 
3365                                      context->SegDs,context->Edx);
3366     char *toA = CTX_SEG_OFF_TO_LIN(context, 
3367                                    context->SegEs,context->Edi);
3368
3369     TRACE( "RENAME FILE %s to %s\n", fromA, toA );
3370     MultiByteToWideChar(CP_OEMCP, 0, fromA, -1, fromW, MAX_PATH);
3371     MultiByteToWideChar(CP_OEMCP, 0, toA, -1, toW, MAX_PATH);
3372
3373     return MoveFileW( fromW, toW );
3374 }
3375
3376
3377 /***********************************************************************
3378  *           INT21_NetworkFunc
3379  *
3380  * Handler for:
3381  * - function 0x5e
3382  */
3383 static BOOL INT21_NetworkFunc (CONTEXT86 *context)
3384 {
3385     switch (AL_reg(context)) 
3386     {
3387     case 0x00: /* Get machine name. */
3388         {
3389             WCHAR dstW[MAX_COMPUTERNAME_LENGTH + 1];
3390             DWORD s = sizeof(dstW) / sizeof(WCHAR);
3391             int len;
3392
3393             char *dst = CTX_SEG_OFF_TO_LIN (context,context->SegDs,context->Edx);
3394             TRACE("getting machine name to %p\n", dst);
3395             if (!GetComputerNameW(dstW, &s) ||
3396                 !WideCharToMultiByte(CP_OEMCP, 0, dstW, -1, dst, 16, NULL, NULL))
3397             {
3398                 WARN("failed!\n");
3399                 SetLastError( ER_NoNetwork );
3400                 return TRUE;
3401             }
3402             for (len = strlen(dst); len < 15; len++) dst[len] = ' ';
3403             dst[15] = 0;
3404             SET_CH( context, 1 ); /* Valid */
3405             SET_CL( context, 1 ); /* NETbios number??? */
3406             TRACE("returning %s\n", debugstr_an(dst, 16));
3407             return FALSE;
3408         }
3409
3410     default:
3411         SetLastError( ER_NoNetwork );
3412         return TRUE;
3413     }
3414 }
3415
3416 /******************************************************************
3417  *              INT21_GetDiskSerialNumber
3418  *
3419  */
3420 static int INT21_GetDiskSerialNumber( CONTEXT86 *context )
3421 {
3422     BYTE *dataptr = CTX_SEG_OFF_TO_LIN(context, context->SegDs, context->Edx);
3423     WCHAR path[] = {'A',':',0}, label[11];
3424     DWORD serial;
3425
3426     path[0] += INT21_MapDrive(BL_reg(context));
3427     if (!GetVolumeInformationW( path, label, 11, &serial, NULL, NULL, NULL, 0))
3428     {
3429         SetLastError( ERROR_INVALID_DRIVE );
3430         return 0;
3431     }
3432
3433     *(WORD *)dataptr = 0;
3434     memcpy(dataptr + 2, &serial, sizeof(DWORD));
3435     WideCharToMultiByte(CP_OEMCP, 0, label, 11, (LPSTR)dataptr + 6, 11, NULL, NULL);
3436     memcpy(dataptr + 17, "FAT16   ", 8);
3437     return 1;
3438 }
3439
3440
3441 /******************************************************************
3442  *              INT21_SetDiskSerialNumber
3443  *
3444  */
3445 static int INT21_SetDiskSerialNumber( CONTEXT86 *context )
3446 {
3447 #if 0
3448     BYTE *dataptr = CTX_SEG_OFF_TO_LIN(context, context->SegDs, context->Edx);
3449     int drive = INT21_MapDrive(BL_reg(context));
3450
3451     if (!is_valid_drive(drive))
3452     {
3453         SetLastError( ERROR_INVALID_DRIVE );
3454         return 0;
3455     }
3456
3457     DRIVE_SetSerialNumber( drive, *(DWORD *)(dataptr + 2) );
3458     return 1;
3459 #else
3460     FIXME("Setting drive serial number is no longer supported\n");
3461     SetLastError( ERROR_NOT_SUPPORTED );
3462     return 0;
3463 #endif
3464 }
3465
3466
3467 /******************************************************************
3468  *              INT21_GetFreeDiskSpace
3469  *
3470  */
3471 static int INT21_GetFreeDiskSpace( CONTEXT86 *context )
3472 {
3473     DWORD cluster_sectors, sector_bytes, free_clusters, total_clusters;
3474     WCHAR root[] = {'A',':','\\',0};
3475
3476     root[0] += INT21_MapDrive(DL_reg(context));
3477     if (!GetDiskFreeSpaceW( root, &cluster_sectors, &sector_bytes,
3478                             &free_clusters, &total_clusters )) return 0;
3479     /* make sure that the number of clusters fits in a 16 bits value */
3480     while( total_clusters & 0xffff0000) {
3481         cluster_sectors <<= 1;
3482         free_clusters >>= 1;
3483         total_clusters >>= 1;
3484     }
3485     SET_AX( context, cluster_sectors );
3486     SET_BX( context, free_clusters );
3487     SET_CX( context, sector_bytes );
3488     SET_DX( context, total_clusters );
3489     return 1;
3490 }
3491
3492 /******************************************************************
3493  *              INT21_GetDriveAllocInfo
3494  *
3495  */
3496 static int INT21_GetDriveAllocInfo( CONTEXT86 *context, BYTE drive )
3497 {
3498     INT21_DPB  *dpb;
3499
3500     drive = INT21_MapDrive( drive );
3501     if (!INT21_FillDrivePB( drive )) return 0;
3502     dpb = &(INT21_GetHeapPointer()->misc_dpb_list[drive]);
3503     SET_AL( context, dpb->cluster_sectors + 1 );
3504     SET_CX( context, dpb->sector_bytes );
3505     SET_DX( context, dpb->num_clusters1 );
3506
3507     context->SegDs = INT21_GetHeapSelector( context );
3508     SET_BX( context, offsetof( INT21_HEAP, misc_dpb_list[drive].media_ID ) );
3509     return 1;
3510 }
3511
3512 /***********************************************************************
3513  *           INT21_GetExtendedError
3514  */
3515 static void INT21_GetExtendedError( CONTEXT86 *context )
3516 {
3517     BYTE class, action, locus;
3518     WORD error = GetLastError();
3519
3520     switch(error)
3521     {
3522     case ERROR_SUCCESS:
3523         class = action = locus = 0;
3524         break;
3525     case ERROR_DIR_NOT_EMPTY:
3526         class  = EC_Exists;
3527         action = SA_Ignore;
3528         locus  = EL_Disk;
3529         break;
3530     case ERROR_ACCESS_DENIED:
3531         class  = EC_AccessDenied;
3532         action = SA_Abort;
3533         locus  = EL_Disk;
3534         break;
3535     case ERROR_CANNOT_MAKE:
3536         class  = EC_AccessDenied;
3537         action = SA_Abort;
3538         locus  = EL_Unknown;
3539         break;
3540     case ERROR_DISK_FULL:
3541     case ERROR_HANDLE_DISK_FULL:
3542         class  = EC_MediaError;
3543         action = SA_Abort;
3544         locus  = EL_Disk;
3545         break;
3546     case ERROR_FILE_EXISTS:
3547     case ERROR_ALREADY_EXISTS:
3548         class  = EC_Exists;
3549         action = SA_Abort;
3550         locus  = EL_Disk;
3551         break;
3552     case ERROR_FILE_NOT_FOUND:
3553         class  = EC_NotFound;
3554         action = SA_Abort;
3555         locus  = EL_Disk;
3556         break;
3557     case ERROR_GEN_FAILURE:
3558         class  = EC_SystemFailure;
3559         action = SA_Abort;
3560         locus  = EL_Unknown;
3561         break;
3562     case ERROR_INVALID_DRIVE:
3563         class  = EC_MediaError;
3564         action = SA_Abort;
3565         locus  = EL_Disk;
3566         break;
3567     case ERROR_INVALID_HANDLE:
3568         class  = EC_ProgramError;
3569         action = SA_Abort;
3570         locus  = EL_Disk;
3571         break;
3572     case ERROR_LOCK_VIOLATION:
3573         class  = EC_AccessDenied;
3574         action = SA_Abort;
3575         locus  = EL_Disk;
3576         break;
3577     case ERROR_NO_MORE_FILES:
3578         class  = EC_MediaError;
3579         action = SA_Abort;
3580         locus  = EL_Disk;
3581         break;
3582     case ER_NoNetwork:
3583         class  = EC_NotFound;
3584         action = SA_Abort;
3585         locus  = EL_Network;
3586         break;
3587     case ERROR_NOT_ENOUGH_MEMORY:
3588         class  = EC_OutOfResource;
3589         action = SA_Abort;
3590         locus  = EL_Memory;
3591         break;
3592     case ERROR_PATH_NOT_FOUND:
3593         class  = EC_NotFound;
3594         action = SA_Abort;
3595         locus  = EL_Disk;
3596         break;
3597     case ERROR_SEEK:
3598         class  = EC_NotFound;
3599         action = SA_Ignore;
3600         locus  = EL_Disk;
3601         break;
3602     case ERROR_SHARING_VIOLATION:
3603         class  = EC_Temporary;
3604         action = SA_Retry;
3605         locus  = EL_Disk;
3606         break;
3607     case ERROR_TOO_MANY_OPEN_FILES:
3608         class  = EC_ProgramError;
3609         action = SA_Abort;
3610         locus  = EL_Disk;
3611         break;
3612     default:
3613         FIXME("Unknown error %d\n", error );
3614         class  = EC_SystemFailure;
3615         action = SA_Abort;
3616         locus  = EL_Unknown;
3617         break;
3618     }
3619     TRACE("GET EXTENDED ERROR code 0x%02x class 0x%02x action 0x%02x locus %02x\n",
3620            error, class, action, locus );
3621     SET_AX( context, error );
3622     SET_BH( context, class );
3623     SET_BL( context, action );
3624     SET_CH( context, locus );
3625 }
3626
3627 static BOOL INT21_CreateTempFile( CONTEXT86 *context )
3628 {
3629     static int counter = 0;
3630     char *name = CTX_SEG_OFF_TO_LIN(context,  context->SegDs, context->Edx );
3631     char *p = name + strlen(name);
3632
3633     /* despite what Ralf Brown says, some programs seem to call without
3634      * ending backslash (DOS accepts that, so we accept it too) */
3635     if ((p == name) || (p[-1] != '\\')) *p++ = '\\';
3636
3637     for (;;)
3638     {
3639         sprintf( p, "wine%04x.%03d", (int)getpid(), counter );
3640         counter = (counter + 1) % 1000;
3641
3642         SET_AX( context, 
3643                 Win32HandleToDosFileHandle( 
3644                     CreateFileA( name, GENERIC_READ | GENERIC_WRITE,
3645                                  FILE_SHARE_READ | FILE_SHARE_WRITE, NULL,
3646                                  CREATE_NEW, 0, 0 ) ) );
3647         if (AX_reg(context) != HFILE_ERROR16)
3648         {
3649             TRACE("created %s\n", name );
3650             return TRUE;
3651         }
3652         if (GetLastError() != ERROR_FILE_EXISTS) return FALSE;
3653     }
3654 }
3655
3656 /***********************************************************************
3657  *           DOSFS_ToDosFCBFormat
3658  *
3659  * Convert a file name to DOS FCB format (8+3 chars, padded with blanks),
3660  * expanding wild cards and converting to upper-case in the process.
3661  * File name can be terminated by '\0', '\\' or '/'.
3662  * Return FALSE if the name is not a valid DOS name.
3663  * 'buffer' must be at least 12 characters long.
3664  */
3665 /* Chars we don't want to see in DOS file names */
3666 static BOOL INT21_ToDosFCBFormat( LPCWSTR name, LPWSTR buffer )
3667 {
3668     static const WCHAR invalid_chars[] = {'*','?','<','>','|','\\','"','+','=',',',';','[',']',' ','\345',0};
3669     LPCWSTR p = name;
3670     int i;
3671
3672     /* Check for "." and ".." */
3673     if (*p == '.')
3674     {
3675         p++;
3676         buffer[0] = '.';
3677         for(i = 1; i < 11; i++) buffer[i] = ' ';
3678         buffer[11] = 0;
3679         if (*p == '.')
3680         {
3681             buffer[1] = '.';
3682             p++;
3683         }
3684         return (!*p || (*p == '/') || (*p == '\\'));
3685     }
3686
3687     for (i = 0; i < 8; i++)
3688     {
3689         switch(*p)
3690         {
3691         case '\0':
3692         case '\\':
3693         case '/':
3694         case '.':
3695             buffer[i] = ' ';
3696             break;
3697         case '?':
3698             p++;
3699             /* fall through */
3700         case '*':
3701             buffer[i] = '?';
3702             break;
3703         default:
3704             if (strchrW( invalid_chars, *p )) return FALSE;
3705             buffer[i] = toupperW(*p);
3706             p++;
3707             break;
3708         }
3709     }
3710
3711     if (*p == '*')
3712     {
3713         /* Skip all chars after wildcard up to first dot */
3714         while (*p && (*p != '/') && (*p != '\\') && (*p != '.')) p++;
3715     }
3716     else
3717     {
3718         /* Check if name too long */
3719         if (*p && (*p != '/') && (*p != '\\') && (*p != '.')) return FALSE;
3720     }
3721     if (*p == '.') p++;  /* Skip dot */
3722
3723     for (i = 8; i < 11; i++)
3724     {
3725         switch(*p)
3726         {
3727         case '\0':
3728         case '\\':
3729         case '/':
3730             buffer[i] = ' ';
3731             break;
3732         case '.':
3733             return FALSE;  /* Second extension not allowed */
3734         case '?':
3735             p++;
3736             /* fall through */
3737         case '*':
3738             buffer[i] = '?';
3739             break;
3740         default:
3741             if (strchrW( invalid_chars, *p )) return FALSE;
3742             buffer[i] = toupperW(*p);
3743             p++;
3744             break;
3745         }
3746     }
3747     buffer[11] = '\0';
3748
3749     /* at most 3 character of the extension are processed
3750      * is something behind this ?
3751      */
3752     while (*p == '*' || *p == ' ') p++; /* skip wildcards and spaces */
3753     return (!*p || (*p == '/') || (*p == '\\'));
3754 }
3755
3756 static HANDLE       INT21_FindHandle;
3757 static const WCHAR *INT21_FindPath; /* will point to current dta->fullPath search */
3758
3759 /******************************************************************
3760  *              INT21_FindFirst
3761  */
3762 static int INT21_FindFirst( CONTEXT86 *context )
3763 {
3764     WCHAR *p, *q;
3765     const char *path;
3766     FINDFILE_DTA *dta = (FINDFILE_DTA *)INT21_GetCurrentDTA(context);
3767     WCHAR maskW[12], pathW[MAX_PATH];
3768     static const WCHAR wildcardW[] = {'*','.','*',0};
3769
3770     path = (const char *)CTX_SEG_OFF_TO_LIN(context, context->SegDs, context->Edx);
3771     MultiByteToWideChar(CP_OEMCP, 0, path, -1, pathW, MAX_PATH);
3772
3773     p = strrchrW( pathW, '\\');
3774     q = strrchrW( pathW, '/');
3775     if (q>p) p = q;
3776     if (!p)
3777     {
3778         if (pathW[0] && pathW[1] == ':') p = pathW + 2;
3779         else p = pathW;
3780     }
3781     else p++;
3782
3783     /* Note: terminating NULL in dta->mask overwrites dta->search_attr
3784      *       (doesn't matter as it is set below anyway)
3785      */
3786     if (!INT21_ToDosFCBFormat( p, maskW ))
3787     {
3788         SetLastError( ERROR_FILE_NOT_FOUND );
3789         SET_AX( context, ERROR_FILE_NOT_FOUND );
3790         SET_CFLAG(context);
3791         return 0;
3792     }
3793     WideCharToMultiByte(CP_OEMCP, 0, maskW, 12, dta->mask, sizeof(dta->mask), NULL, NULL);
3794
3795     dta->fullPath = HeapAlloc( GetProcessHeap(), 0, sizeof(wildcardW) + (p - pathW)*sizeof(WCHAR) );
3796     memcpy( dta->fullPath, pathW, (p - pathW) * sizeof(WCHAR) );
3797     memcpy( dta->fullPath + (p - pathW), wildcardW, sizeof(wildcardW) );
3798     /* we must have a fully qualified file name in dta->fullPath
3799      * (we could have a UNC path, but this would lead to some errors later on)
3800      */
3801     dta->drive = toupperW(dta->fullPath[0]) - 'A';
3802     dta->count = 0;
3803     dta->search_attr = CL_reg(context);
3804     return 1;
3805 }
3806
3807 /******************************************************************
3808  *              match_short
3809  *
3810  * Check is a short path name (DTA unicode) matches a mask (FCB ansi)
3811  */
3812 static BOOL match_short(LPCWSTR shortW, LPCSTR maskA)
3813 {
3814     WCHAR mask[11], file[12];
3815     int i;
3816
3817     if (!INT21_ToDosFCBFormat( shortW, file )) return FALSE;
3818     MultiByteToWideChar(CP_OEMCP, 0, maskA, 11, mask, 11);
3819     for (i = 0; i < 11; i++)
3820         if (mask[i] != '?' && mask[i] != file[i]) return FALSE;
3821     return TRUE;
3822 }
3823
3824 static unsigned INT21_FindHelper(LPCWSTR fullPath, unsigned drive, unsigned count, 
3825                                  LPCSTR mask, unsigned search_attr, 
3826                                  WIN32_FIND_DATAW* entry)
3827 {
3828     unsigned ncalls;
3829
3830     if ((search_attr & ~(FA_UNUSED | FA_ARCHIVE | FA_RDONLY)) == FA_LABEL)
3831     {
3832         WCHAR path[] = {' ',':',0};
3833
3834         if (count) return 0;
3835         path[0] = drive + 'A';
3836         if (!GetVolumeInformationW(path, entry->cAlternateFileName, 13, NULL, NULL, NULL, NULL, 0)) return 0;
3837         RtlSecondsSince1970ToTime( (time_t)0, (LARGE_INTEGER *)&entry->ftCreationTime );
3838         RtlSecondsSince1970ToTime( (time_t)0, (LARGE_INTEGER *)&entry->ftLastAccessTime );
3839         RtlSecondsSince1970ToTime( (time_t)0, (LARGE_INTEGER *)&entry->ftLastWriteTime );
3840         entry->dwFileAttributes = FA_LABEL;
3841         entry->nFileSizeHigh = entry->nFileSizeLow = 0;
3842         TRACE("returning %s as label\n", debugstr_w(entry->cAlternateFileName));
3843         return 1;
3844     }
3845
3846     if (!INT21_FindHandle || INT21_FindPath != fullPath || count == 0)
3847     {
3848         if (INT21_FindHandle) FindClose(INT21_FindHandle);
3849         INT21_FindHandle = FindFirstFileW(fullPath, entry);
3850         if (INT21_FindHandle == INVALID_HANDLE_VALUE)
3851         {
3852             INT21_FindHandle = 0;
3853             return 0;
3854         }
3855         INT21_FindPath = fullPath;
3856         /* we need to resync search */
3857         ncalls = count;
3858     }
3859     else ncalls = 1;
3860
3861     while (ncalls-- != 0)
3862     {
3863         if (!FindNextFileW(INT21_FindHandle, entry))
3864         {
3865             FindClose(INT21_FindHandle); INT21_FindHandle = 0;
3866             return 0;
3867         }
3868     }
3869     while (count < 0xffff)
3870     {
3871         count++;
3872         /* Check the file attributes, and path */
3873         if (!(entry->dwFileAttributes & ~search_attr) &&
3874             match_short(entry->cAlternateFileName[0] ? entry->cAlternateFileName : entry->cFileName,
3875                         mask))
3876         {
3877             return count;
3878         }
3879         if (!FindNextFileW(INT21_FindHandle, entry))
3880         {
3881             FindClose(INT21_FindHandle); INT21_FindHandle = 0;
3882             return 0;
3883         }
3884     }
3885     WARN("Too many directory entries in %s\n", debugstr_w(fullPath) );
3886     return 0;
3887 }
3888
3889 /******************************************************************
3890  *              INT21_FindNext
3891  */
3892 static int INT21_FindNext( CONTEXT86 *context )
3893 {
3894     FINDFILE_DTA *dta = (FINDFILE_DTA *)INT21_GetCurrentDTA(context);
3895     DWORD attr = dta->search_attr | FA_UNUSED | FA_ARCHIVE | FA_RDONLY;
3896     WIN32_FIND_DATAW entry;
3897     int n;
3898
3899     if (!dta->fullPath) return 0;
3900
3901     n = INT21_FindHelper(dta->fullPath, dta->drive, dta->count, 
3902                          dta->mask, attr, &entry);
3903     if (n)
3904     {
3905         dta->fileattr = entry.dwFileAttributes;
3906         dta->filesize = entry.nFileSizeLow;
3907         FileTimeToDosDateTime( &entry.ftLastWriteTime, &dta->filedate, &dta->filetime );
3908         if (entry.cAlternateFileName[0])
3909             WideCharToMultiByte(CP_OEMCP, 0, entry.cAlternateFileName, -1,
3910                                 dta->filename, 13, NULL, NULL);
3911         else
3912             WideCharToMultiByte(CP_OEMCP, 0, entry.cFileName, -1, dta->filename, 13, NULL, NULL);
3913
3914         if (!memchr(dta->mask,'?',11))
3915         {
3916             /* wildcardless search, release resources in case no findnext will
3917              * be issued, and as a workaround in case file creation messes up
3918              * findnext, as sometimes happens with pkunzip
3919              */
3920             HeapFree( GetProcessHeap(), 0, dta->fullPath );
3921             INT21_FindPath = dta->fullPath = NULL;
3922         }
3923         dta->count = n;
3924         return 1;
3925     }
3926     HeapFree( GetProcessHeap(), 0, dta->fullPath );
3927     INT21_FindPath = dta->fullPath = NULL;
3928     return 0;
3929 }
3930
3931 /* microsoft's programmers should be shot for using CP/M style int21
3932    calls in Windows for Workgroup's winfile.exe */
3933
3934 /******************************************************************
3935  *              INT21_FindFirstFCB
3936  *
3937  */
3938 static int INT21_FindFirstFCB( CONTEXT86 *context )
3939 {
3940     BYTE *fcb = (BYTE *)CTX_SEG_OFF_TO_LIN(context, context->SegDs, context->Edx);
3941     FINDFILE_FCB *pFCB;
3942     int drive;
3943     WCHAR p[] = {' ',':',};
3944
3945     if (*fcb == 0xff) pFCB = (FINDFILE_FCB *)(fcb + 7);
3946     else pFCB = (FINDFILE_FCB *)fcb;
3947     drive = INT21_MapDrive( pFCB->drive );
3948     if (drive == MAX_DOS_DRIVES) return 0;
3949
3950     p[0] = 'A' + drive;
3951     pFCB->fullPath = HeapAlloc(GetProcessHeap(), 0, MAX_PATH * sizeof(WCHAR));
3952     if (!pFCB->fullPath) return 0;
3953     GetLongPathNameW(p, pFCB->fullPath, MAX_PATH);
3954     pFCB->count = 0;
3955     return 1;
3956 }
3957
3958 /******************************************************************
3959  *              INT21_FindNextFCB
3960  *
3961  */
3962 static int INT21_FindNextFCB( CONTEXT86 *context )
3963 {
3964     BYTE *fcb = (BYTE *)CTX_SEG_OFF_TO_LIN(context, context->SegDs, context->Edx);
3965     FINDFILE_FCB *pFCB;
3966     LPBYTE pResult = INT21_GetCurrentDTA(context);
3967     DOS_DIRENTRY_LAYOUT *ddl;
3968     WIN32_FIND_DATAW entry;
3969     BYTE attr;
3970     int n;
3971     WCHAR nameW[12];
3972
3973     if (*fcb == 0xff) /* extended FCB ? */
3974     {
3975         attr = fcb[6];
3976         pFCB = (FINDFILE_FCB *)(fcb + 7);
3977     }
3978     else
3979     {
3980         attr = 0;
3981         pFCB = (FINDFILE_FCB *)fcb;
3982     }
3983
3984     if (!pFCB->fullPath) return 0;
3985     n = INT21_FindHelper(pFCB->fullPath, INT21_MapDrive( pFCB->drive ),
3986                          pFCB->count, pFCB->filename, attr, &entry);
3987     if (!n)
3988     {
3989         HeapFree( GetProcessHeap(), 0, pFCB->fullPath );
3990         INT21_FindPath = pFCB->fullPath = NULL;
3991         return 0;
3992     }
3993     pFCB->count += n;
3994
3995     if (*fcb == 0xff)
3996     {
3997         /* place extended FCB header before pResult if called with extended FCB */
3998         *pResult = 0xff;
3999          pResult += 6; /* leave reserved field behind */
4000         *pResult++ = entry.dwFileAttributes;
4001     }
4002     *pResult++ = INT21_MapDrive( pFCB->drive ); /* DOS_DIRENTRY_LAYOUT after current drive number */
4003     ddl = (DOS_DIRENTRY_LAYOUT*)pResult;
4004     ddl->fileattr = entry.dwFileAttributes;
4005     ddl->cluster  = 0;  /* what else? */
4006     ddl->filesize = entry.nFileSizeLow;
4007     memset( ddl->reserved, 0, sizeof(ddl->reserved) );
4008     FileTimeToDosDateTime( &entry.ftLastWriteTime,
4009                            &ddl->filedate, &ddl->filetime );
4010
4011     /* Convert file name to FCB format */
4012     if (entry.cAlternateFileName[0])
4013         INT21_ToDosFCBFormat( entry.cAlternateFileName, nameW );
4014     else
4015         INT21_ToDosFCBFormat( entry.cFileName, nameW );
4016     WideCharToMultiByte(CP_OEMCP, 0, nameW, 11, ddl->filename, 11, NULL, NULL);
4017     return 1;
4018 }
4019
4020
4021 /******************************************************************
4022  *              INT21_ParseFileNameIntoFCB
4023  *
4024  */
4025 static void INT21_ParseFileNameIntoFCB( CONTEXT86 *context )
4026 {
4027     char *filename =
4028         CTX_SEG_OFF_TO_LIN(context, context->SegDs, context->Esi );
4029     char *fcb =
4030         CTX_SEG_OFF_TO_LIN(context, context->SegEs, context->Edi );
4031     char *s;
4032     WCHAR *buffer;
4033     WCHAR fcbW[12];
4034     INT buffer_len, len;
4035
4036     SET_AL( context, 0xff ); /* failed */
4037
4038     TRACE("filename: '%s'\n", filename);
4039
4040     s = filename;
4041     while (*s && (*s != ' ') && (*s != '\r') && (*s != '\n'))
4042         s++;
4043     len = filename - s;
4044
4045     buffer_len = MultiByteToWideChar(CP_OEMCP, 0, filename, len, NULL, 0);
4046     buffer = HeapAlloc( GetProcessHeap(), 0, (buffer_len + 1) * sizeof(WCHAR));
4047     len = MultiByteToWideChar(CP_OEMCP, 0, filename, len, buffer, buffer_len);
4048     buffer[len] = 0;
4049     INT21_ToDosFCBFormat(buffer, fcbW);
4050     HeapFree(GetProcessHeap(), 0, buffer);
4051     WideCharToMultiByte(CP_OEMCP, 0, fcbW, 12, fcb + 1, 12, NULL, NULL);
4052     *fcb = 0;
4053     TRACE("FCB: '%s'\n", fcb + 1);
4054
4055     SET_AL( context, ((strchr(filename, '*')) || (strchr(filename, '$'))) != 0 );
4056
4057     /* point DS:SI to first unparsed character */
4058     SET_SI( context, context->Esi + (int)s - (int)filename );
4059 }
4060
4061 static BOOL     INT21_Dup2(HFILE16 hFile1, HFILE16 hFile2)
4062 {
4063     HFILE16     res = HFILE_ERROR16;
4064     HANDLE      handle, new_handle;
4065 #define DOS_TABLE_SIZE  256
4066     DWORD       map[DOS_TABLE_SIZE / 32];
4067     int         i;
4068
4069     handle = DosFileHandleToWin32Handle(hFile1);
4070     if (handle == INVALID_HANDLE_VALUE)
4071         return FALSE;
4072
4073     _lclose16(hFile2);
4074     /* now loop to allocate the same one... */
4075     memset(map, 0, sizeof(map));
4076     for (i = 0; i < DOS_TABLE_SIZE; i++)
4077     {
4078         if (!DuplicateHandle(GetCurrentProcess(), handle,
4079                              GetCurrentProcess(), &new_handle,
4080                              0, FALSE, DUPLICATE_SAME_ACCESS))
4081         {
4082             res = HFILE_ERROR16;
4083             break;
4084         }
4085         res = Win32HandleToDosFileHandle(new_handle);
4086         if (res == HFILE_ERROR16 || res == hFile2) break;
4087         map[res / 32] |= 1 << (res % 32);
4088     }
4089     /* clean up the allocated slots */
4090     for (i = 0; i < DOS_TABLE_SIZE; i++)
4091     {
4092         if (map[i / 32] & (1 << (i % 32)))
4093             _lclose16((HFILE16)i);
4094     }
4095     return res == hFile2;
4096 }
4097
4098
4099 /***********************************************************************
4100  *           DOSVM_Int21Handler
4101  *
4102  * Interrupt 0x21 handler.
4103  */
4104 void WINAPI DOSVM_Int21Handler( CONTEXT86 *context )
4105 {
4106     BOOL bSetDOSExtendedError = FALSE;
4107
4108     TRACE( "AX=%04x BX=%04x CX=%04x DX=%04x "
4109            "SI=%04x DI=%04x DS=%04x ES=%04x EFL=%08lx\n",
4110            AX_reg(context), BX_reg(context), 
4111            CX_reg(context), DX_reg(context),
4112            SI_reg(context), DI_reg(context),
4113            (WORD)context->SegDs, (WORD)context->SegEs,
4114            context->EFlags );
4115
4116    /*
4117     * Extended error is used by (at least) functions 0x2f to 0x62.
4118     * Function 0x59 returns extended error and error should not
4119     * be cleared before handling the function.
4120     */
4121     if (AH_reg(context) >= 0x2f && AH_reg(context) != 0x59) 
4122         SetLastError(0);
4123
4124     RESET_CFLAG(context); /* Not sure if this is a good idea. */
4125
4126     switch(AH_reg(context))
4127     {
4128     case 0x00: /* TERMINATE PROGRAM */
4129         TRACE("TERMINATE PROGRAM\n");
4130         if (DOSVM_IsWin16())
4131             ExitThread( 0 );
4132         else if(ISV86(context))
4133             MZ_Exit( context, FALSE, 0 );
4134         else
4135             ERR( "Called from DOS protected mode\n" );
4136         break;
4137
4138     case 0x01: /* READ CHARACTER FROM STANDARD INPUT, WITH ECHO */
4139         {
4140             BYTE ascii;
4141             TRACE("DIRECT CHARACTER INPUT WITH ECHO\n");
4142             INT21_ReadChar( &ascii, context );
4143             SET_AL( context, ascii );
4144             /*
4145              * FIXME: What to echo when extended keycodes are read?
4146              */
4147             DOSVM_PutChar(AL_reg(context));
4148         }
4149         break;
4150
4151     case 0x02: /* WRITE CHARACTER TO STANDARD OUTPUT */
4152         TRACE("Write Character to Standard Output\n");
4153         DOSVM_PutChar(DL_reg(context));
4154         break;
4155
4156     case 0x03: /* READ CHARACTER FROM STDAUX  */
4157     case 0x04: /* WRITE CHARACTER TO STDAUX */
4158     case 0x05: /* WRITE CHARACTER TO PRINTER */
4159         INT_BARF( context, 0x21 );
4160         break;
4161
4162     case 0x06: /* DIRECT CONSOLE IN/OUTPUT */
4163         if (DL_reg(context) == 0xff) 
4164         {
4165             TRACE("Direct Console Input\n");
4166
4167             if (INT21_ReadChar( NULL, NULL ))
4168             {
4169                 BYTE ascii;
4170                 INT21_ReadChar( &ascii, context );
4171                 SET_AL( context, ascii );
4172                 RESET_ZFLAG( context );
4173             }
4174             else
4175             {
4176                 /* no character available */
4177                 SET_AL( context, 0 );
4178                 SET_ZFLAG( context );
4179             }
4180         } 
4181         else 
4182         {
4183             TRACE("Direct Console Output\n");
4184             DOSVM_PutChar(DL_reg(context));
4185             /*
4186              * At least DOS versions 2.1-7.0 return character 
4187              * that was written in AL register.
4188              */
4189             SET_AL( context, DL_reg(context) );
4190         }
4191         break;
4192
4193     case 0x07: /* DIRECT CHARACTER INPUT WITHOUT ECHO */
4194         {
4195             BYTE ascii;
4196             TRACE("DIRECT CHARACTER INPUT WITHOUT ECHO\n");
4197             INT21_ReadChar( &ascii, context );
4198             SET_AL( context, ascii );
4199         }
4200         break;
4201
4202     case 0x08: /* CHARACTER INPUT WITHOUT ECHO */
4203         {
4204             BYTE ascii;
4205             TRACE("CHARACTER INPUT WITHOUT ECHO\n");
4206             INT21_ReadChar( &ascii, context );
4207             SET_AL( context, ascii );
4208         }
4209         break;
4210
4211     case 0x09: /* WRITE STRING TO STANDARD OUTPUT */
4212         TRACE("WRITE '$'-terminated string from %04lX:%04X to stdout\n",
4213               context->SegDs, DX_reg(context) );
4214         {
4215             LPSTR data = CTX_SEG_OFF_TO_LIN( context, 
4216                                              context->SegDs, context->Edx );
4217             LPSTR p = data;
4218             DWORD w;
4219             /*
4220              * Do NOT use strchr() to calculate the string length,
4221              * as '\0' is valid string content, too!
4222              * Maybe we should check for non-'$' strings, but DOS doesn't.
4223              */
4224             while (*p != '$') p++;
4225
4226             if (DOSVM_IsWin16())
4227                 WriteFile( DosFileHandleToWin32Handle(1), 
4228                            data, p - data, &w, NULL );
4229             else
4230                 for(; data != p; data++)
4231                     DOSVM_PutChar( *data );
4232
4233             SET_AL( context, '$' ); /* yes, '$' (0x24) gets returned in AL */
4234         }
4235         break;
4236
4237     case 0x0a: /* BUFFERED INPUT */
4238         {
4239             BYTE *ptr = CTX_SEG_OFF_TO_LIN(context,
4240                                            context->SegDs,
4241                                            context->Edx);
4242             WORD result;
4243
4244             TRACE( "BUFFERED INPUT (size=%d)\n", ptr[0] );
4245
4246             /*
4247              * FIXME: Some documents state that
4248              *        ptr[1] holds number of chars from last input which 
4249              *        may be recalled on entry, other documents do not mention
4250              *        this at all.
4251              */
4252             if (ptr[1])
4253                 TRACE( "Handle old chars in buffer!\n" );
4254
4255             /*
4256              * ptr[0] - capacity (includes terminating CR)
4257              * ptr[1] - characters read (excludes terminating CR)
4258              */
4259             result = INT21_BufferedInput( context, ptr + 2, ptr[0] );
4260             if (result > 0)
4261                 ptr[1] = (BYTE)result - 1;
4262             else
4263                 ptr[1] = 0;
4264         }
4265         break;
4266
4267     case 0x0b: /* GET STDIN STATUS */
4268         TRACE( "GET STDIN STATUS\n" );
4269         {
4270             if (INT21_ReadChar( NULL, NULL ))
4271                 SET_AL( context, 0xff ); /* character available */
4272             else
4273                 SET_AL( context, 0 ); /* no character available */
4274         }
4275         break;
4276
4277     case 0x0c: /* FLUSH BUFFER AND READ STANDARD INPUT */
4278         {
4279             BYTE al = AL_reg(context); /* Input function to execute after flush. */
4280
4281             TRACE( "FLUSH BUFFER AND READ STANDARD INPUT - 0x%02x\n", al );
4282
4283             /* FIXME: buffers are not flushed */
4284
4285             /*
4286              * If AL is one of 0x01, 0x06, 0x07, 0x08, or 0x0a,
4287              * int21 function identified by AL will be called.
4288              */
4289             if(al == 0x01 || al == 0x06 || al == 0x07 || al == 0x08 || al == 0x0a)
4290             {
4291                 SET_AH( context, al );
4292                 DOSVM_Int21Handler( context );
4293             }
4294         }
4295         break;
4296
4297     case 0x0d: /* DISK BUFFER FLUSH */
4298         TRACE("DISK BUFFER FLUSH ignored\n");
4299         break;
4300
4301     case 0x0e: /* SELECT DEFAULT DRIVE */
4302         TRACE( "SELECT DEFAULT DRIVE - %c:\n", 'A' + DL_reg(context) );
4303         INT21_SetCurrentDrive( DL_reg(context) );
4304         SET_AL( context, MAX_DOS_DRIVES );
4305         break;
4306
4307     case 0x0f: /* OPEN FILE USING FCB */
4308         INT21_OpenFileUsingFCB( context );
4309         break;
4310
4311     case 0x10: /* CLOSE FILE USING FCB */
4312         INT21_CloseFileUsingFCB( context );
4313         break;
4314
4315     case 0x11: /* FIND FIRST MATCHING FILE USING FCB */
4316         TRACE("FIND FIRST MATCHING FILE USING FCB %p\n",
4317               CTX_SEG_OFF_TO_LIN(context, context->SegDs, context->Edx));
4318         if (!INT21_FindFirstFCB(context))
4319         {
4320             SET_AL( context, 0xff );
4321             break;
4322         }
4323         /* else fall through */
4324
4325     case 0x12: /* FIND NEXT MATCHING FILE USING FCB */
4326         SET_AL( context, INT21_FindNextFCB(context) ? 0x00 : 0xff );
4327         break;
4328
4329      case 0x13: /* DELETE FILE USING FCB */
4330         INT_BARF( context, 0x21 );
4331         break;
4332
4333     case 0x14: /* SEQUENTIAL READ FROM FCB FILE */
4334         INT21_SequentialReadFromFCB( context );
4335         break;
4336
4337     case 0x15: /* SEQUENTIAL WRITE TO FCB FILE */
4338         INT21_SequentialWriteToFCB( context );
4339         break;
4340
4341     case 0x16: /* CREATE OR TRUNCATE FILE USING FCB */
4342     case 0x17: /* RENAME FILE USING FCB */
4343         INT_BARF( context, 0x21 );
4344         break;
4345
4346     case 0x18: /* NULL FUNCTION FOR CP/M COMPATIBILITY */
4347         SET_AL( context, 0 );
4348         break;
4349
4350     case 0x19: /* GET CURRENT DEFAULT DRIVE */
4351         SET_AL( context, INT21_GetCurrentDrive() );
4352         TRACE( "GET CURRENT DRIVE -> %c:\n", 'A' + AL_reg( context ) );
4353         break;
4354
4355     case 0x1a: /* SET DISK TRANSFER AREA ADDRESS */
4356         TRACE( "SET DISK TRANSFER AREA ADDRESS %04lX:%04X\n",
4357                context->SegDs, DX_reg(context) );
4358         {
4359             TDB *task = GlobalLock16( GetCurrentTask() );
4360             task->dta = MAKESEGPTR( context->SegDs, DX_reg(context) );
4361         }
4362         break;
4363
4364     case 0x1b: /* GET ALLOCATION INFORMATION FOR DEFAULT DRIVE */
4365         if (!INT21_GetDriveAllocInfo(context, 0))
4366             SET_AX( context, 0xffff );
4367         break;
4368
4369     case 0x1c: /* GET ALLOCATION INFORMATION FOR SPECIFIC DRIVE */
4370         if (!INT21_GetDriveAllocInfo(context, DL_reg(context)))
4371             SET_AX( context, 0xffff );
4372         break;
4373
4374     case 0x1d: /* NULL FUNCTION FOR CP/M COMPATIBILITY */
4375     case 0x1e: /* NULL FUNCTION FOR CP/M COMPATIBILITY */
4376         SET_AL( context, 0 );
4377         break;
4378
4379     case 0x1f: /* GET DRIVE PARAMETER BLOCK FOR DEFAULT DRIVE */
4380         {
4381             BYTE drive = INT21_MapDrive( 0 ); 
4382             TRACE( "GET DPB FOR DEFAULT DRIVE\n" );
4383
4384             if (INT21_FillDrivePB( drive ))
4385             {
4386                 SET_AL( context, 0x00 ); /* success */
4387                 SET_BX( context, offsetof( INT21_HEAP, misc_dpb_list[drive] ) );
4388                 context->SegDs = INT21_GetHeapSelector( context );
4389             }
4390             else
4391             {
4392                 SET_AL( context, 0xff ); /* invalid or network drive */
4393             }
4394         }
4395         break;
4396
4397     case 0x20: /* NULL FUNCTION FOR CP/M COMPATIBILITY */
4398         SET_AL( context, 0 );
4399         break;
4400
4401     case 0x21: /* READ RANDOM RECORD FROM FCB FILE */
4402         INT21_ReadRandomRecordFromFCB( context );
4403         break;
4404
4405     case 0x22: /* WRITE RANDOM RECORD TO FCB FILE */
4406         INT21_WriteRandomRecordToFCB( context );
4407         break;
4408
4409     case 0x23: /* GET FILE SIZE FOR FCB */
4410     case 0x24: /* SET RANDOM RECORD NUMBER FOR FCB */
4411         INT_BARF( context, 0x21 );
4412         break;
4413
4414     case 0x25: /* SET INTERRUPT VECTOR */
4415         TRACE("SET INTERRUPT VECTOR 0x%02x\n",AL_reg(context));
4416         {
4417             FARPROC16 ptr = (FARPROC16)MAKESEGPTR( context->SegDs, DX_reg(context) );
4418             if (!ISV86(context) && DOSVM_IsWin16())
4419                 DOSVM_SetPMHandler16(  AL_reg(context), ptr );
4420             else
4421                 DOSVM_SetRMHandler( AL_reg(context), ptr );
4422         }
4423         break;
4424
4425     case 0x26: /* CREATE NEW PROGRAM SEGMENT PREFIX */
4426         INT_BARF( context, 0x21 );
4427         break;
4428
4429     case 0x27: /* RANDOM BLOCK READ FROM FCB FILE */
4430         INT21_RandomBlockReadFromFCB( context );
4431         break;
4432
4433     case 0x28: /* RANDOM BLOCK WRITE TO FCB FILE */
4434         INT21_RandomBlockWriteToFCB( context );
4435         break;
4436
4437     case 0x29: /* PARSE FILENAME INTO FCB */
4438         INT21_ParseFileNameIntoFCB(context);
4439         break;
4440
4441     case 0x2a: /* GET SYSTEM DATE */
4442         TRACE( "GET SYSTEM DATE\n" );
4443         {
4444             SYSTEMTIME systime;
4445             GetLocalTime( &systime );
4446             SET_CX( context, systime.wYear );
4447             SET_DH( context, systime.wMonth );
4448             SET_DL( context, systime.wDay );
4449             SET_AL( context, systime.wDayOfWeek );
4450         }
4451         break;
4452
4453     case 0x2b: /* SET SYSTEM DATE */
4454         TRACE( "SET SYSTEM DATE\n" );
4455         {
4456             WORD year  = CX_reg(context);
4457             BYTE month = DH_reg(context);
4458             BYTE day   = DL_reg(context);
4459
4460             if (year  >= 1980 && year  <= 2099 &&
4461                 month >= 1    && month <= 12   &&
4462                 day   >= 1    && day   <= 31)
4463             {
4464                 FIXME( "SetSystemDate(%02d/%02d/%04d): not allowed\n",
4465                        day, month, year );
4466                 SET_AL( context, 0 );  /* Let's pretend we succeeded */
4467             }
4468             else
4469             {
4470                 SET_AL( context, 0xff ); /* invalid date */
4471                 TRACE( "SetSystemDate(%02d/%02d/%04d): invalid date\n",
4472                        day, month, year );
4473             }
4474         }
4475         break;
4476
4477     case 0x2c: /* GET SYSTEM TIME */
4478         TRACE( "GET SYSTEM TIME\n" );
4479         {
4480             SYSTEMTIME systime;
4481             GetLocalTime( &systime );
4482             SET_CH( context, systime.wHour );
4483             SET_CL( context, systime.wMinute );
4484             SET_DH( context, systime.wSecond );
4485             SET_DL( context, systime.wMilliseconds / 10 );
4486         }
4487         break;
4488
4489     case 0x2d: /* SET SYSTEM TIME */
4490         if( CH_reg(context) >= 24 || CL_reg(context) >= 60 || DH_reg(context) >= 60 || DL_reg(context) >= 100 ) {
4491             TRACE("SetSystemTime(%02d:%02d:%02d.%02d): wrong time\n",
4492               CH_reg(context), CL_reg(context),
4493               DH_reg(context), DL_reg(context) );
4494             SET_AL( context, 0xFF );
4495         }
4496         else
4497         {
4498             FIXME("SetSystemTime(%02d:%02d:%02d.%02d): not allowed\n",
4499                   CH_reg(context), CL_reg(context),
4500                   DH_reg(context), DL_reg(context) );
4501             SET_AL( context, 0 );  /* Let's pretend we succeeded */
4502         }
4503         break;
4504
4505     case 0x2e: /* SET VERIFY FLAG */
4506         TRACE("SET VERIFY FLAG ignored\n");
4507         /* we cannot change the behaviour anyway, so just ignore it */
4508         break;
4509
4510     case 0x2f: /* GET DISK TRANSFER AREA ADDRESS */
4511         TRACE( "GET DISK TRANSFER AREA ADDRESS\n" );
4512         {
4513             TDB *task = GlobalLock16( GetCurrentTask() );
4514             context->SegEs = SELECTOROF( task->dta );
4515             SET_BX( context, OFFSETOF( task->dta ) );
4516         }
4517         break;
4518
4519     case 0x30: /* GET DOS VERSION */
4520         TRACE( "GET DOS VERSION - %s requested\n",
4521                (AL_reg(context) == 0x00) ? "OEM number" : "version flag" );
4522
4523         if (AL_reg(context) == 0x00)
4524             SET_BH( context, 0xff ); /* OEM number => undefined */
4525         else
4526             SET_BH( context, 0x08 ); /* version flag => DOS is in ROM */
4527
4528         SET_AL( context, HIBYTE(HIWORD(GetVersion16())) ); /* major version */
4529         SET_AH( context, LOBYTE(HIWORD(GetVersion16())) ); /* minor version */
4530
4531         SET_BL( context, 0x12 );     /* 0x123456 is 24-bit Wine's serial # */
4532         SET_CX( context, 0x3456 );
4533         break;
4534
4535     case 0x31: /* TERMINATE AND STAY RESIDENT */
4536         FIXME("TERMINATE AND STAY RESIDENT stub\n");
4537         break;
4538
4539     case 0x32: /* GET DOS DRIVE PARAMETER BLOCK FOR SPECIFIC DRIVE */
4540         {
4541             BYTE drive = INT21_MapDrive( DL_reg(context) );           
4542             TRACE( "GET DPB FOR SPECIFIC DRIVE %d\n", DL_reg(context) );
4543
4544             if (INT21_FillDrivePB( drive ))
4545             {
4546                 SET_AL( context, 0x00 ); /* success */
4547                 SET_DX( context, offsetof( INT21_HEAP, misc_dpb_list[drive] ) );
4548                 context->SegDs = INT21_GetHeapSelector( context );
4549             }
4550             else
4551             {
4552                 SET_AL( context, 0xff ); /* invalid or network drive */
4553             }
4554         }
4555         break;
4556
4557     case 0x33: /* MULTIPLEXED */
4558         switch (AL_reg(context))
4559         {
4560         case 0x00: /* GET CURRENT EXTENDED BREAK STATE */
4561             TRACE("GET CURRENT EXTENDED BREAK STATE\n");
4562             SET_DL( context, DOSCONF_GetConfig()->brk_flag );
4563             break;
4564
4565         case 0x01: /* SET EXTENDED BREAK STATE */
4566             TRACE("SET CURRENT EXTENDED BREAK STATE\n");
4567             DOSCONF_GetConfig()->brk_flag = (DL_reg(context) > 0) ? 1 : 0;
4568             break;
4569
4570         case 0x02: /* GET AND SET EXTENDED CONTROL-BREAK CHECKING STATE*/
4571             TRACE("GET AND SET EXTENDED CONTROL-BREAK CHECKING STATE\n");
4572             /* ugly coding in order to stay reentrant */
4573             if (DL_reg(context))
4574             {
4575                 SET_DL( context, DOSCONF_GetConfig()->brk_flag );
4576                 DOSCONF_GetConfig()->brk_flag = 1;
4577             }
4578             else
4579             {
4580                 SET_DL( context, DOSCONF_GetConfig()->brk_flag );
4581                 DOSCONF_GetConfig()->brk_flag = 0;
4582             }
4583             break;
4584
4585         case 0x05: /* GET BOOT DRIVE */
4586             TRACE("GET BOOT DRIVE\n");
4587             SET_DL( context, 3 );
4588             /* c: is Wine's bootdrive (a: is 1)*/
4589             break;
4590
4591         case 0x06: /* GET TRUE VERSION NUMBER */
4592             TRACE("GET TRUE VERSION NUMBER\n");
4593             SET_BL( context, HIBYTE(HIWORD(GetVersion16())) ); /* major */
4594             SET_BH( context, LOBYTE(HIWORD(GetVersion16())) ); /* minor */
4595             SET_DL( context, 0x00 ); /* revision */
4596             SET_DH( context, 0x08 ); /* DOS is in ROM */
4597             break;
4598
4599         default:
4600             INT_BARF( context, 0x21 );
4601             break;
4602         }
4603         break;
4604
4605     case 0x34: /* GET ADDRESS OF INDOS FLAG */
4606         TRACE( "GET ADDRESS OF INDOS FLAG\n" );
4607         context->SegEs = INT21_GetHeapSelector( context );
4608         SET_BX( context, offsetof(INT21_HEAP, misc_indos) );
4609         break;
4610
4611     case 0x35: /* GET INTERRUPT VECTOR */
4612         TRACE("GET INTERRUPT VECTOR 0x%02x\n",AL_reg(context));
4613         {
4614             FARPROC16 addr;
4615             if (!ISV86(context) && DOSVM_IsWin16())
4616                 addr = DOSVM_GetPMHandler16( AL_reg(context) );
4617             else
4618                 addr = DOSVM_GetRMHandler( AL_reg(context) );
4619             context->SegEs = SELECTOROF(addr);
4620             SET_BX( context, OFFSETOF(addr) );
4621         }
4622         break;
4623
4624     case 0x36: /* GET FREE DISK SPACE */
4625         TRACE("GET FREE DISK SPACE FOR DRIVE %s\n",
4626               INT21_DriveName( DL_reg(context) ));
4627         if (!INT21_GetFreeDiskSpace(context)) SET_AX( context, 0xffff );
4628         break;
4629
4630     case 0x37: /* SWITCHAR */
4631         {
4632             switch (AL_reg(context))
4633             {
4634             case 0x00: /* "SWITCHAR" - GET SWITCH CHARACTER */
4635                 TRACE( "SWITCHAR - GET SWITCH CHARACTER\n" );
4636                 SET_AL( context, 0x00 ); /* success*/
4637                 SET_DL( context, '/' );
4638                 break;
4639             case 0x01: /*"SWITCHAR" - SET SWITCH CHARACTER*/
4640                 FIXME( "SWITCHAR - SET SWITCH CHARACTER: %c\n",
4641                        DL_reg( context ));
4642                 SET_AL( context, 0x00 ); /* success*/
4643                 break;
4644             default:
4645                 INT_BARF( context, 0x21 );
4646                 break;
4647             }
4648         }
4649         break;
4650
4651     case 0x38: /* GET COUNTRY-SPECIFIC INFORMATION */
4652         TRACE( "GET COUNTRY-SPECIFIC INFORMATION\n" );
4653         if (AL_reg(context))
4654         {
4655             WORD country = AL_reg(context);
4656             if (country == 0xff)
4657                 country = BX_reg(context);
4658             if (country != INT21_GetSystemCountryCode()) {
4659                 FIXME( "Requested info on non-default country %04x\n", country );
4660                 SET_AX(context, 2);
4661                 SET_CFLAG(context);
4662             }
4663         }
4664         if(AX_reg(context) != 2 )
4665         {
4666             INT21_FillCountryInformation( CTX_SEG_OFF_TO_LIN(context,
4667                                                              context->SegDs,
4668                                                              context->Edx) );
4669             SET_AX( context, INT21_GetSystemCountryCode() );
4670             SET_BX( context, INT21_GetSystemCountryCode() );
4671             RESET_CFLAG(context);
4672         }
4673         break;
4674
4675     case 0x39: /* "MKDIR" - CREATE SUBDIRECTORY */
4676         if (!INT21_CreateDirectory( context ))
4677             bSetDOSExtendedError = TRUE;
4678         else
4679             RESET_CFLAG(context);
4680         break;
4681
4682     case 0x3a: /* "RMDIR" - REMOVE DIRECTORY */
4683         {
4684             WCHAR dirW[MAX_PATH];
4685             char *dirA = CTX_SEG_OFF_TO_LIN(context,
4686                                             context->SegDs, context->Edx);
4687
4688             TRACE( "REMOVE DIRECTORY %s\n", dirA );
4689
4690             MultiByteToWideChar(CP_OEMCP, 0, dirA, -1, dirW, MAX_PATH);
4691
4692             if (!RemoveDirectoryW( dirW ))
4693                 bSetDOSExtendedError = TRUE;
4694             else
4695                 RESET_CFLAG(context);
4696         }
4697         break;
4698
4699     case 0x3b: /* "CHDIR" - SET CURRENT DIRECTORY */
4700         if (!INT21_SetCurrentDirectory( context ))
4701             bSetDOSExtendedError = TRUE;
4702         else
4703             RESET_CFLAG(context);
4704         break;
4705
4706     case 0x3c: /* "CREAT" - CREATE OR TRUNCATE FILE */
4707         if (!INT21_CreateFile( context, context->Edx, FALSE, 
4708                                OF_READWRITE | OF_SHARE_COMPAT, 0x12 ))
4709             bSetDOSExtendedError = TRUE;
4710         else
4711             RESET_CFLAG(context);
4712         break;
4713
4714     case 0x3d: /* "OPEN" - OPEN EXISTING FILE */
4715         if (!INT21_CreateFile( context, context->Edx, FALSE, 
4716                                AL_reg(context), 0x01 ))
4717             bSetDOSExtendedError = TRUE;
4718         else
4719             RESET_CFLAG(context);
4720         break;
4721
4722     case 0x3e: /* "CLOSE" - CLOSE FILE */
4723         TRACE( "CLOSE handle %d\n", BX_reg(context) );
4724         if (_lclose16( BX_reg(context) ) == HFILE_ERROR16)
4725             bSetDOSExtendedError = TRUE;
4726         else
4727             RESET_CFLAG(context);
4728         break;
4729
4730     case 0x3f: /* "READ" - READ FROM FILE OR DEVICE */
4731         TRACE( "READ from %d to %04lX:%04X for %d bytes\n",
4732                BX_reg(context),
4733                context->SegDs,
4734                DX_reg(context),
4735                CX_reg(context) );
4736         {
4737             DWORD result;
4738             WORD  count  = CX_reg(context);
4739             BYTE *buffer = CTX_SEG_OFF_TO_LIN( context, 
4740                                                context->SegDs,
4741                                                context->Edx );
4742
4743             /* Some programs pass a count larger than the allocated buffer */
4744             if (DOSVM_IsWin16())
4745             {
4746                 DWORD maxcount = GetSelectorLimit16( context->SegDs )
4747                     - DX_reg(context) + 1;
4748                 if (count > maxcount)
4749                     count = maxcount;
4750             }
4751
4752             /*
4753              * FIXME: Reading from console (BX=1) in DOS mode
4754              *        does not work as it is supposed to work.
4755              */
4756
4757             RESET_CFLAG(context); /* set if error */
4758             if (!DOSVM_IsWin16() && BX_reg(context) == 0)
4759             {
4760                 result = INT21_BufferedInput( context, buffer, count );
4761                 SET_AX( context, (WORD)result );
4762             }
4763             else if (ReadFile( DosFileHandleToWin32Handle(BX_reg(context)),
4764                                buffer, count, &result, NULL ))
4765                 SET_AX( context, (WORD)result );
4766             else
4767                 bSetDOSExtendedError = TRUE;
4768         }
4769         break;
4770
4771     case 0x40:  /* "WRITE" - WRITE TO FILE OR DEVICE */
4772         TRACE( "WRITE from %04lX:%04X to handle %d for %d byte\n",
4773                context->SegDs, DX_reg(context),
4774                BX_reg(context), CX_reg(context) );
4775         {
4776             char *ptr = CTX_SEG_OFF_TO_LIN(context, context->SegDs, context->Edx);
4777
4778             if (!DOSVM_IsWin16() && 
4779                 (BX_reg(context) == 1 || BX_reg(context) == 2))
4780             {
4781                 int i;
4782                 for(i=0; i<CX_reg(context); i++)
4783                     DOSVM_PutChar(ptr[i]);
4784                 SET_AX(context, CX_reg(context));
4785                 RESET_CFLAG(context);
4786             }
4787             else
4788             {
4789                 HFILE handle = (HFILE)DosFileHandleToWin32Handle(BX_reg(context));
4790                 LONG result = _hwrite( handle, ptr, CX_reg(context) );
4791                 if (result == HFILE_ERROR)
4792                     bSetDOSExtendedError = TRUE;
4793                 else
4794                 {
4795                     SET_AX( context, (WORD)result );
4796                     RESET_CFLAG(context);
4797                 }
4798             }
4799         }
4800         break;
4801
4802     case 0x41: /* "UNLINK" - DELETE FILE */
4803         {
4804             WCHAR fileW[MAX_PATH];
4805             char *fileA = CTX_SEG_OFF_TO_LIN(context, 
4806                                              context->SegDs, 
4807                                              context->Edx);
4808
4809             TRACE( "UNLINK %s\n", fileA );
4810             MultiByteToWideChar(CP_OEMCP, 0, fileA, -1, fileW, MAX_PATH);
4811
4812             if (!DeleteFileW( fileW ))
4813                 bSetDOSExtendedError = TRUE;
4814             else
4815                 RESET_CFLAG(context);
4816         }
4817         break;
4818
4819     case 0x42: /* "LSEEK" - SET CURRENT FILE POSITION */
4820         TRACE( "LSEEK handle %d offset %ld from %s\n",
4821                BX_reg(context), 
4822                MAKELONG( DX_reg(context), CX_reg(context) ),
4823                (AL_reg(context) == 0) ? 
4824                "start of file" : ((AL_reg(context) == 1) ? 
4825                                   "current file position" : "end of file") );
4826         {
4827             HANDLE handle = DosFileHandleToWin32Handle(BX_reg(context));
4828             LONG   offset = MAKELONG( DX_reg(context), CX_reg(context) );
4829             DWORD  status = SetFilePointer( handle, offset, 
4830                                             NULL, AL_reg(context) );
4831             if (status == INVALID_SET_FILE_POINTER)
4832                 bSetDOSExtendedError = TRUE;
4833             else
4834             {
4835                 SET_AX( context, LOWORD(status) );
4836                 SET_DX( context, HIWORD(status) );
4837                 RESET_CFLAG(context);
4838             }
4839         }
4840         break;
4841
4842     case 0x43: /* FILE ATTRIBUTES */
4843         if (!INT21_FileAttributes( context, AL_reg(context), FALSE ))
4844             bSetDOSExtendedError = TRUE;
4845         else
4846             RESET_CFLAG(context);
4847         break;
4848
4849     case 0x44: /* IOCTL */
4850         INT21_Ioctl( context );
4851         break;
4852
4853     case 0x45: /* "DUP" - DUPLICATE FILE HANDLE */
4854         TRACE( "DUPLICATE FILE HANDLE %d\n", BX_reg(context) );
4855         {
4856             HANDLE handle32;
4857             HFILE  handle16 = HFILE_ERROR;
4858
4859             if (DuplicateHandle( GetCurrentProcess(),
4860                                  DosFileHandleToWin32Handle(BX_reg(context)),
4861                                  GetCurrentProcess(), 
4862                                  &handle32,
4863                                  0, TRUE, DUPLICATE_SAME_ACCESS ))
4864                 handle16 = Win32HandleToDosFileHandle(handle32);
4865
4866             if (handle16 == HFILE_ERROR)
4867                 bSetDOSExtendedError = TRUE;
4868             else
4869             {
4870                 SET_AX( context, handle16 );
4871                 RESET_CFLAG(context);
4872             }
4873         }
4874         break;
4875
4876     case 0x46: /* "DUP2", "FORCEDUP" - FORCE DUPLICATE FILE HANDLE */
4877         TRACE( "FORCEDUP - FORCE DUPLICATE FILE HANDLE %d to %d\n",
4878                BX_reg(context), CX_reg(context) );
4879         if (!INT21_Dup2(BX_reg(context), CX_reg(context)))
4880             bSetDOSExtendedError = TRUE;
4881         else
4882             RESET_CFLAG(context);
4883         break;
4884
4885     case 0x47: /* "CWD" - GET CURRENT DIRECTORY */
4886         if (!INT21_GetCurrentDirectory( context, FALSE ))
4887             bSetDOSExtendedError = TRUE;
4888         else
4889             RESET_CFLAG(context);
4890         break;
4891
4892     case 0x48: /* ALLOCATE MEMORY */
4893         TRACE( "ALLOCATE MEMORY for %d paragraphs\n", BX_reg(context) );
4894         {
4895             WORD  selector = 0;
4896             DWORD bytes = (DWORD)BX_reg(context) << 4;
4897
4898             if (!ISV86(context) && DOSVM_IsWin16())
4899             {
4900                 DWORD rv = GlobalDOSAlloc16( bytes );
4901                 selector = LOWORD( rv );
4902             }
4903             else
4904                 DOSMEM_AllocBlock( bytes, &selector );
4905
4906             if (selector)
4907             {
4908                 SET_AX( context, selector );
4909                 RESET_CFLAG(context);
4910             }
4911             else
4912             {
4913                 SET_CFLAG(context);
4914                 SET_AX( context, 0x0008 ); /* insufficient memory */
4915                 SET_BX( context, DOSMEM_Available() >> 4 );
4916             }
4917         }
4918         break;
4919
4920     case 0x49: /* FREE MEMORY */
4921         TRACE( "FREE MEMORY segment %04lX\n", context->SegEs );
4922         {
4923             BOOL ok;
4924             
4925             if (!ISV86(context) && DOSVM_IsWin16())
4926             {
4927                 ok = !GlobalDOSFree16( context->SegEs );
4928
4929                 /* If we don't reset ES_reg, we will fail in the relay code */
4930                 if (ok)
4931                     context->SegEs = 0;
4932             }
4933             else
4934                 ok = DOSMEM_FreeBlock( PTR_REAL_TO_LIN(context->SegEs, 0) );
4935
4936             if (!ok)
4937             {
4938                 TRACE("FREE MEMORY failed\n");
4939                 SET_CFLAG(context);
4940                 SET_AX( context, 0x0009 ); /* memory block address invalid */
4941             }
4942         }
4943         break;
4944
4945     case 0x4a: /* RESIZE MEMORY BLOCK */
4946         TRACE( "RESIZE MEMORY segment %04lX to %d paragraphs\n", 
4947                context->SegEs, BX_reg(context) );
4948         {
4949             DWORD newsize = (DWORD)BX_reg(context) << 4;
4950             
4951             if (!ISV86(context) && DOSVM_IsWin16())
4952             {
4953                 FIXME( "Resize memory block - unsupported under Win16\n" );
4954                 SET_CFLAG(context);
4955             }
4956             else
4957             {
4958                 LPVOID address = (void*)((DWORD)context->SegEs << 4);
4959                 UINT blocksize = DOSMEM_ResizeBlock( address, newsize, FALSE );
4960
4961                 RESET_CFLAG(context);
4962                 if (blocksize == (UINT)-1)
4963                 {
4964                     SET_CFLAG( context );
4965                     SET_AX( context, 0x0009 ); /* illegal address */
4966                 }
4967                 else if(blocksize != newsize)
4968                 {
4969                     SET_CFLAG( context );
4970                     SET_AX( context, 0x0008 );    /* insufficient memory */
4971                     SET_BX( context, blocksize >> 4 ); /* new block size */
4972                 }
4973             }
4974         }
4975         break;
4976
4977     case 0x4b: /* "EXEC" - LOAD AND/OR EXECUTE PROGRAM */
4978         {
4979             char *program = CTX_SEG_OFF_TO_LIN(context, context->SegDs, context->Edx);
4980             BYTE *paramblk = CTX_SEG_OFF_TO_LIN(context, context->SegEs, context->Ebx);
4981
4982             TRACE( "EXEC %s\n", program );
4983
4984             RESET_CFLAG(context);
4985             if (DOSVM_IsWin16())
4986             {
4987                 HINSTANCE16 instance = WinExec16( program, SW_NORMAL );
4988                 if (instance < 32)
4989                 {
4990                     SET_CFLAG( context );
4991                     SET_AX( context, instance );
4992                 }
4993             }
4994             else
4995             {
4996                 if (!MZ_Exec( context, program, AL_reg(context), paramblk))
4997                     bSetDOSExtendedError = TRUE;
4998             }
4999         }
5000         break;
5001
5002     case 0x4c: /* "EXIT" - TERMINATE WITH RETURN CODE */
5003         TRACE( "EXIT with return code %d\n", AL_reg(context) );
5004         if (DOSVM_IsWin16())
5005             ExitThread( AL_reg(context) );
5006         else if(ISV86(context))
5007             MZ_Exit( context, FALSE, AL_reg(context) );
5008         else
5009         {
5010             /*
5011              * Exit from DPMI.
5012              */            
5013             DWORD rv = AL_reg(context);
5014             RaiseException( EXCEPTION_VM86_INTx, 0, 1, &rv );
5015         }
5016         break;
5017
5018     case 0x4d: /* GET RETURN CODE */
5019         TRACE("GET RETURN CODE (ERRORLEVEL)\n");
5020         SET_AX( context, DOSVM_retval );
5021         DOSVM_retval = 0;
5022         break;
5023
5024     case 0x4e: /* "FINDFIRST" - FIND FIRST MATCHING FILE */
5025         TRACE("FINDFIRST mask 0x%04x spec %s\n",CX_reg(context),
5026               (LPCSTR)CTX_SEG_OFF_TO_LIN(context,  context->SegDs, context->Edx));
5027         if (!INT21_FindFirst(context)) break;
5028         /* fall through */
5029
5030     case 0x4f: /* "FINDNEXT" - FIND NEXT MATCHING FILE */
5031         TRACE("FINDNEXT\n");
5032         if (!INT21_FindNext(context))
5033         {
5034             SetLastError( ERROR_NO_MORE_FILES );
5035             SET_AX( context, ERROR_NO_MORE_FILES );
5036             SET_CFLAG(context);
5037         }
5038         else SET_AX( context, 0 );  /* OK */
5039         break;
5040
5041     case 0x50: /* SET CURRENT PROCESS ID (SET PSP ADDRESS) */
5042         TRACE("SET CURRENT PROCESS ID (SET PSP ADDRESS)\n");
5043         DOSVM_psp = BX_reg(context);
5044         break;
5045
5046     case 0x51: /* GET PSP ADDRESS */
5047         INT21_GetPSP( context );
5048         break;
5049
5050     case 0x52: /* "SYSVARS" - GET LIST OF LISTS */
5051         {
5052             SEGPTR ptr = DOSDEV_GetLOL( ISV86(context) || !DOSVM_IsWin16() );
5053             context->SegEs = SELECTOROF(ptr);
5054             SET_BX( context, OFFSETOF(ptr) );
5055         }
5056         break;
5057
5058     case 0x54: /* Get Verify Flag */
5059         TRACE("Get Verify Flag - Not Supported\n");
5060         SET_AL( context, 0x00 );  /* pretend we can tell. 00h = off 01h = on */
5061         break;
5062
5063     case 0x56: /* "RENAME" - RENAME FILE */
5064         if (!INT21_RenameFile( context ))
5065             bSetDOSExtendedError = TRUE;
5066         else
5067             RESET_CFLAG(context);
5068         break;
5069
5070     case 0x57: /* FILE DATE AND TIME */
5071         if (!INT21_FileDateTime( context ))
5072             bSetDOSExtendedError = TRUE;
5073         else
5074             RESET_CFLAG(context);
5075         break;
5076
5077     case 0x58: /* GET OR SET MEMORY ALLOCATION STRATEGY */
5078         switch (AL_reg(context))
5079         {
5080         case 0x00: /* GET MEMORY ALLOCATION STRATEGY */
5081             TRACE( "GET MEMORY ALLOCATION STRATEGY\n" );
5082             SET_AX( context, 0 ); /* low memory first fit */
5083             break;
5084
5085         case 0x01: /* SET ALLOCATION STRATEGY */
5086             TRACE( "SET MEMORY ALLOCATION STRATEGY to %d - ignored\n",
5087                    BL_reg(context) );
5088             break;
5089
5090         case 0x02: /* GET UMB LINK STATE */
5091             TRACE( "GET UMB LINK STATE\n" );
5092             SET_AL( context, 0 ); /* UMBs not part of DOS memory chain */
5093             break;
5094
5095         case 0x03: /* SET UMB LINK STATE */
5096             TRACE( "SET UMB LINK STATE to %d - ignored\n",
5097                    BX_reg(context) );
5098             break;
5099
5100         default:
5101             INT_BARF( context, 0x21 );
5102         }
5103         break;
5104
5105     case 0x59: /* GET EXTENDED ERROR INFO */
5106         INT21_GetExtendedError( context );
5107         break;
5108
5109     case 0x5a: /* CREATE TEMPORARY FILE */
5110         TRACE("CREATE TEMPORARY FILE\n");
5111         bSetDOSExtendedError = !INT21_CreateTempFile(context);
5112         break;
5113
5114     case 0x5b: /* CREATE NEW FILE */ 
5115         if (!INT21_CreateFile( context, context->Edx, FALSE,
5116                                OF_READWRITE | OF_SHARE_COMPAT, 0x10 ))
5117             bSetDOSExtendedError = TRUE;
5118         else
5119             RESET_CFLAG(context);
5120         break;
5121
5122     case 0x5c: /* "FLOCK" - RECORD LOCKING */
5123         {
5124             DWORD  offset = MAKELONG(DX_reg(context), CX_reg(context));
5125             DWORD  length = MAKELONG(DI_reg(context), SI_reg(context));
5126             HANDLE handle = DosFileHandleToWin32Handle(BX_reg(context));
5127
5128             RESET_CFLAG(context);
5129             switch (AL_reg(context))
5130             {
5131             case 0x00: /* LOCK */
5132                 TRACE( "lock handle %d offset %ld length %ld\n",
5133                        BX_reg(context), offset, length );
5134                 if (!LockFile( handle, offset, 0, length, 0 ))
5135                     bSetDOSExtendedError = TRUE;
5136                 break;
5137
5138             case 0x01: /* UNLOCK */
5139                 TRACE( "unlock handle %d offset %ld length %ld\n",
5140                        BX_reg(context), offset, length );
5141                 if (!UnlockFile( handle, offset, 0, length, 0 ))
5142                     bSetDOSExtendedError = TRUE;
5143                 break;
5144
5145             default:
5146                 INT_BARF( context, 0x21 );
5147             }
5148         }
5149         break;
5150
5151     case 0x5d: /* NETWORK 5D */
5152         FIXME( "Network function 5D not implemented.\n" );
5153         SetLastError( ER_NoNetwork );
5154         bSetDOSExtendedError = TRUE;
5155         break;
5156
5157     case 0x5e: /* NETWORK 5E */
5158         bSetDOSExtendedError = INT21_NetworkFunc( context);
5159         break;
5160
5161     case 0x5f: /* NETWORK 5F */
5162         /* FIXME: supporting this would need to 1:
5163          * - implement per drive current directory (as kernel32 doesn't)
5164          * - assign enabled/disabled flag on a per drive basis
5165          */
5166         /* network software not installed */
5167         TRACE("NETWORK function AX=%04x not implemented\n",AX_reg(context));
5168         SetLastError( ER_NoNetwork );
5169         bSetDOSExtendedError = TRUE;
5170         break;
5171
5172     case 0x60: /* "TRUENAME" - CANONICALIZE FILENAME OR PATH */
5173         {
5174             WCHAR       pathW[MAX_PATH], res[MAX_PATH];
5175             /* FIXME: likely to be broken */
5176
5177             TRACE("TRUENAME %s\n",
5178                   (LPCSTR)CTX_SEG_OFF_TO_LIN(context, context->SegDs,context->Esi));
5179             MultiByteToWideChar(CP_OEMCP, 0, CTX_SEG_OFF_TO_LIN(context, context->SegDs, context->Esi), -1, pathW, MAX_PATH);
5180             if (!GetFullPathNameW( pathW, 128, res, NULL ))
5181                 bSetDOSExtendedError = TRUE;
5182             else
5183             {
5184                 SET_AX( context, 0 );
5185                 WideCharToMultiByte(CP_OEMCP, 0, res, -1, 
5186                                     CTX_SEG_OFF_TO_LIN(context, context->SegEs, context->Edi), 
5187                                     128, NULL, NULL);
5188             }
5189         }
5190         break;
5191
5192     case 0x61: /* UNUSED */
5193         SET_AL( context, 0 );
5194         break;
5195
5196     case 0x62: /* GET PSP ADDRESS */
5197         INT21_GetPSP( context );
5198         break;
5199
5200     case 0x63: /* MISC. LANGUAGE SUPPORT */
5201         switch (AL_reg(context)) {
5202         case 0x00: /* GET DOUBLE BYTE CHARACTER SET LEAD-BYTE TABLE */
5203             TRACE( "GET DOUBLE BYTE CHARACTER SET LEAD-BYTE TABLE\n" );
5204             context->SegDs = INT21_GetHeapSelector( context );
5205             SET_SI( context, offsetof(INT21_HEAP, dbcs_table) );
5206             SET_AL( context, 0 ); /* success */
5207             break;
5208         }
5209         break;
5210
5211     case 0x64: /* OS/2 DOS BOX */
5212         INT_BARF( context, 0x21 );
5213         SET_CFLAG(context);
5214         break;
5215
5216     case 0x65: /* EXTENDED COUNTRY INFORMATION */
5217         INT21_ExtendedCountryInformation( context );
5218         break;
5219
5220     case 0x66: /* GLOBAL CODE PAGE TABLE */
5221         switch (AL_reg(context))
5222         {
5223         case 0x01:
5224             TRACE( "GET GLOBAL CODE PAGE TABLE\n" );
5225             SET_BX( context, GetOEMCP() );
5226             SET_DX( context, GetOEMCP() );
5227             break;
5228         case 0x02:
5229             FIXME( "SET GLOBAL CODE PAGE TABLE, active %d, system %d - ignored\n",
5230                    BX_reg(context), DX_reg(context) );
5231             break;
5232         }
5233         break;
5234
5235     case 0x67: /* SET HANDLE COUNT */
5236         TRACE( "SET HANDLE COUNT to %d\n", BX_reg(context) );
5237         if (SetHandleCount( BX_reg(context) ) < BX_reg(context) )
5238             bSetDOSExtendedError = TRUE;
5239         break;
5240
5241     case 0x68: /* "FFLUSH" - COMMIT FILE */
5242         TRACE( "FFLUSH - handle %d\n", BX_reg(context) );
5243         if (!FlushFileBuffers( DosFileHandleToWin32Handle(BX_reg(context)) ))
5244             bSetDOSExtendedError = TRUE;
5245         break;
5246
5247     case 0x69: /* DISK SERIAL NUMBER */
5248         switch (AL_reg(context))
5249         {
5250         case 0x00:
5251             TRACE("GET DISK SERIAL NUMBER for drive %s\n",
5252                   INT21_DriveName(BL_reg(context)));
5253             if (!INT21_GetDiskSerialNumber(context)) bSetDOSExtendedError = TRUE;
5254             else SET_AX( context, 0 );
5255             break;
5256
5257         case 0x01:
5258             TRACE("SET DISK SERIAL NUMBER for drive %s\n",
5259                   INT21_DriveName(BL_reg(context)));
5260             if (!INT21_SetDiskSerialNumber(context)) bSetDOSExtendedError = TRUE;
5261             else SET_AX( context, 1 );
5262             break;
5263         }
5264         break;
5265
5266     case 0x6a: /* COMMIT FILE */
5267         TRACE( "COMMIT FILE - handle %d\n", BX_reg(context) );
5268         if (!FlushFileBuffers( DosFileHandleToWin32Handle(BX_reg(context)) ))
5269             bSetDOSExtendedError = TRUE;
5270         break;
5271
5272     case 0x6b: /* NULL FUNCTION FOR CP/M COMPATIBILITY */
5273         SET_AL( context, 0 );
5274         break;
5275
5276     case 0x6c: /* EXTENDED OPEN/CREATE */
5277         if (!INT21_CreateFile( context, context->Esi, TRUE,
5278                                BX_reg(context), DL_reg(context) ))
5279             bSetDOSExtendedError = TRUE;
5280         break;
5281
5282     case 0x70: /* MSDOS 7 - GET/SET INTERNATIONALIZATION INFORMATION */
5283         FIXME( "MS-DOS 7 - GET/SET INTERNATIONALIZATION INFORMATION\n" );
5284         SET_CFLAG( context );
5285         SET_AL( context, 0 );
5286         break;
5287
5288     case 0x71: /* MSDOS 7 - LONG FILENAME FUNCTIONS */
5289         INT21_LongFilename( context );
5290         break;
5291
5292     case 0x73: /* MSDOS7 - FAT32 */
5293         RESET_CFLAG( context );
5294         if (!INT21_Fat32( context ))
5295             bSetDOSExtendedError = TRUE;
5296         break;
5297
5298     case 0xdc: /* CONNECTION SERVICES - GET CONNECTION NUMBER */
5299         TRACE( "CONNECTION SERVICES - GET CONNECTION NUMBER - ignored\n" );
5300         break;
5301
5302     case 0xea: /* NOVELL NETWARE - RETURN SHELL VERSION */
5303         TRACE( "NOVELL NETWARE - RETURN SHELL VERSION - ignored\n" );
5304         break;
5305
5306     case 0xff: /* DOS32 EXTENDER (DOS/4GW) - API */
5307         /* we don't implement a DOS32 extender */
5308         TRACE( "DOS32 EXTENDER API - ignored\n" );
5309         break;
5310
5311     default:
5312         INT_BARF( context, 0x21 );
5313         break;
5314
5315     } /* END OF SWITCH */
5316
5317     /* Set general error condition. */
5318     if (bSetDOSExtendedError)
5319     {
5320         SET_AX( context, GetLastError() );
5321         SET_CFLAG( context );
5322     }
5323
5324     /* Print error code if carry flag is set. */
5325     if (context->EFlags & 0x0001)
5326         TRACE("failed, error %ld\n", GetLastError() );
5327
5328     TRACE( "returning: AX=%04x BX=%04x CX=%04x DX=%04x "
5329            "SI=%04x DI=%04x DS=%04x ES=%04x EFL=%08lx\n",
5330            AX_reg(context), BX_reg(context), 
5331            CX_reg(context), DX_reg(context), 
5332            SI_reg(context), DI_reg(context),
5333            (WORD)context->SegDs, (WORD)context->SegEs,
5334            context->EFlags );
5335 }