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