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