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