Release 941122
[wine/multimedia.git] / miscemu / int21.c
blob5756d8a314578b2fe9bc15d241559c7f71ef703b
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 "prototypes.h"
19 #include "regfunc.h"
20 #include "windows.h"
21 #include "wine.h"
22 #include "msdos.h"
23 #include "registers.h"
24 #include "options.h"
25 #include "stddebug.h"
26 /* #define DEBUG_INT */
27 /* #undef DEBUG_INT */
28 #include "debug.h"
30 WORD ExtendedError, CodePage = 437;
31 BYTE ErrorClass, Action, ErrorLocus;
32 BYTE *dta;
34 struct DosHeap {
35 BYTE dta[256];
36 BYTE InDosFlag;
37 BYTE biosdate[8];
39 static struct DosHeap *heap;
41 WORD sharing_retries = 3; /* number of retries at sharing violation */
42 WORD sharing_pause = 1; /* pause between retries */
44 extern char TempDirectory[];
45 void IntBarf(int i, struct sigcontext_struct *context);
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 int drive;
184 long size, available;
185 BYTE mediaID;
187 if (!DOS_ValidDrive(DL)) {
188 AX = 4;
189 CX = 512;
190 DX = 0;
191 Error (InvalidDrive, EC_MediaError, EL_Disk);
192 return;
195 if (!DOS_GetFreeSpace(DL, &size, &available)) {
196 Error(GeneralFailure, EC_MediaError , EL_Disk);
197 AX = 0xffff;
198 return;
201 EAX = 4;
202 ECX = 512;
203 EDX = (size / (CX * AX));
205 mediaID = 0xf0;
207 DS = segment(mediaID);
208 BX = offset(mediaID);
209 Error (0,0,0);
212 static void GetDefDriveAllocInfo(struct sigcontext_struct *context)
214 DX = DOS_GetDefaultDrive();
215 GetDriveAllocInfo(context);
218 static void GetDrivePB(struct sigcontext_struct *context)
220 Error (InvalidDrive, EC_MediaError, EL_Disk);
221 AX = 0x00ff;
222 /* I'm sorry but I only got networked drives :-) */
225 static void ReadFile(struct sigcontext_struct *context)
227 char *ptr;
228 int size;
230 /* can't read from stdout / stderr */
231 if ((BX == 1) || (BX == 2)) {
232 Error (InvalidHandle, EL_Unknown, EC_Unknown);
233 AX = InvalidHandle;
234 SetCflag;
235 return;
238 ptr = SAFEMAKEPTR (DS,DX);
239 if (BX == 0) {
240 *ptr = EOF;
241 Error (0,0,0);
242 AX = 1;
243 ResetCflag;
244 return;
245 } else {
246 size = read(BX, ptr, CX);
247 if (size == -1) {
248 errno_to_doserr();
249 AL = ExtendedError;
250 SetCflag;
251 return;
253 Error (0,0,0);
254 AX = size;
255 ResetCflag;
259 static void WriteFile(struct sigcontext_struct *context)
261 char *ptr;
262 int x,size;
264 ptr = SAFEMAKEPTR (DS,DX);
266 if (BX == 0) {
267 Error (InvalidHandle, EC_Unknown, EL_Unknown);
268 EAX = InvalidHandle;
269 SetCflag;
270 return;
273 if (BX < 3) {
274 for (x = 0;x != CX;x++) {
275 dprintf_int(stddeb, "%c", *ptr++);
277 fflush(stddeb);
279 Error (0,0,0);
280 AL = CX;
281 ResetCflag;
282 } else {
283 size = write(BX, ptr , CX);
284 if (size == 0) {
285 Error (WriteFault, EC_Unknown, EL_Unknown);
286 AL = ExtendedError;
287 return;
290 if (size == -1) {
291 errno_to_doserr();
292 AL = ExtendedError;
293 SetCflag;
294 return;
296 Error (0,0,0);
297 AX = size;
298 ResetCflag;
302 static void SeekFile(struct sigcontext_struct *context)
304 off_t status, fileoffset;
306 switch (AL) {
307 case 1: fileoffset = SEEK_CUR;
308 break;
309 case 2: fileoffset = SEEK_END;
310 break;
311 default:
312 case 0: fileoffset = SEEK_SET;
313 break;
315 status = lseek(BX, (CX << 16) + DX, fileoffset);
316 if (status == -1) {
317 errno_to_doserr();
318 AL = ExtendedError; SetCflag;
319 return;
321 Error (0,0,0);
322 AX = (status & 0xffff);
323 DX = ((status >> 16) & 0xffff);
325 ResetCflag;
328 static void ioctlGetDeviceInfo(struct sigcontext_struct *context)
330 switch (BX) {
331 case 0:
332 case 1:
333 case 2:
334 DX = 0x80d3;
335 break;
337 default:
339 struct stat sbuf;
341 if (fstat(BX, &sbuf) < 0)
343 IntBarf(0x21, context);
344 DX = 0x50;
345 SetCflag;
346 return;
349 /* This isn't the right answer, but should be close enough. */
350 DX = 0x0943;
353 ResetCflag;
356 static void ioctlGenericBlkDevReq(struct sigcontext_struct *context)
358 BYTE *dataptr = SAFEMAKEPTR(DS, DX);
359 int drive;
361 if (BL == 0)
362 drive = DOS_GetDefaultDrive();
363 else
364 drive = BL - 1;
366 if (!DOS_ValidDrive(drive)) {
367 AX = 0x02;
368 SetCflag;
369 return;
372 if (CH != 0x08) {
373 IntBarf(0x21, context);
374 return;
376 switch (CL) {
377 case 0x60: /* get device parameters */
378 /* used by w4wgrp's winfile */
379 memset(dataptr, 0, 0x26);
380 dataptr[0] = 0x04;
381 dataptr[6] = 0; /* media type */
382 if (drive > 1)
384 dataptr[1] = 0x05; /* fixed disk */
385 setword(&dataptr[2], 0x01); /* non removable */
386 setword(&dataptr[4], 0x300); /* # of cylinders */
388 else
390 dataptr[1] = 0x07; /* block dev, floppy */
391 setword(&dataptr[2], 0x02); /* removable */
392 setword(&dataptr[4], 80); /* # of cylinders */
394 CreateBPB(drive, &dataptr[7]);
395 ResetCflag;
396 return;
397 default:
398 IntBarf(0x21, context);
402 static void GetSystemDate(struct sigcontext_struct *context)
404 struct tm *now;
405 time_t ltime;
407 ltime = time(NULL);
408 now = localtime(&ltime);
410 CX = now->tm_year + 1900;
411 DX = ((now->tm_mon + 1) << 8) | now->tm_mday;
412 AX = now->tm_wday;
415 static void GetSystemTime(struct sigcontext_struct *context)
417 struct tm *now;
418 struct timeval tv;
420 gettimeofday(&tv,NULL); /* Note use of gettimeofday(), instead of time() */
421 now = localtime(&tv.tv_sec);
423 CX = (now->tm_hour<<8) | now->tm_min;
424 DX = (now->tm_sec<<8) | tv.tv_usec/10000;
425 /* Note hundredths of seconds */
428 static void GetExtendedErrorInfo(struct sigcontext_struct *context)
430 AL = ExtendedError;
431 BX = (ErrorClass << 8) | Action;
432 CH = ErrorLocus << 8;
435 static void CreateFile(struct sigcontext_struct *context)
437 int handle;
439 if ((handle = open(GetUnixFileName( SAFEMAKEPTR(DS,DX)),
440 O_CREAT | O_TRUNC | O_RDWR )) == -1) {
441 errno_to_doserr();
442 AL = ExtendedError;
443 SetCflag;
444 return;
446 Error (0,0,0);
447 EAX = (EAX & 0xffff0000) | handle;
448 ResetCflag;
451 void OpenExistingFile(struct sigcontext_struct *context)
453 int handle;
454 int mode;
455 int lock;
457 switch (AX & 0x0007)
459 case 0:
460 mode = O_RDONLY;
461 break;
463 case 1:
464 mode = O_WRONLY;
465 break;
467 default:
468 mode = O_RDWR;
469 break;
472 if ((handle = open(GetUnixFileName(SAFEMAKEPTR(DS,DX)), mode)) == -1) {
473 errno_to_doserr();
474 AL = ExtendedError;
475 SetCflag;
476 return;
479 switch (AX & 0x0070)
481 case 0x00: /* compatability mode */
482 case 0x40: /* DENYNONE */
483 lock = -1;
484 break;
486 case 0x30: /* DENYREAD */
487 dprintf_int(stdnimp,
488 "OpenExistingFile (%s): DENYREAD changed to DENYALL\n",
489 SAFEMAKEPTR(DS,DX));
490 case 0x10: /* DENYALL */
491 lock = LOCK_EX;
492 break;
494 case 0x20: /* DENYWRITE */
495 lock = LOCK_SH;
496 break;
498 default:
499 lock = -1;
502 if (lock != -1)
505 int result,retries=sharing_retries;
507 result = flock(handle, lock | LOCK_NB);
508 if ( retries && (!result) )
510 int i;
511 for(i=0;i<32768*((int)sharing_pause);i++)
512 result++; /* stop the optimizer */
513 for(i=0;i<32768*((int)sharing_pause);i++)
514 result--;
517 while( (!result) && (!(retries--)) );
519 if(result)
521 errno_to_doserr();
522 EAX = (EAX & 0xffffff00) | ExtendedError;
523 close(handle);
524 SetCflag;
525 return;
530 Error (0,0,0);
531 EAX = (EAX & 0xffff0000) | handle;
532 ResetCflag;
535 static void CloseFile(struct sigcontext_struct *context)
537 if (close(BX) == -1) {
538 errno_to_doserr();
539 AL = ExtendedError;
540 SetCflag;
541 return;
543 Error (0,0,0);
544 AL = NoError;
545 ResetCflag;
548 static void RenameFile(struct sigcontext_struct *context)
550 char *newname, *oldname;
552 dprintf_int(stddeb,"int21: renaming %s to %s\n",
553 SAFEMAKEPTR(DS,DX), SAFEMAKEPTR(ES,DI) );
555 oldname = GetUnixFileName( SAFEMAKEPTR(DS,DX) );
556 newname = GetUnixFileName( SAFEMAKEPTR(ES,DI) );
558 rename( oldname, newname);
559 ResetCflag;
563 static void MakeDir(struct sigcontext_struct *context)
565 char *dirname;
567 dprintf_int(stddeb,"int21: makedir %s\n", SAFEMAKEPTR(DS,DX) );
569 if ((dirname = GetUnixFileName( SAFEMAKEPTR(DS,DX) ))== NULL) {
570 AL = CanNotMakeDir;
571 SetCflag;
572 return;
575 if (mkdir(dirname,0) == -1) {
576 AL = CanNotMakeDir;
577 SetCflag;
578 return;
580 ResetCflag;
583 static void ChangeDir(struct sigcontext_struct *context)
585 int drive;
586 char *dirname = SAFEMAKEPTR(DS,DX);
587 drive = DOS_GetDefaultDrive();
588 dprintf_int(stddeb,"int21: changedir %s\n", dirname);
589 if (dirname != NULL && dirname[1] == ':') {
590 drive = toupper(dirname[0]) - 'A';
591 dirname += 2;
593 if (!DOS_ChangeDir(drive, dirname))
595 SetCflag;
596 AX=0x03;
600 static void RemoveDir(struct sigcontext_struct *context)
602 char *dirname;
604 dprintf_int(stddeb,"int21: removedir %s\n", SAFEMAKEPTR(DS,DX) );
606 if ((dirname = GetUnixFileName( SAFEMAKEPTR(DS,DX) ))== NULL) {
607 AL = CanNotMakeDir;
608 SetCflag;
609 return;
613 if (strcmp(unixname,DosDrives[drive].CurrentDirectory)) {
614 AL = CanNotRemoveCwd;
615 SetCflag;
618 if (rmdir(dirname) == -1) {
619 AL = CanNotMakeDir;
620 SetCflag;
622 ResetCflag;
625 static void ExecProgram(struct sigcontext_struct *context)
627 execl("wine", GetUnixFileName( SAFEMAKEPTR(DS,DX)) );
630 static void FindNext(struct sigcontext_struct *context)
632 struct dosdirent *dp;
634 memcpy(&dp, dta+0x0d, sizeof(dp));
636 do {
637 if ((dp = DOS_readdir(dp)) == NULL) {
638 Error(NoMoreFiles, EC_MediaError , EL_Disk);
639 AL = NoMoreFiles;
640 SetCflag;
641 return;
643 } /* while (*(dta + 0x0c) != dp->attribute);*/
644 while ( ( dp->search_attribute & dp->attribute) != dp->attribute);
646 *(dta + 0x15) = dp->attribute;
647 setword(&dta[0x16], 0x1234); /* time */
648 setword(&dta[0x18], 0x1234); /* date */
649 setdword(&dta[0x1a], dp->filesize);
650 strncpy(dta + 0x1e, dp->filename, 13);
652 AL = 0;
653 ResetCflag;
654 return;
657 static void FindFirst(struct sigcontext_struct *context)
659 BYTE drive, *path = SAFEMAKEPTR(DS, DX);
660 struct dosdirent *dp;
662 if ((*path)&&(path[1] == ':')) {
663 drive = (islower(*path) ? toupper(*path) : *path) - 'A';
665 if (!DOS_ValidDrive(drive)) {
666 Error(InvalidDrive, EC_MediaError , EL_Disk);
667 AL = InvalidDrive;
668 SetCflag;
669 return;
671 } else
672 drive = DOS_GetDefaultDrive();
674 *dta = drive;
675 memset(dta + 1 , '?', 11);
676 *(dta + 0x0c) = ECX & (FA_LABEL | FA_DIREC);
678 if (ECX & FA_LABEL) {
679 /* return volume label */
681 if (DOS_GetVolumeLabel(drive) != NULL)
682 strncpy(dta + 0x1e, DOS_GetVolumeLabel(drive), 8);
684 AL = 0;
685 ResetCflag;
686 return;
689 if ((dp = DOS_opendir(path)) == NULL) {
690 Error(PathNotFound, EC_MediaError, EL_Disk);
691 AL = FileNotFound;
692 SetCflag;
693 return;
696 dp->search_attribute = ECX & (FA_LABEL | FA_DIREC);
697 memcpy(dta + 0x0d, &dp, sizeof(dp));
698 FindNext(context);
701 static void GetFileDateTime(struct sigcontext_struct *context)
703 char *filename;
704 struct stat filestat;
705 struct tm *now;
707 if ((filename = GetUnixFileName( SAFEMAKEPTR(DS,DX) ))== NULL) {
708 AL = FileNotFound;
709 SetCflag;
710 return;
712 stat(filename, &filestat);
714 now = localtime (&filestat.st_mtime);
716 CX = ((now->tm_hour * 0x2000) + (now->tm_min * 0x20) + now->tm_sec/2);
717 DX = ((now->tm_year * 0x200) + (now->tm_mon * 0x20) + now->tm_mday);
719 ResetCflag;
722 static void SetFileDateTime(struct sigcontext_struct *context)
724 char *filename;
725 struct utimbuf filetime;
727 filename = GetUnixFileName( SAFEMAKEPTR(DS,DX) );
729 filetime.actime = 0L;
730 filetime.modtime = filetime.actime;
732 utime(filename, &filetime);
733 ResetCflag;
736 static void CreateTempFile(struct sigcontext_struct *context)
738 char temp[256];
739 int handle;
741 sprintf(temp,"%s\\win%d.tmp",TempDirectory,(int) getpid());
743 dprintf_int(stddeb,"CreateTempFile %s\n",temp);
745 handle = open(GetUnixFileName(temp), O_CREAT | O_TRUNC | O_RDWR);
747 if (handle == -1) {
748 AL = WriteProtected;
749 SetCflag;
750 return;
753 strcpy(SAFEMAKEPTR(DS,DX), temp);
755 AX = handle;
756 ResetCflag;
759 static void CreateNewFile(struct sigcontext_struct *context)
761 int handle;
763 if ((handle = open(GetUnixFileName( SAFEMAKEPTR(DS,DX) ), O_CREAT | O_EXCL | O_RDWR)) == -1) {
764 AL = WriteProtected;
765 SetCflag;
766 return;
769 AX = handle;
770 ResetCflag;
773 static void GetCurrentDirectory(struct sigcontext_struct *context)
775 int drive;
777 if (DL == 0)
778 drive = DOS_GetDefaultDrive();
779 else
780 drive = DL - 1;
782 if (!DOS_ValidDrive(drive)) {
783 AL = InvalidDrive;
784 SetCflag;
785 return;
788 strcpy(SAFEMAKEPTR(DS,SI), DOS_GetCurrentDir(drive) );
789 ResetCflag;
792 static void GetDiskSerialNumber(struct sigcontext_struct *context)
794 int drive;
795 BYTE *dataptr = SAFEMAKEPTR(DS, DX);
796 DWORD serialnumber;
798 if (BL == 0)
799 drive = DOS_GetDefaultDrive();
800 else
801 drive = BL - 1;
803 if (!DOS_ValidDrive(drive)) {
804 AL =InvalidDrive;
805 SetCflag;
806 return;
809 DOS_GetSerialNumber(drive, &serialnumber);
811 setword(dataptr, 0);
812 setdword(&dataptr[2], serialnumber);
813 strncpy(dataptr + 6, DOS_GetVolumeLabel(drive), 8);
814 strncpy(dataptr + 0x11, "FAT16 ", 8);
816 AL = 0;
817 ResetCflag;
820 static void SetDiskSerialNumber(struct sigcontext_struct *context)
822 int drive;
823 BYTE *dataptr = SAFEMAKEPTR(DS, DX);
824 DWORD serialnumber;
826 if (BL == 0)
827 drive = DOS_GetDefaultDrive();
828 else
829 drive = BL - 1;
831 if (!DOS_ValidDrive(drive)) {
832 AL = InvalidDrive;
833 SetCflag;
834 return;
837 serialnumber = dataptr[1] + (dataptr[2] << 8) + (dataptr[3] << 16) +
838 (dataptr[4] << 24);
840 DOS_SetSerialNumber(drive, serialnumber);
841 AL = 1L;
842 ResetCflag;
845 static void DumpFCB(BYTE *fcb)
847 int x, y;
849 fcb -= 7;
851 for (y = 0; y !=2 ; y++) {
852 for (x = 0; x!=15;x++)
853 dprintf_int(stddeb, "%02x ", *fcb++);
854 dprintf_int(stddeb,"\n");
858 /* microsoft's programmers should be shot for using CP/M style int21
859 calls in Windows for Workgroup's winfile.exe */
861 static void FindFirstFCB(struct sigcontext_struct *context)
863 BYTE *fcb = SAFEMAKEPTR(DS, DX);
864 struct fcb *standard_fcb;
865 struct fcb *output_fcb;
866 int drive;
867 char path[12];
869 DumpFCB( fcb );
871 if ((*fcb) == 0xff)
873 standard_fcb = (struct fcb *)(fcb + 7);
874 output_fcb = (struct fcb *)(dta + 7);
875 *dta = 0xff;
877 else
879 standard_fcb = (struct fcb *)fcb;
880 output_fcb = (struct fcb *)dta;
883 if (standard_fcb->drive)
885 drive = standard_fcb->drive - 1;
886 if (!DOS_ValidDrive(drive))
888 Error (InvalidDrive, EC_MediaError, EL_Disk);
889 AL = 0xff;
890 return;
893 else
894 drive = DOS_GetDefaultDrive();
896 output_fcb->drive = drive;
898 if (*(fcb) == 0xff)
900 if (*(fcb+6) & FA_LABEL) /* return volume label */
902 *(dta+6) = FA_LABEL;
903 memset(&output_fcb->name, ' ', 11);
904 if (DOS_GetVolumeLabel(drive) != NULL)
906 strncpy(output_fcb->name, DOS_GetVolumeLabel(drive), 11);
907 AL = 0x00;
908 return;
913 strncpy(output_fcb->name, standard_fcb->name, 11);
914 if (*fcb == 0xff)
915 *(dta+6) = ( *(fcb+6) & (!FA_DIREC));
917 sprintf(path,"%c:*.*",drive+'A');
918 if ((output_fcb->directory = DOS_opendir(path))==NULL)
920 Error (PathNotFound, EC_MediaError, EL_Disk);
921 AL = 0xff;
922 return;
928 static void DeleteFileFCB(struct sigcontext_struct *context)
930 BYTE *fcb = SAFEMAKEPTR(DS, DX);
931 int drive;
932 struct dosdirent *dp;
933 char temp[256], *ptr;
935 DumpFCB( fcb );
937 if (*fcb)
938 drive = *fcb - 1;
939 else
940 drive = DOS_GetDefaultDrive();
942 strcpy(temp, DOS_GetCurrentDir(drive));
943 strcat(temp, "\\");
944 strncat(temp, fcb + 1, 8);
945 ChopOffWhiteSpace(temp);
946 strncat(temp, fcb + 9, 3);
947 ChopOffWhiteSpace(temp);
949 if ((dp = DOS_opendir(temp)) == NULL) {
950 Error(InvalidDrive, EC_MediaError , EL_Disk);
951 AL = 0xffL;
952 return;
955 strcpy(temp, DOS_GetCurrentDir(drive) );
956 strcat(temp, "\\");
958 ptr = temp + strlen(temp);
960 while (DOS_readdir(dp) != NULL)
962 strcpy(ptr, dp->filename);
963 dprintf_int(stddeb, "int21: delete file %s\n", temp);
964 /* unlink(GetUnixFileName(temp)); */
966 DOS_closedir(dp);
967 AL = 0;
970 static void RenameFileFCB(struct sigcontext_struct *context)
972 BYTE *fcb = SAFEMAKEPTR(DS, DX);
973 int drive;
974 struct dosdirent *dp;
975 char temp[256], oldname[256], newname[256], *oldnameptr, *newnameptr;
977 DumpFCB( fcb );
979 if (*fcb)
980 drive = *fcb - 1;
981 else
982 drive = DOS_GetDefaultDrive();
984 strcpy(temp, DOS_GetCurrentDir(drive));
985 strcat(temp, "\\");
986 strncat(temp, fcb + 1, 8);
987 ChopOffWhiteSpace(temp);
988 strncat(temp, fcb + 9, 3);
989 ChopOffWhiteSpace(temp);
991 if ((dp = DOS_opendir(temp)) == NULL) {
992 Error(InvalidDrive, EC_MediaError , EL_Disk);
993 AL = 0xffL;
994 return;
997 strcpy(oldname, DOS_GetCurrentDir(drive) );
998 strcat(oldname, "\\");
999 oldnameptr = oldname + strlen(oldname);
1001 strcpy(newname, DOS_GetCurrentDir(drive) );
1002 strcat(newname, "\\");
1003 newnameptr = newname + strlen(newname);
1005 while (DOS_readdir(dp) != NULL)
1007 strcpy(oldnameptr, dp->filename);
1008 strcpy(newnameptr, fcb + 1);
1009 dprintf_int(stddeb, "int21: renamefile %s -> %s\n",
1010 oldname, newname);
1012 DOS_closedir(dp);
1013 AL = 0;
1018 static void fLock (struct sigcontext_struct * context)
1020 struct flock f;
1021 int result,retries=sharing_retries;
1023 f.l_start = MAKELONG(DX,CX);
1024 f.l_len = MAKELONG(DI,SI);
1025 f.l_whence = 0;
1026 f.l_pid = 0;
1028 switch ( AX & 0xff )
1030 case 0x00: /* LOCK */
1031 f.l_type = F_WRLCK;
1032 break;
1034 case 0x01: /* UNLOCK */
1035 f.l_type = F_UNLCK;
1036 break;
1038 default:
1039 EAX = (EAX & 0xffff0000) | 0x0001;
1040 SetCflag;
1041 return;
1045 result = fcntl(BX,F_SETLK,&f);
1046 if ( retries && (!result) )
1048 int i;
1049 for(i=0;i<32768*((int)sharing_pause);i++)
1050 result++; /* stop the optimizer */
1051 for(i=0;i<32768*((int)sharing_pause);i++)
1052 result--;
1055 while( (!result) && (!(retries--)) );
1057 if(result)
1059 errno_to_doserr();
1060 EAX = (EAX & 0xffffff00) | ExtendedError;
1061 SetCflag;
1062 return;
1065 Error (0,0,0);
1066 ResetCflag;
1070 static void GetFileAttribute (struct sigcontext_struct * context)
1072 char *filename = SAFEMAKEPTR (DS,DX);
1073 struct stat s;
1074 int res,cx;
1076 res = stat(GetUnixFileName(filename), &s);
1077 if (res==-1)
1079 errno_to_doserr();
1080 EAX = (EAX & 0xffffff00) | ExtendedError;
1081 SetCflag;
1082 return;
1085 cx = 0;
1086 if (S_ISDIR(s.st_mode))
1087 cx|=0x10;
1088 if ((S_IWRITE & s.st_mode) != S_IWRITE)
1089 cx|=0x01;
1091 ECX = (ECX & 0xffff0000) | cx;
1092 ResetCflag;
1093 Error (0,0,0);
1098 /************************************************************************/
1100 int do_int21(struct sigcontext_struct * context)
1102 if (Options.relay_debug)
1104 fprintf(stddeb,"int21: AX %04x, BX %04x, CX %04x, DX %04x, "
1105 "SI %04x, DI %04x, DS %04x, ES %04x\n",
1106 AX, BX, CX, DX, SI, DI, DS, ES);
1109 if (AH == 0x59)
1111 GetExtendedErrorInfo(context);
1112 return 1;
1114 else
1116 Error (0,0,0);
1117 switch(AH)
1119 case 0x00: /* TERMINATE PROGRAM */
1120 exit(0);
1122 case 0x01: /* READ CHARACTER FROM STANDARD INPUT, WITH ECHO */
1123 case 0x02: /* WRITE CHARACTER TO STANDARD OUTPUT */
1124 case 0x03: /* READ CHARACTER FROM STDAUX */
1125 case 0x04: /* WRITE CHARACTER TO STDAUX */
1126 case 0x05: /* WRITE CHARACTER TO PRINTER */
1127 case 0x06: /* DIRECT CONSOLE IN/OUTPUT */
1128 case 0x07: /* DIRECT CHARACTER INPUT, WITHOUT ECHO */
1129 case 0x08: /* CHARACTER INPUT WITHOUT ECHO */
1130 case 0x09: /* WRITE STRING TO STANDARD OUTPUT */
1131 case 0x0a: /* BUFFERED INPUT */
1132 case 0x0b: /* GET STDIN STATUS */
1133 case 0x0c: /* FLUSH BUFFER AND READ STANDARD INPUT */
1134 case 0x0f: /* OPEN FILE USING FCB */
1135 case 0x10: /* CLOSE FILE USING FCB */
1136 case 0x12: /* FIND NEXT MATCHING FILE USING FCB */
1137 case 0x14: /* SEQUENTIAL READ FROM FCB FILE */
1138 case 0x15: /* SEQUENTIAL WRITE TO FCB FILE */
1139 case 0x16: /* CREATE OR TRUNCATE FILE USING FCB */
1140 case 0x21: /* READ RANDOM RECORD FROM FCB FILE */
1141 case 0x22: /* WRITE RANDOM RECORD TO FCB FILE */
1142 case 0x23: /* GET FILE SIZE FOR FCB */
1143 case 0x24: /* SET RANDOM RECORD NUMBER FOR FCB */
1144 case 0x26: /* CREATE NEW PROGRAM SEGMENT PREFIX */
1145 case 0x27: /* RANDOM BLOCK READ FROM FCB FILE */
1146 case 0x28: /* RANDOM BLOCK WRITE TO FCB FILE */
1147 case 0x29: /* PARSE FILENAME INTO FCB */
1148 case 0x2e: /* SET VERIFY FLAG */
1149 IntBarf(0x21, context);
1150 break;
1152 case 0x18: /* NULL FUNCTIONS FOR CP/M COMPATIBILITY */
1153 case 0x1d:
1154 case 0x1e:
1155 case 0x20:
1156 case 0x2b: /* SET SYSTEM DATE */
1157 case 0x2d: /* SET SYSTEM TIME */
1158 case 0x37: /* "SWITCHAR" - GET SWITCH CHARACTER
1159 "SWITCHAR" - SET SWITCH CHARACTER
1160 "AVAILDEV" - SPECIFY \DEV\ PREFIX USE */
1161 case 0x54: /* GET VERIFY FLAG */
1162 case 0x6b: /* NULL FUNCTION */
1163 IntBarf(0x21, context);
1164 EAX &= 0xff00;
1165 break;
1167 case 0x5c: /* "FLOCK" - RECORD LOCKING */
1168 fLock(context);
1169 break;
1171 case 0x0d: /* DISK BUFFER FLUSH */
1172 ResetCflag; /* dos 6+ only */
1173 break;
1175 case 0x0e: /* SELECT DEFAULT DRIVE */
1176 if (!DOS_ValidDrive(DL)) {
1177 Error (InvalidDrive, EC_MediaError, EL_Disk);
1178 break;
1179 } else {
1180 DOS_SetDefaultDrive(DL);
1181 AX = MAX_DOS_DRIVES;
1182 Error(0,0,0);
1184 break;
1186 case 0x11: /* FIND FIRST MATCHING FILE USING FCB */
1187 FindFirstFCB(context);
1188 break;
1190 case 0x13: /* DELETE FILE USING FCB */
1191 DeleteFileFCB(context);
1192 break;
1194 case 0x17: /* RENAME FILE USING FCB */
1195 RenameFileFCB(context);
1196 break;
1198 case 0x19: /* GET CURRENT DEFAULT DRIVE */
1199 AL = DOS_GetDefaultDrive();
1200 Error (0,0,0);
1201 break;
1203 case 0x1a: /* SET DISK TRANSFER AREA ADDRESS */
1204 dta = SAFEMAKEPTR(DS, DX);
1205 break;
1207 case 0x1b: /* GET ALLOCATION INFORMATION FOR DEFAULT DRIVE */
1208 GetDefDriveAllocInfo(context);
1209 break;
1211 case 0x1c: /* GET ALLOCATION INFORMATION FOR SPECIFIC DRIVE */
1212 GetDriveAllocInfo(context);
1213 break;
1215 case 0x1f: /* GET DRIVE PARAMETER BLOCK FOR DEFAULT DRIVE */
1216 GetDrivePB(context);
1217 break;
1219 case 0x25: /* SET INTERRUPT VECTOR */
1220 /* Ignore any attempt to set a segment vector */
1221 dprintf_int(stdnimp,
1222 "int21: set interrupt vector %2x (%04x:%04x)\n",
1223 AL, DS, DX);
1224 break;
1226 case 0x2a: /* GET SYSTEM DATE */
1227 GetSystemDate(context);
1228 break;
1230 case 0x2c: /* GET SYSTEM TIME */
1231 GetSystemTime(context);
1232 break;
1234 case 0x2f: /* GET DISK TRANSFER AREA ADDRESS */
1235 ES = segment(dta);
1236 BX = offset(dta);
1237 break;
1239 case 0x30: /* GET DOS VERSION */
1240 AX = DOSVERSION;
1241 BX = 0x0012; /* 0x123456 is Wine's serial # */
1242 CX = 0x3456;
1243 break;
1245 case 0x31: /* TERMINATE AND STAY RESIDENT */
1246 IntBarf(0x21, context);
1247 break;
1249 case 0x32: /* GET DOS DRIVE PARAMETER BLOCK FOR SPECIFIC DRIVE */
1250 GetDrivePB(context);
1251 break;
1253 case 0x33: /* MULTIPLEXED */
1254 switch (AL) {
1255 case 0x00: /* GET CURRENT EXTENDED BREAK STATE */
1256 if (!(AL))
1257 EDX &= 0xff00L;
1258 break;
1260 case 0x01: /* SET EXTENDED BREAK STATE */
1261 break;
1263 case 0x02: /* GET AND SET EXTENDED CONTROL-BREAK CHECKING STATE*/
1264 DL = 0;
1265 break;
1267 case 0x05: /* GET BOOT DRIVE */
1268 DL = 2;
1269 /* c: is Wine's bootdrive */
1270 break;
1272 case 0x06: /* GET TRUE VERSION NUMBER */
1273 EBX = DOSVERSION;
1274 EDX = 0x00;
1275 break;
1277 default:
1278 IntBarf(0x21, context);
1279 break;
1281 break;
1283 case 0x34: /* GET ADDRESS OF INDOS FLAG */
1284 ES = segment(heap->InDosFlag);
1285 BX = offset(heap->InDosFlag);
1286 break;
1288 case 0x35: /* GET INTERRUPT VECTOR */
1289 /* Return a NULL segment selector - this will bomb,
1290 if anyone ever tries to use it */
1291 dprintf_int(stdnimp, "int21: get interrupt vector %2x\n",
1292 AX & 0xff);
1293 ES = 0;
1294 BX = 0;
1295 break;
1297 case 0x36: /* GET FREE DISK SPACE */
1298 GetFreeDiskSpace(context);
1299 break;
1301 case 0x38: /* GET COUNTRY-SPECIFIC INFORMATION */
1302 AX = 0x02; /* no country support available */
1303 SetCflag;
1304 break;
1306 case 0x39: /* "MKDIR" - CREATE SUBDIRECTORY */
1307 MakeDir(context);
1308 break;
1310 case 0x3a: /* "RMDIR" - REMOVE SUBDIRECTORY */
1311 RemoveDir(context);
1312 break;
1314 case 0x3b: /* "CHDIR" - SET CURRENT DIRECTORY */
1315 ChangeDir(context);
1316 break;
1318 case 0x3c: /* "CREAT" - CREATE OR TRUNCATE FILE */
1319 CreateFile(context);
1320 break;
1322 case 0x3d: /* "OPEN" - OPEN EXISTING FILE */
1323 OpenExistingFile(context);
1324 break;
1326 case 0x3e: /* "CLOSE" - CLOSE FILE */
1327 CloseFile(context);
1328 break;
1330 case 0x3f: /* "READ" - READ FROM FILE OR DEVICE */
1331 ReadFile(context);
1332 break;
1334 case 0x40: /* "WRITE" - WRITE TO FILE OR DEVICE */
1335 WriteFile(context);
1336 break;
1338 case 0x41: /* "UNLINK" - DELETE FILE */
1339 if (unlink( GetUnixFileName( SAFEMAKEPTR(DS,DX)) ) == -1) {
1340 errno_to_doserr();
1341 AL = ExtendedError;
1342 SetCflag;
1343 break;
1345 Error(0,0,0);
1346 ResetCflag;
1347 break;
1349 case 0x42: /* "LSEEK" - SET CURRENT FILE POSITION */
1350 SeekFile(context);
1351 break;
1353 case 0x43: /* FILE ATTRIBUTES */
1354 switch (AL)
1356 case 0x00:
1357 GetFileAttribute(context);
1358 break;
1359 case 0x01:
1360 ResetCflag;
1361 break;
1363 break;
1365 case 0x44: /* IOCTL */
1366 switch (AL)
1368 case 0x00:
1369 ioctlGetDeviceInfo(context);
1370 break;
1372 case 0x09: /* CHECK IF BLOCK DEVICE REMOTE */
1373 EDX = (EDX & 0xffff0000) | (1<<9) | (1<<12) | (1<<15);
1374 ResetCflag;
1375 break;
1377 case 0x0b: /* SET SHARING RETRY COUNT */
1378 if (!CX)
1380 EAX = (EAX & 0xffff0000) | 0x0001;
1381 SetCflag;
1382 break;
1384 sharing_pause = CX;
1385 if (!DX)
1386 sharing_retries = DX;
1387 ResetCflag;
1388 break;
1390 case 0x0d:
1391 ioctlGenericBlkDevReq(context);
1392 break;
1394 default:
1395 IntBarf(0x21, context);
1396 break;
1398 break;
1400 case 0x45: /* "DUP" - DUPLICATE FILE HANDLE */
1401 case 0x46: /* "DUP2", "FORCEDUP" - FORCE DUPLICATE FILE HANDLE */
1402 AX = dup(BX);
1403 ResetCflag;
1404 break;
1406 case 0x47: /* "CWD" - GET CURRENT DIRECTORY */
1407 GetCurrentDirectory(context);
1408 AX = 0x0100;
1409 /* intlist: many Microsoft products for Windows rely on this */
1410 break;
1412 case 0x48: /* ALLOCATE MEMORY */
1413 case 0x49: /* FREE MEMORY */
1414 case 0x4a: /* RESIZE MEMORY BLOCK */
1415 IntBarf(0x21, context);
1416 break;
1418 case 0x4b: /* "EXEC" - LOAD AND/OR EXECUTE PROGRAM */
1419 ExecProgram(context);
1420 break;
1422 case 0x4c: /* "EXIT" - TERMINATE WITH RETURN CODE */
1423 exit(AL);
1424 break;
1426 case 0x4d: /* GET RETURN CODE */
1427 AL = NoError; /* normal exit */
1428 break;
1430 case 0x4e: /* "FINDFIRST" - FIND FIRST MATCHING FILE */
1431 FindFirst(context);
1432 break;
1434 case 0x4f: /* "FINDNEXT" - FIND NEXT MATCHING FILE */
1435 FindNext(context);
1436 break;
1438 case 0x52: /* "SYSVARS" - GET LIST OF LISTS */
1439 ES = 0x0;
1440 BX = 0x0;
1441 IntBarf(0x21, context);
1442 break;
1444 case 0x56: /* "RENAME" - RENAME FILE */
1445 RenameFile(context);
1446 break;
1448 case 0x57: /* FILE DATE AND TIME */
1449 switch (AL)
1451 case 0x00:
1452 GetFileDateTime(context);
1453 break;
1454 case 0x01:
1455 SetFileDateTime(context);
1456 break;
1458 break;
1460 case 0x58: /* GET OR SET MEMORY/UMB ALLOCATION STRATEGY */
1461 switch (AL)
1463 case 0x00:
1464 AL = 0x01L;
1465 break;
1466 case 0x02:
1467 EAX &= 0xff00L;
1468 break;
1469 case 0x01:
1470 case 0x03:
1471 break;
1473 ResetCflag;
1474 break;
1476 case 0x5a: /* CREATE TEMPORARY FILE */
1477 CreateTempFile(context);
1478 break;
1480 case 0x5b: /* CREATE NEW FILE */
1481 CreateNewFile(context);
1482 break;
1484 case 0x5d: /* NETWORK */
1485 case 0x5e:
1486 /* network software not installed */
1487 AL = NoNetwork;
1488 SetCflag;
1489 break;
1491 case 0x5f: /* NETWORK */
1492 switch (AL)
1494 case 0x07: /* ENABLE DRIVE */
1495 if (!DOS_EnableDrive(DL))
1497 Error(InvalidDrive, EC_MediaError , EL_Disk);
1498 AL = InvalidDrive;
1499 SetCflag;
1500 break;
1502 else
1504 ResetCflag;
1505 break;
1507 case 0x08: /* DISABLE DRIVE */
1508 if (!DOS_DisableDrive(DL))
1510 Error(InvalidDrive, EC_MediaError , EL_Disk);
1511 AL = InvalidDrive;
1512 SetCflag;
1513 break;
1515 else
1517 ResetCflag;
1518 break;
1520 default:
1521 /* network software not installed */
1522 AL = NoNetwork;
1523 SetCflag;
1524 break;
1526 break;
1528 case 0x60: /* "TRUENAME" - CANONICALIZE FILENAME OR PATH */
1529 strncpy(SAFEMAKEPTR(ES,DI), SAFEMAKEPTR(DS,SI), strlen(SAFEMAKEPTR(DS,SI)) & 0x7f);
1530 ResetCflag;
1531 break;
1533 case 0x61: /* UNUSED */
1534 case 0x62: /* GET CURRENT PSP ADDRESS */
1535 case 0x63: /* UNUSED */
1536 case 0x64: /* OS/2 DOS BOX */
1537 case 0x65: /* GET EXTENDED COUNTRY INFORMATION */
1538 IntBarf(0x21, context);
1539 break;
1541 case 0x66: /* GLOBAL CODE PAGE TABLE */
1542 switch (AL) {
1543 case 0x01:
1544 BX = CodePage;
1545 DX = BX;
1546 ResetCflag;
1547 break;
1548 case 0x02:
1549 CodePage = BX;
1550 ResetCflag;
1551 break;
1553 break;
1555 case 0x67: /* SET HANDLE COUNT */
1556 ResetCflag;
1557 break;
1559 case 0x68: /* "FFLUSH" - COMMIT FILE */
1560 ResetCflag;
1561 break;
1563 case 0x69: /* DISK SERIAL NUMBER */
1564 switch (AL)
1566 case 0x00:
1567 GetDiskSerialNumber(context);
1568 break;
1569 case 0x01:
1570 SetDiskSerialNumber(context);
1571 break;
1573 break;
1575 case 0x6a: /* COMMIT FILE */
1576 ResetCflag;
1577 break;
1579 case 0xea: /* NOVELL NETWARE - RETURN SHELL VERSION */
1580 break;
1582 default:
1583 IntBarf(0x21, context);
1584 return 1;
1587 return 1;
1590 /**********************************************************************
1591 * DOS3Call
1593 void DOS3Call()
1595 do_int21((struct sigcontext_struct *) _CONTEXT);
1596 ReturnFromRegisterFunc();
1599 void INT21_Init(void)
1601 int handle;
1602 MDESC *DosHeapDesc;
1604 if ((handle = GlobalAlloc(GMEM_FIXED,sizeof(struct DosHeap))) == 0)
1605 myerror("out of memory");
1607 heap = (struct DosHeap *) GlobalLock(handle);
1608 HEAP_Init(&DosHeapDesc, heap, sizeof(struct DosHeap));
1610 dta = heap->dta;
1611 heap->InDosFlag = 0;
1612 strcpy(heap->biosdate, "01/01/80");