FreeDOS/kernel/dosfns.c
2001-04-02 23:18:30 +00:00

1736 lines
45 KiB
C

/****************************************************************/
/* */
/* dosfns.c */
/* */
/* DOS functions */
/* */
/* 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 "portab.h"
#ifdef VERSION_STRINGS
static BYTE *dosfnsRcsId = "$Id$";
#endif
/*
*
* /// Added SHARE support. 2000/09/04 Ron Cemer
*
* $Log$
* Revision 1.14 2001/04/02 23:18:30 bartoldeman
* Misc, zero terminated device names and redirector bugs fixed.
*
* Revision 1.13 2001/03/30 22:27:42 bartoldeman
* Saner lastdrive handling.
*
* Revision 1.12 2001/03/30 19:30:06 bartoldeman
* Misc fixes and implementation of SHELLHIGH. See history.txt for details.
*
* Revision 1.11 2001/03/21 02:56:25 bartoldeman
* See history.txt for changes. Bug fixes and HMA support are the main ones.
*
* Revision 1.10 2001/03/08 21:15:00 bartoldeman
* Redirector and DosSelectDrv() (Martin Stromberg) fixes
*
* Revision 1.9 2000/10/29 23:51:56 jimtabor
* Adding Share Support by Ron Cemer
*
* Revision 1.8 2000/08/06 05:50:17 jimtabor
* Add new files and update cvs with patches and changes
*
* Revision 1.7 2000/06/21 18:16:46 jimtabor
* Add UMB code, patch, and code fixes
*
* Revision 1.6 2000/06/01 06:37:38 jimtabor
* Read History for Changes
*
* Revision 1.5 2000/05/26 19:25:19 jimtabor
* Read History file for Change info
*
* Revision 1.4 2000/05/25 20:56:21 jimtabor
* Fixed project history
*
* Revision 1.3 2000/05/17 19:15:12 jimtabor
* Cleanup, add and fix source.
*
* Revision 1.2 2000/05/08 04:29:59 jimtabor
* Update CVS to 2020
*
* Revision 1.1.1.1 2000/05/06 19:34:53 jhall1
* The FreeDOS Kernel. A DOS kernel that aims to be 100% compatible with
* MS-DOS. Distributed under the GNU GPL.
*
* Revision 1.14 2000/04/02 05:01:08 jtabor
* Replaced ChgDir Code
*
* Revision 1.13 2000/04/02 04:53:56 jtabor
* Fix to DosChgDir
*
* Revision 1.12 2000/03/31 05:40:09 jtabor
* Added Eric W. Biederman Patches
*
* Revision 1.11 2000/03/09 06:07:11 kernel
* 2017f updates by James Tabor
*
* Revision 1.10 1999/09/23 04:40:46 jprice
* *** empty log message ***
*
* Revision 1.8 1999/09/14 01:01:53 jprice
* Fixed bug where you could write over directories.
*
* Revision 1.7 1999/08/25 03:18:07 jprice
* ror4 patches to allow TC 2.01 compile.
*
* Revision 1.6 1999/05/03 06:25:45 jprice
* Patches from ror4 and many changed of signed to unsigned variables.
*
* Revision 1.5 1999/04/16 12:21:22 jprice
* Steffen c-break handler changes
*
* Revision 1.4 1999/04/12 03:21:17 jprice
* more ror4 patches. Changes for multi-block IO
*
* Revision 1.3 1999/04/11 04:33:38 jprice
* ror4 patches
*
* Revision 1.2 1999/04/04 18:51:43 jprice
* no message
*
* Revision 1.1.1.1 1999/03/29 15:41:52 jprice
* New version without IPL.SYS
*
* Revision 1.4 1999/02/09 02:54:23 jprice
* Added Pat's 1937 kernel patches
*
* Revision 1.3 1999/02/01 01:43:28 jprice
* Fixed findfirst function to find volume label with Windows long filenames
*
* Revision 1.2 1999/01/22 04:15:28 jprice
* Formating
*
* Revision 1.1.1.1 1999/01/20 05:51:00 jprice
* Imported sources
*
* Rev 1.10 06 Dec 1998 8:44:42 patv
* Expanded dos functions due to new I/O subsystem.
*
* Rev 1.9 04 Jan 1998 23:14:38 patv
* Changed Log for strip utility
*
* Rev 1.8 03 Jan 1998 8:36:04 patv
* Converted data area to SDA format
*
* Rev 1.7 22 Jan 1997 12:59:56 patv
* pre-0.92 bug fixes
*
* Rev 1.6 16 Jan 1997 12:46:32 patv
* pre-Release 0.92 feature additions
*
* Rev 1.5 29 May 1996 21:15:20 patv
* bug fixes for v0.91a
*
* Rev 1.4 19 Feb 1996 3:20:08 patv
* Added NLS, int2f and config.sys processing
*
* Rev 1.2 01 Sep 1995 17:48:48 patv
* First GPL release.
*
* Rev 1.1 30 Jul 1995 20:50:24 patv
* Eliminated version strings in ipl
*
* Rev 1.0 02 Jul 1995 8:04:20 patv
* Initial revision.
*/
#include "globals.h"
sft FAR *get_sft(COUNT);
WORD get_free_hndl(VOID);
sft FAR *get_free_sft(WORD FAR *);
BOOL cmatch(COUNT, COUNT, COUNT);
struct f_node FAR *xlt_fd(COUNT);
/* /// Added for SHARE. - Ron Cemer */
int share_installed = 0;
/* DOS calls this to see if it's okay to open the file.
Returns a file_table entry number to use (>= 0) if okay
to open. Otherwise returns < 0 and may generate a critical
error. If < 0 is returned, it is the negated error return
code, so DOS simply negates this value and returns it in
AX. */
static int share_open_check
(char far *filename, /* far pointer to fully qualified filename */
unsigned short pspseg, /* psp segment address of owner process */
int openmode, /* 0=read-only, 1=write-only, 2=read-write */
int sharemode); /* SHARE_COMPAT, etc... */
/* DOS calls this to record the fact that it has successfully
closed a file, or the fact that the open for this file failed. */
static void share_close_file
(int fileno); /* file_table entry number */
/* DOS calls this to determine whether it can access (read or
write) a specific section of a file. We call it internally
from lock_unlock (only when locking) to see if any portion
of the requested region is already locked. If pspseg is zero,
then it matches any pspseg in the lock table. Otherwise, only
locks which DO NOT belong to pspseg will be considered.
Returns zero if okay to access or lock (no portion of the
region is already locked). Otherwise returns non-zero and
generates a critical error (if allowcriter is non-zero).
If non-zero is returned, it is the negated return value for
the DOS call. */
static int share_access_check
(unsigned short pspseg,/* psp segment address of owner process */
int fileno, /* file_table entry number */
unsigned long ofs, /* offset into file */
unsigned long len, /* length (in bytes) of region to access */
int allowcriter); /* allow a critical error to be generated */
/* DOS calls this to lock or unlock a specific section of a file.
Returns zero if successfully locked or unlocked. Otherwise
returns non-zero.
If the return value is non-zero, it is the negated error
return code for the DOS 0x5c call. */
static int share_lock_unlock
(unsigned short pspseg,/* psp segment address of owner process */
int fileno, /* file_table entry number */
unsigned long ofs, /* offset into file */
unsigned long len, /* length (in bytes) of region to lock or unlock */
int unlock); /* non-zero to unlock; zero to lock */
/* /// End of additions for SHARE. - Ron Cemer */
static VOID DosGetFile(BYTE FAR * lpszPath, BYTE FAR * lpszDosFileName)
{
BYTE szLclName[FNAME_SIZE + 1];
BYTE szLclExt[FEXT_SIZE + 1];
ParseDosName(lpszPath, (COUNT *) 0, (BYTE *) 0,
szLclName, szLclExt, FALSE);
SpacePad(szLclName, FNAME_SIZE);
SpacePad(szLclExt, FEXT_SIZE);
fbcopy((BYTE FAR *) szLclName, lpszDosFileName, FNAME_SIZE);
fbcopy((BYTE FAR *) szLclExt, &lpszDosFileName[FNAME_SIZE], FEXT_SIZE);
}
sft FAR *get_sft(COUNT hndl)
{
psp FAR *p = MK_FP(cu_psp, 0);
WORD sys_idx;
sfttbl FAR *sp;
if (hndl >= p->ps_maxfiles)
return (sft FAR *) - 1;
/* Get the SFT block that contains the SFT */
if (p->ps_filetab[hndl] == 0xff)
return (sft FAR *) - 1;
sys_idx = p->ps_filetab[hndl];
for (sp = sfthead; sp != (sfttbl FAR *) - 1; sp = sp->sftt_next)
{
if (sys_idx < sp->sftt_count)
break;
else
sys_idx -= sp->sftt_count;
}
/* If not found, return an error */
if (sp == (sfttbl FAR *) - 1)
return (sft FAR *) - 1;
/* finally, point to the right entry */
return (sft FAR *) & (sp->sftt_table[sys_idx]);
}
/*
* The `force_binary' parameter is a hack to allow functions 0x01, 0x06, 0x07,
* and function 0x40 to use the same code for performing reads, even though the
* two classes of functions behave quite differently: 0x01 etc. always do
* binary reads, while for 0x40 the type of read (binary/text) depends on what
* the SFT says. -- ror4
*/
UCOUNT GenericRead(COUNT hndl, UCOUNT n, BYTE FAR * bp, COUNT FAR * err,
BOOL force_binary)
{
sft FAR *s;
/* WORD sys_idx;*/
/*sfttbl FAR *sp;*/
UCOUNT ReadCount;
/* Test that the handle is valid */
if (hndl < 0)
{
*err = DE_INVLDHNDL;
return 0;
}
/* Get the SFT block that contains the SFT */
if ((s = get_sft(hndl)) == (sft FAR *) - 1)
{
*err = DE_INVLDHNDL;
return 0;
}
/* If not open or write permission - exit */
if (s->sft_count == 0 || (s->sft_mode & SFT_MWRITE))
{
*err = DE_INVLDACC;
return 0;
}
/*
* Do remote first or return error.
* must have been opened from remote.
*/
if (s->sft_flags & SFT_FSHARED)
{
ReadCount = Remote_RW(REM_READ, n, bp, s, err);
return *err == SUCCESS ? ReadCount : 0;
}
/* Do a device read if device */
if (s->sft_flags & SFT_FDEVICE)
{
request rq;
/* First test for eof and exit */
/* immediately if it is */
if (!(s->sft_flags & SFT_FEOF) || (s->sft_flags & SFT_FNUL))
{
s->sft_flags &= ~SFT_FEOF;
*err = SUCCESS;
return 0;
}
/* Now handle raw and cooked modes */
if (force_binary || (s->sft_flags & SFT_FBINARY))
{
rq.r_length = sizeof(request);
rq.r_command = C_INPUT;
rq.r_count = n;
rq.r_trans = (BYTE FAR *) bp;
rq.r_status = 0;
execrh((request FAR *) & rq, s->sft_dev);
if (rq.r_status & S_ERROR)
{
char_error(&rq, s->sft_dev);
}
else
{
*err = SUCCESS;
return rq.r_count;
}
}
else if (s->sft_flags & SFT_FCONIN)
{
kb_buf.kb_size = LINESIZE - 1;
kb_buf.kb_count = 0;
sti((keyboard FAR *) & kb_buf);
fbcopy((BYTE FAR *) kb_buf.kb_buf, bp, kb_buf.kb_count);
*err = SUCCESS;
return kb_buf.kb_count;
}
else
{
*bp = _sti();
*err = SUCCESS;
return 1;
}
}
else
/* a block read */
{
COUNT rc;
/* /// Added for SHARE - Ron Cemer */
if (IsShareInstalled()) {
if (s->sft_shroff >= 0) {
int share_result = share_access_check
(cu_psp,
s->sft_shroff,
s->sft_posit,
(unsigned long)n,
1);
if (share_result != 0) {
*err = share_result;
return 0;
}
}
}
/* /// End of additions for SHARE - Ron Cemer */
ReadCount = readblock(s->sft_status, bp, n, &rc);
if (rc != SUCCESS)
{
*err = rc;
return 0;
}
else
{
*err = SUCCESS;
return ReadCount;
}
}
*err = SUCCESS;
return 0;
}
UCOUNT DosRead(COUNT hndl, UCOUNT n, BYTE FAR * bp, COUNT FAR * err)
{
return GenericRead(hndl, n, bp, err, FALSE);
}
UCOUNT DosWrite(COUNT hndl, UCOUNT n, BYTE FAR * bp, COUNT FAR * err)
{
sft FAR *s;
/* WORD sys_idx;*/
/*sfttbl FAR *sp;*/
UCOUNT WriteCount;
/* Test that the handle is valid */
if (hndl < 0)
{
*err = DE_INVLDHNDL;
return 0;
}
/* Get the SFT block that contains the SFT */
if ((s = get_sft(hndl)) == (sft FAR *) - 1)
{
*err = DE_INVLDHNDL;
return 0;
}
/* If this is not opened and it's not a write */
/* another error */
if (s->sft_count == 0 ||
(!(s->sft_mode & SFT_MWRITE) && !(s->sft_mode & SFT_MRDWR)))
{
*err = DE_ACCESS;
return 0;
}
if (s->sft_flags & SFT_FSHARED)
{
WriteCount = Remote_RW(REM_WRITE, n, bp, s, err);
return *err == SUCCESS ? WriteCount : 0;
}
/* Do a device write if device */
if (s->sft_flags & SFT_FDEVICE)
{
request rq;
/* set to no EOF */
s->sft_flags &= ~SFT_FEOF;
/* if null just report full transfer */
if (s->sft_flags & SFT_FNUL)
{
*err = SUCCESS;
return n;
}
/* Now handle raw and cooked modes */
if (s->sft_flags & SFT_FBINARY)
{
rq.r_length = sizeof(request);
rq.r_command = C_OUTPUT;
rq.r_count = n;
rq.r_trans = (BYTE FAR *) bp;
rq.r_status = 0;
execrh((request FAR *) & rq, s->sft_dev);
if (rq.r_status & S_ERROR)
{
char_error(&rq, s->sft_dev);
}
else
{
if (s->sft_flags & SFT_FCONOUT)
{
WORD cnt = rq.r_count;
while (cnt--)
{
switch (*bp++)
{
case CR:
scr_pos = 0;
break;
case LF:
case BELL:
break;
case BS:
--scr_pos;
break;
default:
++scr_pos;
}
}
}
*err = SUCCESS;
return rq.r_count;
}
}
else
{
REG WORD /*c,*/
cnt = n,
spaces_left = 0,
next_pos,
xfer = 0;
static BYTE space = ' ';
start:
if (cnt-- == 0)
goto end;
if (*bp == CTL_Z)
goto end;
if (s->sft_flags & SFT_FCONOUT)
{
switch (*bp)
{
case CR:
next_pos = 0;
break;
case LF:
case BELL:
next_pos = scr_pos;
break;
case BS:
next_pos = scr_pos ? scr_pos - 1 : 0;
break;
case HT:
spaces_left = 8 - (scr_pos & 7);
next_pos = scr_pos + spaces_left;
goto output_space;
default:
next_pos = scr_pos + 1;
}
}
rq.r_length = sizeof(request);
rq.r_command = C_OUTPUT;
rq.r_count = 1;
rq.r_trans = bp;
rq.r_status = 0;
execrh((request FAR *) & rq, s->sft_dev);
if (rq.r_status & S_ERROR)
char_error(&rq, s->sft_dev);
goto post;
output_space:
rq.r_length = sizeof(request);
rq.r_command = C_OUTPUT;
rq.r_count = 1;
rq.r_trans = &space;
rq.r_status = 0;
execrh((request FAR *) & rq, s->sft_dev);
if (rq.r_status & S_ERROR)
char_error(&rq, s->sft_dev);
--spaces_left;
post:
if (spaces_left)
goto output_space;
++bp;
++xfer;
if (s->sft_flags & SFT_FCONOUT)
scr_pos = next_pos;
if (break_ena && control_break())
{
handle_break();
goto end;
}
goto start;
end:
*err = SUCCESS;
return xfer;
}
}
else
/* a block write */
{
COUNT rc;
/* /// Added for SHARE - Ron Cemer */
if (IsShareInstalled()) {
if (s->sft_shroff >= 0) {
int share_result = share_access_check
(cu_psp,
s->sft_shroff,
s->sft_posit,
(unsigned long)n,
1);
if (share_result != 0) {
*err = share_result;
return 0;
}
}
}
/* /// End of additions for SHARE - Ron Cemer */
WriteCount = writeblock(s->sft_status, bp, n, &rc);
if (rc < SUCCESS)
{
*err = rc;
return 0;
}
else
{
*err = SUCCESS;
return WriteCount;
}
}
*err = SUCCESS;
return 0;
}
COUNT SftSeek(sft FAR *s, LONG new_pos, COUNT mode)
{
ULONG data;
/* Test for invalid mode */
if (mode < 0 || mode > 2)
return DE_INVLDFUNC;
lpCurSft = (sfttbl FAR *) s;
if (s->sft_flags & SFT_FSHARED)
{
/* seek from end of file */
if (mode == 2) {
/*
* RB list has it as Note:
* this function is called by the DOS 3.1+ kernel, but only when seeking
* from the end of a file opened with sharing modes set in such a manner
* that another process is able to change the size of the file while it
* is already open
* Tested this with Shsucdx ver 0.06 and 1.0. Both now work.
* Lredir via mfs.c from DosEMU works when writing appended files.
* Mfs.c looks for these mode bits set, so here is my best guess.;^)
*/
if ((s->sft_mode & SFT_MDENYREAD) || (s->sft_mode & SFT_MDENYNONE))
{
int2f_Remote_call(REM_LSEEK, 0, (UWORD) FP_SEG(new_pos), (UWORD) FP_OFF(new_pos), (VOID FAR *) s, 0, (VOID FAR *)&data);
s->sft_posit = data;
return SUCCESS;
}
else
{
s->sft_posit = s->sft_size + new_pos;
return SUCCESS;
}
}
if (mode == 0) {
s->sft_posit = new_pos;
return SUCCESS;
}
if (mode == 1) {
s->sft_posit += new_pos;
return SUCCESS;
}
return DE_INVLDFUNC;
}
/* Do special return for character devices */
if (s->sft_flags & SFT_FDEVICE)
{
s->sft_posit = 0l;
return SUCCESS;
}
else
{
LONG result = dos_lseek(s->sft_status, new_pos, mode);
if (result < 0l)
return (int)result;
else {
s->sft_posit = result;
return SUCCESS;
}
}
}
COUNT DosSeek(COUNT hndl, LONG new_pos, COUNT mode, ULONG * set_pos)
{
sft FAR *s;
COUNT result;
/* Test that the handle is valid */
if (hndl < 0)
return DE_INVLDHNDL;
/* Get the SFT block that contains the SFT */
if ((s = get_sft(hndl)) == (sft FAR *) - 1)
return DE_INVLDHNDL;
result = SftSeek(s, new_pos, mode);
if (result == SUCCESS) {
*set_pos = s->sft_posit;
}
return result;
}
static WORD get_free_hndl(void)
{
psp FAR *p = MK_FP(cu_psp, 0);
WORD hndl;
for (hndl = 0; hndl < p->ps_maxfiles; hndl++)
{
if (p->ps_filetab[hndl] == 0xff)
return hndl;
}
return 0xff;
}
sft FAR *get_free_sft(WORD FAR * sft_idx)
{
WORD sys_idx = 0;
sfttbl FAR *sp;
/* Get the SFT block that contains the SFT */
for (sp = sfthead; sp != (sfttbl FAR *) - 1; sp = sp->sftt_next)
{
REG WORD i;
for (i = 0; i < sp->sftt_count; i++)
{
if (sp->sftt_table[i].sft_count == 0)
{
*sft_idx = sys_idx + i;
return (sft FAR *) & sp->sftt_table[sys_idx + i];
}
}
sys_idx += i;
}
/* If not found, return an error */
return (sft FAR *) - 1;
}
BYTE FAR *get_root(BYTE FAR * fname)
{
BYTE FAR *froot;
REG WORD length;
/* find the end */
for (length = 0, froot = fname; *froot != '\0'; ++froot)
++length;
/* now back up to first path seperator or start */
for (--froot; length > 0 && !(*froot == '/' || *froot == '\\'); --froot)
--length;
return ++froot;
}
/* Ascii only file name match routines */
static BOOL cmatch(COUNT s, COUNT d, COUNT mode)
{
if (s >= 'a' && s <= 'z')
s -= 'a' - 'A';
if (d >= 'a' && d <= 'z')
d -= 'a' - 'A';
if (mode && s == '?' && (d >= 'A' && s <= 'Z'))
return TRUE;
return s == d;
}
BOOL fnmatch(BYTE FAR * s, BYTE FAR * d, COUNT n, COUNT mode)
{
while (n--)
{
if (!cmatch(*s++, *d++, mode))
return FALSE;
}
return TRUE;
}
COUNT DosCreat(BYTE FAR * fname, COUNT attrib)
{
psp FAR *p = MK_FP(cu_psp, 0);
WORD hndl, sft_idx;
sft FAR *sftp;
struct dhdr FAR *dhp;
/* BYTE FAR *froot;*/
/* WORD i;*/
COUNT result, drive;
/* get a free handle */
if ((hndl = get_free_hndl()) == 0xff)
return DE_TOOMANY;
/* now get a free system file table entry */
if ((sftp = get_free_sft((WORD FAR *) & sft_idx)) == (sft FAR *) - 1)
return DE_TOOMANY;
sftp->sft_shroff = -1; /* /// Added for SHARE - Ron Cemer */
/* check for a device */
dhp = IsDevice(fname);
if ( dhp )
{
sftp->sft_count += 1;
sftp->sft_mode = SFT_MRDWR;
sftp->sft_attrib = attrib;
sftp->sft_flags =
((dhp->dh_attr & ~SFT_MASK) & ~SFT_FSHARED) | SFT_FDEVICE | SFT_FEOF;
sftp->sft_psp = cu_psp;
fbcopy((BYTE FAR *) SecPathName, sftp->sft_name, FNAME_SIZE + FEXT_SIZE);
sftp->sft_dev = dhp;
p->ps_filetab[hndl] = sft_idx;
return hndl;
}
drive = get_verify_drive(fname);
if(drive < 0) {
return drive;
}
result = truename(fname, PriPathName, FALSE);
if (result != SUCCESS) {
return result;
}
if (CDSp->cds_table[drive].cdsFlags & CDSNETWDRV) {
lpCurSft = (sfttbl FAR *)sftp;
sftp->sft_mode = attrib;
result = int2f_Remote_call(REM_CREATE, 0, 0, 0, (VOID FAR *) sftp, 0, MK_FP(0, attrib));
result = -result;
if (result == SUCCESS) {
sftp->sft_count += 1;
p->ps_filetab[hndl] = sft_idx;
return hndl;
}
return result;
}
/* /// Added for SHARE. - Ron Cemer */
if (IsShareInstalled()) {
if ((sftp->sft_shroff = share_open_check
((char far *)PriPathName,
cu_psp,
0x02, /* read-write */
0)) < 0) /* compatibility mode */
return sftp->sft_shroff;
}
/* /// End of additions for SHARE. - Ron Cemer */
sftp->sft_status = dos_creat(fname, attrib);
if (sftp->sft_status >= 0)
{
p->ps_filetab[hndl] = sft_idx;
sftp->sft_count += 1;
sftp->sft_mode = SFT_MRDWR;
sftp->sft_attrib = attrib;
sftp->sft_flags = 0;
sftp->sft_psp = cu_psp;
DosGetFile(fname, sftp->sft_name);
return hndl;
} else {
/* /// Added for SHARE *** CURLY BRACES ADDED ALSO!!! ***. - Ron Cemer */
if (IsShareInstalled()) {
share_close_file(sftp->sft_shroff);
sftp->sft_shroff = -1;
}
/* /// End of additions for SHARE. - Ron Cemer */
return sftp->sft_status;
}
}
COUNT CloneHandle(COUNT hndl)
{
sft FAR *sftp;
/* now get the system file table entry */
if ((sftp = get_sft(hndl)) == (sft FAR *) - 1)
return DE_INVLDHNDL;
/* now that we have the system file table entry, get the fnode */
/* index, and increment the count, so that we've effectively */
/* cloned the file. */
sftp->sft_count += 1;
return SUCCESS;
}
COUNT DosDup(COUNT Handle)
{
psp FAR *p = MK_FP(cu_psp, 0);
COUNT NewHandle;
sft FAR *Sftp;
/* Get the SFT block that contains the SFT */
if ((Sftp = get_sft(Handle)) == (sft FAR *) - 1)
return DE_INVLDHNDL;
/* If not open - exit */
if (Sftp->sft_count <= 0)
return DE_INVLDHNDL;
/* now get a free handle */
if ((NewHandle = get_free_hndl()) == 0xff)
return DE_TOOMANY;
/* If everything looks ok, bump it up. */
if ((Sftp->sft_flags & SFT_FDEVICE) || (Sftp->sft_status >= 0))
{
p->ps_filetab[NewHandle] = p->ps_filetab[Handle];
Sftp->sft_count += 1;
return NewHandle;
}
else
return DE_INVLDHNDL;
}
COUNT DosForceDup(COUNT OldHandle, COUNT NewHandle)
{
psp FAR *p = MK_FP(cu_psp, 0);
sft FAR *Sftp;
/* Get the SFT block that contains the SFT */
if ((Sftp = get_sft(OldHandle)) == (sft FAR *) - 1)
return DE_INVLDHNDL;
/* If not open - exit */
if (Sftp->sft_count <= 0)
return DE_INVLDHNDL;
/* now close the new handle if it's open */
if ((UBYTE) p->ps_filetab[NewHandle] != 0xff)
{
COUNT ret;
if ((ret = DosClose(NewHandle)) != SUCCESS)
return ret;
}
/* If everything looks ok, bump it up. */
if ((Sftp->sft_flags & SFT_FDEVICE) || (Sftp->sft_status >= 0))
{
p->ps_filetab[NewHandle] = p->ps_filetab[OldHandle];
Sftp->sft_count += 1;
return NewHandle;
}
else
return DE_INVLDHNDL;
}
COUNT DosOpen(BYTE FAR * fname, COUNT mode)
{
psp FAR *p = MK_FP(cu_psp, 0);
WORD hndl;
WORD sft_idx;
sft FAR *sftp;
struct dhdr FAR *dhp;
/* BYTE FAR *froot;*/
/* WORD i;*/
COUNT drive, result;
/* /// Added to adjust for filenames which begin with ".\"
The problem was manifesting itself in the inability
to run an program whose filename (without the extension)
was longer than six characters and the PATH variable
contained ".", unless you explicitly specified the full
path to the executable file.
Jun 11, 2000 - rbc */
if ( (fname[0] == '.') && (fname[1] == '\\') ) fname += 2;
/* test if mode is in range */
if ((mode & ~SFT_OMASK) != 0)
return DE_INVLDACC;
mode &= 3;
/* get a free handle */
if ((hndl = get_free_hndl()) == 0xff)
return DE_TOOMANY;
OpenMode = (BYTE) mode;
/* now get a free system file table entry */
if ((sftp = get_free_sft((WORD FAR *) & sft_idx)) == (sft FAR *) - 1)
return DE_TOOMANY;
sftp->sft_shroff = -1; /* /// Added for SHARE - Ron Cemer */
sftp->sft_mode = mode;
/* check for a device */
dhp = IsDevice(fname);
if ( dhp )
{
sftp->sft_count += 1;
sftp->sft_attrib = 0;
sftp->sft_flags =
((dhp->dh_attr & ~SFT_MASK) & ~SFT_FSHARED) | SFT_FDEVICE | SFT_FEOF;
sftp->sft_psp = cu_psp;
fbcopy((BYTE FAR *) SecPathName, sftp->sft_name, FNAME_SIZE + FEXT_SIZE);
sftp->sft_dev = dhp;
sftp->sft_date = dos_getdate();
sftp->sft_time = dos_gettime();
p->ps_filetab[hndl] = sft_idx;
return hndl;
}
drive = get_verify_drive(fname);
if (drive < 0) {
return drive;
}
result = truename(fname, PriPathName, FALSE);
if (result != SUCCESS) {
return result;
}
if (CDSp->cds_table[drive].cdsFlags & CDSNETWDRV) {
lpCurSft = (sfttbl FAR *)sftp;
result = int2f_Remote_call(REM_OPEN, 0, 0, 0, (VOID FAR *) sftp, 0, MK_FP(0, mode));
result = -result;
if (result == SUCCESS) {
sftp->sft_count += 1;
p->ps_filetab[hndl] = sft_idx;
return hndl;
}
return result;
}
/* /// Added for SHARE. - Ron Cemer */
if (IsShareInstalled()) {
if ((sftp->sft_shroff = share_open_check
((char far *)PriPathName,
(unsigned short)cu_psp,
mode & 0x03,
(mode >> 2) & 0x07)) < 0)
return sftp->sft_shroff;
}
/* /// End of additions for SHARE. - Ron Cemer */
sftp->sft_status = dos_open(fname, mode);
if (sftp->sft_status >= 0)
{
struct f_node FAR *fnp = xlt_fd(sftp->sft_status);
sftp->sft_attrib = fnp->f_dir.dir_attrib;
/* Check permissions. -- JPP */
if ((sftp->sft_attrib & (D_DIR | D_VOLID)) ||
((sftp->sft_attrib & D_RDONLY) && (mode != O_RDONLY)))
{
return DE_ACCESS;
}
p->ps_filetab[hndl] = sft_idx;
sftp->sft_count += 1;
sftp->sft_mode = mode;
sftp->sft_attrib = 0;
sftp->sft_flags = 0;
sftp->sft_psp = cu_psp;
DosGetFile(fname, sftp->sft_name);
return hndl;
} else {
/* /// Added for SHARE *** CURLY BRACES ADDED ALSO!!! ***. - Ron Cemer */
if (IsShareInstalled()) {
share_close_file(sftp->sft_shroff);
sftp->sft_shroff = -1;
}
/* /// End of additions for SHARE. - Ron Cemer */
return sftp->sft_status;
}
}
COUNT DosClose(COUNT hndl)
{
psp FAR *p = MK_FP(cu_psp, 0);
sft FAR *s;
/* Test that the handle is valid */
if (hndl < 0)
return DE_INVLDHNDL;
/* Get the SFT block that contains the SFT */
if ((s = get_sft(hndl)) == (sft FAR *) - 1)
return DE_INVLDHNDL;
/* If this is not opened another error */
if (s->sft_count == 0)
return DE_ACCESS;
lpCurSft = (sfttbl FAR *) s;
/*
remote sub sft_count.
*/
if (s->sft_flags & SFT_FSHARED)
{
int2f_Remote_call(REM_CLOSE, 0, 0, 0, (VOID FAR *) s, 0, 0);
p->ps_filetab[hndl] = 0xff;
return SUCCESS;
}
/* now just drop the count if a device, else */
/* call file system handler */
if (s->sft_flags & SFT_FDEVICE)
{
p->ps_filetab[hndl] = 0xff;
s->sft_count -= 1;
return SUCCESS;
}
else
{
p->ps_filetab[hndl] = 0xff;
s->sft_count -= 1;
if (s->sft_count > 0)
return SUCCESS;
else {
/* /// Added for SHARE *** CURLY BRACES ADDED ALSO!!! ***. - Ron Cemer */
if (IsShareInstalled()) {
if (s->sft_shroff >= 0) share_close_file(s->sft_shroff);
s->sft_shroff = -1;
}
/* /// End of additions for SHARE. - Ron Cemer */
return dos_close(s->sft_status);
}
}
}
VOID DosGetFree(COUNT drive, COUNT FAR * spc, COUNT FAR * navc, COUNT FAR * bps, COUNT FAR * nc)
{
struct dpb *dpbp;
struct cds FAR *cdsp;
static COUNT rg[4];
/* next - "log" in the drive */
drive = (drive == 0 ? default_drive : drive - 1);
/* first check for valid drive */
if (drive < 0 || drive >= lastdrive)
{
*spc = -1;
return;
}
cdsp = &CDSp->cds_table[drive];
if (cdsp->cdsFlags & CDSNETWDRV)
{
int2f_Remote_call(REM_GETSPACE, 0, 0, 0, cdsp, 0, &rg);
*spc = (COUNT) rg[0];
*nc = (COUNT) rg[1];
*bps = (COUNT) rg[2];
*navc = (COUNT) rg[3];
return;
}
dpbp = (struct dpb *)CDSp->cds_table[drive].cdsDpb;
if (dpbp == 0)
{
*spc = -1;
return;
}
if ((media_check(dpbp) < 0))
{
*spc = -1;
return;
}
/* get the data vailable from dpb */
*nc = dpbp->dpb_size;
*spc = dpbp->dpb_clsmask + 1;
*bps = dpbp->dpb_secsize;
/* now tell fs to give us free cluster */
/* count */
*navc = dos_free(dpbp);
}
COUNT DosGetCuDir(COUNT drive, BYTE FAR * s)
{
REG struct cds FAR *cdsp;
/* next - "log" in the drive */
drive = (drive == 0 ? default_drive : drive - 1);
/* first check for valid drive */
if (drive < 0 || drive >= lastdrive) {
return DE_INVLDDRV;
}
cdsp = &CDSp->cds_table[drive];
current_ldt = cdsp;
if (!(cdsp->cdsFlags & CDSNETWDRV) && (cdsp->cdsDpb == 0)) {
return DE_INVLDDRV;
}
fsncopy((BYTE FAR *) & cdsp->cdsCurrentPath[1 + cdsp->cdsJoinOffset], s, 64);
return SUCCESS;
}
#undef CHDIR_DEBUG
COUNT DosChangeDir(BYTE FAR * s)
{
REG struct cds FAR *cdsp;
REG COUNT drive;
COUNT result;
BYTE FAR *p;
/* don't do wildcard CHDIR --TE*/
for (p = s; *p; p++)
if (*p == '*' || *p == '?')
return DE_PATHNOTFND;
drive = get_verify_drive(s);
if (drive < 0 ) {
return drive;
}
result = truename(s, PriPathName, FALSE);
if (result != SUCCESS) {
return result;
}
cdsp = &CDSp->cds_table[drive];
current_ldt = cdsp;
if (cdsp->cdsFlags & CDSNETWDRV)
{
#if defined(CHDIR_DEBUG)
printf("Remote Chdir: n='");
p = s; while(*p) printf("%c", *p++);
printf("' p='");
p = PriPathName; while(*p) printf("%c", *p++);
printf("'\n");
#endif
result = int2f_Remote_call(REM_CHDIR, 0, 0, 0, PriPathName, 0, 0);
#if defined(CHDIR_DEBUG)
printf("status = %04x, new_path='", result);
p = cdsd->cdsCurrentPath; while(p) printf("%c", *p++)
printf("'\n");
#endif
result = -result;
if (result != SUCCESS) {
return DE_PATHNOTFND;
}
/*
Some redirectors do not write back to the CDS.
SHSUCdX needs this. jt
*/
fscopy(&PriPathName[0], cdsp->cdsCurrentPath);
if (PriPathName[7] == 0)
cdsp->cdsCurrentPath[8] = 0; /* Need two Zeros at the end */
} else {
/* now get fs to change to new */
/* directory */
result = dos_cd(cdsp, PriPathName);
}
if (result == SUCCESS) {
fscopy(&PriPathName[0], cdsp->cdsCurrentPath);
}
return result;
}
COUNT DosFindFirst(UCOUNT attr, BYTE FAR * name)
{
return dos_findfirst(attr, name);
}
COUNT DosFindNext(void)
{
return dos_findnext();
}
COUNT DosGetFtime(COUNT hndl, date FAR * dp, time FAR * tp)
{
sft FAR *s;
/*sfttbl FAR *sp;*/
/* Test that the handle is valid */
if (hndl < 0)
return DE_INVLDHNDL;
/* Get the SFT block that contains the SFT */
if ((s = get_sft(hndl)) == (sft FAR *) - 1)
return DE_INVLDHNDL;
/* If this is not opened another error */
if (s->sft_count == 0)
return DE_ACCESS;
/* If SFT entry refers to a device, return the date and time of opening */
if (s->sft_flags & (SFT_FDEVICE | SFT_FSHARED))
{
*dp = s->sft_date;
*tp = s->sft_time;
return SUCCESS;
}
/* call file system handler */
return dos_getftime(s->sft_status, dp, tp);
}
COUNT DosSetFtime(COUNT hndl, date FAR * dp, time FAR * tp)
{
sft FAR *s;
/*sfttbl FAR *sp;*/
/* Test that the handle is valid */
if (hndl < 0)
return DE_INVLDHNDL;
/* Get the SFT block that contains the SFT */
if ((s = get_sft(hndl)) == (sft FAR *) - 1)
return DE_INVLDHNDL;
/* If this is not opened another error */
if (s->sft_count == 0)
return DE_ACCESS;
/* If SFT entry refers to a device, do nothing */
if (s->sft_flags & SFT_FDEVICE)
return SUCCESS;
if (s->sft_flags & SFT_FSHARED)
{
s->sft_date = *dp;
s->sft_time = *tp;
return SUCCESS;
}
/* call file system handler */
return dos_setftime(s->sft_status, dp, tp);
}
COUNT DosGetFattr(BYTE FAR * name, UWORD FAR * attrp)
{
static UWORD srfa[5];
COUNT result, drive;
struct cds FAR *last_cds;
BYTE FAR * p;
if (IsDevice(name)) {
return DE_PATHNOTFND;
}
drive = get_verify_drive(name);
if (drive < 0) {
return drive;
}
result = truename(name, PriPathName, FALSE);
if (result != SUCCESS) {
return result;
}
/* /// Added check for "d:\", which returns 0x10 (subdirectory) under DOS.
- Ron Cemer */
if ( (PriPathName[0] != '\0')
&& (PriPathName[1] == ':')
&& ( (PriPathName[2] == '/') || (PriPathName[2] == '\\') )
&& (PriPathName[3] == '\0') ) {
*attrp = 0x10;
return SUCCESS;
}
if (CDSp->cds_table[drive].cdsFlags & CDSNETWDRV)
{
last_cds = current_ldt;
current_ldt = &CDSp->cds_table[drive];
result = int2f_Remote_call(REM_GETATTRZ, 0, 0, 0, 0, 0, (VOID FAR *) srfa);
result = -result;
current_ldt = last_cds;
*attrp = srfa[0];
}
else {
/* /// Use truename()'s result, which we already have in PriPathName.
I copy it to tmp_name because PriPathName is global and seems
to get trashed somewhere in transit.
The reason for using truename()'s result is that dos_?etfattr()
are very low-level functions and don't handle full path expansion
or cleanup, such as converting "c:\a\b\.\c\.." to "C:\A\B".
- Ron Cemer
*/
BYTE tmp_name[128];
int i;
for (i = 0; PriPathName[i] != '\0'; i++) tmp_name[i] = PriPathName[i];
tmp_name[i] = '\0';
result = dos_getfattr(tmp_name, attrp);
}
/* Sorry Ron someone else found this, see history.txt */
return result;
}
COUNT DosSetFattr(BYTE FAR * name, UWORD FAR * attrp)
{
COUNT result, drive;
struct cds FAR *last_cds;
BYTE FAR *p;
if (IsDevice(name) ) {
return DE_PATHNOTFND;
}
drive = get_verify_drive(name);
if (drive < 0) {
return drive;
}
result = truename(name, PriPathName, FALSE);
if (result != SUCCESS) {
return result;
}
if (CDSp->cds_table[drive].cdsFlags & CDSNETWDRV)
{
last_cds = current_ldt;
current_ldt = &CDSp->cds_table[drive];
result = int2f_Remote_call(REM_SETATTR, 0, 0, 0, 0, 0, MK_FP(0, attrp));
result = -result;
current_ldt = last_cds;
}
else {
/* /// Use truename()'s result, which we already have in PriPathName.
I copy it to tmp_name because PriPathName is global and seems
to get trashed somewhere in transit.
- Ron Cemer
*/
BYTE tmp_name[128];
int i;
for (i = 0; PriPathName[i] != '\0'; i++) tmp_name[i] = PriPathName[i];
tmp_name[i] = '\0';
result = dos_setfattr(name, attrp);
}
return result;
}
BYTE DosSelectDrv(BYTE drv)
{
if ((0 <= drv) && (drv < lastdrive) &&(CDSp->cds_table[drv].cdsFlags & 0xf000))
{
current_ldt = &CDSp->cds_table[drv];
default_drive = drv;
}
return lastdrive;
}
COUNT DosDelete(BYTE FAR *path)
{
COUNT result, drive;
if (IsDevice(path)) {
return DE_PATHNOTFND;
}
drive = get_verify_drive(path);
if (drive < 0) {
return drive;
}
result = truename(path, PriPathName, FALSE);
if (result != SUCCESS) {
return result;
}
current_ldt = &CDSp->cds_table[drive];
if (CDSp->cds_table[drive].cdsFlags & CDSNETWDRV) {
result = int2f_Remote_call(REM_DELETE, 0, 0, 0, 0, 0, 0);
result = -result;
} else {
result = dos_delete(path);
}
return result;
}
COUNT DosRename(BYTE FAR * path1, BYTE FAR * path2)
{
COUNT result, drive1, drive2;
if (IsDevice(path1) || IsDevice(path2)) {
return DE_PATHNOTFND;
}
drive1 = get_verify_drive(path1);
result = truename(path1, PriPathName, FALSE);
if (result != SUCCESS) {
return result;
}
drive2 = get_verify_drive(path2);
result = truename(path2, SecPathName, FALSE);
if (result != SUCCESS) {
return result;
}
if ((drive1 != drive2) || (drive1 < 0)) {
return DE_INVLDDRV;
}
current_ldt = &CDSp->cds_table[drive1];
if (CDSp->cds_table[drive1].cdsFlags & CDSNETWDRV) {
result = int2f_Remote_call(REM_RENAME, 0, 0, 0, 0, 0, 0);
result = -result;
} else {
result = dos_rename(path1, path2);
}
return result;
}
COUNT DosMkdir(BYTE FAR * dir)
{
COUNT result, drive;
if (IsDevice(dir)) {
return DE_PATHNOTFND;
}
drive = get_verify_drive(dir);
if (drive < 0) {
return drive;
}
result = truename(dir, PriPathName, FALSE);
if (result != SUCCESS) {
return result;
}
current_ldt = &CDSp->cds_table[drive];
if (CDSp->cds_table[drive].cdsFlags & CDSNETWDRV) {
result = int2f_Remote_call(REM_MKDIR, 0, 0, 0, 0, 0, 0);
result = -result;
} else {
result = dos_mkdir(dir);
}
return result;
}
COUNT DosRmdir(BYTE FAR * dir)
{
COUNT result, drive;
if (IsDevice(dir)) {
return DE_PATHNOTFND;
}
drive = get_verify_drive(dir);
if (drive < 0) {
return drive;
}
result = truename(dir, PriPathName, FALSE);
if (result != SUCCESS) {
return result;
}
current_ldt = &CDSp->cds_table[drive];
if (CDSp->cds_table[drive].cdsFlags & CDSNETWDRV) {
result = int2f_Remote_call(REM_RMDIR, 0, 0, 0, 0, 0, 0);
result = -result;
} else {
result = dos_rmdir(dir);
}
return result;
}
/* /// Added for SHARE. - Ron Cemer */
COUNT DosLockUnlock(COUNT hndl, LONG pos, LONG len, COUNT unlock)
{
sft FAR *s;
/* Invalid function unless SHARE is installed. */
if (!IsShareInstalled()) return DE_INVLDFUNC;
/* Test that the handle is valid */
if (hndl < 0) return DE_INVLDHNDL;
/* Get the SFT block that contains the SFT */
if ((s = get_sft(hndl)) == (sft FAR *) -1) return DE_INVLDHNDL;
/* Lock violation if this SFT entry does not support locking. */
if (s->sft_shroff < 0) return DE_LOCK;
/* Let SHARE do the work. */
return share_lock_unlock(cu_psp, s->sft_shroff, pos, len, unlock);
}
/* /// End of additions for SHARE. - Ron Cemer */
/*
* This seems to work well.
*/
struct dhdr FAR * IsDevice(BYTE FAR * fname)
{
struct dhdr FAR *dhp;
BYTE FAR *froot;
WORD i;
BYTE tmpPathName[FNAME_SIZE+1];
/* check for a device */
froot = get_root(fname);
for (i = 0; i < FNAME_SIZE; i++)
{
if (*froot != '\0' && *froot != '.')
tmpPathName[i] = *froot++;
else
break;
}
for (; i < FNAME_SIZE; i++)
tmpPathName[i] = ' ';
tmpPathName[i] = 0;
/* /// BUG!!! This is absolutely wrong. A filename of "NUL.LST" must be
treated EXACTLY the same as a filename of "NUL". The existence or
content of the extension is irrelevent in determining whether a
filename refers to a device.
- Ron Cemer
// if we have an extension, can't be a device <--- WRONG.
if (*froot != '.')
{
*/
for (dhp = (struct dhdr FAR *)&nul_dev; dhp != (struct dhdr FAR *)-1; dhp = dhp->dh_next)
{
/* BUGFIX: MSCD000<00> should be handled like MSCD000<20> TE */
char dev_name_buff[FNAME_SIZE];
int namelen = fstrlen(dhp->dh_name);
memset(dev_name_buff, ' ', FNAME_SIZE);
fmemcpy(dev_name_buff,dhp->dh_name, min(namelen,FNAME_SIZE));
if (fnmatch((BYTE FAR *) tmpPathName, (BYTE FAR *) dev_name_buff, FNAME_SIZE, FALSE))
{
memcpy(SecPathName, tmpPathName, i+1);
return dhp;
}
}
return (struct dhdr FAR *)0;
}
/* /// Added for SHARE. - Ron Cemer */
BOOL IsShareInstalled(void) {
if (!share_installed) {
iregs regs;
regs.a.x = 0x1000;
intr(0x2f, &regs);
share_installed = ((regs.a.x & 0xff) == 0xff);
}
return share_installed;
}
/* DOS calls this to see if it's okay to open the file.
Returns a file_table entry number to use (>= 0) if okay
to open. Otherwise returns < 0 and may generate a critical
error. If < 0 is returned, it is the negated error return
code, so DOS simply negates this value and returns it in
AX. */
static int share_open_check
(char far *filename, /* far pointer to fully qualified filename */
unsigned short pspseg, /* psp segment address of owner process */
int openmode, /* 0=read-only, 1=write-only, 2=read-write */
int sharemode) { /* SHARE_COMPAT, etc... */
iregs regs;
regs.a.x = 0x10a0;
regs.ds = FP_SEG(filename);
regs.si = FP_OFF(filename);
regs.b.x = pspseg;
regs.c.x = openmode;
regs.d.x = sharemode;
intr(0x2f, &regs);
return (int)regs.a.x;
}
/* DOS calls this to record the fact that it has successfully
closed a file, or the fact that the open for this file failed. */
static void share_close_file
(int fileno) { /* file_table entry number */
iregs regs;
regs.a.x = 0x10a1;
regs.b.x = fileno;
intr(0x2f, &regs);
}
/* DOS calls this to determine whether it can access (read or
write) a specific section of a file. We call it internally
from lock_unlock (only when locking) to see if any portion
of the requested region is already locked. If pspseg is zero,
then it matches any pspseg in the lock table. Otherwise, only
locks which DO NOT belong to pspseg will be considered.
Returns zero if okay to access or lock (no portion of the
region is already locked). Otherwise returns non-zero and
generates a critical error (if allowcriter is non-zero).
If non-zero is returned, it is the negated return value for
the DOS call. */
static int share_access_check
(unsigned short pspseg,/* psp segment address of owner process */
int fileno, /* file_table entry number */
unsigned long ofs, /* offset into file */
unsigned long len, /* length (in bytes) of region to access */
int allowcriter) { /* allow a critical error to be generated */
iregs regs;
regs.a.x = 0x10a2 | (allowcriter ? 0x01 : 0x00);
regs.b.x = pspseg;
regs.c.x = fileno;
regs.si = (unsigned short)((ofs >> 16) & 0xffffL);
regs.di = (unsigned short)(ofs & 0xffffL);
regs.es = (unsigned short)((len >> 16) & 0xffffL);
regs.d.x = (unsigned short)(len & 0xffffL);
intr(0x2f, &regs);
return (int)regs.a.x;
}
/* DOS calls this to lock or unlock a specific section of a file.
Returns zero if successfully locked or unlocked. Otherwise
returns non-zero.
If the return value is non-zero, it is the negated error
return code for the DOS 0x5c call. */
static int share_lock_unlock
(unsigned short pspseg,/* psp segment address of owner process */
int fileno, /* file_table entry number */
unsigned long ofs, /* offset into file */
unsigned long len, /* length (in bytes) of region to lock or unlock */
int unlock) { /* non-zero to unlock; zero to lock */
iregs regs;
regs.a.x = 0x10a4 | (unlock ? 0x01 : 0x00);
regs.b.x = pspseg;
regs.c.x = fileno;
regs.si = (unsigned short)((ofs >> 16) & 0xffffL);
regs.di = (unsigned short)(ofs & 0xffffL);
regs.es = (unsigned short)((len >> 16) & 0xffffL);
regs.d.x = (unsigned short)(len & 0xffffL);
intr(0x2f, &regs);
return (int)regs.a.x;
}
/* /// End of additions for SHARE. - Ron Cemer */