Release 940815
[wine/gsoc-2012-control.git] / miscemu / int21.c
blob5ed34e0196b76da18a8d70cb274c2764905f92df
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"
26 WORD ExtendedError, CodePage = 437;
27 BYTE ErrorClass, Action, ErrorLocus;
28 BYTE *dta;
30 struct DosHeap {
31 BYTE dta[256];
32 BYTE InDosFlag;
33 BYTE biosdate[8];
35 static struct DosHeap *heap;
37 WORD sharing_retries = 3; /* number of retries at sharing violation */
38 WORD sharing_pause = 1; /* pause between retries */
40 extern char TempDirectory[];
42 static int Error(int e, int class, int el)
44 ErrorClass = class;
45 Action = SA_Ask4Retry;
46 ErrorLocus = el;
47 ExtendedError = e;
49 return e;
52 void errno_to_doserr(void)
54 switch (errno) {
55 case EAGAIN:
56 Error (ShareViolation, EC_Temporary, EL_Unknown);
57 break;
58 case EBADF:
59 Error (InvalidHandle, EC_AppError, EL_Unknown);
60 break;
61 case ENOSPC:
62 Error (DiskFull, EC_MediaError, EL_Disk);
63 break;
64 case EACCES:
65 case EPERM:
66 case EROFS:
67 Error (WriteProtected, EC_AccessDenied, EL_Unknown);
68 break;
69 case EBUSY:
70 Error (LockViolation, EC_AccessDenied, EL_Unknown);
71 break;
72 case ENOENT:
73 Error (FileNotFound, EC_NotFound, EL_Unknown);
74 break;
75 case EISDIR:
76 Error (CanNotMakeDir, EC_AccessDenied, EL_Unknown);
77 break;
78 case ENFILE:
79 case EMFILE:
80 Error (NoMoreFiles, EC_MediaError, EL_Unknown);
81 break;
82 case EEXIST:
83 Error (FileExists, EC_Exists, EL_Disk);
84 break;
85 default:
86 fprintf(stderr, "int21: unknown errno %d!\n", errno);
87 Error (GeneralFailure, EC_SystemFailure, EL_Unknown);
88 break;
92 static void Barf(struct sigcontext_struct *context)
94 fprintf(stderr, "int21: unknown/not implemented parameters:\n");
95 fprintf(stderr, "int21: AX %04x, BX %04x, CX %04x, DX %04x, "
96 "SI %04x, DI %04x, DS %04x, ES %04x\n",
97 AX, BX, CX, DX, SI, DI, DS, ES);
100 void ChopOffWhiteSpace(char *string)
102 int length;
104 for (length = strlen(string) ; length ; length--)
105 if (string[length] == ' ')
106 string[length] = '\0';
109 static void CreateBPB(int drive, BYTE *data)
111 if (drive > 1) {
112 setword(data, 512);
113 data[2] = 2;
114 setword(&data[3], 0);
115 data[5] = 2;
116 setword(&data[6], 240);
117 setword(&data[8], 64000);
118 data[0x0a] = 0xf8;
119 setword(&data[0x0b], 40);
120 setword(&data[0x0d], 56);
121 setword(&data[0x0f], 2);
122 setword(&data[0x11], 0);
123 setword(&data[0x1f], 800);
124 data[0x21] = 5;
125 setword(&data[0x22], 1);
126 } else { /* 1.44mb */
127 setword(data, 512);
128 data[2] = 2;
129 setword(&data[3], 0);
130 data[5] = 2;
131 setword(&data[6], 240);
132 setword(&data[8], 2880);
133 data[0x0a] = 0xf8;
134 setword(&data[0x0b], 6);
135 setword(&data[0x0d], 18);
136 setword(&data[0x0f], 2);
137 setword(&data[0x11], 0);
138 setword(&data[0x1f], 80);
139 data[0x21] = 7;
140 setword(&data[0x22], 2);
144 static void GetFreeDiskSpace(struct sigcontext_struct *context)
146 int drive;
147 long size,available;
149 if (DL == 0)
150 drive = DOS_GetDefaultDrive();
151 else
152 drive = DL - 1;
154 if (!DOS_ValidDrive(drive)) {
155 Error(InvalidDrive, EC_MediaError , EL_Disk);
156 AX = 0xffff;
157 return;
160 if (!DOS_GetFreeSpace(drive, &size, &available)) {
161 Error(GeneralFailure, EC_MediaError , EL_Disk);
162 AX = 0xffff;
163 return;
166 AX = 4;
167 CX = 512;
169 BX = (available / (CX * AX));
170 DX = (size / (CX * AX));
171 Error (0,0,0);
174 static void GetDriveAllocInfo(struct sigcontext_struct *context)
176 int drive;
177 long size, available;
178 BYTE mediaID;
180 if (!DOS_ValidDrive(DL)) {
181 AX = 4;
182 CX = 512;
183 DX = 0;
184 Error (InvalidDrive, EC_MediaError, EL_Disk);
185 return;
188 if (!DOS_GetFreeSpace(DL, &size, &available)) {
189 Error(GeneralFailure, EC_MediaError , EL_Disk);
190 AX = 0xffff;
191 return;
194 EAX = 4;
195 ECX = 512;
196 EDX = (size / (CX * AX));
198 mediaID = 0xf0;
200 DS = segment(mediaID);
201 BX = offset(mediaID);
202 Error (0,0,0);
205 static void GetDefDriveAllocInfo(struct sigcontext_struct *context)
207 DX = DOS_GetDefaultDrive();
208 GetDriveAllocInfo(context);
211 static void GetDrivePB(struct sigcontext_struct *context)
213 Error (InvalidDrive, EC_MediaError, EL_Disk);
214 AX = 0x00ff;
215 /* I'm sorry but I only got networked drives :-) */
218 static void ReadFile(struct sigcontext_struct *context)
220 char *ptr;
221 int size;
223 /* can't read from stdout / stderr */
224 if ((BX == 1) || (BX == 2)) {
225 Error (InvalidHandle, EL_Unknown, EC_Unknown);
226 AX = InvalidHandle;
227 SetCflag;
228 return;
231 ptr = pointer (DS,DX);
232 if (BX == 0) {
233 *ptr = EOF;
234 Error (0,0,0);
235 AX = 1;
236 ResetCflag;
237 return;
238 } else {
239 size = read(BX, ptr, CX);
240 if (size == -1) {
241 errno_to_doserr();
242 AL = ExtendedError;
243 SetCflag;
244 return;
246 Error (0,0,0);
247 AX = size;
248 ResetCflag;
252 static void WriteFile(struct sigcontext_struct *context)
254 char *ptr;
255 int x,size;
257 ptr = pointer (DS,DX);
259 if (BX == 0) {
260 Error (InvalidHandle, EC_Unknown, EL_Unknown);
261 EAX = InvalidHandle;
262 SetCflag;
263 return;
266 if (BX < 3) {
267 for (x = 0;x != CX;x++) {
268 fprintf(stderr, "%c", *ptr++);
270 fflush(stderr);
272 Error (0,0,0);
273 AL = CX;
274 ResetCflag;
275 } else {
276 size = write(BX, ptr , CX);
277 if (size == 0) {
278 Error (WriteFault, EC_Unknown, EL_Unknown);
279 AL = ExtendedError;
280 return;
283 if (size == -1) {
284 errno_to_doserr();
285 AL = ExtendedError;
286 SetCflag;
287 return;
289 Error (0,0,0);
290 AX = size;
291 ResetCflag;
295 static void SeekFile(struct sigcontext_struct *context)
297 off_t status, fileoffset;
299 switch (AL) {
300 case 1: fileoffset = SEEK_CUR;
301 break;
302 case 2: fileoffset = SEEK_END;
303 break;
304 default:
305 case 0: fileoffset = SEEK_SET;
306 break;
308 status = lseek(BX, (CX * 0x100) + DX, fileoffset);
309 if (status == -1) {
310 errno_to_doserr();
311 AL = ExtendedError; SetCflag;
312 return;
314 Error (0,0,0);
315 AX = (status & 0xffff);
316 DX = ((status >> 16) & 0xffff);
318 ResetCflag;
321 static void ioctlGetDeviceInfo(struct sigcontext_struct *context)
323 switch (BX) {
324 case 0:
325 case 1:
326 case 2:
327 DX = 0x80d3;
328 break;
330 default:
332 struct stat sbuf;
334 if (fstat(BX, &sbuf) < 0)
336 IntBarf(0x21, context);
337 DX = 0x50;
338 SetCflag;
339 return;
342 /* This isn't the right answer, but should be close enough. */
343 DX = 0x0943;
346 ResetCflag;
349 static void ioctlGenericBlkDevReq(struct sigcontext_struct *context)
351 BYTE *dataptr = pointer(DS, DX);
352 int drive;
354 if (BL == 0)
355 drive = DOS_GetDefaultDrive();
356 else
357 drive = BL - 1;
359 if (CH != 0x08) {
360 IntBarf(0x21, context);
361 return;
363 switch (CL) {
364 case 0x60: /* get device parameters */
365 /* used by w4wgrp's winfile */
366 dataptr[0] = 0x04;
367 dataptr[6] = 0; /* media type */
368 if (drive > 1)
370 dataptr[1] = 0x05; /* fixed disk */
371 setword(&dataptr[2], 0x01); /* non removable */
372 setword(&dataptr[4], 0x300); /* # of cylinders */
374 else
376 dataptr[1] = 0x07; /* block dev, floppy */
377 setword(&dataptr[2], 0x02); /* removable */
378 setword(&dataptr[4], 80); /* # of cylinders */
380 CreateBPB(drive, &dataptr[7]);
381 AL = 0;
382 ResetCflag;
383 return;
384 default:
385 IntBarf(0x21, context);
389 static void GetSystemDate(struct sigcontext_struct *context)
391 struct tm *now;
392 time_t ltime;
394 ltime = time(NULL);
395 now = localtime(&ltime);
397 CX = now->tm_year + 1900;
398 DX = ((now->tm_mon + 1) << 8) | now->tm_mday;
399 AX = now->tm_wday;
402 static void GetSystemTime(struct sigcontext_struct *context)
404 struct tm *now;
405 struct timeval tv;
407 gettimeofday(&tv,NULL); /* Note use of gettimeofday(), instead of time() */
408 now = localtime(&tv.tv_sec);
410 CX = (now->tm_hour<<8) | now->tm_min;
411 DX = (now->tm_sec<<8) | tv.tv_usec/10000;
412 /* Note hundredths of seconds */
415 static void GetExtendedErrorInfo(struct sigcontext_struct *context)
417 AL = ExtendedError;
418 BX = (ErrorClass << 8) | Action;
419 CH = ErrorLocus << 8;
422 static void CreateFile(struct sigcontext_struct *context)
424 int handle;
426 if ((handle = open(GetUnixFileName( pointer(DS,DX)),
427 O_CREAT | O_TRUNC | O_RDWR )) == -1) {
428 errno_to_doserr();
429 AL = ExtendedError;
430 SetCflag;
431 return;
433 Error (0,0,0);
434 EAX = (EAX & 0xffff0000) | handle;
435 ResetCflag;
438 void OpenExistingFile(struct sigcontext_struct *context)
440 int handle;
441 int mode;
442 int lock;
444 switch (AX & 0x0007)
446 case 0:
447 mode = O_RDONLY;
448 break;
450 case 1:
451 mode = O_WRONLY;
452 break;
454 default:
455 mode = O_RDWR;
456 break;
459 if ((handle = open(GetUnixFileName(pointer(DS,DX)), mode)) == -1) {
460 errno_to_doserr();
461 AL = ExtendedError;
462 SetCflag;
463 return;
466 switch (AX & 0x0070)
468 case 0x00: /* compatability mode */
469 case 0x40: /* DENYNONE */
470 lock = -1;
471 break;
473 case 0x30: /* DENYREAD */
474 fprintf(stderr,
475 "OpenExistingFile (%s): DENYREAD changed to DENYALL\n",
476 pointer(DS,DX));
477 case 0x10: /* DENYALL */
478 lock = LOCK_EX;
479 break;
481 case 0x20: /* DENYWRITE */
482 lock = LOCK_SH;
483 break;
485 default:
486 lock = -1;
489 if (lock != -1)
492 int result,retries=sharing_retries;
494 result = flock(handle, lock | LOCK_NB);
495 if ( retries && (!result) )
497 int i;
498 for(i=0;i<32768*((int)sharing_pause);i++)
499 result++; /* stop the optimizer */
500 for(i=0;i<32768*((int)sharing_pause);i++)
501 result--;
504 while( (!result) && (!(retries--)) );
506 if(result)
508 errno_to_doserr();
509 EAX = (EAX & 0xffffff00) | ExtendedError;
510 close(handle);
511 SetCflag;
512 return;
517 Error (0,0,0);
518 EAX = (EAX & 0xffff0000) | handle;
519 ResetCflag;
522 static void CloseFile(struct sigcontext_struct *context)
524 if (close(BX) == -1) {
525 errno_to_doserr();
526 AL = ExtendedError;
527 SetCflag;
528 return;
530 Error (0,0,0);
531 AL = NoError;
532 ResetCflag;
535 static void RenameFile(struct sigcontext_struct *context)
537 char *newname, *oldname;
539 fprintf(stderr,"int21: renaming %s to %s\n",
540 pointer(DS,DX), pointer(ES,DI) );
542 oldname = GetUnixFileName( pointer(DS,DX) );
543 newname = GetUnixFileName( pointer(ES,DI) );
545 rename( oldname, newname);
546 ResetCflag;
550 static void MakeDir(struct sigcontext_struct *context)
552 char *dirname;
554 fprintf(stderr,"int21: makedir %s\n", pointer(DS,DX) );
556 if ((dirname = GetUnixFileName( pointer(DS,DX) ))== NULL) {
557 AL = CanNotMakeDir;
558 SetCflag;
559 return;
562 if (mkdir(dirname,0) == -1) {
563 AL = CanNotMakeDir;
564 SetCflag;
565 return;
567 ResetCflag;
570 static void ChangeDir(struct sigcontext_struct *context)
572 int drive;
573 char *dirname = pointer(DS,DX);
574 drive = DOS_GetDefaultDrive();
575 fprintf(stderr,"int21: changedir %s\n", dirname);
576 if (dirname != NULL && dirname[1] == ':') {
577 drive = toupper(dirname[0]) - 'A';
578 dirname += 2;
580 DOS_ChangeDir(drive, dirname);
583 static void RemoveDir(struct sigcontext_struct *context)
585 char *dirname;
587 fprintf(stderr,"int21: removedir %s\n", pointer(DS,DX) );
589 if ((dirname = GetUnixFileName( pointer(DS,DX) ))== NULL) {
590 AL = CanNotMakeDir;
591 SetCflag;
592 return;
596 if (strcmp(unixname,DosDrives[drive].CurrentDirectory)) {
597 AL = CanNotRemoveCwd;
598 SetCflag;
601 if (rmdir(dirname) == -1) {
602 AL = CanNotMakeDir;
603 SetCflag;
605 ResetCflag;
608 static void ExecProgram(struct sigcontext_struct *context)
610 execl("wine", GetUnixFileName( pointer(DS,DX)) );
613 static void FindNext(struct sigcontext_struct *context)
615 struct dosdirent *dp;
617 memcpy(&dp, dta+0x0d, sizeof(dp));
619 do {
620 if ((dp = DOS_readdir(dp)) == NULL) {
621 Error(NoMoreFiles, EC_MediaError , EL_Disk);
622 AL = NoMoreFiles;
623 SetCflag;
624 return;
626 } while (*(dta + 0x0c) != dp->attribute);
628 setword(&dta[0x16], 0x1234); /* time */
629 setword(&dta[0x18], 0x1234); /* date */
630 setdword(&dta[0x1a], dp->filesize);
631 strncpy(dta + 0x1e, dp->filename, 13);
634 ResetCflag;
635 return;
638 static void FindFirst(struct sigcontext_struct *context)
640 BYTE drive, *path = pointer(DS, DX);
641 struct dosdirent *dp;
643 if (path[1] == ':') {
644 drive = (islower(*path) ? toupper(*path) : *path) - 'A';
646 if (!DOS_ValidDrive(drive)) {
647 Error(InvalidDrive, EC_MediaError , EL_Disk);
648 AL = InvalidDrive;
649 SetCflag;
650 return;
652 } else
653 drive = DOS_GetDefaultDrive();
655 *dta = drive;
656 memset(dta + 1 , '?', 11);
657 *(dta + 0x0c) = ECX & (FA_LABEL | FA_DIREC);
659 if (ECX & FA_LABEL) {
660 /* return volume label */
662 if (DOS_GetVolumeLabel(drive) != NULL)
663 strncpy(dta + 0x1e, DOS_GetVolumeLabel(drive), 8);
665 AL = 0;
666 ResetCflag;
667 return;
670 if ((dp = DOS_opendir(path)) == NULL) {
671 Error(PathNotFound, EC_MediaError, EL_Disk);
672 AL = FileNotFound;
673 SetCflag;
674 return;
677 memcpy(dta + 0x0d, &dp, sizeof(dp));
678 FindNext(context);
681 static void GetFileDateTime(struct sigcontext_struct *context)
683 char *filename;
684 struct stat filestat;
685 struct tm *now;
687 if ((filename = GetUnixFileName( pointer(DS,DX) ))== NULL) {
688 AL = FileNotFound;
689 SetCflag;
690 return;
692 stat(filename, &filestat);
694 now = localtime (&filestat.st_mtime);
696 CX = ((now->tm_hour * 0x2000) + (now->tm_min * 0x20) + now->tm_sec/2);
697 DX = ((now->tm_year * 0x200) + (now->tm_mon * 0x20) + now->tm_mday);
699 ResetCflag;
702 static void SetFileDateTime(struct sigcontext_struct *context)
704 char *filename;
705 struct utimbuf filetime;
707 filename = GetUnixFileName( pointer(DS,DX) );
709 filetime.actime = 0L;
710 filetime.modtime = filetime.actime;
712 utime(filename, &filetime);
713 ResetCflag;
716 static void CreateTempFile(struct sigcontext_struct *context)
718 char temp[256];
719 int handle;
721 sprintf(temp,"%s\\win%d.tmp",TempDirectory,(int) getpid());
723 fprintf(stderr,"CreateTempFile %s\n",temp);
725 handle = open(GetUnixFileName(temp), O_CREAT | O_TRUNC | O_RDWR);
727 if (handle == -1) {
728 AL = WriteProtected;
729 SetCflag;
730 return;
733 strcpy(pointer(DS,DX), temp);
735 AX = handle;
736 ResetCflag;
739 static void CreateNewFile(struct sigcontext_struct *context)
741 int handle;
743 if ((handle = open(GetUnixFileName( pointer(DS,DX) ), O_CREAT | O_EXCL | O_RDWR)) == -1) {
744 AL = WriteProtected;
745 SetCflag;
746 return;
749 AX = handle;
750 ResetCflag;
753 static void GetCurrentDirectory(struct sigcontext_struct *context)
755 int drive;
757 if (DL == 0)
758 drive = DOS_GetDefaultDrive();
759 else
760 drive = DL - 1;
762 if (!DOS_ValidDrive(drive)) {
763 AL = InvalidDrive;
764 SetCflag;
765 return;
768 strcpy(pointer(DS,SI), DOS_GetCurrentDir(drive) );
769 ResetCflag;
772 static void GetDiskSerialNumber(struct sigcontext_struct *context)
774 int drive;
775 BYTE *dataptr = pointer(DS, DX);
776 DWORD serialnumber;
778 if (BL == 0)
779 drive = DOS_GetDefaultDrive();
780 else
781 drive = BL - 1;
783 if (!DOS_ValidDrive(drive)) {
784 AL =InvalidDrive;
785 SetCflag;
786 return;
789 DOS_GetSerialNumber(drive, &serialnumber);
791 setword(dataptr, 0);
792 setdword(&dataptr[2], serialnumber);
793 strncpy(dataptr + 6, DOS_GetVolumeLabel(drive), 8);
794 strncpy(dataptr + 0x11, "FAT16 ", 8);
797 ResetCflag;
800 static void SetDiskSerialNumber(struct sigcontext_struct *context)
802 int drive;
803 BYTE *dataptr = pointer(DS, DX);
804 DWORD serialnumber;
806 if (BL == 0)
807 drive = DOS_GetDefaultDrive();
808 else
809 drive = BL - 1;
811 if (!DOS_ValidDrive(drive)) {
812 AL = InvalidDrive;
813 SetCflag;
814 return;
817 serialnumber = dataptr[1] + (dataptr[2] << 8) + (dataptr[3] << 16) +
818 (dataptr[4] << 24);
820 DOS_SetSerialNumber(drive, serialnumber);
821 AL = 1L;
822 ResetCflag;
825 static void DumpFCB(BYTE *fcb)
827 int x, y;
829 fcb -= 7;
831 for (y = 0; y !=2 ; y++) {
832 for (x = 0; x!=15;x++)
833 fprintf(stderr, "%02x ", *fcb++);
834 fprintf(stderr,"\n");
838 /* microsoft's programmers should be shot for using CP/M style int21
839 calls in Windows for Workgroup's winfile.exe */
841 static void FindFirstFCB(struct sigcontext_struct *context)
843 BYTE *fcb = pointer(DS, DX);
844 int drive;
846 DumpFCB( fcb );
848 if (*fcb)
849 drive = *fcb - 1;
850 else
851 drive = DOS_GetDefaultDrive();
853 if (*(fcb - 7) == 0xff) {
854 if (*(fcb - 1) == FA_DIREC) {
855 /* return volume label */
857 memset(dta, ' ', 11);
858 if (DOS_GetVolumeLabel(drive) != NULL)
859 strncpy(dta, DOS_GetVolumeLabel(drive), 8);
860 *(dta + 0x0b) = FA_DIREC;
863 return;
866 IntBarf(0x21, context);
869 static void DeleteFileFCB(struct sigcontext_struct *context)
871 BYTE *fcb = pointer(DS, DX);
872 int drive;
873 struct dosdirent *dp;
874 char temp[256], *ptr;
876 DumpFCB( fcb );
878 if (*fcb)
879 drive = *fcb - 1;
880 else
881 drive = DOS_GetDefaultDrive();
883 strcpy(temp, DOS_GetCurrentDir(drive));
884 strcat(temp, "\\");
885 strncat(temp, fcb + 1, 8);
886 ChopOffWhiteSpace(temp);
887 strncat(temp, fcb + 9, 3);
888 ChopOffWhiteSpace(temp);
890 if ((dp = DOS_opendir(temp)) == NULL) {
891 Error(InvalidDrive, EC_MediaError , EL_Disk);
892 AL = 0xffL;
893 return;
896 strcpy(temp, DOS_GetCurrentDir(drive) );
897 strcat(temp, "\\");
899 ptr = temp + strlen(temp);
901 while (DOS_readdir(dp) != NULL)
903 strcpy(ptr, dp->filename);
904 fprintf(stderr, "int21: delete file %s\n", temp);
905 /* unlink(GetUnixFileName(temp)); */
907 DOS_closedir(dp);
911 static void RenameFileFCB(struct sigcontext_struct *context)
913 BYTE *fcb = pointer(DS, DX);
914 int drive;
915 struct dosdirent *dp;
916 char temp[256], oldname[256], newname[256], *oldnameptr, *newnameptr;
918 DumpFCB( fcb );
920 if (*fcb)
921 drive = *fcb - 1;
922 else
923 drive = DOS_GetDefaultDrive();
925 strcpy(temp, DOS_GetCurrentDir(drive));
926 strcat(temp, "\\");
927 strncat(temp, fcb + 1, 8);
928 ChopOffWhiteSpace(temp);
929 strncat(temp, fcb + 9, 3);
930 ChopOffWhiteSpace(temp);
932 if ((dp = DOS_opendir(temp)) == NULL) {
933 Error(InvalidDrive, EC_MediaError , EL_Disk);
934 AL = 0xffL;
935 return;
938 strcpy(oldname, DOS_GetCurrentDir(drive) );
939 strcat(oldname, "\\");
940 oldnameptr = oldname + strlen(oldname);
942 strcpy(newname, DOS_GetCurrentDir(drive) );
943 strcat(newname, "\\");
944 newnameptr = newname + strlen(newname);
946 while (DOS_readdir(dp) != NULL)
948 strcpy(oldnameptr, dp->filename);
949 strcpy(newnameptr, fcb + 1);
950 fprintf(stderr, "int21: renamefile %s -> %s\n", oldname, newname);
952 DOS_closedir(dp);
958 static void fLock (struct sigcontext_struct * context)
960 struct flock f;
961 int result,retries=sharing_retries;
963 f.l_start = MAKELONG(DX,CX);
964 f.l_len = MAKELONG(DI,SI);
965 f.l_whence = 0;
966 f.l_pid = 0;
968 switch ( AX & 0xff )
970 case 0x00: /* LOCK */
971 f.l_type = F_WRLCK;
972 break;
974 case 0x01: /* UNLOCK */
975 f.l_type = F_UNLCK;
976 break;
978 default:
979 EAX = (EAX & 0xffff0000) | 0x0001;
980 SetCflag;
981 return;
985 result = fcntl(BX,F_SETLK,&f);
986 if ( retries && (!result) )
988 int i;
989 for(i=0;i<32768*((int)sharing_pause);i++)
990 result++; /* stop the optimizer */
991 for(i=0;i<32768*((int)sharing_pause);i++)
992 result--;
995 while( (!result) && (!(retries--)) );
997 if(result)
999 errno_to_doserr();
1000 EAX = (EAX & 0xffffff00) | ExtendedError;
1001 SetCflag;
1002 return;
1005 Error (0,0,0);
1006 ResetCflag;
1010 static void GetFileAttribute (struct sigcontext_struct * context)
1012 char *filename = pointer (DS,DX);
1013 struct stat s;
1014 int res,cx;
1016 res = stat(GetUnixFileName(filename), &s);
1017 if (res==-1)
1019 errno_to_doserr();
1020 EAX = (EAX & 0xffffff00) | ExtendedError;
1021 SetCflag;
1022 return;
1025 cx = 0;
1026 if (S_ISDIR(s.st_mode))
1027 cx|=0x10;
1028 if ((S_IWRITE & s.st_mode) != S_IWRITE)
1029 cx|=0x01;
1031 ECX = (ECX & 0xffff0000) | cx;
1032 ResetCflag;
1033 Error (0,0,0);
1038 /************************************************************************/
1040 int do_int21(struct sigcontext_struct * context)
1042 if (Options.relay_debug)
1044 printf("int21: AX %04x, BX %04x, CX %04x, DX %04x, "
1045 "SI %04x, DI %04x, DS %04x, ES %04x\n",
1046 AX, BX, CX, DX, SI, DI, DS, ES);
1049 if (AH == 0x59)
1051 GetExtendedErrorInfo(context);
1052 return 1;
1054 else
1056 Error (0,0,0);
1057 switch(AH)
1059 case 0x00: /* TERMINATE PROGRAM */
1060 exit(0);
1062 case 0x01: /* READ CHARACTER FROM STANDARD INPUT, WITH ECHO */
1063 case 0x02: /* WRITE CHARACTER TO STANDARD OUTPUT */
1064 case 0x03: /* READ CHARACTER FROM STDAUX */
1065 case 0x04: /* WRITE CHARACTER TO STDAUX */
1066 case 0x05: /* WRITE CHARACTER TO PRINTER */
1067 case 0x06: /* DIRECT CONSOLE IN/OUTPUT */
1068 case 0x07: /* DIRECT CHARACTER INPUT, WITHOUT ECHO */
1069 case 0x08: /* CHARACTER INPUT WITHOUT ECHO */
1070 case 0x09: /* WRITE STRING TO STANDARD OUTPUT */
1071 case 0x0a: /* BUFFERED INPUT */
1072 case 0x0b: /* GET STDIN STATUS */
1073 case 0x0c: /* FLUSH BUFFER AND READ STANDARD INPUT */
1074 case 0x0f: /* OPEN FILE USING FCB */
1075 case 0x10: /* CLOSE FILE USING FCB */
1076 case 0x12: /* FIND NEXT MATCHING FILE USING FCB */
1077 case 0x14: /* SEQUENTIAL READ FROM FCB FILE */
1078 case 0x15: /* SEQUENTIAL WRITE TO FCB FILE */
1079 case 0x16: /* CREATE OR TRUNCATE FILE USING FCB */
1080 case 0x21: /* READ RANDOM RECORD FROM FCB FILE */
1081 case 0x22: /* WRITE RANDOM RECORD TO FCB FILE */
1082 case 0x23: /* GET FILE SIZE FOR FCB */
1083 case 0x24: /* SET RANDOM RECORD NUMBER FOR FCB */
1084 case 0x26: /* CREATE NEW PROGRAM SEGMENT PREFIX */
1085 case 0x27: /* RANDOM BLOCK READ FROM FCB FILE */
1086 case 0x28: /* RANDOM BLOCK WRITE TO FCB FILE */
1087 case 0x29: /* PARSE FILENAME INTO FCB */
1088 case 0x2e: /* SET VERIFY FLAG */
1089 IntBarf(0x21, context);
1090 break;
1092 case 0x18: /* NULL FUNCTIONS FOR CP/M COMPATIBILITY */
1093 case 0x1d:
1094 case 0x1e:
1095 case 0x20:
1096 case 0x2b: /* SET SYSTEM DATE */
1097 case 0x2d: /* SET SYSTEM TIME */
1098 case 0x37: /* "SWITCHAR" - GET SWITCH CHARACTER
1099 "SWITCHAR" - SET SWITCH CHARACTER
1100 "AVAILDEV" - SPECIFY \DEV\ PREFIX USE */
1101 case 0x54: /* GET VERIFY FLAG */
1102 case 0x6b: /* NULL FUNCTION */
1103 IntBarf(0x21, context);
1104 EAX &= 0xff00;
1105 break;
1107 case 0x5c: /* "FLOCK" - RECORD LOCKING */
1108 fLock(context);
1109 break;
1111 case 0x0d: /* DISK BUFFER FLUSH */
1112 ResetCflag; /* dos 6+ only */
1113 break;
1115 case 0x0e: /* SELECT DEFAULT DRIVE */
1116 if (!DOS_ValidDrive(DL)) {
1117 Error (InvalidDrive, EC_MediaError, EL_Disk);
1118 return;
1119 } else {
1120 DOS_SetDefaultDrive(DL);
1121 AX = MAX_DOS_DRIVES;
1122 Error(0,0,0);
1124 break;
1126 case 0x11: /* FIND FIRST MATCHING FILE USING FCB */
1127 FindFirstFCB(context);
1128 break;
1130 case 0x13: /* DELETE FILE USING FCB */
1131 DeleteFileFCB(context);
1132 break;
1134 case 0x17: /* RENAME FILE USING FCB */
1135 RenameFileFCB(context);
1136 break;
1138 case 0x19: /* GET CURRENT DEFAULT DRIVE */
1139 AL = DOS_GetDefaultDrive();
1140 Error (0,0,0);
1141 break;
1143 case 0x1a: /* SET DISK TRANSFER AREA ADDRESS */
1144 dta = pointer(DS, DX);
1145 break;
1147 case 0x1b: /* GET ALLOCATION INFORMATION FOR DEFAULT DRIVE */
1148 GetDefDriveAllocInfo(context);
1149 break;
1151 case 0x1c: /* GET ALLOCATION INFORMATION FOR SPECIFIC DRIVE */
1152 GetDriveAllocInfo(context);
1153 break;
1155 case 0x1f: /* GET DRIVE PARAMETER BLOCK FOR DEFAULT DRIVE */
1156 GetDrivePB(context);
1157 break;
1159 case 0x25: /* SET INTERRUPT VECTOR */
1160 /* Ignore any attempt to set a segment vector */
1161 fprintf(stderr, "int21: set interrupt vector %2x (%04x:%04x)\n", AL, DS, DX);
1162 break;
1164 case 0x2a: /* GET SYSTEM DATE */
1165 GetSystemDate(context);
1166 break;
1168 case 0x2c: /* GET SYSTEM TIME */
1169 GetSystemTime(context);
1170 break;
1172 case 0x2f: /* GET DISK TRANSFER AREA ADDRESS */
1173 ES = segment(dta);
1174 BX = offset(dta);
1175 break;
1177 case 0x30: /* GET DOS VERSION */
1178 AX = DOSVERSION;
1179 BX = 0x0012; /* 0x123456 is Wine's serial # */
1180 CX = 0x3456;
1181 break;
1183 case 0x31: /* TERMINATE AND STAY RESIDENT */
1184 IntBarf(0x21, context);
1185 break;
1187 case 0x32: /* GET DOS DRIVE PARAMETER BLOCK FOR SPECIFIC DRIVE */
1188 GetDrivePB(context);
1189 break;
1191 case 0x33: /* MULTIPLEXED */
1192 switch (AL) {
1193 case 0x00: /* GET CURRENT EXTENDED BREAK STATE */
1194 if (!(AL))
1195 EDX &= 0xff00L;
1196 break;
1198 case 0x01: /* SET EXTENDED BREAK STATE */
1199 break;
1201 case 0x02: /* GET AND SET EXTENDED CONTROL-BREAK CHECKING STATE*/
1202 DL = 0;
1203 break;
1205 case 0x05: /* GET BOOT DRIVE */
1206 DL = 2;
1207 /* c: is Wine's bootdrive */
1208 break;
1210 case 0x06: /* GET TRUE VERSION NUMBER */
1211 EBX = DOSVERSION;
1212 EDX = 0x00;
1213 break;
1215 default:
1216 IntBarf(0x21, context);
1217 break;
1219 break;
1221 case 0x34: /* GET ADDRESS OF INDOS FLAG */
1222 ES = segment(heap->InDosFlag);
1223 BX = offset(heap->InDosFlag);
1224 break;
1226 case 0x35: /* GET INTERRUPT VECTOR */
1227 /* Return a NULL segment selector - this will bomb,
1228 if anyone ever tries to use it */
1229 fprintf(stderr, "int21: get interrupt vector %2x\n", AX & 0xff);
1230 ES = 0;
1231 BX = 0;
1232 break;
1234 case 0x36: /* GET FREE DISK SPACE */
1235 GetFreeDiskSpace(context);
1236 break;
1238 case 0x38: /* GET COUNTRY-SPECIFIC INFORMATION */
1239 AX = 0x02; /* no country support available */
1240 SetCflag;
1241 break;
1243 case 0x39: /* "MKDIR" - CREATE SUBDIRECTORY */
1244 MakeDir(context);
1245 break;
1247 case 0x3a: /* "RMDIR" - REMOVE SUBDIRECTORY */
1248 RemoveDir(context);
1249 break;
1251 case 0x3b: /* "CHDIR" - SET CURRENT DIRECTORY */
1252 ChangeDir(context);
1253 break;
1255 case 0x3c: /* "CREAT" - CREATE OR TRUNCATE FILE */
1256 CreateFile(context);
1257 break;
1259 case 0x3d: /* "OPEN" - OPEN EXISTING FILE */
1260 OpenExistingFile(context);
1261 break;
1263 case 0x3e: /* "CLOSE" - CLOSE FILE */
1264 CloseFile(context);
1265 break;
1267 case 0x3f: /* "READ" - READ FROM FILE OR DEVICE */
1268 ReadFile(context);
1269 break;
1271 case 0x40: /* "WRITE" - WRITE TO FILE OR DEVICE */
1272 WriteFile(context);
1273 break;
1275 case 0x41: /* "UNLINK" - DELETE FILE */
1276 if (unlink( GetUnixFileName( pointer(DS,DX)) ) == -1) {
1277 errno_to_doserr();
1278 AL = ExtendedError;
1279 SetCflag;
1280 return;
1282 Error(0,0,0);
1283 ResetCflag;
1284 break;
1286 case 0x42: /* "LSEEK" - SET CURRENT FILE POSITION */
1287 SeekFile(context);
1288 break;
1290 case 0x43: /* FILE ATTRIBUTES */
1291 switch (AL)
1293 case 0x00:
1294 GetFileAttribute(context);
1295 break;
1296 case 0x01:
1297 ResetCflag;
1298 break;
1300 break;
1302 case 0x44: /* IOCTL */
1303 switch (AL)
1305 case 0x00:
1306 ioctlGetDeviceInfo(context);
1307 break;
1309 case 0x09: /* CHECK IF BLOCK DEVICE REMOTE */
1310 EDX = (EDX & 0xffff0000) | (1<<9) | (1<<12) | (1<<15);
1311 ResetCflag;
1312 break;
1314 case 0x0b: /* SET SHARING RETRY COUNT */
1315 if (!CX)
1317 EAX = (EAX & 0xffff0000) | 0x0001;
1318 SetCflag;
1319 break;
1321 sharing_pause = CX;
1322 if (!DX)
1323 sharing_retries = DX;
1324 ResetCflag;
1325 break;
1327 case 0x0d:
1328 ioctlGenericBlkDevReq(context);
1329 break;
1331 default:
1332 IntBarf(0x21, context);
1333 break;
1335 break;
1337 case 0x45: /* "DUP" - DUPLICATE FILE HANDLE */
1338 case 0x46: /* "DUP2", "FORCEDUP" - FORCE DUPLICATE FILE HANDLE */
1339 AX = dup(BX);
1340 ResetCflag;
1341 break;
1343 case 0x47: /* "CWD" - GET CURRENT DIRECTORY */
1344 GetCurrentDirectory(context);
1345 AX = 0x0100;
1346 /* intlist: many Microsoft products for Windows rely on this */
1347 break;
1349 case 0x48: /* ALLOCATE MEMORY */
1350 case 0x49: /* FREE MEMORY */
1351 case 0x4a: /* RESIZE MEMORY BLOCK */
1352 IntBarf(0x21, context);
1353 break;
1355 case 0x4b: /* "EXEC" - LOAD AND/OR EXECUTE PROGRAM */
1356 ExecProgram(context);
1357 break;
1359 case 0x4c: /* "EXIT" - TERMINATE WITH RETURN CODE */
1360 exit(AL);
1361 break;
1363 case 0x4d: /* GET RETURN CODE */
1364 AL = NoError; /* normal exit */
1365 break;
1367 case 0x4e: /* "FINDFIRST" - FIND FIRST MATCHING FILE */
1368 FindFirst(context);
1369 break;
1371 case 0x4f: /* "FINDNEXT" - FIND NEXT MATCHING FILE */
1372 FindNext(context);
1373 break;
1375 case 0x52: /* "SYSVARS" - GET LIST OF LISTS */
1376 ES = 0x0;
1377 BX = 0x0;
1378 IntBarf(0x21, context);
1379 break;
1381 case 0x56: /* "RENAME" - RENAME FILE */
1382 RenameFile(context);
1383 break;
1385 case 0x57: /* FILE DATE AND TIME */
1386 switch (AL)
1388 case 0x00:
1389 GetFileDateTime(context);
1390 break;
1391 case 0x01:
1392 SetFileDateTime(context);
1393 break;
1395 break;
1397 case 0x58: /* GET OR SET MEMORY/UMB ALLOCATION STRATEGY */
1398 switch (AL)
1400 case 0x00:
1401 AL = 0x01L;
1402 break;
1403 case 0x02:
1404 EAX &= 0xff00L;
1405 break;
1406 case 0x01:
1407 case 0x03:
1408 break;
1410 ResetCflag;
1411 break;
1413 case 0x5a: /* CREATE TEMPORARY FILE */
1414 CreateTempFile(context);
1415 break;
1417 case 0x5b: /* CREATE NEW FILE */
1418 CreateNewFile(context);
1419 break;
1421 case 0x5d: /* NETWORK */
1422 case 0x5e:
1423 /* network software not installed */
1424 AL = NoNetwork;
1425 SetCflag;
1426 break;
1428 case 0x5f: /* NETWORK */
1429 switch (AL)
1431 case 0x07: /* ENABLE DRIVE */
1432 if (!DOS_EnableDrive(DL))
1434 Error(InvalidDrive, EC_MediaError , EL_Disk);
1435 AL = InvalidDrive;
1436 SetCflag;
1437 break;
1439 else
1441 ResetCflag;
1442 break;
1444 case 0x08: /* DISABLE DRIVE */
1445 if (!DOS_DisableDrive(DL))
1447 Error(InvalidDrive, EC_MediaError , EL_Disk);
1448 AL = InvalidDrive;
1449 SetCflag;
1450 break;
1452 else
1454 ResetCflag;
1455 break;
1457 default:
1458 /* network software not installed */
1459 AL = NoNetwork;
1460 SetCflag;
1461 break;
1463 break;
1465 case 0x60: /* "TRUENAME" - CANONICALIZE FILENAME OR PATH */
1466 strncpy(pointer(ES,DI), pointer(DS,SI), strlen(pointer(DS,SI)) & 0x7f);
1467 ResetCflag;
1468 break;
1470 case 0x61: /* UNUSED */
1471 case 0x62: /* GET CURRENT PSP ADDRESS */
1472 case 0x63: /* UNUSED */
1473 case 0x64: /* OS/2 DOS BOX */
1474 case 0x65: /* GET EXTENDED COUNTRY INFORMATION */
1475 IntBarf(0x21, context);
1476 break;
1478 case 0x66: /* GLOBAL CODE PAGE TABLE */
1479 switch (AL) {
1480 case 0x01:
1481 BX = CodePage;
1482 DX = BX;
1483 ResetCflag;
1484 break;
1485 case 0x02:
1486 CodePage = BX;
1487 ResetCflag;
1488 break;
1490 break;
1492 case 0x67: /* SET HANDLE COUNT */
1493 ResetCflag;
1494 break;
1496 case 0x68: /* "FFLUSH" - COMMIT FILE */
1497 ResetCflag;
1498 break;
1500 case 0x69: /* DISK SERIAL NUMBER */
1501 switch (AL)
1503 case 0x00:
1504 GetDiskSerialNumber(context);
1505 break;
1506 case 0x01:
1507 SetDiskSerialNumber(context);
1508 break;
1510 break;
1512 case 0x6a: /* COMMIT FILE */
1513 ResetCflag;
1514 break;
1516 case 0xea: /* NOVELL NETWARE - RETURN SHELL VERSION */
1517 break;
1519 default:
1520 IntBarf(0x21, context);
1521 return 1;
1524 return 1;
1527 /**********************************************************************
1528 * DOS3Call
1530 void DOS3Call()
1532 do_int21((struct sigcontext_struct *) _CONTEXT);
1533 ReturnFromRegisterFunc();
1536 void INT21_Init(void)
1538 int handle;
1539 MDESC *DosHeapDesc;
1541 if ((handle = GlobalAlloc(GMEM_FIXED,sizeof(struct DosHeap))) == 0)
1542 myerror("out of memory");
1544 heap = (struct DosHeap *) GlobalLock(handle);
1545 HEAP_Init(&DosHeapDesc, heap, sizeof(struct DosHeap));
1547 dta = heap->dta;
1548 heap->InDosFlag = 0;
1549 strcpy(heap->biosdate, "01/01/80");