/* * libc/sys/syscall.c * * System Call (Trap) Interface. This file contains those functions * which are in Chapter 2 but emulate system traps as opposed to directly * trapping to the GNO kernel. The actual kernel trap calls are in trap.c * There are also a few support routines in here. * * Unless otherwise specified, see the respective man pages for details * about these routines. * * $Id: syscall.c,v 1.6 1997/12/21 20:13:19 gdr Exp $ * * This file is formatted with tab stops every 3 columns. */ #ifdef __ORCAC__ segment "libc_sys__"; #endif #define __USE_DYNAMIC_GSSTRING__ /* * Use bits 0, 1, 2, 6 (== decimal 71) for optimization. In Orca/C v2.1.0, * bits 4 and 5 are still reported to be buggy. * * Around variadic routines, we also add in optimization bit 3 (== 79). */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include /* * This is the maximum file name length allowed to be returned by GS/OS * in these stubs. */ #define GSOS_NAME_MAX PATH_MAX /* these are prototyped in the Orca/C manual, but not in any header */ extern pascal void SystemQuitFlags (unsigned int); extern pascal void SystemQuitPath (GSStringPtr); /* these are the access bits to the GS/OS Create call */ #define GSOS_READ 0x0001 #define GSOS_WRITE 0x0002 #define GSOS_INVISIBLE 0x0004 #define GSOS_BACKUP 0x0020 #define GSOS_RENAME 0x0040 #define GSOS_DESTROY 0x0080 /* file types and, for EXEC, the auxtype */ #define TXT 0x04 /* text file */ #define BIN 0x06 /* binary file */ #define EXE 0xB5 /* shell command */ #define DIR 0x0F /* directory */ #define S16 0xB3 /* system file (application) */ #define SRC 0xB0 /* SRC + EXEC = shell script */ #define EXEC 0x0006 #ifdef VERSION_CHECK static void _libcPanic (const char *fmt, ...); #endif /* * _chdir * * chdir and fchdir are implemented in terms of this function. Note that * _might_ be changed by _chdir() -- if an error occurs setting * prefix 0, then the length of will be set to zero. */ static int _chdir(GSStringPtr pathname) { PrefixRecGS prefx; struct { /* truncated version of GetFileInfoRec */ word pCount; GSStringPtr pathname; word access; word fileType; longword auxType; word storageType; } shortFileInfo; int err; /* make sure it's a directory */ shortFileInfo.pCount = 5; shortFileInfo.pathname = pathname; GetFileInfoGS(&shortFileInfo); if ((errno = _mapErr(_toolErr)) != 0) { return -1; } if (shortFileInfo.storageType != 0x0d && /* subdirectory */ shortFileInfo.storageType != 0x0f) { /* volume directory */ errno = ENOTDIR; return -1; } prefx.pCount = 2; prefx.prefixNum = 8; prefx.buffer.setPrefix = shortFileInfo.pathname; SetPrefixGS(&prefx); if ((errno = _mapErr(_toolErr)) != 0) { return -1; } prefx.prefixNum = 0; if (prefx.buffer.setPrefix->length < 64) { SetPrefixGS(&prefx); if (_toolErr == 0) { return 0; } } prefx.buffer.setPrefix->length = 0; SetPrefixGS(&prefx); return 0; } #define CHMOD_MODE 1 #define CHMOD_TYPE 2 #define CHMOD_AUXTYPE 4 static int _chmod (unsigned short op, GSStringPtr path, mode_t mode, unsigned short type, unsigned long auxtype) { FileInfoRecGS *infop; int err; if ((infop = malloc(sizeof(FileInfoRecGS))) == NULL) { return -1; } infop->pCount = 4; infop->pathname = path; /* get the original data */ GetFileInfoGS(infop); if (_toolErr) { err = _toolErr; free(infop); errno = _mapErr(err); return -1; } /* * Special case: If the type is TXT or SRC, *and* the S_IXUSR bit * is set, *and* this is a "UNIX" mode, *and* no filetype or auxtype * was specified, then change the type to SRC and the AUXTYPE to EXEC. */ if (((op & (CHMOD_TYPE | CHMOD_AUXTYPE)) == 0) && (mode & S_IXUSR) && _getModeEmulation() && (infop->fileType == TXT || infop->fileType == SRC)) { infop->fileType = SRC; infop->auxType = EXEC; } /* modify it */ if (op & CHMOD_MODE) { infop->access = _mapMode2GS(mode); } if (op & CHMOD_TYPE) { infop->fileType = type; } if (op & CHMOD_AUXTYPE) { infop->auxType = auxtype; } /* set the info and return */ SetFileInfoGS(infop); err = _toolErr; free(infop); if (err) { errno = _mapErr(err); return -1; } else { return 0; } } /* * _kernMinVersion * * This is used as an assert from within trap.asm (unless the global short * _kernDisableVersionCheck is set to a non-zero value. It's argument is * the kernel version required by the given system call. Returns on success. * On failure, it aborts the program with a suitable error message. * * It would be more efficient as an inline macro. * * THIS ROUTINE IS CURRENTLY DISABLED DUE TO PERFORMANCE CONCERNS. SEE * THE gno-devel MAILING LIST ARCHIVE FOR DETAILS. */ #ifdef VERSION_CHECK unsigned short _kernDisableVersionCheck; void _kernMinVersion (unsigned int required) { static int gnoActiveKnown = 0; static u_short gnoVersion = 0; /* make sure GNO is active */ if (! gnoActiveKnown) { kernStatus(); if (_toolErr) { _libcPanic("This program requires GNO.\n"); /*NOTREACHED*/ } gnoActiveKnown = 1; } /* get the current kernel version if we don't already have it */ if (gnoVersion == 0) { gnoVersion = kernVersion(); } /* make sure our version meets the minimum required */ if (required > gnoVersion) { _libcPanic ("This program requires GNO v%d.%d.%d or later\n", (required & 0xFF00) >> 8, (required & 0x00F0) >> 4, (required & 0x000F)); } return; } #endif /* * _setFdTranslation, _getFdTranslation * * Does newline translation occur for read/write calls on the specified * file descriptor? Off by default. Returns previous value for the * specified fd. * * If by any chance we manage to open more than OPEN_MAX files, then * translation is always off for those file descriptors. */ static int fdTranslationTable[OPEN_MAX]; #define _getFdTranslation(fd) \ (((fd) >= 0) && ((fd) < OPEN_MAX) && fdTranslationTable[fd]) static int _setFdTranslation(int fd, int isOn) { int oldval; if (fd < 0 || fd >= OPEN_MAX) { return 0; } oldval = fdTranslationTable[fd]; fdTranslationTable[fd] = (isOn != 0); return oldval; } /* * _statfs * * _statfs is a routine common to both statfs and fstatfs. The first * parameter, gstr, is a pointer to a GSString containing the pathname. * Other than the type of the first argument, this call is identical * to the regular definition of statfs. */ static int _statfs (GSStringPtr gstr, struct statfs *buf) { DevNumRecGS gd; VolumeRecGS vo; char printbuf[20]; /* device name in .dxx format */ int err; /* get the volume number for the file name gstr */ gd.pCount = 2; gd.devName = gstr; /* does this work with a pathname? */ GetDevNumberGS(&gd); if (_toolErr) { errno = _mapErr(_toolErr); return -1; } /* get the other volume info */ vo.pCount = 6; sprintf(printbuf,".d%d", gd.devNum); vo.devName = __C2GSMALLOC(printbuf); vo.volName = (ResultBuf255Ptr) GOinit(32, NULL); VolumeGS(&vo); err = _toolErr; /* copy over our information */ buf->f_type = (long) vo.fileSysID; /* FST type */ buf->f_bsize = (long) vo.blockSize; /* size of blocks in filesystem */ buf->f_blocks = vo.totalBlocks; /* number of blocks on the volume */ buf->f_bfree = vo.freeBlocks; /* number of free blocks */ buf->f_bavail = vo.freeBlocks; /* none reserved for superuser */ buf->f_files = -1; /* undefined by this filesystem */ buf->f_ffree = -1; /* undefined by this filesystem */ buf->f_fsid.hi = 0; buf->f_fsid.lo = gd.devNum; /* device number */ buf->f_spare[0] = -1; buf->f_spare[1] = -1; buf->f_spare[2] = -1; buf->f_spare[3] = -1; buf->f_spare[4] = -1; buf->f_spare[5] = -1; buf->f_spare[6] = -1; /* clean up, set up return conditions */ GOfree(vo.volName); free(vo.devName); if (err) { errno = _mapErr(err); return -1; } return 0; } /* * access -- a replacement for the GNO v2.0.4 one; this one will actually * return 0 when testing X_OK on a directory. * * This function uses gotos. Too bad; sometimes it's more efficient. */ int access (const char *name, int mode) { FileInfoRecGS *recptr; GSStringPtr gptr; size_t len; int i; int result = 0; /* verify validity of args */ if (!name || !*name) { /* for SYSV */ errno=ENOENT; return -1; } len = strlen(name); if (len >= USHRT_MAX || (mode & ~(R_OK|W_OK|X_OK|F_OK))) { errno = EINVAL; return -1; } /* allocate and initialize the GS/OS variables */ if ((gptr = GIinit(len, name)) == NULL) { errno = ENOMEM; return -1; } if ((recptr = malloc(sizeof(FileInfoRecGS))) == NULL) { GIfree(gptr); errno = ENOMEM; return -1; } recptr->pCount = 4; recptr->pathname = gptr; /* get the info and check for errors */ GetFileInfoGS(recptr); i = toolerror(); if (i) { errno = _mapErr(i); result = -1; goto done; } /* check read permission */ if ((mode & R_OK) && !(recptr->access & GSOS_READ)) { errno = EACCES; result = -1; goto done; } /* check write permission */ if ((mode & W_OK) && !((recptr->access & GSOS_WRITE) && (recptr->access & GSOS_RENAME) && (recptr->access & GSOS_DESTROY))) { errno = EACCES; result = -1; goto done; } /* * Check execute mode. This is true if: * the file is a directory; * the file is a shell command; * the file is a shell script; * the file is a S16 file; * But NOT if * the file is a SYS or other type of file. */ if ((mode & X_OK) && !((recptr->fileType == EXE) || (recptr->fileType == DIR) || (recptr->fileType == SRC && recptr->auxType == EXEC) || (recptr->fileType == S16))) { errno = EACCES; result = -1; } done: GIfree(gptr); free(recptr); return result; } /* * chdir */ int chdir (const char *pathname) { GSStringPtr pathnameGS; int result, err; if ((pathnameGS = __C2GSMALLOC(pathname)) == NULL) { errno = ENOMEM; return -1; } result = _chdir(pathnameGS); err = errno; free(pathnameGS); if (result != 0) { errno = err; } return result; } /* * chmod */ int chmod (const char *pathname, mode_t mode) { GSStringPtr pathnameGS; int result, err; if ((pathnameGS = __C2GSMALLOC(pathname)) == NULL) { errno = ENOMEM; return -1; } result = _chmod(CHMOD_MODE, pathnameGS, mode, 0, 0L); err = errno; free(pathnameGS); if (result != 0) { errno = err; } return result; } /* * close */ int close (int filds) { int cl[2] = {1, filds}; int err; _setFdTranslation(filds, 0); CloseGS(cl); if (_toolErr) { errno = _mapErr(_toolErr); return -1; } return 0; } /* * creat */ int creat(const char *path, mode_t mode) { return open (path, O_CREAT | O_TRUNC | O_WRONLY, mode); } /* * fchdir */ int fchdir (int fd) { RefInfoRecGS inforec; int err, result; /* get the pathname based on the file descriptor */ inforec.pCount = 3; inforec.refNum = fd; inforec.pathname = (ResultBuf255Ptr) GOinit(GSOS_NAME_MAX, NULL); GetRefInfoGS (&inforec); if ((err = _mapErr(_toolErr)) != 0) { GOfree(inforec.pathname); errno = err; return -1; } /* change directory */ result = _chdir(&(inforec.pathname->bufString)); err = errno; GOfree(inforec.pathname); errno = err; return result; } /* * fchmod */ int fchmod (int fd, mode_t mode) { RefInfoRecGS inforec; int err, result; /* get the pathname based on the file descriptor */ inforec.pCount = 3; inforec.refNum = fd; inforec.pathname = (ResultBuf255Ptr) GOinit(GSOS_NAME_MAX, NULL); GetRefInfoGS (&inforec); if ((err = _mapErr(_toolErr)) != 0) { GOfree(inforec.pathname); errno = err; return -1; } /* change the mode */ result = _chmod(CHMOD_MODE, &(inforec.pathname->bufString), mode, 0, 0L); err = errno; GOfree(inforec.pathname); errno = err; return result; } /* * fstatfs */ int fstatfs (int fd, struct statfs *buf) { RefInfoRecGS inforec; int err, result; /* get the pathname based on the file descriptor */ inforec.pCount = 3; inforec.refNum = fd; inforec.pathname = (ResultBuf255Ptr) GOinit(GSOS_NAME_MAX, NULL); GetRefInfoGS (&inforec); if ((err = _mapErr(_toolErr)) != 0) { GOfree(inforec.pathname); errno = err; return -1; } /* _statfs does the rest */ result = _statfs(&(inforec.pathname->bufString), buf); err = errno; GOfree(inforec.pathname); errno = err; return result; } /* * fsync */ int fsync(int fd) { short ff[2]; ff[0] = 1; ff[1] = fd; FlushGS(ff); if (_toolErr) { errno = _mapErr(_toolErr); return -1; } return 0; } /* * ftruncate */ int ftruncate(int fd, off_t length) { SetPositionRecGS p; p.pCount = 3; p.base = 0; p.refNum = fd; p.displacement = length; SetEOFGS(&p); if (_toolErr) { errno = _mapErr(_toolErr); return -1; } return 0; } /* * getdtablesize */ int getdtablesize (void) { return OPEN_MAX; } /* * getpgrp */ pid_t getpgrp (void) { return _getpgrp(getpid()); } /* * gettimeofday * * IIgs HACK! HACK! HACK! We need a real gettimeofday! * The implementation for settimeofday is a start, but we're not currently * handling timezones properly. */ int gettimeofday (struct timeval *tp, struct timezone *tzp) { tp->tv_sec = time(NULL); tp->tv_usec = 0l; } /* * lseek */ off_t lseek(int filds, off_t offset, int whence) { SetPositionRecGS s; PositionRecGS m; EOFRecGS e; int err; e.pCount = m.pCount = 2; e.refNum = s.refNum = m.refNum = filds; GetEOFGS(&e); if (err = _mapErr(_toolErr)) { errno = err; return -1L; } GetMarkGS(&m); s.pCount = 3; s.base = 0; switch (whence) { case SEEK_SET: s.displacement = offset; break; case SEEK_CUR: s.displacement = m.position + offset; break; case SEEK_END: s.displacement = e.eof + offset; break; default: errno = EINVAL; return -1L; } if (s.displacement < 0) { errno = EINVAL; return -1L; } if (s.displacement > e.eof) { SetEOFGS(&s); if (err = _mapErr(_toolErr)) { errno = err; return -1L; } } SetMarkGS(&s); if (err = _mapErr(_toolErr)) { errno = err; return -1L; } return s.displacement; } /* * mkdir */ int mkdir(char *dirname) { CreateRecGS cr; int err; cr.pCount = 5; cr.pathname = __C2GSMALLOC(dirname); if (cr.pathname == NULL) { errno = ENOMEM; return -1; } cr.access = 0xC3; cr.fileType = 0x0F; cr.auxType = 0L; cr.storageType = 0x0D; CreateGS(&cr); err = _toolErr; free(cr.pathname); if (err) { errno = _mapErr(err); return -1; } errno = 0; return 0; } /* * raise -- This routine has been moved into ORCALib so that we can avoid * backward references through abort(3), which the current linker * (ZapLink) cannot handle. */ #if 0 int raise (int sig) { return kill (getpid(), sig); } #endif /* * read */ ssize_t read (int filds, void *buf, size_t bytecount) { IORecGS iorec = {4, filds, buf, (long) bytecount, 0L}; char *p; size_t i; int err; ssize_t result; /* read in the buffer */ ReadGS(&iorec); if (_toolErr == 0 || _toolErr == 0x4C) { result = (size_t) iorec.transferCount; } else if (err = _mapErr(_toolErr)) { errno = err; return -1; } /* translate newlines if necessary */ if (_getFdTranslation(filds)) { p = (char *) buf; for (i = 0; i < result; i++, p++) { if (*p == '\r') { *p = '\n'; } } } return result; } /* * rename */ int rename (const char *from, const char *to) { struct { word pCount; GSStringPtr from; GSStringPtr to; } renblock; struct stat sfrom, sto; int ret2; if (stat(from, &sfrom) < 0) { return -1; } ret2 = stat(to, &sto); if ((ret2 < 0) && (errno != ENOENT)) { /* problem stat'ing destination */ return -1; } /* make sure the source and destination (if it exists) are of the same type */ if (ret2 == 0) { if (S_ISDIR(sfrom.st_mode) && ! S_ISDIR(sto.st_mode)) { errno = ENOTDIR; return -1; } if (S_ISDIR(sto.st_mode) && ! S_ISDIR(sfrom.st_mode)) { errno = EISDIR; return -1; } if (unlink(to) < 0) { if (errno == EACCES) { errno = EEXIST; /* proper POSIX side effect */ } return -1; } } /* get GS/OS-style copies of the filenames */ renblock.pCount = 2; if ((renblock.from = __C2GSMALLOC(from)) == NULL) { errno = ENOMEM; return -1; } if ((renblock.to = __C2GSMALLOC(to)) == NULL) { GIfree(renblock.from); errno = ENOMEM; return -1; } ChangePathGS(&renblock); ret2 = _toolErr; GIfree(renblock.from); GIfree(renblock.to); if (ret2) { errno = _mapErr(ret2); return -1; } else { return 0; } } /* * rexit */ void rexit (int code) { SystemQuitFlags (0x4000); SystemQuitPath (NULL); exit(code); } /* * rmdir */ int rmdir (const char *path) { struct { Word pCount; GSStringPtr pathname; Word access; Word fileType; LongWord auxType; Word storageType; } frec; int result; /* make a GSString copy of path */ frec.pCount=5; if ((frec.pathname = __C2GSMALLOC(path)) == NULL) { errno = ENOMEM; return -1; } /* check to ensure that it's a directory */ GetFileInfoGS(&frec); if ((result = _toolErr) != 0) { GIfree(frec.pathname); errno = _mapErr(result); return -1; } if (frec.storageType != 0x0D) { /* not a directory */ GIfree(frec.pathname); /* volume directory, or plain file? */ errno = (frec.storageType == 0x0F) ? EPERM : ENOTDIR; return -1; } /* it's a directory; try to delete it */ frec.pCount=1; DestroyGS(&frec); result = _toolErr; GIfree(frec.pathname); if (result != 0) { errno = _mapErr(result); result = -1; } return result; } /* * settimeofday * * This implementation is not complete; it doesn't currently handle * timezone information. */ #define SECS_PER_YEAR 31536000 /* number of seconds in 365 days */ int settimeofday (struct timeval *tp, struct timezone *notused) { Byte month, day, curYear, hour, minute, second; long mytime; int i, year; HexTime hextime; int monthanddays[12] = { 31,28,31,30,31,30,31,31,30,31,30,31}; if (geteuid() != 0) { /* not superuser */ errno = EPERM; return -1; } if (tp == NULL) { /* No time specified */ return 0; } mytime = tp->tv_sec; /* Since microseconds cannot be set, tv_usec is ignored. */ year = 1970; for (i = 1970; i < 2155; i++) { mytime -= SECS_PER_YEAR; if (mytime < 0) { break; } if ((year % 4 == 0) && (year != 2100)) { /* leap year */ mytime -= 86400; if (mytime < 0) { mytime += 86400; i=2155; break; } } year++; } hextime.curYear = (Byte) year - 1900; mytime += SECS_PER_YEAR; /* add the last subtraction back in */ if ((year % 4 == 0) && (year != 2100)) { monthanddays[1] = 29; } hextime.month = (Byte) 0; for (i = 0; i < 12; i++) { mytime -= (monthanddays[i]) * 86400; if (mytime < 0) { mytime += monthanddays[i] * 86400; i=12; break; } hextime.month++; } hextime.day = (Byte) mytime / 86400; mytime = mytime % 86400; hextime.hour = (Byte) mytime / 3600; mytime = mytime % 3600; hextime.minute = (Byte) mytime / 60; mytime = mytime % 60; hextime.second = (Byte) mytime; WriteTimeHex(hextime); return 0; } /* * sigprocmask - This would be much more efficient if it was implemented * in the kernel. Note that in most cases we are doing * two kernel traps (and thus context switches). */ int sigprocmask (int how, const sigset_t *set, sigset_t *oset) { sigset_t old; if (set != NULL) { switch(how) { case SIG_BLOCK: old = sigblock(*set); break; case SIG_UNBLOCK: old = sigblock(0); sigblock(old & ~(*set)); break; case SIG_SETMASK: old = sigblock(0); sigblock(*set); break; default: errno = EINVAL; return -1; } } else if (oset != NULL) { /* set == NULL */ old = sigblock(0); sigblock(old); } /* if both set and oset are NULL, this routine is a no-op */ if (oset != NULL) { *oset = old; } return 0; } /* * statfs */ int statfs(char *path, struct statfs *buf) { ExpandPathRecGS ep; int err, result; /* get the full pathname of this file */ ep.pCount = 3; if ((ep.inputPath = __C2GSMALLOC(path)) == NULL) { return -1; } ep.outputPath = (ResultBuf255Ptr) GOinit(GSOS_NAME_MAX, NULL); if (ep.outputPath == NULL) { err = errno; free(ep.inputPath); errno = err; return -1; } ep.flags = 0; ExpandPathGS(&ep); if (_toolErr) { result = -1; err = _mapErr(_toolErr); } else { result = _statfs(&(ep.outputPath->bufString), buf); err = errno; } free(ep.inputPath); GOfree(ep.outputPath); errno = err; return result; } /* * truncate */ int truncate(const char *path, off_t length) { SetPositionRecGS p; int closerec[2]; struct { Word pCount; Word refNum; GSStringPtr pathname; Word requestAccess; } openrec; /* abbreviated version of OpenRecGS */ int err, result; /* open the file */ openrec.pCount = 3; if ((openrec.pathname = __C2GSMALLOC(path)) == NULL) { return -1; } openrec.requestAccess = readWriteEnable; OpenGS(&openrec); err = _mapErr(_toolErr); free(openrec.pathname); if (err) { errno = err; return -1; } /* set up the close block */ closerec[0] = 1; closerec[1] = openrec.refNum; p.pCount = 3; p.base = 0; p.refNum = openrec.refNum; p.displacement = length; SetEOFGS(&p); if (_toolErr) { errno = _mapErr(_toolErr); result = -1; } else { result = 0; } CloseGS(closerec); return result; } /* * umask */ mode_t umask (mode_t mask) { static mode_t currentMask = 0xFFFF; static int maskInitialized = 0; char *p; mode_t result; char maskStr[5]; const char *umaskStr = "UMASK"; /* initialize off of environment first time through -- hack */ if (! maskInitialized) { if ((p = getenv(umaskStr)) == NULL) { currentMask = 022; } else { currentMask = strtoul(p, NULL, 8); } maskInitialized = 1; } result = currentMask; currentMask = mask & 0777; maskStr[0] = '0'; maskStr[1] = '0' + ((currentMask & 0700) >> 6); maskStr[2] = '0' + ((currentMask & 0070) >> 3); maskStr[3] = '0' + (currentMask & 0007); maskStr[4] = '\0'; setenv(umaskStr, maskStr, 1); /* ignore errors */ return result; } /* * unlink */ int unlink(char *fname) { struct { word pCount; GSStringPtr pathname; } drec; int err; drec.pCount = 1; drec.pathname = __C2GSMALLOC(fname); if (drec.pathname == NULL) { return -1; } DestroyGS(&drec); err = _mapErr(_toolErr); GIfree(drec.pathname); if (err) { errno = err; return -1; } return 0; } /* * When GNO supports wait4(2) (and assuming that it doesn't have waitpid()), * this routine can be changed to the following: * * return (wait4(pid, istat, options, (struct rusage *)0)); * * This implementation is flawed since it's not done in the kernel. See * the BUGS section of the man page for details. */ pid_t waitpid(pid_t pid, union wait *istat, int options) { int result; pid_t pgid; #if 1 /* * there's a note in about the implementation of * getpgrp() being buggy. */ if (pid < -1 || pid == 0) { fprintf(stderr,"waitpid: process groups not implemented. Aborted.\n"); abort(); } #endif /* We really need to do this in the kernel. */ if (pid < -1) { pgid = -pid; } else if (pid == 0) { pgid = _getpgrp(getpid()); } else { pgid = -1; } for(;;) { result = wait(istat); if ((result == -1) || (pid == result) || ((pgid > 1) && (pgid == _getpgrp(result)))) { return result; } } } /* * write */ ssize_t write(int filds, void *buf, size_t bytecount) { IORecGS iorec = {4, filds, buf, (long) bytecount, 0L}; int err; size_t i; char *p; /* translate newlines if necessary */ if (_getFdTranslation(filds)) { p = (char *) buf; for (i = 0; i < bytecount; i++, p++) { if (*p == '\n') { *p = '\r'; } } } /* write the file block */ WriteGS(&iorec); if (err = _mapErr(_toolErr)) { errno = err; return -1; } return (size_t) iorec.transferCount; } #pragma optimize 78 #pragma debug 0 #ifdef VERSION_CHECK /* * _libcPanic * * Get a message out to the user and exit. This is at the end of the * file because of the higher optimization level required for variadic * functions. */ static void _libcPanic (const char *fmt, ...) { va_list list; va_start(list, fmt); vfprintf(stderr, fmt, list); va_end(list); exit(EXIT_FAILURE); } #endif /* VERSION_CHECK */ /* * fcntl */ int fcntl (int fd, int cmd, ...) { struct stat sbuf; va_list list; int result; unsigned short mode; int err = 0; va_start(list, cmd); switch(cmd) { case F_DUPFD: result = va_arg(list, int); result = dup2(fd, result); break; case F_GETFL: if (fstat(fd, &sbuf) == -1) { err = errno; result = -1; } else { mode = sbuf.st_mode & (S_IRUSR | S_IWUSR); if ((mode == 0) && (S_ISCHR(sbuf.st_mode) || S_ISFIFO(sbuf.st_mode))) { /* * There's a bug in the (beta 2.0.6) kernel where it's not setting * the read/write bits for pipes and character special files. * Fake it out. If/when the bug is fixed, this just becomes * dead code. */ result = O_RDWR; } else if (mode == (S_IRUSR | S_IWUSR)) { result = O_RDWR; } else if (mode == S_IRUSR) { result = O_RDONLY; } else if (mode == S_IWUSR) { result = O_WRONLY; } else { result = 0; } } break; default: err = EINVAL; result = -1; } va_end(list); if (err != 0) { errno = err; } return result; } /* * open -- end of file because of higher optimization required */ int open (const char *path, int oflag, ...) { OpenRecGS openRec; CreateRecGS createRec; SetPositionRecGS setMarkRec; va_list list; mode_t openmode; int err; /* saved errno */ int result; /* returned value */ size_t currentEof; /* saved eof nec for append */ /* grab extra parameter if necessary */ va_start(list, oflag); if (oflag & O_CREAT) { openmode = va_arg(list, mode_t); } err = 0; /* try to open the file */ openRec.pCount = 12; openRec.pathname = __C2GSMALLOC(path); if (openRec.pathname == NULL) { va_end(list); errno = ENOMEM; return -1; /* DON'T goto label 'done' ... spurious free() */ } if ((oflag & O_ACCMODE) == O_RDONLY) { openRec.requestAccess = readEnable; } else if ((oflag & O_ACCMODE) == O_WRONLY) { openRec.requestAccess = writeEnable; } else if ((oflag & O_ACCMODE) == O_RDWR) { openRec.requestAccess = readWriteEnable; } else { openRec.requestAccess = 0; } openRec.resourceNumber = 0; /* data fork */ openRec.optionList = NULL; /* no FST-specific info */ OpenGS(&openRec); if ((_toolErr == 0) && (oflag & O_CREAT) && (oflag & O_EXCL)) { /* file already existed */ close(openRec.refNum); err = EEXIST; result = -1; goto done; } else if ((_toolErr == 0) && (oflag & O_WRONLY) && (openRec.storageType == 0x0d || openRec.storageType == 0x0f)) { /* opening a volume directory or subdirectory for writing not permitted */ close(openRec.refNum); err = EISDIR; result = -1; goto done; } else if ((err = _mapErr(_toolErr)) && (err == ENOENT)) { /* file doesn't exist -- create? */ if (oflag & O_CREAT) { createRec.pCount = 3; createRec.pathname = openRec.pathname; createRec.access = _mapMode2GS(openmode); createRec.fileType = (oflag & O_BINARY) ? BIN : TXT; CreateGS(&createRec); if (err = _mapErr(_toolErr)) { result = -1; goto done; } OpenGS(&openRec); if (err = _mapErr(_toolErr)) { result = -1; goto done; } } else { /* no create and didn't exist -- error */ result = -1; goto done; } } else if (err) { /* unknown error on open */ result = -1; goto done; } /* if we got here, the file is open */ currentEof = openRec.eof; /* truncate the file if necessary */ if ((oflag & O_TRUNC) && ((oflag & O_ACCMODE) != O_RDONLY)) { ftruncate(openRec.refNum, 0L); currentEof = 0L; } /* append to file? */ if ((oflag & O_APPEND) && ((oflag & O_ACCMODE) != O_RDONLY)) { setMarkRec.pCount = 3; setMarkRec.refNum = openRec.refNum; setMarkRec.base = 0; setMarkRec.displacement = currentEof; SetMarkGS(&setMarkRec); if (err = _mapErr(_toolErr)) { result = -1; goto done; } } /* success! */ err = 0; result = openRec.refNum; if (oflag & O_TRANS) { _setFdTranslation(result, 1); } done: free(openRec.pathname); va_end(list); errno = err; return result; }