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