Release 941227
[wine/multimedia.git] / miscemu / int21.c
blob5ab5c11e2133704bd097425da2f1cea05eb2c696
1 /*
2 * (c) 1993, 1994 Erik Bos
3 */
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 "regfunc.h"
20 #include "windows.h"
21 #include "heap.h"
22 #include "msdos.h"
23 #include "registers.h"
24 #include "options.h"
25 #include "prototypes.h"
26 #include "miscemu.h"
27 #include "stddebug.h"
28 /* #define DEBUG_INT */
29 #include "debug.h"
31 WORD ExtendedError, CodePage = 437;
32 BYTE ErrorClass, Action, ErrorLocus;
33 BYTE *dta;
35 struct DosHeap {
36 BYTE dta[256];
37 BYTE InDosFlag;
38 BYTE biosdate[8];
40 static struct DosHeap *heap;
42 WORD sharing_retries = 3; /* number of retries at sharing violation */
43 WORD sharing_pause = 1; /* pause between retries */
45 extern char TempDirectory[];
47 static int Error(int e, int class, int el)
49 ErrorClass = class;
50 Action = SA_Ask4Retry;
51 ErrorLocus = el;
52 ExtendedError = e;
54 return e;
57 void errno_to_doserr(void)
59 switch (errno) {
60 case EAGAIN:
61 Error (ShareViolation, EC_Temporary, EL_Unknown);
62 break;
63 case EBADF:
64 Error (InvalidHandle, EC_AppError, EL_Unknown);
65 break;
66 case ENOSPC:
67 Error (DiskFull, EC_MediaError, EL_Disk);
68 break;
69 case EACCES:
70 case EPERM:
71 case EROFS:
72 Error (WriteProtected, EC_AccessDenied, EL_Unknown);
73 break;
74 case EBUSY:
75 Error (LockViolation, EC_AccessDenied, EL_Unknown);
76 break;
77 case ENOENT:
78 Error (FileNotFound, EC_NotFound, EL_Unknown);
79 break;
80 case EISDIR:
81 Error (CanNotMakeDir, EC_AccessDenied, EL_Unknown);
82 break;
83 case ENFILE:
84 case EMFILE:
85 Error (NoMoreFiles, EC_MediaError, EL_Unknown);
86 break;
87 case EEXIST:
88 Error (FileExists, EC_Exists, EL_Disk);
89 break;
90 default:
91 fprintf(stderr, "int21: unknown errno %d!\n", errno);
92 Error (GeneralFailure, EC_SystemFailure, EL_Unknown);
93 break;
98 static void Barf(struct sigcontext_struct *context)
100 fprintf(stdnimp, "int21: unknown/not implemented parameters:\n");
101 fprintf(stdnimp, "int21: AX %04x, BX %04x, CX %04x, DX %04x, "
102 "SI %04x, DI %04x, DS %04x, ES %04x\n",
103 AX, BX, CX, DX, SI, DI, DS, ES);
107 void ChopOffWhiteSpace(char *string)
109 int length;
111 for (length = strlen(string) ; length ; length--)
112 if (string[length] == ' ')
113 string[length] = '\0';
116 static void CreateBPB(int drive, BYTE *data)
118 if (drive > 1) {
119 setword(data, 512);
120 data[2] = 2;
121 setword(&data[3], 0);
122 data[5] = 2;
123 setword(&data[6], 240);
124 setword(&data[8], 64000);
125 data[0x0a] = 0xf8;
126 setword(&data[0x0b], 40);
127 setword(&data[0x0d], 56);
128 setword(&data[0x0f], 2);
129 setword(&data[0x11], 0);
130 setword(&data[0x1f], 800);
131 data[0x21] = 5;
132 setword(&data[0x22], 1);
133 } else { /* 1.44mb */
134 setword(data, 512);
135 data[2] = 2;
136 setword(&data[3], 0);
137 data[5] = 2;
138 setword(&data[6], 240);
139 setword(&data[8], 2880);
140 data[0x0a] = 0xf8;
141 setword(&data[0x0b], 6);
142 setword(&data[0x0d], 18);
143 setword(&data[0x0f], 2);
144 setword(&data[0x11], 0);
145 setword(&data[0x1f], 80);
146 data[0x21] = 7;
147 setword(&data[0x22], 2);
151 static void GetFreeDiskSpace(struct sigcontext_struct *context)
153 int drive;
154 long size,available;
156 if (DL == 0)
157 drive = DOS_GetDefaultDrive();
158 else
159 drive = DL - 1;
161 if (!DOS_ValidDrive(drive)) {
162 Error(InvalidDrive, EC_MediaError , EL_Disk);
163 AX = 0xffff;
164 return;
167 if (!DOS_GetFreeSpace(drive, &size, &available)) {
168 Error(GeneralFailure, EC_MediaError , EL_Disk);
169 AX = 0xffff;
170 return;
173 AX = 4;
174 CX = 512;
176 BX = (available / (CX * AX));
177 DX = (size / (CX * AX));
178 Error (0,0,0);
181 static void GetDriveAllocInfo(struct sigcontext_struct *context)
183 long size, available;
184 BYTE mediaID;
186 if (!DOS_ValidDrive(DL)) {
187 AX = 4;
188 CX = 512;
189 DX = 0;
190 Error (InvalidDrive, EC_MediaError, EL_Disk);
191 return;
194 if (!DOS_GetFreeSpace(DL, &size, &available)) {
195 Error(GeneralFailure, EC_MediaError , EL_Disk);
196 AX = 0xffff;
197 return;
200 EAX = 4;
201 ECX = 512;
202 EDX = (size / (CX * AX));
204 mediaID = 0xf0;
206 DS = segment(mediaID);
207 BX = offset(mediaID);
208 Error (0,0,0);
211 static void GetDefDriveAllocInfo(struct sigcontext_struct *context)
213 DX = DOS_GetDefaultDrive();
214 GetDriveAllocInfo(context);
217 static void GetDrivePB(struct sigcontext_struct *context)
219 Error (InvalidDrive, EC_MediaError, EL_Disk);
220 AX = 0x00ff;
221 /* I'm sorry but I only got networked drives :-) */
224 static void ReadFile(struct sigcontext_struct *context)
226 char *ptr;
227 int size;
229 /* can't read from stdout / stderr */
230 if ((BX == 1) || (BX == 2)) {
231 Error (InvalidHandle, EL_Unknown, EC_Unknown);
232 AX = InvalidHandle;
233 SetCflag;
234 return;
237 ptr = SAFEMAKEPTR (DS,DX);
238 if (BX == 0) {
239 *ptr = EOF;
240 Error (0,0,0);
241 AX = 1;
242 ResetCflag;
243 return;
244 } else {
245 size = read(BX, ptr, CX);
246 if (size == -1) {
247 errno_to_doserr();
248 AL = ExtendedError;
249 SetCflag;
250 return;
252 Error (0,0,0);
253 AX = size;
254 ResetCflag;
258 static void WriteFile(struct sigcontext_struct *context)
260 char *ptr;
261 int x,size;
263 ptr = SAFEMAKEPTR (DS,DX);
265 if (BX == 0) {
266 Error (InvalidHandle, EC_Unknown, EL_Unknown);
267 EAX = InvalidHandle;
268 SetCflag;
269 return;
272 if (BX < 3) {
273 for (x = 0;x != CX;x++) {
274 dprintf_int(stddeb, "%c", *ptr++);
276 fflush(stddeb);
278 Error (0,0,0);
279 AL = CX;
280 ResetCflag;
281 } else {
282 size = write(BX, ptr , CX);
283 if (size == 0) {
284 Error (WriteFault, EC_Unknown, EL_Unknown);
285 AL = ExtendedError;
286 return;
289 if (size == -1) {
290 errno_to_doserr();
291 AL = ExtendedError;
292 SetCflag;
293 return;
295 Error (0,0,0);
296 AX = size;
297 ResetCflag;
301 static void SeekFile(struct sigcontext_struct *context)
303 off_t status, fileoffset;
305 switch (AL) {
306 case 1: fileoffset = SEEK_CUR;
307 break;
308 case 2: fileoffset = SEEK_END;
309 break;
310 default:
311 case 0: fileoffset = SEEK_SET;
312 break;
314 status = lseek(BX, (CX << 16) + DX, fileoffset);
315 if (status == -1) {
316 errno_to_doserr();
317 AL = ExtendedError; SetCflag;
318 return;
320 Error (0,0,0);
321 AX = (status & 0xffff);
322 DX = ((status >> 16) & 0xffff);
324 ResetCflag;
327 static void ioctlGetDeviceInfo(struct sigcontext_struct *context)
329 switch (BX) {
330 case 0:
331 case 1:
332 case 2:
333 DX = 0x80d3;
334 break;
336 default:
338 struct stat sbuf;
340 if (fstat(BX, &sbuf) < 0)
342 IntBarf(0x21, context);
343 DX = 0x50;
344 SetCflag;
345 return;
348 /* This isn't the right answer, but should be close enough. */
349 DX = 0x0943;
352 ResetCflag;
355 static void ioctlGenericBlkDevReq(struct sigcontext_struct *context)
357 BYTE *dataptr = SAFEMAKEPTR(DS, DX);
358 int drive;
360 if (BL == 0)
361 drive = DOS_GetDefaultDrive();
362 else
363 drive = BL - 1;
365 if (!DOS_ValidDrive(drive)) {
366 AX = 0x02;
367 SetCflag;
368 return;
371 if (CH != 0x08) {
372 IntBarf(0x21, context);
373 return;
375 switch (CL) {
376 case 0x60: /* get device parameters */
377 /* used by w4wgrp's winfile */
378 memset(dataptr, 0, 0x26);
379 dataptr[0] = 0x04;
380 dataptr[6] = 0; /* media type */
381 if (drive > 1)
383 dataptr[1] = 0x05; /* fixed disk */
384 setword(&dataptr[2], 0x01); /* non removable */
385 setword(&dataptr[4], 0x300); /* # of cylinders */
387 else
389 dataptr[1] = 0x07; /* block dev, floppy */
390 setword(&dataptr[2], 0x02); /* removable */
391 setword(&dataptr[4], 80); /* # of cylinders */
393 CreateBPB(drive, &dataptr[7]);
394 ResetCflag;
395 return;
396 default:
397 IntBarf(0x21, context);
401 static void GetSystemDate(struct sigcontext_struct *context)
403 struct tm *now;
404 time_t ltime;
406 ltime = time(NULL);
407 now = localtime(&ltime);
409 CX = now->tm_year + 1900;
410 DX = ((now->tm_mon + 1) << 8) | now->tm_mday;
411 AX = now->tm_wday;
414 static void GetSystemTime(struct sigcontext_struct *context)
416 struct tm *now;
417 struct timeval tv;
419 gettimeofday(&tv,NULL); /* Note use of gettimeofday(), instead of time() */
420 now = localtime(&tv.tv_sec);
422 CX = (now->tm_hour<<8) | now->tm_min;
423 DX = (now->tm_sec<<8) | tv.tv_usec/10000;
424 /* Note hundredths of seconds */
427 static void GetExtendedErrorInfo(struct sigcontext_struct *context)
429 AL = ExtendedError;
430 BX = (ErrorClass << 8) | Action;
431 CH = ErrorLocus << 8;
434 static void CreateFile(struct sigcontext_struct *context)
436 int handle;
438 if ((handle = open(DOS_GetUnixFileName( SAFEMAKEPTR(DS,DX)),
439 O_CREAT | O_TRUNC | O_RDWR )) == -1) {
440 errno_to_doserr();
441 AL = ExtendedError;
442 SetCflag;
443 return;
445 Error (0,0,0);
446 EAX = (EAX & 0xffff0000) | handle;
447 ResetCflag;
450 void OpenExistingFile(struct sigcontext_struct *context)
452 int handle;
453 int mode;
454 int lock;
456 switch (AX & 0x0007)
458 case 0:
459 mode = O_RDONLY;
460 break;
462 case 1:
463 mode = O_WRONLY;
464 break;
466 default:
467 mode = O_RDWR;
468 break;
471 if ((handle = open(DOS_GetUnixFileName(SAFEMAKEPTR(DS,DX)), mode)) == -1) {
472 errno_to_doserr();
473 AL = ExtendedError;
474 SetCflag;
475 return;
478 switch (AX & 0x0070)
480 case 0x00: /* compatability mode */
481 case 0x40: /* DENYNONE */
482 lock = -1;
483 break;
485 case 0x30: /* DENYREAD */
486 dprintf_int(stdnimp,
487 "OpenExistingFile (%s): DENYREAD changed to DENYALL\n",
488 SAFEMAKEPTR(DS,DX));
489 case 0x10: /* DENYALL */
490 lock = LOCK_EX;
491 break;
493 case 0x20: /* DENYWRITE */
494 lock = LOCK_SH;
495 break;
497 default:
498 lock = -1;
501 if (lock != -1)
504 int result,retries=sharing_retries;
506 result = flock(handle, lock | LOCK_NB);
507 if ( retries && (!result) )
509 int i;
510 for(i=0;i<32768*((int)sharing_pause);i++)
511 result++; /* stop the optimizer */
512 for(i=0;i<32768*((int)sharing_pause);i++)
513 result--;
516 while( (!result) && (!(retries--)) );
518 if(result)
520 errno_to_doserr();
521 EAX = (EAX & 0xffffff00) | ExtendedError;
522 close(handle);
523 SetCflag;
524 return;
529 Error (0,0,0);
530 EAX = (EAX & 0xffff0000) | handle;
531 ResetCflag;
534 static void CloseFile(struct sigcontext_struct *context)
536 if (close(BX) == -1) {
537 errno_to_doserr();
538 AL = ExtendedError;
539 SetCflag;
540 return;
542 Error (0,0,0);
543 AL = NoError;
544 ResetCflag;
547 static void RenameFile(struct sigcontext_struct *context)
549 char *newname, *oldname;
551 dprintf_int(stddeb,"int21: renaming %s to %s\n",
552 SAFEMAKEPTR(DS,DX), SAFEMAKEPTR(ES,DI) );
554 oldname = DOS_GetUnixFileName( SAFEMAKEPTR(DS,DX) );
555 newname = DOS_GetUnixFileName( SAFEMAKEPTR(ES,DI) );
557 rename( oldname, newname);
558 ResetCflag;
562 static void MakeDir(struct sigcontext_struct *context)
564 char *dirname;
566 dprintf_int(stddeb,"int21: makedir %s\n", SAFEMAKEPTR(DS,DX) );
568 if ((dirname = DOS_GetUnixFileName( SAFEMAKEPTR(DS,DX) ))== NULL) {
569 AL = CanNotMakeDir;
570 SetCflag;
571 return;
574 if (mkdir(dirname,0) == -1) {
575 AL = CanNotMakeDir;
576 SetCflag;
577 return;
579 ResetCflag;
582 static void ChangeDir(struct sigcontext_struct *context)
584 int drive;
585 char *dirname = SAFEMAKEPTR(DS,DX);
586 drive = DOS_GetDefaultDrive();
587 dprintf_int(stddeb,"int21: changedir %s\n", dirname);
588 if (dirname != NULL && dirname[1] == ':') {
589 drive = toupper(dirname[0]) - 'A';
590 dirname += 2;
592 if (!DOS_ChangeDir(drive, dirname))
594 SetCflag;
595 AX=0x03;
599 static void RemoveDir(struct sigcontext_struct *context)
601 char *dirname;
603 dprintf_int(stddeb,"int21: removedir %s\n", SAFEMAKEPTR(DS,DX) );
605 if ((dirname = DOS_GetUnixFileName( SAFEMAKEPTR(DS,DX) ))== NULL) {
606 AL = CanNotMakeDir;
607 SetCflag;
608 return;
612 if (strcmp(unixname,DosDrives[drive].CurrentDirectory)) {
613 AL = CanNotRemoveCwd;
614 SetCflag;
617 if (rmdir(dirname) == -1) {
618 AL = CanNotMakeDir;
619 SetCflag;
621 ResetCflag;
624 static void ExecProgram(struct sigcontext_struct *context)
626 execl("wine", DOS_GetUnixFileName( SAFEMAKEPTR(DS,DX)) );
629 static void FindNext(struct sigcontext_struct *context)
631 struct dosdirent *dp;
633 memcpy(&dp, dta+0x0d, sizeof(dp));
635 do {
636 if ((dp = DOS_readdir(dp)) == NULL) {
637 Error(NoMoreFiles, EC_MediaError , EL_Disk);
638 AL = NoMoreFiles;
639 SetCflag;
640 return;
642 } /* while (*(dta + 0x0c) != dp->attribute);*/
643 while ( ( dp->search_attribute & dp->attribute) != dp->attribute);
645 *(dta + 0x15) = dp->attribute;
646 setword(&dta[0x16], 0x1234); /* time */
647 setword(&dta[0x18], 0x1234); /* date */
648 setdword(&dta[0x1a], dp->filesize);
649 strncpy(dta + 0x1e, dp->filename, 13);
651 AL = 0;
652 ResetCflag;
653 return;
656 static void FindFirst(struct sigcontext_struct *context)
658 BYTE drive, *path = SAFEMAKEPTR(DS, DX);
659 struct dosdirent *dp;
661 if ((*path)&&(path[1] == ':')) {
662 drive = (islower(*path) ? toupper(*path) : *path) - 'A';
664 if (!DOS_ValidDrive(drive)) {
665 Error(InvalidDrive, EC_MediaError , EL_Disk);
666 AL = InvalidDrive;
667 SetCflag;
668 return;
670 } else
671 drive = DOS_GetDefaultDrive();
673 *dta = drive;
674 memset(dta + 1 , '?', 11);
675 *(dta + 0x0c) = ECX & (FA_LABEL | FA_DIREC);
677 if (ECX & FA_LABEL) {
678 /* return volume label */
680 if (DOS_GetVolumeLabel(drive) != NULL)
681 strncpy(dta + 0x1e, DOS_GetVolumeLabel(drive), 8);
683 AL = 0;
684 ResetCflag;
685 return;
688 if ((dp = DOS_opendir(path)) == NULL) {
689 Error(PathNotFound, EC_MediaError, EL_Disk);
690 AL = FileNotFound;
691 SetCflag;
692 return;
695 dp->search_attribute = ECX & (FA_LABEL | FA_DIREC);
696 memcpy(dta + 0x0d, &dp, sizeof(dp));
697 FindNext(context);
700 static void GetFileDateTime(struct sigcontext_struct *context)
702 char *filename;
703 struct stat filestat;
704 struct tm *now;
706 if ((filename = DOS_GetUnixFileName( SAFEMAKEPTR(DS,DX) ))== NULL) {
707 AL = FileNotFound;
708 SetCflag;
709 return;
711 stat(filename, &filestat);
713 now = localtime (&filestat.st_mtime);
715 CX = ((now->tm_hour * 0x2000) + (now->tm_min * 0x20) + now->tm_sec/2);
716 DX = ((now->tm_year * 0x200) + (now->tm_mon * 0x20) + now->tm_mday);
718 ResetCflag;
721 static void SetFileDateTime(struct sigcontext_struct *context)
723 char *filename;
724 struct utimbuf filetime;
726 filename = DOS_GetUnixFileName( SAFEMAKEPTR(DS,DX) );
728 filetime.actime = 0L;
729 filetime.modtime = filetime.actime;
731 utime(filename, &filetime);
732 ResetCflag;
735 static void CreateTempFile(struct sigcontext_struct *context)
737 char temp[256];
738 int handle;
740 sprintf(temp,"%s\\win%d.tmp",TempDirectory,(int) getpid());
742 dprintf_int(stddeb,"CreateTempFile %s\n",temp);
744 handle = open(DOS_GetUnixFileName(temp), O_CREAT | O_TRUNC | O_RDWR);
746 if (handle == -1) {
747 AL = WriteProtected;
748 SetCflag;
749 return;
752 strcpy(SAFEMAKEPTR(DS,DX), temp);
754 AX = handle;
755 ResetCflag;
758 static void CreateNewFile(struct sigcontext_struct *context)
760 int handle;
762 if ((handle = open(DOS_GetUnixFileName( SAFEMAKEPTR(DS,DX) ), O_CREAT | O_EXCL | O_RDWR)) == -1) {
763 AL = WriteProtected;
764 SetCflag;
765 return;
768 AX = handle;
769 ResetCflag;
772 static void GetCurrentDirectory(struct sigcontext_struct *context)
774 int drive;
776 if (DL == 0)
777 drive = DOS_GetDefaultDrive();
778 else
779 drive = DL - 1;
781 if (!DOS_ValidDrive(drive)) {
782 AL = InvalidDrive;
783 SetCflag;
784 return;
787 strcpy(SAFEMAKEPTR(DS,SI), DOS_GetCurrentDir(drive) );
788 ResetCflag;
791 static void GetDiskSerialNumber(struct sigcontext_struct *context)
793 int drive;
794 BYTE *dataptr = SAFEMAKEPTR(DS, DX);
795 DWORD serialnumber;
797 if (BL == 0)
798 drive = DOS_GetDefaultDrive();
799 else
800 drive = BL - 1;
802 if (!DOS_ValidDrive(drive)) {
803 AL =InvalidDrive;
804 SetCflag;
805 return;
808 DOS_GetSerialNumber(drive, &serialnumber);
810 setword(dataptr, 0);
811 setdword(&dataptr[2], serialnumber);
812 strncpy(dataptr + 6, DOS_GetVolumeLabel(drive), 8);
813 strncpy(dataptr + 0x11, "FAT16 ", 8);
815 AL = 0;
816 ResetCflag;
819 static void SetDiskSerialNumber(struct sigcontext_struct *context)
821 int drive;
822 BYTE *dataptr = SAFEMAKEPTR(DS, DX);
823 DWORD serialnumber;
825 if (BL == 0)
826 drive = DOS_GetDefaultDrive();
827 else
828 drive = BL - 1;
830 if (!DOS_ValidDrive(drive)) {
831 AL = InvalidDrive;
832 SetCflag;
833 return;
836 serialnumber = dataptr[1] + (dataptr[2] << 8) + (dataptr[3] << 16) +
837 (dataptr[4] << 24);
839 DOS_SetSerialNumber(drive, serialnumber);
840 AL = 1L;
841 ResetCflag;
844 static void DumpFCB(BYTE *fcb)
846 int x, y;
848 fcb -= 7;
850 for (y = 0; y !=2 ; y++) {
851 for (x = 0; x!=15;x++)
852 dprintf_int(stddeb, "%02x ", *fcb++);
853 dprintf_int(stddeb,"\n");
857 /* microsoft's programmers should be shot for using CP/M style int21
858 calls in Windows for Workgroup's winfile.exe */
860 static void FindFirstFCB(struct sigcontext_struct *context)
862 BYTE *fcb = SAFEMAKEPTR(DS, DX);
863 struct fcb *standard_fcb;
864 struct fcb *output_fcb;
865 int drive;
866 char path[12];
868 DumpFCB( fcb );
870 if ((*fcb) == 0xff)
872 standard_fcb = (struct fcb *)(fcb + 7);
873 output_fcb = (struct fcb *)(dta + 7);
874 *dta = 0xff;
876 else
878 standard_fcb = (struct fcb *)fcb;
879 output_fcb = (struct fcb *)dta;
882 if (standard_fcb->drive)
884 drive = standard_fcb->drive - 1;
885 if (!DOS_ValidDrive(drive))
887 Error (InvalidDrive, EC_MediaError, EL_Disk);
888 AL = 0xff;
889 return;
892 else
893 drive = DOS_GetDefaultDrive();
895 output_fcb->drive = drive;
897 if (*(fcb) == 0xff)
899 if (*(fcb+6) & FA_LABEL) /* return volume label */
901 *(dta+6) = FA_LABEL;
902 memset(&output_fcb->name, ' ', 11);
903 if (DOS_GetVolumeLabel(drive) != NULL)
905 strncpy(output_fcb->name, DOS_GetVolumeLabel(drive), 11);
906 AL = 0x00;
907 return;
912 strncpy(output_fcb->name, standard_fcb->name, 11);
913 if (*fcb == 0xff)
914 *(dta+6) = ( *(fcb+6) & (!FA_DIREC));
916 sprintf(path,"%c:*.*",drive+'A');
917 if ((output_fcb->directory = DOS_opendir(path))==NULL)
919 Error (PathNotFound, EC_MediaError, EL_Disk);
920 AL = 0xff;
921 return;
927 static void DeleteFileFCB(struct sigcontext_struct *context)
929 BYTE *fcb = SAFEMAKEPTR(DS, DX);
930 int drive;
931 struct dosdirent *dp;
932 char temp[256], *ptr;
934 DumpFCB( fcb );
936 if (*fcb)
937 drive = *fcb - 1;
938 else
939 drive = DOS_GetDefaultDrive();
941 strcpy(temp, DOS_GetCurrentDir(drive));
942 strcat(temp, "\\");
943 strncat(temp, fcb + 1, 8);
944 ChopOffWhiteSpace(temp);
945 strncat(temp, fcb + 9, 3);
946 ChopOffWhiteSpace(temp);
948 if ((dp = DOS_opendir(temp)) == NULL) {
949 Error(InvalidDrive, EC_MediaError , EL_Disk);
950 AL = 0xffL;
951 return;
954 strcpy(temp, DOS_GetCurrentDir(drive) );
955 strcat(temp, "\\");
957 ptr = temp + strlen(temp);
959 while (DOS_readdir(dp) != NULL)
961 strcpy(ptr, dp->filename);
962 dprintf_int(stddeb, "int21: delete file %s\n", temp);
963 /* unlink(DOS_GetUnixFileName(temp)); */
965 DOS_closedir(dp);
966 AL = 0;
969 static void RenameFileFCB(struct sigcontext_struct *context)
971 BYTE *fcb = SAFEMAKEPTR(DS, DX);
972 int drive;
973 struct dosdirent *dp;
974 char temp[256], oldname[256], newname[256], *oldnameptr, *newnameptr;
976 DumpFCB( fcb );
978 if (*fcb)
979 drive = *fcb - 1;
980 else
981 drive = DOS_GetDefaultDrive();
983 strcpy(temp, DOS_GetCurrentDir(drive));
984 strcat(temp, "\\");
985 strncat(temp, fcb + 1, 8);
986 ChopOffWhiteSpace(temp);
987 strncat(temp, fcb + 9, 3);
988 ChopOffWhiteSpace(temp);
990 if ((dp = DOS_opendir(temp)) == NULL) {
991 Error(InvalidDrive, EC_MediaError , EL_Disk);
992 AL = 0xffL;
993 return;
996 strcpy(oldname, DOS_GetCurrentDir(drive) );
997 strcat(oldname, "\\");
998 oldnameptr = oldname + strlen(oldname);
1000 strcpy(newname, DOS_GetCurrentDir(drive) );
1001 strcat(newname, "\\");
1002 newnameptr = newname + strlen(newname);
1004 while (DOS_readdir(dp) != NULL)
1006 strcpy(oldnameptr, dp->filename);
1007 strcpy(newnameptr, fcb + 1);
1008 dprintf_int(stddeb, "int21: renamefile %s -> %s\n",
1009 oldname, newname);
1011 DOS_closedir(dp);
1012 AL = 0;
1017 static void fLock (struct sigcontext_struct * context)
1019 struct flock f;
1020 int result,retries=sharing_retries;
1022 f.l_start = MAKELONG(DX,CX);
1023 f.l_len = MAKELONG(DI,SI);
1024 f.l_whence = 0;
1025 f.l_pid = 0;
1027 switch ( AX & 0xff )
1029 case 0x00: /* LOCK */
1030 f.l_type = F_WRLCK;
1031 break;
1033 case 0x01: /* UNLOCK */
1034 f.l_type = F_UNLCK;
1035 break;
1037 default:
1038 EAX = (EAX & 0xffff0000) | 0x0001;
1039 SetCflag;
1040 return;
1044 result = fcntl(BX,F_SETLK,&f);
1045 if ( retries && (!result) )
1047 int i;
1048 for(i=0;i<32768*((int)sharing_pause);i++)
1049 result++; /* stop the optimizer */
1050 for(i=0;i<32768*((int)sharing_pause);i++)
1051 result--;
1054 while( (!result) && (!(retries--)) );
1056 if(result)
1058 errno_to_doserr();
1059 EAX = (EAX & 0xffffff00) | ExtendedError;
1060 SetCflag;
1061 return;
1064 Error (0,0,0);
1065 ResetCflag;
1069 static void GetFileAttribute (struct sigcontext_struct * context)
1071 char *filename = SAFEMAKEPTR (DS,DX);
1072 struct stat s;
1073 int res,cx;
1075 res = stat(DOS_GetUnixFileName(filename), &s);
1076 if (res==-1)
1078 errno_to_doserr();
1079 EAX = (EAX & 0xffffff00) | ExtendedError;
1080 SetCflag;
1081 return;
1084 cx = 0;
1085 if (S_ISDIR(s.st_mode))
1086 cx|=0x10;
1087 if ((S_IWRITE & s.st_mode) != S_IWRITE)
1088 cx|=0x01;
1090 ECX = (ECX & 0xffff0000) | cx;
1091 ResetCflag;
1092 Error (0,0,0);
1097 /************************************************************************/
1099 int do_int21(struct sigcontext_struct * context)
1101 if (debugging_relay)
1103 fprintf(stddeb,"int21: AX %04x, BX %04x, CX %04x, DX %04x, "
1104 "SI %04x, DI %04x, DS %04x, ES %04x\n",
1105 AX, BX, CX, DX, SI, DI, DS, ES);
1108 if (AH == 0x59)
1110 GetExtendedErrorInfo(context);
1111 return 1;
1113 else
1115 Error (0,0,0);
1116 switch(AH)
1118 case 0x00: /* TERMINATE PROGRAM */
1119 exit(0);
1121 case 0x01: /* READ CHARACTER FROM STANDARD INPUT, WITH ECHO */
1122 case 0x02: /* WRITE CHARACTER TO STANDARD OUTPUT */
1123 case 0x03: /* READ CHARACTER FROM STDAUX */
1124 case 0x04: /* WRITE CHARACTER TO STDAUX */
1125 case 0x05: /* WRITE CHARACTER TO PRINTER */
1126 case 0x06: /* DIRECT CONSOLE IN/OUTPUT */
1127 case 0x07: /* DIRECT CHARACTER INPUT, WITHOUT ECHO */
1128 case 0x08: /* CHARACTER INPUT WITHOUT ECHO */
1129 case 0x09: /* WRITE STRING TO STANDARD OUTPUT */
1130 case 0x0a: /* BUFFERED INPUT */
1131 case 0x0b: /* GET STDIN STATUS */
1132 case 0x0c: /* FLUSH BUFFER AND READ STANDARD INPUT */
1133 case 0x0f: /* OPEN FILE USING FCB */
1134 case 0x10: /* CLOSE FILE USING FCB */
1135 case 0x12: /* FIND NEXT MATCHING FILE USING FCB */
1136 case 0x14: /* SEQUENTIAL READ FROM FCB FILE */
1137 case 0x15: /* SEQUENTIAL WRITE TO FCB FILE */
1138 case 0x16: /* CREATE OR TRUNCATE FILE USING FCB */
1139 case 0x21: /* READ RANDOM RECORD FROM FCB FILE */
1140 case 0x22: /* WRITE RANDOM RECORD TO FCB FILE */
1141 case 0x23: /* GET FILE SIZE FOR FCB */
1142 case 0x24: /* SET RANDOM RECORD NUMBER FOR FCB */
1143 case 0x26: /* CREATE NEW PROGRAM SEGMENT PREFIX */
1144 case 0x27: /* RANDOM BLOCK READ FROM FCB FILE */
1145 case 0x28: /* RANDOM BLOCK WRITE TO FCB FILE */
1146 case 0x29: /* PARSE FILENAME INTO FCB */
1147 case 0x2e: /* SET VERIFY FLAG */
1148 IntBarf(0x21, context);
1149 break;
1151 case 0x18: /* NULL FUNCTIONS FOR CP/M COMPATIBILITY */
1152 case 0x1d:
1153 case 0x1e:
1154 case 0x20:
1155 case 0x2b: /* SET SYSTEM DATE */
1156 case 0x2d: /* SET SYSTEM TIME */
1157 case 0x37: /* "SWITCHAR" - GET SWITCH CHARACTER
1158 "SWITCHAR" - SET SWITCH CHARACTER
1159 "AVAILDEV" - SPECIFY \DEV\ PREFIX USE */
1160 case 0x54: /* GET VERIFY FLAG */
1161 case 0x6b: /* NULL FUNCTION */
1162 IntBarf(0x21, context);
1163 EAX &= 0xff00;
1164 break;
1166 case 0x5c: /* "FLOCK" - RECORD LOCKING */
1167 fLock(context);
1168 break;
1170 case 0x0d: /* DISK BUFFER FLUSH */
1171 ResetCflag; /* dos 6+ only */
1172 break;
1174 case 0x0e: /* SELECT DEFAULT DRIVE */
1175 if (!DOS_ValidDrive(DL)) {
1176 Error (InvalidDrive, EC_MediaError, EL_Disk);
1177 break;
1178 } else {
1179 DOS_SetDefaultDrive(DL);
1180 AX = MAX_DOS_DRIVES;
1181 Error(0,0,0);
1183 break;
1185 case 0x11: /* FIND FIRST MATCHING FILE USING FCB */
1186 FindFirstFCB(context);
1187 break;
1189 case 0x13: /* DELETE FILE USING FCB */
1190 DeleteFileFCB(context);
1191 break;
1193 case 0x17: /* RENAME FILE USING FCB */
1194 RenameFileFCB(context);
1195 break;
1197 case 0x19: /* GET CURRENT DEFAULT DRIVE */
1198 AL = DOS_GetDefaultDrive();
1199 Error (0,0,0);
1200 break;
1202 case 0x1a: /* SET DISK TRANSFER AREA ADDRESS */
1203 dta = SAFEMAKEPTR(DS, DX);
1204 break;
1206 case 0x1b: /* GET ALLOCATION INFORMATION FOR DEFAULT DRIVE */
1207 GetDefDriveAllocInfo(context);
1208 break;
1210 case 0x1c: /* GET ALLOCATION INFORMATION FOR SPECIFIC DRIVE */
1211 GetDriveAllocInfo(context);
1212 break;
1214 case 0x1f: /* GET DRIVE PARAMETER BLOCK FOR DEFAULT DRIVE */
1215 GetDrivePB(context);
1216 break;
1218 case 0x25: /* SET INTERRUPT VECTOR */
1219 /* Ignore any attempt to set a segment vector */
1220 dprintf_int(stdnimp,
1221 "int21: set interrupt vector %2x (%04x:%04x)\n",
1222 AL, DS, DX);
1223 break;
1225 case 0x2a: /* GET SYSTEM DATE */
1226 GetSystemDate(context);
1227 break;
1229 case 0x2c: /* GET SYSTEM TIME */
1230 GetSystemTime(context);
1231 break;
1233 case 0x2f: /* GET DISK TRANSFER AREA ADDRESS */
1234 ES = segment(dta);
1235 BX = offset(dta);
1236 break;
1238 case 0x30: /* GET DOS VERSION */
1239 AX = DOSVERSION;
1240 BX = 0x0012; /* 0x123456 is Wine's serial # */
1241 CX = 0x3456;
1242 break;
1244 case 0x31: /* TERMINATE AND STAY RESIDENT */
1245 IntBarf(0x21, context);
1246 break;
1248 case 0x32: /* GET DOS DRIVE PARAMETER BLOCK FOR SPECIFIC DRIVE */
1249 GetDrivePB(context);
1250 break;
1252 case 0x33: /* MULTIPLEXED */
1253 switch (AL) {
1254 case 0x00: /* GET CURRENT EXTENDED BREAK STATE */
1255 if (!(AL))
1256 EDX &= 0xff00L;
1257 break;
1259 case 0x01: /* SET EXTENDED BREAK STATE */
1260 break;
1262 case 0x02: /* GET AND SET EXTENDED CONTROL-BREAK CHECKING STATE*/
1263 DL = 0;
1264 break;
1266 case 0x05: /* GET BOOT DRIVE */
1267 DL = 2;
1268 /* c: is Wine's bootdrive */
1269 break;
1271 case 0x06: /* GET TRUE VERSION NUMBER */
1272 EBX = DOSVERSION;
1273 EDX = 0x00;
1274 break;
1276 default:
1277 IntBarf(0x21, context);
1278 break;
1280 break;
1282 case 0x34: /* GET ADDRESS OF INDOS FLAG */
1283 ES = segment(heap->InDosFlag);
1284 BX = offset(heap->InDosFlag);
1285 break;
1287 case 0x35: /* GET INTERRUPT VECTOR */
1288 /* Return a NULL segment selector - this will bomb,
1289 if anyone ever tries to use it */
1290 dprintf_int(stdnimp, "int21: get interrupt vector %2x\n",
1291 AX & 0xff);
1292 ES = 0;
1293 BX = 0;
1294 break;
1296 case 0x36: /* GET FREE DISK SPACE */
1297 GetFreeDiskSpace(context);
1298 break;
1300 case 0x38: /* GET COUNTRY-SPECIFIC INFORMATION */
1301 AX = 0x02; /* no country support available */
1302 SetCflag;
1303 break;
1305 case 0x39: /* "MKDIR" - CREATE SUBDIRECTORY */
1306 MakeDir(context);
1307 break;
1309 case 0x3a: /* "RMDIR" - REMOVE SUBDIRECTORY */
1310 RemoveDir(context);
1311 break;
1313 case 0x3b: /* "CHDIR" - SET CURRENT DIRECTORY */
1314 ChangeDir(context);
1315 break;
1317 case 0x3c: /* "CREAT" - CREATE OR TRUNCATE FILE */
1318 CreateFile(context);
1319 break;
1321 case 0x3d: /* "OPEN" - OPEN EXISTING FILE */
1322 OpenExistingFile(context);
1323 break;
1325 case 0x3e: /* "CLOSE" - CLOSE FILE */
1326 CloseFile(context);
1327 break;
1329 case 0x3f: /* "READ" - READ FROM FILE OR DEVICE */
1330 ReadFile(context);
1331 break;
1333 case 0x40: /* "WRITE" - WRITE TO FILE OR DEVICE */
1334 WriteFile(context);
1335 break;
1337 case 0x41: /* "UNLINK" - DELETE FILE */
1338 if (unlink( DOS_GetUnixFileName( SAFEMAKEPTR(DS,DX)) ) == -1) {
1339 errno_to_doserr();
1340 AL = ExtendedError;
1341 SetCflag;
1342 break;
1344 Error(0,0,0);
1345 ResetCflag;
1346 break;
1348 case 0x42: /* "LSEEK" - SET CURRENT FILE POSITION */
1349 SeekFile(context);
1350 break;
1352 case 0x43: /* FILE ATTRIBUTES */
1353 switch (AL)
1355 case 0x00:
1356 GetFileAttribute(context);
1357 break;
1358 case 0x01:
1359 ResetCflag;
1360 break;
1362 break;
1364 case 0x44: /* IOCTL */
1365 switch (AL)
1367 case 0x00:
1368 ioctlGetDeviceInfo(context);
1369 break;
1371 case 0x09: /* CHECK IF BLOCK DEVICE REMOTE */
1372 EDX = (EDX & 0xffff0000) | (1<<9) | (1<<12) | (1<<15);
1373 ResetCflag;
1374 break;
1376 case 0x0b: /* SET SHARING RETRY COUNT */
1377 if (!CX)
1379 EAX = (EAX & 0xffff0000) | 0x0001;
1380 SetCflag;
1381 break;
1383 sharing_pause = CX;
1384 if (!DX)
1385 sharing_retries = DX;
1386 ResetCflag;
1387 break;
1389 case 0x0d:
1390 ioctlGenericBlkDevReq(context);
1391 break;
1393 default:
1394 IntBarf(0x21, context);
1395 break;
1397 break;
1399 case 0x45: /* "DUP" - DUPLICATE FILE HANDLE */
1400 case 0x46: /* "DUP2", "FORCEDUP" - FORCE DUPLICATE FILE HANDLE */
1401 AX = dup(BX);
1402 ResetCflag;
1403 break;
1405 case 0x47: /* "CWD" - GET CURRENT DIRECTORY */
1406 GetCurrentDirectory(context);
1407 AX = 0x0100;
1408 /* intlist: many Microsoft products for Windows rely on this */
1409 break;
1411 case 0x48: /* ALLOCATE MEMORY */
1412 case 0x49: /* FREE MEMORY */
1413 case 0x4a: /* RESIZE MEMORY BLOCK */
1414 IntBarf(0x21, context);
1415 break;
1417 case 0x4b: /* "EXEC" - LOAD AND/OR EXECUTE PROGRAM */
1418 ExecProgram(context);
1419 break;
1421 case 0x4c: /* "EXIT" - TERMINATE WITH RETURN CODE */
1422 exit(AL);
1423 break;
1425 case 0x4d: /* GET RETURN CODE */
1426 AL = NoError; /* normal exit */
1427 break;
1429 case 0x4e: /* "FINDFIRST" - FIND FIRST MATCHING FILE */
1430 FindFirst(context);
1431 break;
1433 case 0x4f: /* "FINDNEXT" - FIND NEXT MATCHING FILE */
1434 FindNext(context);
1435 break;
1437 case 0x52: /* "SYSVARS" - GET LIST OF LISTS */
1438 ES = 0x0;
1439 BX = 0x0;
1440 IntBarf(0x21, context);
1441 break;
1443 case 0x56: /* "RENAME" - RENAME FILE */
1444 RenameFile(context);
1445 break;
1447 case 0x57: /* FILE DATE AND TIME */
1448 switch (AL)
1450 case 0x00:
1451 GetFileDateTime(context);
1452 break;
1453 case 0x01:
1454 SetFileDateTime(context);
1455 break;
1457 break;
1459 case 0x58: /* GET OR SET MEMORY/UMB ALLOCATION STRATEGY */
1460 switch (AL)
1462 case 0x00:
1463 AL = 0x01L;
1464 break;
1465 case 0x02:
1466 EAX &= 0xff00L;
1467 break;
1468 case 0x01:
1469 case 0x03:
1470 break;
1472 ResetCflag;
1473 break;
1475 case 0x5a: /* CREATE TEMPORARY FILE */
1476 CreateTempFile(context);
1477 break;
1479 case 0x5b: /* CREATE NEW FILE */
1480 CreateNewFile(context);
1481 break;
1483 case 0x5d: /* NETWORK */
1484 case 0x5e:
1485 /* network software not installed */
1486 AL = NoNetwork;
1487 SetCflag;
1488 break;
1490 case 0x5f: /* NETWORK */
1491 switch (AL)
1493 case 0x07: /* ENABLE DRIVE */
1494 if (!DOS_EnableDrive(DL))
1496 Error(InvalidDrive, EC_MediaError , EL_Disk);
1497 AL = InvalidDrive;
1498 SetCflag;
1499 break;
1501 else
1503 ResetCflag;
1504 break;
1506 case 0x08: /* DISABLE DRIVE */
1507 if (!DOS_DisableDrive(DL))
1509 Error(InvalidDrive, EC_MediaError , EL_Disk);
1510 AL = InvalidDrive;
1511 SetCflag;
1512 break;
1514 else
1516 ResetCflag;
1517 break;
1519 default:
1520 /* network software not installed */
1521 AL = NoNetwork;
1522 SetCflag;
1523 break;
1525 break;
1527 case 0x60: /* "TRUENAME" - CANONICALIZE FILENAME OR PATH */
1528 strncpy(SAFEMAKEPTR(ES,DI), SAFEMAKEPTR(DS,SI), strlen(SAFEMAKEPTR(DS,SI)) & 0x7f);
1529 ResetCflag;
1530 break;
1532 case 0x61: /* UNUSED */
1533 case 0x62: /* GET CURRENT PSP ADDRESS */
1534 case 0x63: /* UNUSED */
1535 case 0x64: /* OS/2 DOS BOX */
1536 case 0x65: /* GET EXTENDED COUNTRY INFORMATION */
1537 IntBarf(0x21, context);
1538 break;
1540 case 0x66: /* GLOBAL CODE PAGE TABLE */
1541 switch (AL) {
1542 case 0x01:
1543 BX = CodePage;
1544 DX = BX;
1545 ResetCflag;
1546 break;
1547 case 0x02:
1548 CodePage = BX;
1549 ResetCflag;
1550 break;
1552 break;
1554 case 0x67: /* SET HANDLE COUNT */
1555 ResetCflag;
1556 break;
1558 case 0x68: /* "FFLUSH" - COMMIT FILE */
1559 ResetCflag;
1560 break;
1562 case 0x69: /* DISK SERIAL NUMBER */
1563 switch (AL)
1565 case 0x00:
1566 GetDiskSerialNumber(context);
1567 break;
1568 case 0x01:
1569 SetDiskSerialNumber(context);
1570 break;
1572 break;
1574 case 0x6a: /* COMMIT FILE */
1575 ResetCflag;
1576 break;
1578 case 0xea: /* NOVELL NETWARE - RETURN SHELL VERSION */
1579 break;
1581 default:
1582 IntBarf(0x21, context);
1583 return 1;
1586 return 1;
1589 /**********************************************************************
1590 * DOS3Call
1592 void DOS3Call(void)
1594 do_int21((struct sigcontext_struct *) _CONTEXT);
1595 ReturnFromRegisterFunc();
1598 void INT21_Init(void)
1600 int handle;
1601 MDESC *DosHeapDesc;
1603 if ((handle = GlobalAlloc(GMEM_FIXED,sizeof(struct DosHeap))) == 0)
1604 myerror("out of memory");
1606 heap = (struct DosHeap *) GlobalLock(handle);
1607 HEAP_Init(&DosHeapDesc, heap, sizeof(struct DosHeap));
1609 dta = heap->dta;
1610 heap->InDosFlag = 0;
1611 strcpy(heap->biosdate, "01/01/80");