Release 970305
[wine] / msdos / int21.c
1 /*
2  * DOS interrupt 21h handler
3  */
4
5 #include <time.h>
6 #include <fcntl.h>
7 #include <errno.h>
8 #include <stdio.h>
9 #include <stdlib.h>
10 #include <sys/file.h>
11 #include <string.h>
12 #include <sys/stat.h>
13 #include <sys/time.h>
14 #include <sys/types.h>
15 #include <unistd.h>
16 #include <utime.h>
17 #include <ctype.h>
18 #include "windows.h"
19 #include "drive.h"
20 #include "file.h"
21 #include "heap.h"
22 #include "msdos.h"
23 #include "ldt.h"
24 #include "task.h"
25 #include "options.h"
26 #include "miscemu.h"
27 #include "xmalloc.h"
28 #include "stddebug.h"
29 #include "debug.h"
30 #if defined(__svr4__) || defined(_SCO_DS)
31 /* SVR4 DOESNT do locking the same way must implement properly */
32 #define LOCK_EX 0
33 #define LOCK_SH  1
34 #define LOCK_NB  8
35 #endif
36
37
38 #define DOS_GET_DRIVE(reg) ((reg) ? (reg) - 1 : DRIVE_GetCurrentDrive())
39
40 /* Define the drive parameter block, as used by int21/1F
41  * and int21/32.  This table can be accessed through the
42  * global 'dpb' pointer, which points into the local dos
43  * heap.
44  */
45 struct DPB
46 {
47     BYTE drive_num;         /* 0=A, etc. */
48     BYTE unit_num;          /* Drive's unit number (?) */
49     WORD sector_size;       /* Sector size in bytes */
50     BYTE high_sector;       /* Highest sector in a cluster */
51     BYTE shift;             /* Shift count (?) */
52     WORD reserved;          /* Number of reserved sectors at start */
53     BYTE num_FAT;           /* Number of FATs */
54     WORD dir_entries;       /* Number of root dir entries */
55     WORD first_data;        /* First data sector */
56     WORD high_cluster;      /* Highest cluster number */
57     WORD sectors_in_FAT;    /* Number of sectors per FAT */
58     WORD start_dir;         /* Starting sector of first dir */
59     DWORD driver_head;      /* Address of device driver header (?) */
60     BYTE media_ID;          /* Media ID */
61     BYTE access_flag;       /* Prev. accessed flag (0=yes,0xFF=no) */
62     DWORD next;             /* Pointer to next DPB in list */
63     WORD free_search;       /* Free cluster search start */
64     WORD free_clusters;     /* Number of free clusters (0xFFFF=unknown) */
65 };
66
67 WORD CodePage = 437;
68 DWORD dpbsegptr;
69
70 struct DosHeap {
71         BYTE InDosFlag;
72         BYTE mediaID;
73         BYTE biosdate[8];
74         struct DPB dpb;
75 };
76 static struct DosHeap *heap;
77 static WORD DosHeapHandle;
78
79 WORD sharing_retries = 3;      /* number of retries at sharing violation */
80 WORD sharing_pause = 1;        /* pause between retries */
81
82 extern char TempDirectory[];
83
84 static BOOL32 INT21_CreateHeap(void)
85 {
86     if (!(DosHeapHandle = GlobalAlloc16(GMEM_FIXED,sizeof(struct DosHeap))))
87     {
88         fprintf( stderr, "INT21_Init: Out of memory\n");
89         return FALSE;
90     }
91     heap = (struct DosHeap *) GlobalLock16(DosHeapHandle);
92     dpbsegptr = PTR_SEG_OFF_TO_SEGPTR(DosHeapHandle,(int)&heap->dpb-(int)heap);
93     heap->InDosFlag = 0;
94     strcpy(heap->biosdate, "01/01/80");
95     return TRUE;
96 }
97
98 BYTE *GetCurrentDTA(void)
99 {
100     TDB *pTask = (TDB *)GlobalLock16( GetCurrentTask() );
101     return (BYTE *)PTR_SEG_TO_LIN( pTask->dta );
102 }
103
104
105 void ChopOffWhiteSpace(char *string)
106 {
107         int length;
108
109         for (length = strlen(string) ; length ; length--)
110                 if (string[length] == ' ')
111                         string[length] = '\0';
112 }
113
114 void CreateBPB(int drive, BYTE *data)
115 {
116         if (drive > 1) {
117                 setword(data, 512);
118                 data[2] = 2;
119                 setword(&data[3], 0);
120                 data[5] = 2;
121                 setword(&data[6], 240);
122                 setword(&data[8], 64000);
123                 data[0x0a] = 0xf8;
124                 setword(&data[0x0b], 40);
125                 setword(&data[0x0d], 56);
126                 setword(&data[0x0f], 2);
127                 setword(&data[0x11], 0);
128                 setword(&data[0x1f], 800);
129                 data[0x21] = 5;
130                 setword(&data[0x22], 1);
131         } else { /* 1.44mb */
132                 setword(data, 512);
133                 data[2] = 2;
134                 setword(&data[3], 0);
135                 data[5] = 2;
136                 setword(&data[6], 240);
137                 setword(&data[8], 2880);
138                 data[0x0a] = 0xf8;
139                 setword(&data[0x0b], 6);
140                 setword(&data[0x0d], 18);
141                 setword(&data[0x0f], 2);
142                 setword(&data[0x11], 0);
143                 setword(&data[0x1f], 80);
144                 data[0x21] = 7;
145                 setword(&data[0x22], 2);
146         }       
147 }
148
149 static int INT21_GetFreeDiskSpace( CONTEXT *context )
150 {
151     DWORD cluster_sectors, sector_bytes, free_clusters, total_clusters;
152     char root[] = "A:\\";
153
154     *root += DOS_GET_DRIVE( DL_reg(context) );
155     if (!GetDiskFreeSpace32A( root, &cluster_sectors, &sector_bytes,
156                               &free_clusters, &total_clusters )) return 0;
157     AX_reg(context) = cluster_sectors;
158     BX_reg(context) = free_clusters;
159     CX_reg(context) = sector_bytes;
160     DX_reg(context) = total_clusters;
161     return 1;
162 }
163
164 static int INT21_GetDriveAllocInfo( CONTEXT *context )
165 {
166     if (!INT21_GetFreeDiskSpace( context )) return 0;
167     if (!heap && !INT21_CreateHeap()) return 0;
168     heap->mediaID = 0xf0;
169     DS_reg(context) = DosHeapHandle;
170     BX_reg(context) = (int)&heap->mediaID - (int)heap;
171     return 1;
172 }
173
174 static void GetDrivePB( CONTEXT *context, int drive )
175 {
176         if(!DRIVE_IsValid(drive))
177         {
178             DOS_ERROR( ER_InvalidDrive, EC_MediaError, SA_Abort, EL_Disk );
179             AX_reg(context) = 0x00ff;
180         }
181         else if (heap || INT21_CreateHeap())
182         {
183                 dprintf_int(stddeb, "int21: GetDrivePB not fully implemented.\n");
184
185                 /* FIXME: I have no idea what a lot of this information should
186                  * say or whether it even really matters since we're not allowing
187                  * direct block access.  However, some programs seem to depend on
188                  * getting at least _something_ back from here.  The 'next' pointer
189                  * does worry me, though.  Should we have a complete table of
190                  * separate DPBs per drive?  Probably, but I'm lazy. :-)  -CH
191                  */
192                 heap->dpb.drive_num = heap->dpb.unit_num = drive; /*The same?*/
193                 heap->dpb.sector_size = 512;
194                 heap->dpb.high_sector = 1;
195                 heap->dpb.shift = drive < 2 ? 0 : 6; /*6 for HD, 0 for floppy*/
196                 heap->dpb.reserved = 0;
197                 heap->dpb.num_FAT = 1;
198                 heap->dpb.dir_entries = 2;
199                 heap->dpb.first_data = 2;
200                 heap->dpb.high_cluster = 64000;
201                 heap->dpb.sectors_in_FAT = 1;
202                 heap->dpb.start_dir = 1;
203                 heap->dpb.driver_head = 0;
204                 heap->dpb.media_ID = (drive > 1) ? 0xF8 : 0xF0;
205                 heap->dpb.access_flag = 0;
206                 heap->dpb.next = 0;
207                 heap->dpb.free_search = 0;
208                 heap->dpb.free_clusters = 0xFFFF;    /* unknown */
209
210                 AL_reg(context) = 0x00;
211                 DS_reg(context) = SELECTOROF(dpbsegptr);
212                 BX_reg(context) = OFFSETOF(dpbsegptr);
213         }
214 }
215
216
217 static void ioctlGetDeviceInfo( CONTEXT *context )
218 {
219     dprintf_int (stddeb, "int21: ioctl (%d, GetDeviceInfo)\n", BX_reg(context));
220     
221     DX_reg(context) = 0x0942;
222     /* bits 0-5 are current drive
223      * bit 6 - file has NOT been written..FIXME: correct?
224      * bit 8 - generate int24 if no diskspace on write/ read past end of file
225      * bit 11 - media not removable
226      */
227     RESET_CFLAG(context);
228 }
229
230 static void ioctlGenericBlkDevReq( CONTEXT *context )
231 {
232         BYTE *dataptr = PTR_SEG_OFF_TO_LIN(DS_reg(context), DX_reg(context));
233         int drive = DOS_GET_DRIVE( BL_reg(context) );
234
235         if (!DRIVE_IsValid(drive))
236         {
237             DOS_ERROR( ER_FileNotFound, EC_NotFound, SA_Abort, EL_Disk );
238             AX_reg(context) = DOS_ExtendedError;
239             SET_CFLAG(context);
240             return;
241         }
242
243         if (CH_reg(context) != 0x08)
244         {
245             INT_BARF( context, 0x21 );
246             return;
247         }
248         switch (CL_reg(context)) {
249                 case 0x4a: /* lock logical volume */
250                         dprintf_int(stddeb,"int21: lock logical volume (%d) level %d mode %d\n",drive,BH_reg(context),DX_reg(context));
251                         return;
252                 case 0x60: /* get device parameters */
253                            /* used by w4wgrp's winfile */
254                         memset(dataptr, 0, 0x26);
255                         dataptr[0] = 0x04;
256                         dataptr[6] = 0; /* media type */
257                         if (drive > 1) 
258                         {
259                                 dataptr[1] = 0x05; /* fixed disk */
260                                 setword(&dataptr[2], 0x01); /* non removable */
261                                 setword(&dataptr[4], 0x300); /* # of cylinders */
262                         }
263                         else
264                         {
265                                 dataptr[1] = 0x07; /* block dev, floppy */
266                                 setword(&dataptr[2], 0x02); /* removable */
267                                 setword(&dataptr[4], 80); /* # of cylinders */
268                         }
269                         CreateBPB(drive, &dataptr[7]);                  
270                         RESET_CFLAG(context);
271                         return;
272                 case 0x66:/*  get disk serial number */
273                         {       char    label[12],fsname[9],path[4];
274                                 DWORD   serial;
275
276                                 strcpy(path,"x:\\");path[0]=drive+'A';
277                                 GetVolumeInformation32A(
278                                         path,label,12,&serial,NULL,NULL,fsname,9
279                                 );
280                                 *(WORD*)dataptr         = 0;
281                                 memcpy(dataptr+2,&serial,4);
282                                 memcpy(dataptr+6,label  ,11);
283                                 memcpy(dataptr+17,fsname,8);
284                                 return;
285                         }
286                 case 0x6a:
287                         dprintf_int(stddeb,"int21: logical volume %d unlocked.\n",drive);
288                         return;
289                 default:
290                         INT_BARF( context, 0x21 );
291         }
292 }
293
294 static void INT21_GetSystemDate( CONTEXT *context )
295 {
296     SYSTEMTIME systime;
297     GetLocalTime( &systime );
298     CX_reg(context) = systime.wYear;
299     DX_reg(context) = (systime.wMonth << 8) | systime.wDay;
300     AX_reg(context) = systime.wDayOfWeek;
301 }
302
303 static void INT21_GetSystemTime( CONTEXT *context )
304 {
305     SYSTEMTIME systime;
306     GetLocalTime( &systime );
307     CX_reg(context) = (systime.wHour << 8) | systime.wMinute;
308     DX_reg(context) = (systime.wSecond << 8) | (systime.wMilliseconds / 10);
309 }
310
311 static void INT21_CreateFile( CONTEXT *context )
312 {
313     AX_reg(context) = _lcreat16( PTR_SEG_OFF_TO_LIN( DS_reg(context),
314                                           DX_reg(context) ), CX_reg(context) );
315     if (AX_reg(context) == (WORD)HFILE_ERROR16)
316     {
317         AX_reg(context) = DOS_ExtendedError;
318         SET_CFLAG(context);
319     }
320 }
321
322
323 void OpenExistingFile( CONTEXT *context )
324 {
325     AX_reg(context) = _lopen16( PTR_SEG_OFF_TO_LIN(DS_reg(context),DX_reg(context)),
326                               AL_reg(context) );
327     if (AX_reg(context) == (WORD)HFILE_ERROR16)
328     {
329         AX_reg(context) = DOS_ExtendedError;
330         SET_CFLAG(context);
331     }
332 #if 0
333         int handle;
334         int mode;
335         int lock;
336         
337         switch (AX_reg(context) & 0x0070)
338         {
339           case 0x00:    /* compatability mode */
340           case 0x40:    /* DENYNONE */
341             lock = -1;
342             break;
343
344           case 0x30:    /* DENYREAD */
345             dprintf_int(stddeb,
346               "OpenExistingFile (%s): DENYREAD changed to DENYALL\n",
347               (char *)PTR_SEG_OFF_TO_LIN(DS_reg(context),DX_reg(context)));
348           case 0x10:    /* DENYALL */  
349             lock = LOCK_EX;
350             break;
351
352           case 0x20:    /* DENYWRITE */
353             lock = LOCK_SH;
354             break;
355
356           default:
357             lock = -1;
358         }
359
360         if (lock != -1)
361         {
362
363           int result,retries=sharing_retries;
364           {
365 #if defined(__svr4__) || defined(_SCO_DS)
366               printf("Should call flock and needs porting to lockf\n");
367               result = 0;
368               retries = 0;
369 #else
370             result = flock(handle, lock | LOCK_NB);
371 #endif
372             if ( retries && (!result) )
373             {
374               int i;
375               for(i=0;i<32768*((int)sharing_pause);i++)
376                   result++;                          /* stop the optimizer */
377               for(i=0;i<32768*((int)sharing_pause);i++)
378                   result--;
379             }
380           }
381           while( (!result) && (!(retries--)) );
382
383           if(result)  
384           {
385             errno_to_doserr();
386             AX_reg(context) = ExtendedError;
387             close(handle);
388             SET_CFLAG(context);
389             return;
390           }
391
392         }
393
394         Error (0,0,0);
395         AX_reg(context) = handle;
396         RESET_CFLAG(context);
397 #endif
398 }
399
400 static void CloseFile( CONTEXT *context )
401 {
402     if ((AX_reg(context) = _lclose16( BX_reg(context) )) != 0)
403     {
404         AX_reg(context) = DOS_ExtendedError;
405         SET_CFLAG(context);
406     }
407 }
408
409 void ExtendedOpenCreateFile(CONTEXT *context )
410 {
411   BYTE action=DL_reg(context);
412   /* Shuffle arguments to call OpenExistingFile */
413   AL_reg(context) = BL_reg(context);
414   DX_reg(context) = SI_reg(context);
415   /* BX,CX and DX should be preserved */
416   OpenExistingFile(context);
417   if ((EFL_reg(context) & 0x0001)==0) 
418     { /* It exists */
419       /* Now decide what do do */
420       if ((action & 0x07)== 0)
421         {
422           BX_reg(context) = AX_reg(context);
423           CloseFile(context);
424           AX_reg(context) = 0x0050;/*File exists*/
425           CX_reg(context) = 0;
426           SET_CFLAG(context);
427           dprintf_int(stddeb, "int21: extended open/create: failed because file exixts \n");
428           return;
429         }
430       if ((action & 0x07)== 2) {
431         /* Truncate it, but first check if opened for write */
432         if ((BL_reg(context) & 0x0007)== 0) {
433           BX_reg(context) = AX_reg(context);
434           CloseFile(context);
435           dprintf_int(stddeb, "int21: extended open/create: failed, trunc on ro file");
436           AX_reg(context) = 0x000C;/*Access code invalid*/
437           CX_reg(context) = 0;
438           SET_CFLAG(context);
439           return;
440         }
441         /* Shuffle arguments to call CloseFile */
442         dprintf_int(stddeb, "int21: extended open/create: Closing before truncate\n");
443         BX_reg(context) = AX_reg(context);
444         /* BX and DX should be preserved */
445         CloseFile(context);
446         if (EFL_reg(context) & 0x0001) {
447           dprintf_int(stddeb, "int21: extended open/create: close before trunc failed");
448           AX_reg(context) = 0x0019;/*Seek Error*/
449           CX_reg(context) = 0;
450           SET_CFLAG(context);
451         }
452         /* Shuffle arguments to call CreateFile */
453         dprintf_int(stddeb, "int21: extended open/create: Truncating\n");
454         AL_reg(context) = BL_reg(context);
455         /* CX is still the same */
456         DX_reg(context) = SI_reg(context);
457         INT21_CreateFile(context);
458         if (EFL_reg(context) & 0x0001) { /*no file open, flags set */
459           dprintf_int(stddeb, "int21: extended open/create: trunc failed");
460           return;
461         }
462         CX_reg(context) = 3;
463         return;
464       }
465       CX_reg(context) = 1;
466       return;
467     }
468   else /* file does not exist */
469     {
470       RESET_CFLAG(context); /* was set by OpenExistingFile(context) */
471       if ((action & 0xF0)== 0)
472       {
473         CX_reg(context) = 0;
474         SET_CFLAG(context);
475         dprintf_int(stddeb, "int21: extended open/create: failed, file dosen't exist\n");
476         return;
477       }
478       /* Shuffle arguments to call CreateFile */
479       dprintf_int(stddeb, "int21: extended open/create: Creating\n");
480       AL_reg(context) = BL_reg(context);
481       /* CX should still be the same */
482       DX_reg(context) = SI_reg(context);
483       INT21_CreateFile(context);
484       if (EFL_reg(context) & 0x0001) { /*no file open, flags set */
485         dprintf_int(stddeb, "int21: extended open/create: create failed\n");
486         return;
487       }
488       CX_reg(context) = 2;
489       return;
490     }
491 }
492
493
494 static void INT21_ChangeDir( CONTEXT *context )
495 {
496     int drive;
497     char *dirname = PTR_SEG_OFF_TO_LIN(DS_reg(context),DX_reg(context));
498
499     dprintf_int(stddeb,"int21: changedir %s\n", dirname);
500     if (dirname[0] && (dirname[1] == ':'))
501     {
502         drive = toupper(dirname[0]) - 'A';
503         dirname += 2;
504     }
505     else drive = DRIVE_GetCurrentDrive();
506     if (!DRIVE_Chdir( drive, dirname ))
507     {
508         AX_reg(context) = DOS_ExtendedError;
509         SET_CFLAG(context);
510     }
511 }
512
513
514 static int INT21_FindFirst( CONTEXT *context )
515 {
516     char *p;
517     const char *path;
518     DOS_FULL_NAME full_name;
519     FINDFILE_DTA *dta = (FINDFILE_DTA *)GetCurrentDTA();
520
521     path = (const char *)PTR_SEG_OFF_TO_LIN(DS_reg(context), DX_reg(context));
522     dta->unixPath = NULL;
523     if (!DOSFS_GetFullName( path, FALSE, &full_name ))
524     {
525         AX_reg(context) = DOS_ExtendedError;
526         SET_CFLAG(context);
527         return 0;
528     }
529     dta->unixPath = xstrdup( full_name.long_name );
530     p = strrchr( dta->unixPath, '/' );
531     *p = '\0';
532
533     /* Note: terminating NULL in dta->mask overwrites dta->search_attr
534      *       (doesn't matter as it is set below anyway)
535      */
536     if (!DOSFS_ToDosFCBFormat( p + 1, dta->mask ))
537     {
538         free( dta->unixPath );
539         dta->unixPath = NULL;
540         DOS_ERROR( ER_FileNotFound, EC_NotFound, SA_Abort, EL_Disk );
541         AX_reg(context) = ER_FileNotFound;
542         SET_CFLAG(context);
543         return 0;
544     }
545     dta->drive = (path[0] && (path[1] == ':')) ? toupper(path[0]) - 'A'
546                                                : DRIVE_GetCurrentDrive();
547     dta->count = 0;
548     dta->search_attr = CL_reg(context);
549     return 1;
550 }
551
552
553 static int INT21_FindNext( CONTEXT *context )
554 {
555     FINDFILE_DTA *dta = (FINDFILE_DTA *)GetCurrentDTA();
556     WIN32_FIND_DATA32A entry;
557     int count;
558
559     if (!dta->unixPath) return 0;
560     if (!(count = DOSFS_FindNext( dta->unixPath, dta->mask, NULL, dta->drive,
561                                   dta->search_attr, dta->count, &entry )))
562     {
563         free( dta->unixPath );
564         dta->unixPath = NULL;
565         return 0;
566     }
567     if ((int)dta->count + count > 0xffff)
568     {
569         fprintf( stderr, "Too many directory entries in %s\n", dta->unixPath );
570         free( dta->unixPath );
571         dta->unixPath = NULL;
572         return 0;
573     }
574     dta->count += count;
575     dta->fileattr = entry.dwFileAttributes;
576     dta->filesize = entry.nFileSizeLow;
577     FileTimeToDosDateTime( &entry.ftLastWriteTime,
578                            &dta->filedate, &dta->filetime );
579     strcpy( dta->filename, entry.cAlternateFileName );
580     return 1;
581 }
582
583
584 static int INT21_CreateTempFile( CONTEXT *context )
585 {
586     static int counter = 0;
587     char *name = PTR_SEG_OFF_TO_LIN( DS_reg(context), DX_reg(context) );
588     char *p = name + strlen(name);
589
590     for (;;)
591     {
592         sprintf( p, "wine%04x.%03d", (int)getpid(), counter );
593         counter = (counter + 1) % 1000;
594
595         if ((AX_reg(context) = _lcreat_uniq( name, 0 )) != (WORD)HFILE_ERROR16)
596         {
597             dprintf_int( stddeb, "INT21_CreateTempFile: created %s\n", name );
598             return 1;
599         }
600         if (DOS_ExtendedError != ER_FileExists) return 0;
601     }
602 }
603
604
605 static int INT21_GetCurrentDirectory( CONTEXT *context ) 
606 {
607     int drive = DOS_GET_DRIVE( DL_reg(context) );
608     char *ptr = (char *)PTR_SEG_OFF_TO_LIN( DS_reg(context), SI_reg(context) );
609
610     if (!DRIVE_IsValid(drive))
611     {
612         DOS_ERROR( ER_InvalidDrive, EC_NotFound, SA_Abort, EL_Disk );
613         return 0;
614     }
615
616     lstrcpyn32A( ptr, DRIVE_GetDosCwd(drive), 64 );
617     return 1;
618 }
619
620
621 static int INT21_GetDiskSerialNumber( CONTEXT *context )
622 {
623     BYTE *dataptr = PTR_SEG_OFF_TO_LIN(DS_reg(context), DX_reg(context));
624     int drive = DOS_GET_DRIVE( BL_reg(context) );
625         
626     if (!DRIVE_IsValid(drive))
627     {
628         DOS_ERROR( ER_InvalidDrive, EC_NotFound, SA_Abort, EL_Disk );
629         return 0;
630     }
631     
632     *(WORD *)dataptr = 0;
633     *(DWORD *)(dataptr + 2) = DRIVE_GetSerialNumber( drive );
634     memcpy( dataptr + 6, DRIVE_GetLabel( drive ), 11 );
635     strncpy(dataptr + 0x11, "FAT16   ", 8);
636     return 1;
637 }
638
639
640 static int INT21_SetDiskSerialNumber( CONTEXT *context )
641 {
642     BYTE *dataptr = PTR_SEG_OFF_TO_LIN(DS_reg(context), DX_reg(context));
643     int drive = DOS_GET_DRIVE( BL_reg(context) );
644
645     if (!DRIVE_IsValid(drive))
646     {
647         DOS_ERROR( ER_InvalidDrive, EC_NotFound, SA_Abort, EL_Disk );
648         return 0;
649     }
650
651     DRIVE_SetSerialNumber( drive, *(DWORD *)(dataptr + 2) );
652     return 1;
653 }
654
655
656 /* microsoft's programmers should be shot for using CP/M style int21
657    calls in Windows for Workgroup's winfile.exe */
658
659 static int INT21_FindFirstFCB( CONTEXT *context )
660 {
661     BYTE *fcb = (BYTE *)PTR_SEG_OFF_TO_LIN(DS_reg(context), DX_reg(context));
662     FINDFILE_FCB *pFCB;
663     LPCSTR root, cwd;
664     int drive;
665
666     if (*fcb == 0xff) pFCB = (FINDFILE_FCB *)(fcb + 7);
667     else pFCB = (FINDFILE_FCB *)fcb;
668     drive = DOS_GET_DRIVE( pFCB->drive );
669     root = DRIVE_GetRoot( drive );
670     cwd  = DRIVE_GetUnixCwd( drive );
671     pFCB->unixPath = HeapAlloc( SystemHeap, 0, strlen(root)+strlen(cwd)+2 );
672     if (!pFCB->unixPath) return 0;
673     strcpy( pFCB->unixPath, root );
674     strcat( pFCB->unixPath, "/" );
675     strcat( pFCB->unixPath, cwd );
676     pFCB->count = 0;
677     return 1;
678 }
679
680
681 static int INT21_FindNextFCB( CONTEXT *context )
682 {
683     BYTE *fcb = (BYTE *)PTR_SEG_OFF_TO_LIN(DS_reg(context), DX_reg(context));
684     FINDFILE_FCB *pFCB;
685     DOS_DIRENTRY_LAYOUT *pResult = (DOS_DIRENTRY_LAYOUT *)GetCurrentDTA();
686     WIN32_FIND_DATA32A entry;
687     BYTE attr;
688     int count;
689
690     if (*fcb == 0xff)
691     {
692         attr = fcb[6];
693         pFCB = (FINDFILE_FCB *)(fcb + 7);
694     }
695     else
696     {
697         attr = 0;
698         pFCB = (FINDFILE_FCB *)fcb;
699     }
700
701     if (!pFCB->unixPath) return 0;
702     if (!(count = DOSFS_FindNext( pFCB->unixPath, pFCB->filename, NULL,
703                                   DOS_GET_DRIVE( pFCB->drive ), attr,
704                                   pFCB->count, &entry )))
705     {
706         HeapFree( SystemHeap, 0, pFCB->unixPath );
707         pFCB->unixPath = NULL;
708         return 0;
709     }
710     pFCB->count += count;
711
712     pResult->fileattr = entry.dwFileAttributes;
713     pResult->cluster  = 0;  /* what else? */
714     pResult->filesize = entry.nFileSizeLow;
715     memset( pResult->reserved, 0, sizeof(pResult->reserved) );
716     FileTimeToDosDateTime( &entry.ftLastWriteTime,
717                            &pResult->filedate, &pResult->filetime );
718
719     /* Convert file name to FCB format */
720
721     memset( pResult->filename, ' ', sizeof(pResult->filename) );
722     if (!strcmp( entry.cAlternateFileName, "." )) pResult->filename[0] = '.';
723     else if (!strcmp( entry.cAlternateFileName, ".." ))
724         pResult->filename[0] = pResult->filename[1] = '.';
725     else
726     {
727         char *p = strrchr( entry.cAlternateFileName, '.' );
728         if (p && p[1] && (p != entry.cAlternateFileName))
729         {
730             memcpy( pResult->filename, entry.cAlternateFileName,
731                     MIN( (p - entry.cAlternateFileName), 8 ) );
732             memcpy( pResult->filename + 8, p + 1, MIN( strlen(p), 3 ) );
733         }
734         else
735             memcpy( pResult->filename, entry.cAlternateFileName,
736                     MIN( strlen(entry.cAlternateFileName), 8 ) );
737     }
738     return 1;
739 }
740
741
742 static void DeleteFileFCB( CONTEXT *context )
743 {
744     fprintf( stderr, "DeleteFileFCB: not implemented yet\n" );
745 #if 0
746         BYTE *fcb = PTR_SEG_OFF_TO_LIN(DS_reg(context), DX_reg(context));
747         struct dosdirent *dp;
748         char temp[256], *ptr;
749         int drive = DOS_GET_DRIVE( *fcb );
750
751         DumpFCB( fcb );
752
753         temp[0] = '\\';
754         strcpy(temp+1, DRIVE_GetDosCwd(drive));
755         strcat(temp, "\\");
756         strncat(temp, fcb + 1, 8);
757         ChopOffWhiteSpace(temp);
758         strncat(temp, fcb + 9, 3);
759         ChopOffWhiteSpace(temp);
760
761         if ((dp = DOS_opendir(temp)) == NULL) {
762                 Error(InvalidDrive, EC_MediaError , EL_Disk);
763                 AX_reg(context) = 0xff;
764                 return;
765         }
766
767         temp[0] = '\\';
768         strcpy(temp+1, DRIVE_GetDosCwd(drive) );
769         strcat(temp, "\\");
770         
771         ptr = temp + strlen(temp);
772         
773         while (DOS_readdir(dp) != NULL)
774         {
775                 strcpy(ptr, dp->filename);
776                 dprintf_int(stddeb, "int21: delete file %s\n", temp);
777                 /* unlink(DOS_GetUnixFileName(temp)); */
778         }
779         DOS_closedir(dp);
780         AX_reg(context) = 0;
781 #endif
782 }
783
784 static void RenameFileFCB( CONTEXT *context )
785 {
786     fprintf( stderr, "RenameFileFCB: not implemented yet\n" );
787 #if 0
788         BYTE *fcb = PTR_SEG_OFF_TO_LIN(DS_reg(context), DX_reg(context));
789         struct dosdirent *dp;
790         char temp[256], oldname[256], newname[256], *oldnameptr, *newnameptr;
791         int drive = DOS_GET_DRIVE( *fcb );
792
793         DumpFCB( fcb );
794
795         temp[0] = '\\';
796         strcpy(temp+1, DRIVE_GetDosCwd(drive) );
797         strcat(temp, "\\");
798         strncat(temp, fcb + 1, 8);
799         ChopOffWhiteSpace(temp);
800         strncat(temp, fcb + 9, 3);
801         ChopOffWhiteSpace(temp);
802
803         if ((dp = DOS_opendir(temp)) == NULL) {
804                 Error(InvalidDrive, EC_MediaError , EL_Disk);
805                 AX_reg(context) = 0xff;
806                 return;
807         }
808
809         oldname[0] = '\\';
810         strcpy(oldname+1, DRIVE_GetDosCwd(drive) );
811         strcat(oldname, "\\");
812         strcpy( newname, oldname );
813         oldnameptr = oldname + strlen(oldname);
814         newnameptr = newname + strlen(newname);
815         
816         while (DOS_readdir(dp) != NULL)
817         {
818                 strcpy(oldnameptr, dp->filename);
819                 strcpy(newnameptr, fcb + 1);
820                 dprintf_int(stddeb, "int21: renamefile %s -> %s\n",
821                         oldname, newname);
822         }
823         DOS_closedir(dp);
824         AX_reg(context) = 0;
825 #endif
826 }
827
828
829
830 static void fLock( CONTEXT * context )
831 {
832 #if 0
833     struct flock f;
834     int result,retries=sharing_retries;
835
836     f.l_start = MAKELONG(DX_reg(context),CX_reg(context));
837     f.l_len   = MAKELONG(DI_reg(context),SI_reg(context));
838     f.l_whence = 0;
839     f.l_pid = 0;
840
841     switch ( AX_reg(context) & 0xff )
842     {
843         case 0x00: /* LOCK */
844           f.l_type = F_WRLCK;
845           break;
846
847         case 0x01: /* UNLOCK */
848           f.l_type = F_UNLCK;
849           break;
850
851         default:
852           AX_reg(context) = 0x0001;
853           SET_CFLAG(context);
854           return;
855      }
856  
857      {
858           result = fcntl(BX_reg(context),F_SETLK,&f); 
859           if ( retries && (!result) )
860           {
861               int i;
862               for(i=0;i<32768*((int)sharing_pause);i++)
863                   result++;                          /* stop the optimizer */
864               for(i=0;i<32768*((int)sharing_pause);i++)
865                   result--;
866           }
867       }
868       while( (!result) && (!(retries--)) );
869
870       if(result)  
871       {
872           FILE_SetDosError();
873           AX_reg(context) = DOS_ExtendedError;
874           SET_CFLAG(context);
875           return;
876       }
877 #endif
878
879
880
881 extern void LOCAL_PrintHeap (WORD ds);
882
883 /***********************************************************************
884  *           DOS3Call  (KERNEL.102)
885  */
886 void DOS3Call( CONTEXT *context )
887 {
888     dprintf_int( stddeb, "int21: AX=%04x BX=%04x CX=%04x DX=%04x "
889                  "SI=%04x DI=%04x DS=%04x ES=%04x EFL=%08lx\n",
890                  AX_reg(context), BX_reg(context), CX_reg(context),
891                  DX_reg(context), SI_reg(context), DI_reg(context),
892                  (WORD)DS_reg(context), (WORD)ES_reg(context),
893                  EFL_reg(context) );
894
895     if (AH_reg(context) == 0x59)  /* Get extended error info */
896     {
897         AX_reg(context) = DOS_ExtendedError;
898         BH_reg(context) = DOS_ErrorClass;
899         BL_reg(context) = DOS_ErrorAction;
900         CH_reg(context) = DOS_ErrorLocus;
901         return;
902     }
903
904     DOS_ERROR( 0, 0, 0, 0 );
905     RESET_CFLAG(context);  /* Not sure if this is a good idea */
906
907     switch(AH_reg(context)) 
908     {
909     case 0x00: /* TERMINATE PROGRAM */
910         TASK_KillCurrentTask( 0 );
911         break;
912
913     case 0x01: /* READ CHARACTER FROM STANDARD INPUT, WITH ECHO */
914     case 0x02: /* WRITE CHARACTER TO STANDARD OUTPUT */
915     case 0x03: /* READ CHARACTER FROM STDAUX  */
916     case 0x04: /* WRITE CHARACTER TO STDAUX */
917     case 0x05: /* WRITE CHARACTER TO PRINTER */
918     case 0x06: /* DIRECT CONSOLE IN/OUTPUT */
919     case 0x07: /* DIRECT CHARACTER INPUT, WITHOUT ECHO */
920     case 0x08: /* CHARACTER INPUT WITHOUT ECHO */
921     case 0x09: /* WRITE STRING TO STANDARD OUTPUT */
922     case 0x0a: /* BUFFERED INPUT */
923     case 0x0b: /* GET STDIN STATUS */
924     case 0x0c: /* FLUSH BUFFER AND READ STANDARD INPUT */
925     case 0x0f: /* OPEN FILE USING FCB */
926     case 0x10: /* CLOSE FILE USING FCB */
927     case 0x14: /* SEQUENTIAL READ FROM FCB FILE */              
928     case 0x15: /* SEQUENTIAL WRITE TO FCB FILE */
929     case 0x16: /* CREATE OR TRUNCATE FILE USING FCB */
930     case 0x21: /* READ RANDOM RECORD FROM FCB FILE */
931     case 0x22: /* WRITE RANDOM RECORD TO FCB FILE */
932     case 0x23: /* GET FILE SIZE FOR FCB */
933     case 0x24: /* SET RANDOM RECORD NUMBER FOR FCB */
934     case 0x26: /* CREATE NEW PROGRAM SEGMENT PREFIX */
935     case 0x27: /* RANDOM BLOCK READ FROM FCB FILE */
936     case 0x28: /* RANDOM BLOCK WRITE TO FCB FILE */
937     case 0x29: /* PARSE FILENAME INTO FCB */
938     case 0x2e: /* SET VERIFY FLAG */
939     case 0x37: /* "SWITCHAR" - GET SWITCH CHARACTER
940                   "SWITCHAR" - SET SWITCH CHARACTER
941                   "AVAILDEV" - SPECIFY \DEV\ PREFIX USE */
942     case 0x54: /* GET VERIFY FLAG */
943         INT_BARF( context, 0x21 );
944         break;
945
946     case 0x18: /* NULL FUNCTIONS FOR CP/M COMPATIBILITY */
947     case 0x1d:
948     case 0x1e:
949     case 0x20:
950     case 0x6b: /* NULL FUNCTION */
951         AL_reg(context) = 0;
952         break;
953         
954     case 0x5c: /* "FLOCK" - RECORD LOCKING */
955         fLock(context);
956         break;
957
958     case 0x0d: /* DISK BUFFER FLUSH */
959         RESET_CFLAG(context); /* dos 6+ only */
960         break;
961
962     case 0x0e: /* SELECT DEFAULT DRIVE */
963         DRIVE_SetCurrentDrive( DL_reg(context) );
964         AL_reg(context) = MAX_DOS_DRIVES;
965         break;
966
967     case 0x11: /* FIND FIRST MATCHING FILE USING FCB */
968         if (!INT21_FindFirstFCB(context))
969         {
970             AL_reg(context) = 0xff;
971             break;
972         }
973         /* else fall through */
974
975     case 0x12: /* FIND NEXT MATCHING FILE USING FCB */
976         AL_reg(context) = INT21_FindNextFCB(context) ? 0x00 : 0xff;
977         break;
978
979     case 0x13: /* DELETE FILE USING FCB */
980         DeleteFileFCB(context);
981         break;
982             
983     case 0x17: /* RENAME FILE USING FCB */
984         RenameFileFCB(context);
985         break;
986
987     case 0x19: /* GET CURRENT DEFAULT DRIVE */
988         AL_reg(context) = DRIVE_GetCurrentDrive();
989         break;
990
991     case 0x1a: /* SET DISK TRANSFER AREA ADDRESS */
992         {
993             TDB *pTask = (TDB *)GlobalLock16( GetCurrentTask() );
994             pTask->dta = PTR_SEG_OFF_TO_SEGPTR(DS_reg(context),DX_reg(context));
995             dprintf_int(stddeb, "int21: Set DTA: %08lx\n", pTask->dta);
996         }
997         break;
998
999     case 0x1b: /* GET ALLOCATION INFORMATION FOR DEFAULT DRIVE */
1000         DL_reg(context) = 0;
1001         if (!INT21_GetDriveAllocInfo(context)) AX_reg(context) = 0xffff;
1002         break;
1003         
1004     case 0x1c: /* GET ALLOCATION INFORMATION FOR SPECIFIC DRIVE */
1005         if (!INT21_GetDriveAllocInfo(context)) AX_reg(context) = 0xffff;
1006         break;
1007
1008     case 0x1f: /* GET DRIVE PARAMETER BLOCK FOR DEFAULT DRIVE */
1009         GetDrivePB(context, DRIVE_GetCurrentDrive());
1010         break;
1011                 
1012     case 0x25: /* SET INTERRUPT VECTOR */
1013         INT_SetHandler( AL_reg(context),
1014                         (FARPROC16)PTR_SEG_OFF_TO_SEGPTR( DS_reg(context),
1015                                                           DX_reg(context)));
1016         break;
1017
1018     case 0x2a: /* GET SYSTEM DATE */
1019         INT21_GetSystemDate(context);
1020         break;
1021
1022     case 0x2b: /* SET SYSTEM DATE */
1023         fprintf( stdnimp, "SetSystemDate(%02d/%02d/%04d): not allowed\n",
1024                  DL_reg(context), DH_reg(context), CX_reg(context) );
1025         AL_reg(context) = 0;  /* Let's pretend we succeeded */
1026         break;
1027
1028     case 0x2c: /* GET SYSTEM TIME */
1029         INT21_GetSystemTime(context);
1030         break;
1031
1032     case 0x2d: /* SET SYSTEM TIME */
1033         fprintf( stdnimp, "SetSystemTime(%02d:%02d:%02d.%02d): not allowed\n",
1034                  CH_reg(context), CL_reg(context),
1035                  DH_reg(context), DL_reg(context) );
1036         AL_reg(context) = 0;  /* Let's pretend we succeeded */
1037         break;
1038
1039     case 0x2f: /* GET DISK TRANSFER AREA ADDRESS */
1040         {
1041             TDB *pTask = (TDB *)GlobalLock16( GetCurrentTask() );
1042             ES_reg(context) = SELECTOROF( pTask->dta );
1043             BX_reg(context) = OFFSETOF( pTask->dta );
1044         }
1045         break;
1046             
1047     case 0x30: /* GET DOS VERSION */
1048         AX_reg(context) = DOSVERSION;
1049         BX_reg(context) = 0x0012;     /* 0x123456 is Wine's serial # */
1050         CX_reg(context) = 0x3456;
1051         break;
1052
1053     case 0x31: /* TERMINATE AND STAY RESIDENT */
1054         INT_BARF( context, 0x21 );
1055         break;
1056
1057     case 0x32: /* GET DOS DRIVE PARAMETER BLOCK FOR SPECIFIC DRIVE */
1058         GetDrivePB(context, DOS_GET_DRIVE( DL_reg(context) ) );
1059         break;
1060
1061     case 0x33: /* MULTIPLEXED */
1062         switch (AL_reg(context))
1063         {
1064               case 0x00: /* GET CURRENT EXTENDED BREAK STATE */
1065                 DL_reg(context) = 0;
1066                 break;
1067
1068               case 0x01: /* SET EXTENDED BREAK STATE */
1069                 break;          
1070                 
1071               case 0x02: /* GET AND SET EXTENDED CONTROL-BREAK CHECKING STATE*/
1072                 DL_reg(context) = 0;
1073                 break;
1074
1075               case 0x05: /* GET BOOT DRIVE */
1076                 DL_reg(context) = 2;
1077                 /* c: is Wine's bootdrive */
1078                 break;
1079                                 
1080               case 0x06: /* GET TRUE VERSION NUMBER */
1081                 BX_reg(context) = DOSVERSION;
1082                 DX_reg(context) = 0x00;
1083                 break;
1084
1085               default:
1086                 INT_BARF( context, 0x21 );
1087                 break;                  
1088         }
1089         break;  
1090             
1091     case 0x34: /* GET ADDRESS OF INDOS FLAG */
1092         if (!heap) INT21_CreateHeap();
1093         ES_reg(context) = DosHeapHandle;
1094         BX_reg(context) = (int)&heap->InDosFlag - (int)heap;
1095         break;
1096
1097     case 0x35: /* GET INTERRUPT VECTOR */
1098         {
1099             FARPROC16 addr = INT_GetHandler( AL_reg(context) );
1100             ES_reg(context) = SELECTOROF(addr);
1101             BX_reg(context) = OFFSETOF(addr);
1102         }
1103         break;
1104
1105     case 0x36: /* GET FREE DISK SPACE */
1106         if (!INT21_GetFreeDiskSpace(context)) AX_reg(context) = 0xffff;
1107         break;
1108
1109     case 0x38: /* GET COUNTRY-SPECIFIC INFORMATION */
1110         AX_reg(context) = 0x02; /* no country support available */
1111         SET_CFLAG(context);
1112         break;
1113
1114     case 0x39: /* "MKDIR" - CREATE SUBDIRECTORY */
1115         if (!CreateDirectory16( PTR_SEG_OFF_TO_LIN( DS_reg(context),
1116                                                     DX_reg(context) ), NULL))
1117         {
1118             AX_reg(context) = DOS_ExtendedError;
1119             SET_CFLAG(context);
1120         }
1121         break;
1122         
1123     case 0x3a: /* "RMDIR" - REMOVE SUBDIRECTORY */
1124         if (!RemoveDirectory16( PTR_SEG_OFF_TO_LIN( DS_reg(context),
1125                                                     DX_reg(context) )))
1126         {
1127             AX_reg(context) = DOS_ExtendedError;
1128             SET_CFLAG(context);
1129         }
1130         break;
1131
1132     case 0x3b: /* "CHDIR" - SET CURRENT DIRECTORY */
1133         INT21_ChangeDir(context);
1134         break;
1135         
1136     case 0x3c: /* "CREAT" - CREATE OR TRUNCATE FILE */
1137         AX_reg(context) = _lcreat16( PTR_SEG_OFF_TO_LIN( DS_reg(context),
1138                                     DX_reg(context) ), CX_reg(context) );
1139         if (AX_reg(context) == (WORD)HFILE_ERROR16)
1140         {
1141             AX_reg(context) = DOS_ExtendedError;
1142             SET_CFLAG(context);
1143         }
1144         break;
1145
1146     case 0x3d: /* "OPEN" - OPEN EXISTING FILE */
1147         OpenExistingFile(context);
1148         break;
1149
1150     case 0x3e: /* "CLOSE" - CLOSE FILE */
1151         if ((AX_reg(context) = _lclose16( BX_reg(context) )) != 0)
1152         {
1153             AX_reg(context) = DOS_ExtendedError;
1154             SET_CFLAG(context);
1155         }
1156         break;
1157
1158     case 0x3f: /* "READ" - READ FROM FILE OR DEVICE */
1159         {
1160             LONG result = WIN16_hread( BX_reg(context),
1161                                        PTR_SEG_OFF_TO_SEGPTR( DS_reg(context),
1162                                                               DX_reg(context) ),
1163                                        CX_reg(context) );
1164             if (result == -1)
1165             {
1166                 AX_reg(context) = DOS_ExtendedError;
1167                 SET_CFLAG(context);
1168             }
1169             else AX_reg(context) = (WORD)result;
1170         }
1171         break;
1172
1173     case 0x40: /* "WRITE" - WRITE TO FILE OR DEVICE */
1174         {
1175             LONG result = _hwrite16( BX_reg(context),
1176                                      PTR_SEG_OFF_TO_LIN( DS_reg(context),
1177                                                          DX_reg(context) ),
1178                                      CX_reg(context) );
1179             if (result == -1)
1180             {
1181                 AX_reg(context) = DOS_ExtendedError;
1182                 SET_CFLAG(context);
1183             }
1184             else AX_reg(context) = (WORD)result;
1185         }
1186         break;
1187
1188     case 0x41: /* "UNLINK" - DELETE FILE */
1189         if (!DeleteFile32A( PTR_SEG_OFF_TO_LIN( DS_reg(context),
1190                                                 DX_reg(context) )))
1191         {
1192             AX_reg(context) = DOS_ExtendedError;
1193             SET_CFLAG(context);
1194         }               
1195         break;
1196
1197     case 0x42: /* "LSEEK" - SET CURRENT FILE POSITION */
1198         {
1199             LONG status = _llseek16( BX_reg(context),
1200                                      MAKELONG(DX_reg(context),CX_reg(context)),
1201                                      AL_reg(context) );
1202             if (status == -1)
1203             {
1204                 AX_reg(context) = DOS_ExtendedError;
1205                 SET_CFLAG(context);
1206                 break;
1207             }
1208             AX_reg(context) = LOWORD(status);
1209             DX_reg(context) = HIWORD(status);
1210         }
1211         break;
1212
1213     case 0x43: /* FILE ATTRIBUTES */
1214         switch (AL_reg(context))
1215         {
1216         case 0x00:
1217             AX_reg(context) = (WORD)GetFileAttributes32A(
1218                                           PTR_SEG_OFF_TO_LIN(DS_reg(context),
1219                                                              DX_reg(context)));
1220             if (AX_reg(context) == 0xffff)
1221             {
1222                 AX_reg(context) = DOS_ExtendedError;
1223                 SET_CFLAG(context);
1224             }
1225             else CX_reg(context) = AX_reg(context);
1226             break;
1227         case 0x01:
1228             if (!SetFileAttributes32A( PTR_SEG_OFF_TO_LIN(DS_reg(context),
1229                                                           DX_reg(context)),
1230                                        CX_reg(context) ))
1231             {
1232                 AX_reg(context) = DOS_ExtendedError;
1233                 SET_CFLAG(context);
1234             }
1235             break;
1236         }
1237         break;
1238         
1239     case 0x44: /* IOCTL */
1240         switch (AL_reg(context))
1241         {
1242         case 0x00:
1243             ioctlGetDeviceInfo(context);
1244             break;
1245
1246         case 0x01:
1247             break;
1248         case 0x05:{     /* IOCTL - WRITE TO BLOCK DEVICE CONTROL CHANNEL */
1249             BYTE *dataptr = PTR_SEG_OFF_TO_LIN(DS_reg(context),DX_reg(context));
1250             int i;
1251             int drive = DOS_GET_DRIVE(BL_reg(context));
1252
1253             fprintf(stdnimp,"int21: program tried to write to block device control channel of drive %d:\n",drive);
1254             for (i=0;i<CX_reg(context);i++)
1255                 fprintf(stdnimp,"%02x ",dataptr[i]);
1256             fprintf(stdnimp,"\n");
1257             AX_reg(context)=CX_reg(context);
1258             break;
1259         }
1260         case 0x08:   /* Check if drive is removable. */
1261             switch(GetDriveType16( DOS_GET_DRIVE( BL_reg(context) )))
1262             {
1263             case DRIVE_CANNOTDETERMINE:
1264                 DOS_ERROR( ER_InvalidDrive, EC_NotFound, SA_Abort, EL_Disk );
1265                 AX_reg(context) = ER_InvalidDrive;
1266                 SET_CFLAG(context);
1267                 break;
1268             case DRIVE_REMOVABLE:
1269                 AX_reg(context) = 0;      /* removable */
1270                 break;
1271             default:
1272                 AX_reg(context) = 1;   /* not removable */
1273                 break;
1274             }
1275             break;
1276
1277         case 0x09:   /* CHECK IF BLOCK DEVICE REMOTE */
1278             switch(GetDriveType16( DOS_GET_DRIVE( BL_reg(context) )))
1279             {
1280             case DRIVE_CANNOTDETERMINE:
1281                 DOS_ERROR( ER_InvalidDrive, EC_NotFound, SA_Abort, EL_Disk );
1282                 AX_reg(context) = ER_InvalidDrive;
1283                 SET_CFLAG(context);
1284                 break;
1285             case DRIVE_REMOTE:
1286                 DX_reg(context) = (1<<9) | (1<<12);  /* remote */
1287                 break;
1288             default:
1289                 DX_reg(context) = 0;  /* FIXME: use driver attr here */
1290                 break;
1291             }
1292             break;
1293
1294         case 0x0a: /* check if handle (BX) is remote */
1295             /* returns DX, bit 15 set if remote, bit 14 set if date/time
1296              * not set on close
1297              */
1298             DX_reg(context) = 0;
1299             break;
1300
1301         case 0x0b:   /* SET SHARING RETRY COUNT */
1302             if (!CX_reg(context))
1303             { 
1304                 AX_reg(context) = 1;
1305                 SET_CFLAG(context);
1306                 break;
1307             }
1308             sharing_pause = CX_reg(context);
1309             if (!DX_reg(context))
1310                 sharing_retries = DX_reg(context);
1311             RESET_CFLAG(context);
1312             break;
1313
1314         case 0x0d:
1315             ioctlGenericBlkDevReq(context);
1316             break;
1317         case 0x0e: /* get logical drive mapping */
1318             AL_reg(context) = 0; /* drive has no mapping */
1319             break;
1320
1321         case 0x0F:   /* Set logical drive mapping */
1322             /* FIXME: Not implemented at the moment, always returns error
1323              */
1324             INT_BARF( context, 0x21 );
1325             AX_reg(context) = 0x0001; /* invalid function */
1326             SET_CFLAG(context);
1327             break;
1328                 
1329         default:
1330             INT_BARF( context, 0x21 );
1331             break;
1332         }
1333         break;
1334
1335     case 0x45: /* "DUP" - DUPLICATE FILE HANDLE */
1336         if ((AX_reg(context) = FILE_Dup(BX_reg(context))) == (WORD)HFILE_ERROR16)
1337         {
1338             AX_reg(context) = DOS_ExtendedError;
1339             SET_CFLAG(context);
1340         }
1341         break;
1342
1343     case 0x46: /* "DUP2", "FORCEDUP" - FORCE DUPLICATE FILE HANDLE */
1344         if (FILE_Dup2( BX_reg(context), CX_reg(context) ) == HFILE_ERROR32)
1345         {
1346             AX_reg(context) = DOS_ExtendedError;
1347             SET_CFLAG(context);
1348         }
1349         break;
1350
1351     case 0x47: /* "CWD" - GET CURRENT DIRECTORY */
1352         if (!INT21_GetCurrentDirectory(context))
1353         {
1354             AX_reg(context) = DOS_ExtendedError;
1355             SET_CFLAG(context);
1356         }
1357         else AX_reg(context) = 0x0100; 
1358         /* intlist: many Microsoft products for Windows rely on this */
1359         break;
1360         
1361     case 0x48: /* ALLOCATE MEMORY */
1362     case 0x49: /* FREE MEMORY */
1363     case 0x4a: /* RESIZE MEMORY BLOCK */
1364         INT_BARF( context, 0x21 );
1365         break;
1366         
1367     case 0x4b: /* "EXEC" - LOAD AND/OR EXECUTE PROGRAM */
1368         AX_reg(context) = WinExec16( PTR_SEG_OFF_TO_LIN( DS_reg(context),
1369                                                          DX_reg(context) ),
1370                                      SW_NORMAL );
1371         if (AX_reg(context) < 32) SET_CFLAG(context);
1372         break;          
1373         
1374     case 0x4c: /* "EXIT" - TERMINATE WITH RETURN CODE */
1375         TASK_KillCurrentTask( AL_reg(context) );
1376         break;
1377
1378     case 0x4d: /* GET RETURN CODE */
1379         AX_reg(context) = 0; /* normal exit */
1380         break;
1381
1382     case 0x4e: /* "FINDFIRST" - FIND FIRST MATCHING FILE */
1383         if (!INT21_FindFirst(context)) break;
1384         /* fall through */
1385
1386     case 0x4f: /* "FINDNEXT" - FIND NEXT MATCHING FILE */
1387         if (!INT21_FindNext(context))
1388         {
1389             DOS_ERROR( ER_NoMoreFiles, EC_MediaError, SA_Abort, EL_Disk );
1390             AX_reg(context) = ER_NoMoreFiles;
1391             SET_CFLAG(context);
1392         }
1393         break;
1394
1395     case 0x51: /* GET PSP ADDRESS */
1396     case 0x62: /* GET PSP ADDRESS */
1397         /* FIXME: should we return the original DOS PSP upon */
1398         /*        Windows startup ? */
1399         BX_reg(context) = GetCurrentPDB();
1400         break;
1401
1402     case 0x52: /* "SYSVARS" - GET LIST OF LISTS */
1403         ES_reg(context) = 0x0;
1404         BX_reg(context) = 0x0;
1405         INT_BARF( context, 0x21 );
1406         break;
1407
1408     case 0x56: /* "RENAME" - RENAME FILE */
1409         if (!MoveFile32A( PTR_SEG_OFF_TO_LIN(DS_reg(context),DX_reg(context)),
1410                           PTR_SEG_OFF_TO_LIN(ES_reg(context),DI_reg(context))))
1411         {
1412             AX_reg(context) = DOS_ExtendedError;
1413             SET_CFLAG(context);
1414         }
1415         break;
1416
1417     case 0x57: /* FILE DATE AND TIME */
1418         switch (AL_reg(context))
1419         {
1420         case 0x00:  /* Get */
1421             {
1422                 FILETIME filetime;
1423                 if (!GetFileTime( BX_reg(context), NULL, NULL, &filetime ))
1424                 {
1425                     AX_reg(context) = DOS_ExtendedError;
1426                     SET_CFLAG(context);
1427                 }
1428                 else FileTimeToDosDateTime( &filetime, &DX_reg(context),
1429                                             &CX_reg(context) );
1430             }
1431             break;
1432
1433         case 0x01:  /* Set */
1434             {
1435                 FILETIME filetime;
1436                 DosDateTimeToFileTime( DX_reg(context), CX_reg(context),
1437                                        &filetime );
1438                 if (!SetFileTime( BX_reg(context), NULL, NULL, &filetime ))
1439                 {
1440                     AX_reg(context) = DOS_ExtendedError;
1441                     SET_CFLAG(context);
1442                 }
1443             }
1444             break;
1445         }
1446         break;
1447
1448     case 0x58: /* GET OR SET MEMORY/UMB ALLOCATION STRATEGY */
1449         switch (AL_reg(context))
1450         {
1451         case 0x00:
1452             AX_reg(context) = 1;
1453             break;
1454         case 0x02:
1455             AX_reg(context) = 0;
1456             break;
1457         case 0x01:
1458         case 0x03:
1459             break;
1460         }
1461         RESET_CFLAG(context);
1462         break;
1463
1464     case 0x5a: /* CREATE TEMPORARY FILE */
1465         if (!INT21_CreateTempFile(context))
1466         {
1467             AX_reg(context) = DOS_ExtendedError;
1468             SET_CFLAG(context);
1469         }
1470         break;
1471
1472     case 0x5b: /* CREATE NEW FILE */
1473         if ((AX_reg(context) = _lcreat_uniq( PTR_SEG_OFF_TO_LIN(DS_reg(context),DX_reg(context)), 0 )) == (WORD)HFILE_ERROR16)
1474         {
1475             AX_reg(context) = DOS_ExtendedError;
1476             SET_CFLAG(context);
1477         }
1478         break;
1479
1480     case 0x5d: /* NETWORK */
1481     case 0x5e:
1482         /* network software not installed */
1483         DOS_ERROR( ER_NoNetwork, EC_NotFound, SA_Abort, EL_Network );
1484         AX_reg(context) = DOS_ExtendedError;
1485         SET_CFLAG(context);
1486         break;
1487
1488     case 0x5f: /* NETWORK */
1489         switch (AL_reg(context))
1490         {
1491         case 0x07: /* ENABLE DRIVE */
1492             if (!DRIVE_Enable( DL_reg(context) ))
1493             {
1494                 DOS_ERROR( ER_InvalidDrive, EC_MediaError, SA_Abort, EL_Disk );
1495                 AX_reg(context) = DOS_ExtendedError;
1496                 SET_CFLAG(context);
1497             }
1498             break;
1499
1500         case 0x08: /* DISABLE DRIVE */
1501             if (!DRIVE_Disable( DL_reg(context) ))
1502             {
1503                 DOS_ERROR( ER_InvalidDrive, EC_MediaError, SA_Abort, EL_Disk );
1504                 AX_reg(context) = DOS_ExtendedError;
1505                 SET_CFLAG(context);
1506             } 
1507             break;
1508
1509         default:
1510             /* network software not installed */
1511             DOS_ERROR( ER_NoNetwork, EC_NotFound, SA_Abort, EL_Network );
1512             AX_reg(context) = DOS_ExtendedError;
1513             SET_CFLAG(context);
1514             break;
1515         }
1516         break;
1517
1518     case 0x60: /* "TRUENAME" - CANONICALIZE FILENAME OR PATH */
1519         {
1520             if (!GetFullPathName32A( PTR_SEG_OFF_TO_LIN(DS_reg(context),
1521                                                         SI_reg(context)), 128,
1522                                      PTR_SEG_OFF_TO_LIN(ES_reg(context),
1523                                                         DI_reg(context)),NULL))
1524             {
1525                 AX_reg(context) = DOS_ExtendedError;
1526                 SET_CFLAG(context);
1527             }
1528             else AX_reg(context) = 0;
1529         }
1530         break;
1531
1532     case 0x61: /* UNUSED */
1533     case 0x63: /* UNUSED */
1534     case 0x64: /* OS/2 DOS BOX */
1535         INT_BARF( context, 0x21 );
1536         SET_CFLAG(context);
1537         break;
1538     case 0x65:{/* GET EXTENDED COUNTRY INFORMATION */
1539         extern WORD WINE_LanguageId;
1540         BYTE    *dataptr=PTR_SEG_OFF_TO_LIN(ES_reg(context),DI_reg(context));;
1541         switch (AL_reg(context)) {
1542         case 0x01:
1543             dataptr[0] = 0x1;
1544             *(WORD*)(dataptr+1) = 41;
1545             *(WORD*)(dataptr+3) = WINE_LanguageId;
1546             *(WORD*)(dataptr+5) = CodePage;
1547             break;
1548         case 0x06:
1549             dataptr[0] = 0x06;
1550             *(DWORD*)(dataptr+1) = MAKELONG(DOSMEM_CollateTable & 0xFFFF,DOSMEM_AllocSelector(DOSMEM_CollateTable>>16));
1551             CX_reg(context)         = 258;/*FIXME: size of table?*/
1552             break;
1553         default:
1554             INT_BARF( context, 0x21 );
1555             SET_CFLAG(context);
1556             break;
1557         }
1558         break;
1559     }
1560     case 0x66: /* GLOBAL CODE PAGE TABLE */
1561         switch (AL_reg(context))
1562         {
1563         case 0x01:
1564             DX_reg(context) = BX_reg(context) = CodePage;
1565             RESET_CFLAG(context);
1566             break;                      
1567         case 0x02: 
1568             CodePage = BX_reg(context);
1569             RESET_CFLAG(context);
1570             break;
1571         }
1572         break;
1573
1574     case 0x67: /* SET HANDLE COUNT */
1575         SetHandleCount16( BX_reg(context) );
1576         if (DOS_ExtendedError)
1577         {
1578             AX_reg(context) = DOS_ExtendedError;
1579             SET_CFLAG(context);
1580         }
1581         break;
1582
1583     case 0x68: /* "FFLUSH" - COMMIT FILE */
1584     case 0x6a: /* COMMIT FILE */
1585         if (!FlushFileBuffers( BX_reg(context) ))
1586         {
1587             AX_reg(context) = DOS_ExtendedError;
1588             SET_CFLAG(context);
1589         }
1590         break;          
1591         
1592     case 0x69: /* DISK SERIAL NUMBER */
1593         switch (AL_reg(context))
1594         {
1595         case 0x00:
1596             if (!INT21_GetDiskSerialNumber(context))
1597             {
1598                 AX_reg(context) = DOS_ExtendedError;
1599                 SET_CFLAG(context);
1600             }
1601             else AX_reg(context) = 0;
1602             break;
1603         case 0x01:
1604             if (!INT21_SetDiskSerialNumber(context))
1605             {
1606                 AX_reg(context) = DOS_ExtendedError;
1607                 SET_CFLAG(context);
1608             }
1609             else AX_reg(context) = 1;
1610             break;
1611         }
1612         break;
1613     
1614     case 0x6C: /* Extended Open/Create*/
1615         ExtendedOpenCreateFile(context);
1616         break;
1617         
1618     case 0x71: /* MS-DOS 7 (Windows95) - LONG FILENAME FUNCTIONS */
1619         switch(AL_reg(context))
1620         {
1621         case 0x39:  /* Create directory */
1622             if (!CreateDirectory32A( PTR_SEG_OFF_TO_LIN( DS_reg(context),
1623                                                      DX_reg(context) ), NULL))
1624             {
1625                 AX_reg(context) = DOS_ExtendedError;
1626                 SET_CFLAG(context);
1627             }
1628             break;
1629         case 0x3a:  /* Remove directory */
1630             if (!RemoveDirectory32A( PTR_SEG_OFF_TO_LIN( DS_reg(context),
1631                                                          DX_reg(context) )))
1632             {
1633                 AX_reg(context) = DOS_ExtendedError;
1634                 SET_CFLAG(context);
1635             }
1636             break;
1637         case 0x4e:  /* Find first file */
1638             /* FIXME: use attributes in CX */
1639             if (!(AX_reg(context) = FindFirstFile16(
1640                    PTR_SEG_OFF_TO_LIN(DS_reg(context),DX_reg(context)),
1641                    (WIN32_FIND_DATA32A *)PTR_SEG_OFF_TO_LIN(ES_reg(context),
1642                                                             DI_reg(context)))))
1643             {
1644                 AX_reg(context) = DOS_ExtendedError;
1645                 SET_CFLAG(context);
1646             }
1647             break;
1648         case 0x4f:  /* Find next file */
1649             if (!FindNextFile16( BX_reg(context),
1650                     (WIN32_FIND_DATA32A *)PTR_SEG_OFF_TO_LIN(ES_reg(context),
1651                                                              DI_reg(context))))
1652             {
1653                 AX_reg(context) = DOS_ExtendedError;
1654                 SET_CFLAG(context);
1655             }
1656             break;
1657         case 0xa1:  /* Find close */
1658             if (!FindClose16( BX_reg(context) ))
1659             {
1660                 AX_reg(context) = DOS_ExtendedError;
1661                 SET_CFLAG(context);
1662             }
1663             break;
1664         case 0xa0:
1665             break;
1666         case 0x3b:  /* Change directory */
1667         case 0x41:  /* Delete file */
1668         case 0x43:  /* Get/Set file attributes */
1669         case 0x47:  /* Get current directory */
1670         case 0x56:  /* Move (rename) file */
1671         case 0x6c:  /* Create/Open file */
1672         default:
1673             fprintf( stderr, "Unimplemented int21 long file name function:\n");
1674             INT_BARF( context, 0x21 );
1675             SET_CFLAG(context);
1676             AL_reg(context) = 0;
1677             break;
1678         }
1679         break;
1680
1681     case 0x70: /* MS-DOS 7 (Windows95) - ??? (country-specific?)*/
1682     case 0x72: /* MS-DOS 7 (Windows95) - ??? */
1683     case 0x73: /* MS-DOS 7 (Windows95) - DRIVE LOCKING ??? */
1684         dprintf_int(stddeb,"int21: windows95 function AX %04x\n",
1685                     AX_reg(context));
1686         dprintf_int(stddeb, "        returning unimplemented\n");
1687         SET_CFLAG(context);
1688         AL_reg(context) = 0;
1689         break;
1690
1691     case 0xdc: /* CONNECTION SERVICES - GET CONNECTION NUMBER */
1692     case 0xea: /* NOVELL NETWARE - RETURN SHELL VERSION */
1693         break;
1694
1695     default:
1696         INT_BARF( context, 0x21 );
1697         break;
1698     }
1699     dprintf_int( stddeb, "ret21: AX=%04x BX=%04x CX=%04x DX=%04x "
1700                  "SI=%04x DI=%04x DS=%04x ES=%04x EFL=%08lx\n",
1701                  AX_reg(context), BX_reg(context), CX_reg(context),
1702                  DX_reg(context), SI_reg(context), DI_reg(context),
1703                  (WORD)DS_reg(context), (WORD)ES_reg(context),
1704                  EFL_reg(context));
1705 }