/****************************************************************/ /* */ /* inthndlr.c */ /* */ /* Interrupt Handler and Function dispatcher for Kernel */ /* */ /* Copyright (c) 1995 */ /* Pasquale J. Villani */ /* All Rights Reserved */ /* */ /* This file is part of DOS-C. */ /* */ /* DOS-C is free software; you can redistribute it and/or */ /* modify it under the terms of the GNU General Public License */ /* as published by the Free Software Foundation; either version */ /* 2, or (at your option) any later version. */ /* */ /* DOS-C is distributed in the hope that it will be useful, but */ /* WITHOUT ANY WARRANTY; without even the implied warranty of */ /* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See */ /* the GNU General Public License for more details. */ /* */ /* You should have received a copy of the GNU General Public */ /* License along with DOS-C; see the file COPYING. If not, */ /* write to the Free Software Foundation, 675 Mass Ave, */ /* Cambridge, MA 02139, USA. */ /* */ /****************************************************************/ #include "../../hdr/portab.h" #include "globals.h" /* $Logfile: C:/dos-c/src/kernel/inthndlr.c_v $ */ static BYTE *RcsId = "$Header: C:/dos-c/src/kernel/inthndlr.c_v 1.1 01 Sep 1995 17:54:20 patv $"; /* * $Log: C:/dos-c/src/kernel/inthndlr.c_v $ * * Rev 1.1 01 Sep 1995 17:54:20 patv * First GPL release. * * Rev 1.0 02 Jul 1995 8:33:34 patv * Initial revision. */ /* Terminate the current process */ VOID INRPT far int20_handler (int es, int ds, int di, int si, int bp, int sp, int bx, int dx, int cx, int ax, int ip, int cs, int flags) { tsr = FALSE; DosMemCheck(); return_user(); } /* Convert an interrupt into our register struct definition */ VOID INRPT far int21_handler (int es, int ds, int di, int si, int bp, int sp, int bx, int dx, int cx, int ax, int ip, int cs, int flags) { ++InDOS; user_r = (iregs far *)&es; set_stack(); int21_service(user_r); restore_stack(); --InDOS; } /* Special entry for far call into the kernel */ VOID far int21_entry (int es, int ds, int di, int si, int bp, int sp, int bx, int dx, int cx, int ax, int ip, int cs, int flags) { int21_handler(es, ds, di, si, bp, sp, bx, dx, cx, ax, ip, cs, flags); } VOID int21_service(iregs far *r) { COUNT rc, rc1; ULONG lrc; /* The dispatch handler */ #ifdef DEBUG if(bDumpRegs) { fbcopy((VOID FAR *)r, (VOID FAR *)&error_regs, sizeof(iregs)); printf("System call (21h): %02x\n", r -> AX); dump_regs = TRUE; dump(); } #endif switch(r -> AH) { /* print unused and fall into terminate (debug only) */ default: #ifdef DEBUG fbcopy((VOID FAR *)r, (VOID FAR *)&error_regs, sizeof(iregs)); printf("Invalid system call (21h)\n"); dump_regs = TRUE; dump(); # ifdef KDB break; # endif #else r -> AX = -rc; r -> FLAGS |= FLG_CARRY; break; #endif /* Terminate Program */ case 0x00: tsr = FALSE; return_mode = break_flg ? 1 : 0; return_code = r -> AL; DosMemCheck(); return_user(); break; /* Read Keyboard with Echo */ case 0x01: r -> AL = DosCharInputEcho(); break; /* Display Character */ case 0x02: DosDisplayOutput(r -> DL); break; /* Auxiliary Input */ case 0x03: r -> AL = _sti(); break; /* Auxiliary Output */ case 0x04: sto(r -> DL); break; /* Print Character */ case 0x05: sto(r -> DL); break; /* Direct Cosole I/O */ case 0x06: DosDirectConsoleIO(r); break; /* Direct Console Input */ case 0x07: r -> AL = DosCharInput(); break; /* Read Keyboard Without Echo */ case 0x08: r -> AL = _sti(); break; /* Display String */ case 0x09: DosOutputString(MK_FP(r -> DS, r -> DX)); break; /* Buffered Keyboard Input */ case 0x0a: sti((keyboard FAR *)MK_FP(r -> DS, r -> DX)); break; /* Check Keyboard Status */ case 0x0b: if(KbdBusy) r -> AL = 0x00; else r -> AL = 0xff; break; /* Flush Buffer, Read Keayboard */ case 0x0c: KbdFlush(); switch(r -> AL) { /* Read Keyboard with Echo */ case 0x01: r -> AL = DosCharInputEcho(); break; /* Direct Cosole I/O */ case 0x06: DosDirectConsoleIO(r); break; /* Direct Console Input */ case 0x07: r -> AL = DosCharInput(); break; /* Read Keyboard Without Echo */ case 0x08: r -> AL = _sti(); break; /* Buffered Keyboard Input */ case 0x0a: sti((keyboard FAR *)MK_FP(r -> DS, r -> DX)); break; default: r -> AL = 0x00; break; } break; /* Reset Drive */ case 0x0d: flush(); break; /* Set Default Drive */ case 0x0e: if(r -> DL >= 0) default_drive = r -> DL; break; case 0x0f: if(FcbOpen(MK_FP(r -> DS, r -> DX))) r -> AL = 0; else r -> AL = 0xff; break; case 0x10: if(FcbClose(MK_FP(r -> DS, r -> DX))) r -> AL = 0; else r -> AL = 0xff; break; case 0x11: if(FcbFindFirst(MK_FP(r -> DS, r -> DX))) r -> AL = 0; else r -> AL = 0xff; break; case 0x12: if(FcbFindNext(MK_FP(r -> DS, r -> DX))) r -> AL = 0; else r -> AL = 0xff; break; case 0x13: if(FcbDelete(MK_FP(r -> DS, r -> DX))) r -> AL = 0; else r -> AL = 0xff; break; case 0x14: { COUNT nErrorCode; if(FcbRead(MK_FP(r -> DS, r -> DX), &nErrorCode)) r -> AL = 0; else r -> AL = nErrorCode; break; } case 0x15: { COUNT nErrorCode; if(FcbWrite(MK_FP(r -> DS, r -> DX), &nErrorCode)) r -> AL = 0; else r -> AL = nErrorCode; break; } case 0x16: if(FcbCreate(MK_FP(r -> DS, r -> DX))) r -> AL = 0; else r -> AL = 0xff; break; case 0x17: if(FcbRename(MK_FP(r -> DS, r -> DX))) r -> AL = 0; else r -> AL = 0xff; break; /* CP/M compatibility functions */ case 0x18: case 0x1d: case 0x1e: case 0x20: r -> AL = 0; break; /* Get Default Drive */ case 0x19: r -> AL = default_drive; break; /* Set DTA */ case 0x1a: { psp FAR *p = MK_FP(cu_psp,0); p -> ps_dta = MK_FP(r -> DS, r -> DX); dos_setdta(p -> ps_dta); } break; /* Get Default Drive Data */ case 0x1b: { BYTE FAR *p; FatGetDrvData(0, (COUNT FAR *)&r -> AX, (COUNT FAR *)&r -> CX, (COUNT FAR *)&r -> DX, (BYTE FAR *)&p); r -> DS = FP_SEG(p); r -> BX = FP_OFF(p); } break; /* Get Drive Data */ case 0x1c: { BYTE FAR *p; FatGetDrvData(r -> DL, (COUNT FAR *)&r -> AX, (COUNT FAR *)&r -> CX, (COUNT FAR *)&r -> DX, (BYTE FAR *)&p); r -> DS = FP_SEG(p); r -> BX = FP_OFF(p); } break; /* Get default DPB */ case 0x1f: r -> AH = 0xff; break; case 0x21: { COUNT nErrorCode; if(FcbRandomRead(MK_FP(r -> DS, r -> DX), &nErrorCode)) r -> AL = 0; else r -> AL = nErrorCode; break; } case 0x22: { COUNT nErrorCode; if(FcbRandomWrite(MK_FP(r -> DS, r -> DX), &nErrorCode)) r -> AL = 0; else r -> AL = nErrorCode; break; } /* Get file size in records */ case 0x23: if(FcbGetFileSize(MK_FP(r -> DS, r -> DX))) r -> AL = 0; else r -> AL = 0xff; break; case 0x24: FcbSetRandom(MK_FP(r -> DS, r -> DX)); break; /* Set Interrupt Vector */ case 0x25: { VOID (INRPT FAR *p)() = MK_FP(r -> DS, r -> DX); setvec(r -> AL, p); } break; /* Dos Create New Psp */ case 0x26: { psp FAR *p = MK_FP(cu_psp, 0); new_psp((psp FAR *)MK_FP(r -> DX, 0), p -> ps_size); } break; case 0x27: { COUNT nErrorCode; if(FcbRandomBlockRead(MK_FP(r -> DS, r -> DX), r -> CX, &nErrorCode)) r -> AL = 0; else r -> AL = nErrorCode; break; } case 0x28: { COUNT nErrorCode; if(FcbRandomBlockWrite(MK_FP(r -> DS, r -> DX), r -> CX, &nErrorCode)) r -> AL = 0; else r -> AL = nErrorCode; break; } /* Parse File Name */ case 0x29: { BYTE FAR *lpFileName; lpFileName = MK_FP(r -> DS, r -> SI); r -> AL = FcbParseFname(r -> AL, &lpFileName, MK_FP(r -> ES, r -> DI)); r -> DS = FP_SEG(lpFileName); r -> SI = FP_OFF(lpFileName); } break; /* Get Date */ case 0x2a: DosGetDate( (BYTE FAR *)&(r -> AL), /* WeekDay */ (BYTE FAR *)&(r -> DH), /* Month */ (BYTE FAR *)&(r -> DL), /* MonthDay */ (COUNT FAR *)&(r -> CX)); /* Year */ break; /* Set Date */ case 0x2b: rc = DosSetDate( (BYTE FAR *)&(r -> DH), /* Month */ (BYTE FAR *)&(r -> DL), /* MonthDay */ (COUNT FAR *)&(r -> CX)); /* Year */ if(rc != SUCCESS) r -> AL = 0xff; else r -> AL = 0; break; /* Get Time */ case 0x2c: DosGetTime( (BYTE FAR *)&(r -> CH), /* Hour */ (BYTE FAR *)&(r -> CL), /* Minutes */ (BYTE FAR *)&(r -> DH), /* Seconds */ (BYTE FAR *)&(r -> DL)); /* Hundredths */ break; /* Set Date */ case 0x2d: rc = DosSetTime( (BYTE FAR *)&(r -> CH), /* Hour */ (BYTE FAR *)&(r -> CL), /* Minutes */ (BYTE FAR *)&(r -> DH), /* Seconds */ (BYTE FAR *)&(r -> DL)); /* Hundredths */ if(rc != SUCCESS) r -> AL = 0xff; else r -> AL = 0; break; /* Set verify flag */ case 0x2e: verify_ena = (r -> AL ? TRUE : FALSE); break; /* Get DTA */ case 0x2f: r -> ES = FP_SEG(dta); r -> BX = FP_OFF(dta); break; /* Get DOS Version */ case 0x30: r -> AL = os_major; r -> AH = os_minor; r -> BX = 0; r -> CX = 0; break; /* Keep Program */ case 0x31: DosMemChange(cu_psp, r -> DX < 6 ? 6 : r -> DX); flush(); return_user(); break; /* Get DPB */ case 0x32: r -> AH = 0xff; break; /* DosVars - get/set dos variables */ case 0x33: switch((r -> AL) & 0xff) { /* Get Ctrl-C flag */ case 0x00: r -> DL = (break_ena ? TRUE : FALSE); break; /* Set Ctrl-C flag */ case 0x01: break_ena = (r -> DL ? TRUE : FALSE); break; /* Get Boot Drive */ case 0x05: r -> DL = BootDrive; break; /* Get DOS/NT version */ case 0x06: r -> BL = os_major; r -> BH = os_minor; r -> DL = rev_number; r -> DH = version_flags; break; #ifdef DEBUG /* Toggle DOS/NT rdwrblock trace dump */ case 0xfd: bDumpRdWrParms = !bDumpRdWrParms; break; /* Toggle DOS/NT syscall trace dump */ case 0xfe: bDumpRegs = !bDumpRegs; break; #endif /* Get DOS/NT release string pointer */ case 0xff: r -> DX = FP_SEG((BYTE FAR *)os_release); r -> AX = FP_OFF((BYTE FAR *)os_release); break; } break; /* Get InDOS flag */ case 0x34: { BYTE FAR *p; p = (BYTE FAR *)((BYTE *)&InDOS); r -> ES = FP_SEG(p); r -> BX = FP_OFF(p); } break; /* Get Interrupt Vector */ case 0x35: { BYTE FAR *p; p = getvec((COUNT)r -> AL); r -> ES = FP_SEG(p); r -> BX = FP_OFF(p); } break; /* Dos Get Disk Free Space */ case 0x36: DosGetFree( r -> DL, (WORD FAR *)&r -> AX, (WORD FAR *)&r -> BX, (WORD FAR *)&r -> CX, (WORD FAR *)&r -> DX); break; /* Undocumented Get/Set Switchar */ case 0x37: switch(r -> AL) { case 0: r -> DL = switchar; break; case 1: switchar = r -> DL; break; case 2: case 3: r -> DL = 0xff; break; } break; /* Dos Create Directory */ case 0x39: rc = dos_mkdir((BYTE FAR *)MK_FP(r -> DS, r -> DX)); if(rc != SUCCESS) { r -> AX = -rc; r -> FLAGS |= FLG_CARRY; } else { r -> FLAGS &= ~FLG_CARRY; } break; /* Dos Remove Directory */ case 0x3a: rc = dos_rmdir((BYTE FAR *)MK_FP(r -> DS, r -> DX)); if(rc != SUCCESS) { r -> AX = -rc; r -> FLAGS |= FLG_CARRY; } else { r -> FLAGS &= ~FLG_CARRY; } break; /* Dos Change Directory */ case 0x3b: if((rc = DosChangeDir((BYTE FAR *)MK_FP(r -> DS, r -> DX))) < 0) { r -> AX = -rc; r -> FLAGS |= FLG_CARRY; } else { r -> FLAGS &= ~FLG_CARRY; } break; /* Dos Create File */ case 0x3c: if((rc = DosCreat(MK_FP(r -> DS, r -> DX), r -> CX)) < 0) { r -> AX = -rc; r -> FLAGS |= FLG_CARRY; } else { r -> AX = rc; r -> FLAGS &= ~FLG_CARRY; } break; /* Dos Open */ case 0x3d: if((rc = DosOpen(MK_FP(r -> DS, r -> DX), r -> AL)) < 0) { r -> AX = -rc; r -> FLAGS |= FLG_CARRY; } else { r -> AX = rc; r -> FLAGS &= ~FLG_CARRY; } break; /* Dos Close */ case 0x3e: if((rc = DosClose(r -> BX)) < 0) { r -> AX = -rc; r -> FLAGS |= FLG_CARRY; } else r -> FLAGS &= ~FLG_CARRY; break; /* Dos Read */ case 0x3f: rc = DosRead(r -> BX, r -> CX, MK_FP(r -> DS, r -> DX), (COUNT FAR *)&rc1); if(rc1 != SUCCESS) { r -> FLAGS |= FLG_CARRY; r -> AX = -rc1; } else { r -> FLAGS &= ~FLG_CARRY; r -> AX = rc; } break; /* Dos Write */ case 0x40: rc = DosWrite(r -> BX, r -> CX, MK_FP(r -> DS, r -> DX), (COUNT FAR *)&rc1); if(rc1 != SUCCESS) { r -> FLAGS |= FLG_CARRY; r -> AX = -rc1; } else { r -> FLAGS &= ~FLG_CARRY; r -> AX = rc; } break; /* Dos Delete File */ case 0x41: rc = dos_delete((BYTE FAR *)MK_FP(r -> DS, r -> DX)); if(rc < 0) { r -> FLAGS |= FLG_CARRY; r -> AX = -rc1; } else r -> FLAGS &= ~FLG_CARRY; break; /* Dos Seek */ case 0x42: if((rc = DosSeek(r -> BX, (LONG)((((LONG)(r -> CX)) << 16) + r -> DX), r -> AL, &lrc)) < 0) { r -> AX = -rc; r -> FLAGS |= FLG_CARRY; } else { r -> DX = (lrc >> 16); r -> AX = lrc & 0xffff; r -> FLAGS &= ~FLG_CARRY; } break; /* Get/Set File Attributes */ case 0x43: switch(r -> AL) { case 0x00: rc = DosGetFattr((BYTE FAR *)MK_FP(r -> DS, r -> DX), (UWORD FAR *)&r -> CX); if(rc < SUCCESS) { r -> FLAGS |= FLG_CARRY; r -> AX = -rc; } else { r -> FLAGS &= ~FLG_CARRY; r -> CX = rc; } break; case 0x01: rc = DosSetFattr((BYTE FAR *)MK_FP(r -> DS, r -> DX), (UWORD FAR *)&r -> CX); if(rc != SUCCESS) { r -> FLAGS |= FLG_CARRY; r -> AX = -rc; } else r -> FLAGS &= ~FLG_CARRY; break; } break; /* Device I/O Control */ case 0x44: { BOOL bNoChangeAx; bNoChangeAx = ((r -> AL == 0) || (r -> AL == 1)); rc = DosDevIOctl(r, (COUNT FAR *)&rc1); if(rc1 != SUCCESS) { r -> FLAGS |= FLG_CARRY; r -> AX = -rc1; } else { r -> FLAGS &= ~FLG_CARRY; if(!bNoChangeAx) r -> AX = rc; } } break; /* Duplicate File Handle */ case 0x45: rc = DosDup(r -> BX); if(rc < SUCCESS) { r -> FLAGS |= FLG_CARRY; r -> AX = -rc; } else { r -> FLAGS &= ~FLG_CARRY; r -> AX = rc; } break; /* Force Duplicate File Handle */ case 0x46: rc = DosForceDup(r -> BX, r -> CX); if(rc < SUCCESS) { r -> FLAGS |= FLG_CARRY; r -> AX = -rc; } else r -> FLAGS &= ~FLG_CARRY; break; /* Get Current Directory */ case 0x47: if((rc = DosGetCuDir(r -> DL, MK_FP(r -> DS, r -> SI))) < 0) { r -> AX = -rc; r -> FLAGS |= FLG_CARRY; } else { r -> FLAGS &= ~FLG_CARRY; } break; /* Memory management */ case 0x48: if((rc = DosMemAlloc(r -> BX, mem_access_mode, &(r -> AX), &(r -> BX))) < 0) { DosMemLargest(&(r -> BX)); r -> AX = -rc; r -> FLAGS |= FLG_CARRY; } else { ++(r -> AX); r -> FLAGS &= ~FLG_CARRY; } break; case 0x49: if((rc = DosMemFree(--(r -> ES))) < 0) { r -> AX = -rc; r -> FLAGS |= FLG_CARRY; } else r -> FLAGS &= ~FLG_CARRY; break; case 0x4a: if((rc = DosMemChange(r -> ES, r -> BX)) < 0) { r -> AX = -rc; r -> FLAGS |= FLG_CARRY; if(cu_psp == r -> ES) { psp FAR *p; p = MK_FP(cu_psp, 0); p -> ps_size = r -> BX + cu_psp; } } else r -> FLAGS &= ~FLG_CARRY; break; /* Load and Execute Program */ case 0x4b: break_flg = FALSE; if((rc = DosExec(r -> AL, MK_FP(r -> ES, r -> BX), MK_FP(r -> DS, r -> DX))) != SUCCESS) { r -> AX = -rc; r -> FLAGS |= FLG_CARRY; } else r -> FLAGS &= ~FLG_CARRY; break; /* End Program */ case 0x4c: tsr = FALSE; return_mode = break_flg ? 1 : 0; return_code = r -> AL; DosMemCheck(); return_user(); break; /* Get Child-program Return Value */ case 0x4d: r -> AL = return_code; r -> AH = return_mode; break; /* Dos Find First */ case 0x4e: { psp FAR *p = MK_FP(cu_psp,0); /* dta for this call is set on entry. This */ /* needs to be changed for new versions. */ dta = p -> ps_dta; if((rc = DosFindFirst((UCOUNT)r -> CX, (BYTE FAR *)MK_FP(r -> DS, r -> DX))) < 0) { r -> AX = -rc; r -> FLAGS |= FLG_CARRY; } else { r -> FLAGS &= ~FLG_CARRY; } } break; /* Dos Find Next */ case 0x4f: { psp FAR *p = MK_FP(cu_psp,0); /* dta for this call is set on entry. This */ /* needs to be changed for new versions. */ dta = p -> ps_dta; if((rc = DosFindNext()) < 0) { r -> AX = -rc; r -> FLAGS |= FLG_CARRY; } else { r -> FLAGS &= ~FLG_CARRY; } } break; /* Set PSP */ case 0x50: cu_psp = r -> BX; break; /* Get PSP */ case 0x51: r -> BX = cu_psp; break; /* ************UNDOCUMENTED**************************************/ /* Get List of Lists */ case 0x52: { BYTE FAR *p; p = (BYTE FAR *)&DPBp; r -> ES = FP_SEG(p); r -> BX = FP_OFF(p); } break; /* Get verify state */ case 0x54: r -> AL = (verify_ena ? TRUE : FALSE); break; /* ************UNDOCUMENTED**************************************/ /* Dos Create New Psp & set p_size */ case 0x55: new_psp((psp FAR *)MK_FP(r -> DX, 0), r -> SI); break; /* Dos Rename */ case 0x56: rc = dos_rename( (BYTE FAR *)MK_FP(r -> DS, r -> DX), /* OldName */ (BYTE FAR *)MK_FP(r -> ES, r -> DI)); /* NewName */ if(rc != SUCCESS) { r -> AX = -rc; r -> FLAGS |= FLG_CARRY; } else { r -> FLAGS &= ~FLG_CARRY; } break; /* Get/Set File Date and Time */ case 0x57: switch(r -> AL) { case 0x00: if(!DosGetFtime( r -> BX, /* Handle */ (BYTE FAR *)&r -> DX, /* FileDate */ (BYTE FAR *)&r -> CX)) /* FileTime */ { r -> AX = -DE_INVLDHNDL; r -> FLAGS |= FLG_CARRY; } else r -> FLAGS &= ~FLG_CARRY; break; case 0x01: if(!DosSetFtime( r -> BX, /* Handle */ (BYTE FAR *)&r -> DX, /* FileDate */ (BYTE FAR *)&r -> CX)) /* FileTime */ { r -> AX = -DE_INVLDHNDL; r -> FLAGS |= FLG_CARRY; } else r -> FLAGS &= ~FLG_CARRY; break; } break; /* Get/Set Allocation Strategy */ case 0x58: switch(r -> AL) { case 0x00: r -> AX = mem_access_mode; break; case 0x01: if(r -> BX < 0 || r -> BX > 2) { r -> AX = -DE_INVLDFUNC; r -> FLAGS |= FLG_CARRY; } else { mem_access_mode = r -> BX; r -> FLAGS &= ~FLG_CARRY; } break; #ifdef DEBUG case 0xff: show_chain(); break; #endif } break; /* UNDOCUMENTED: share.exe and sda function */ /* not really supported, but will pass. */ case 0x5d: r -> AX = -DE_INVLDFUNC; r -> FLAGS |= FLG_CARRY; break; /* UNDOCUMENTED: return current psp */ case 0x62: r -> BX = cu_psp; break; /* UNDOCUMENTED: Double byte and korean tables */ /* not really supported, but will pass. */ case 0x63: r -> AL = 0xff; break; } #ifdef DEBUG if(bDumpRegs) { printf("Exiting system call (21h)\n"); fbcopy((VOID FAR *)r, (VOID FAR *)&error_regs, sizeof(iregs)); dump_regs = TRUE; dump(); printf("---\n"); } #endif } VOID INRPT FAR int22_handler (void) { } VOID INRPT FAR int23_handler (int es, int ds, int di, int si, int bp, int sp, int bx, int dx, int cx, int ax, int ip, int cs, int flags) { tsr = FALSE; return_mode = 1; return_code = -1; mod_sto(CTL_C); DosMemCheck(); return_user(); } VOID INRPT FAR int24_handler (void) { } VOID INRPT FAR int25_handler (void) { } VOID INRPT FAR int26_handler (void) { } VOID INRPT FAR int27_handler (int es, int ds, int di, int si, int bp, int sp, int bx, int dx, int cx, int ax, int ip, int cs, int flags) { DosMemChange(cs ,dx); flush(); return_user(); } VOID INRPT FAR int28_handler (void) { } VOID INRPT FAR int2f_handler (void) { } /* This file is part of DOS-C. */ /* */ /* DOS-C is free software; you can redistribute it and/or */ /* modify it under the terms of the GNU General Public License */ /* as published by the Free Software Foundation; either version */ /* 2, or (at your option) any later version. */ /* */ /* DOS-C is distributed in the hope that it will be useful, but */ /* WITHOUT ANY WARRANTY; without even the implied warranty of */ /* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See */ /* the GNU General Public License for more details. */ /* */ /* You should have received a copy of the GNU General Public */ /* License along with DOS-C; see the file COPYING. If not, */ /* write to the Free Software Foundation, 675 Mass Ave, */ /* Cambridge, MA 02139, USA. */