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