/* * stat, fstat, lstat emulation for TOS * * written by Eric R. Smith and Jwahar Bammi, based on the original * from the old GCC library (which original was copyright 1988 * Memorial University). */ #include #include #include #include #include #include #include #include #include #include #include #include #include "symdir.h" #include #include "lib.h" #include #ifndef NAME_MAX # include #endif #ifndef _LIB_NAME_MAX # define _LIB_NAME_MAX NAME_MAX #endif ino_t __inode = 32; /* used in readdir also */ int _x_Bit_set_in_stat = 0; /* default 0, need change in emacs:sysdep.c */ DIR *__opendir_chain = 0; /* chain of open directories from opendir */ /* date for files (like root directories) that don't have one */ #define OLDDATE _unixtime(0,0) /* * check file cache for the file whose canonical (_unx2dos) name is fullname. * this is only valid for TOS files, i.e. files that are NOT symbolic * links. If file is not found in cache, use Fsfirst to look it up. * All open directories (via opendir) are cached (we don't lose much by * doing this, and it's a good heuristic for which files are going to * need stat() in the near future; plus, opendir did Fsfirst()/Fsnext() * already. Programs like "ls" should win big time. * * BUG/FEATURE: if the _lHIDE flag is set, opendir() never records the * existence of .dir, and so stat() in an open directory won't find it. */ struct dirent * _do_stat(fullname) char *fullname; { char path[FILENAME_MAX], name[_LIB_NAME_MAX], *tmp; DIR *dir; struct dirent *d; static struct dirent dtmp; static struct _dta dtabuf; struct _dta *olddta; int r; strcpy(path, fullname); if((tmp = strrchr(path, '\\'))) { *tmp++ = 0; _dos2unx(tmp, name); } else { _dos2unx(path, name); path[0] = 0; } /* * search through the chain of open directories, 1 at a time */ for (dir = __opendir_chain; dir; dir = dir->D_nxtdir) { if (!strcmp(path, dir->D_path)) { for (d = dir->D_list; d; d = d->d_next) { if (!strcmp(name,d->d_name) && d->d_attribute != 0xff) { return d; } } } } d = &dtmp; olddta = (struct _dta *)Fgetdta(); Fsetdta(&dtabuf); r = Fsfirst(fullname, FA_SYSTEM|FA_HIDDEN|FA_DIR); Fsetdta(olddta); if (r < 0) { errno = -r; return NULL; } d->d_ino = __inode++; d->d_time = dtabuf.dta_time; d->d_date = dtabuf.dta_date; d->d_attribute = dtabuf.dta_attribute; d->d_size = dtabuf.dta_size; return d; } static int _stat(_path, st) const char *_path; struct stat *st; { int rval, nval; char path[FILENAME_MAX]; char *ext, drv; int fd; short magic; struct dirent *d; struct _device *device; if (!_path) { errno = EFAULT; return -1; } /* * NOTE: the new _unx2dos gives a complete, canonical TOS pathname * (i.e. stripped of . and .., and with a drive letter). It also returns * some useful information about what the given filename was (e.g. symlink, * or device) */ nval = _unx2dos(_path, path); if (nval == _NM_DEV) { device = _dev_dosname(path); st->st_mode = S_IFCHR | 0600; st->st_attr = 0xfe; st->st_ino = st->st_rdev = device->dev; st->st_mtime = st->st_ctime = st->st_atime = time((time_t *)0) - 2; st->st_dev = 0; /* if _tDEV is on, /dev/console & CON: are the same file */ st->st_nlink = _tDEV ? 2 : 1; st->st_uid = geteuid(); st->st_gid = getegid(); st->st_blksize = 1024; return 0; } /* Deal with the root directory of a logical drive */ if (path[0] == '\\' && path[1] == 0) { drv = Dgetdrv() + 'A'; goto rootdir; } if ( (drv = path[0]) && path[1] == ':' && (path[2] == 0 || (path[2] == '\\' && path[3] == 0)) ) { rootdir: st->st_mode = S_IFDIR | 0755; st->st_attr = FA_DIR; st->st_ino = isupper(drv) ? drv - 'A' : drv - 'a'; st->st_mtime = st->st_ctime = st->st_atime = OLDDATE; goto fill_dir; } /* forbid wildcards in path names */ if (index(path, '*') || index(path, '?')) { errno = ENOENT; return -1; } if (!(d = _do_stat(path))) { /* errno was set by _do_stat */ if ((errno == ENOENT || errno == EPATH) && nval == _NM_LINK) { /* here we have a symbolic link to a deleted file */ st->st_mode = S_IFLNK | 0644; st->st_ino = ++__inode; st->st_attr = 0xff; st->st_mtime = st->st_ctime = st->st_atime = OLDDATE; st->st_size = strlen(__link_to); st->st_blocks = 1; st->st_nlink = 1; goto fill_rest; } return -1; } st->st_mtime = st->st_ctime = st->st_atime = _unixtime(d->d_time, d->d_date); st->st_ino = d->d_ino; st->st_attr = d->d_attribute; st->st_mode = 0644 | (d->d_attribute & FA_DIR ? S_IFDIR | 0111 : S_IFREG); if (d->d_attribute & FA_RDONLY) st->st_mode &= ~0222; /* no write permission */ if (d->d_attribute & FA_HIDDEN) st->st_mode &= ~0444; /* no read permission */ /* check for a file with an executable extension */ ext = strrchr(_path, '.'); if (ext) { if (!strcmp(ext, ".ttp") || !strcmp(ext, ".prg") || !strcmp(ext, ".tos") || !strcmp(ext, ".g") || !strcmp(ext, ".sh") || !strcmp(ext, ".bat")) { st->st_mode |= 0111; } } if ( (st->st_mode & S_IFMT) == S_IFREG) { if (_x_Bit_set_in_stat) { if ((fd = Fopen(path,0)) < 0) { errno = -rval; return -1; } (void)Fread(fd,2,(char *)&magic); (void)Fclose(fd); if (magic == 0x601A) st->st_mode |= 0111; } st->st_size = d->d_size; st->st_blocks = (d->d_size + 1023) / 1024; st->st_nlink = 1; /* we dont have hard links */ } else { fill_dir: st->st_size = 1024; st->st_blocks = 1; st->st_nlink = 2; /* "foo" && "foo/.." */ } fill_rest: if ((drv = *path) && path[1] == ':') st->st_dev = islower(drv) ? drv - 'a' : drv - 'A'; else st->st_dev = Dgetdrv(); st->st_rdev = 0; st->st_uid = geteuid(); /* the current user owns every file */ st->st_gid = getegid(); st->st_blksize = 1024; return nval; } int stat(path, st) const char *path; struct stat *st; { return _stat(path, st) < 0 ? -1 : 0; } #include #include int fstat(fd, st) int fd; struct stat *st; { int old; int r; struct _device *dev = _dev_fd(fd); if(dev) { char *devname = (char *)alloca(strlen(dev->unxnm) + 6L); (void) strcpy(devname, "/dev/"); (void) strcat(devname, dev->unxnm); return stat(devname, st); } fd = __OPEN_INDEX(fd); if((fd >= 0) && (fd < __NHANDLES)) { if (__open_stat[fd].filename) { /* we should turn off links, because the name we're going to give * to 'stat' has already been _unx2dos'd */ old = _lOK; _lOK = 0; r = stat(__open_stat[fd].filename, st); _lOK = old; return r; } else { _DOSTIME timestruct; /* the following code is stolen from Eric R. Smith */ r = Fdatime(×truct, fd, 0); if (r < 0) { /* assume TTY */ st->st_mode = S_IFCHR | 0600; st->st_attr = 0; st->st_mtime = st->st_ctime = st->st_atime = time((time_t *)0) - 2; st->st_size = 0; } else { long oldplace; st->st_mtime = st->st_atime = st->st_ctime = _unixtime(timestruct.time,timestruct.date); st->st_mode = S_IFREG | 0644; /* this may be false */ st->st_attr = 0; /* because this is */ /* get current file location */ oldplace = Fseek(0L, fd, SEEK_CUR); if (oldplace < 0) { /* can't seek -- must be pipe */ st->st_mode = S_IFIFO | 0644; st->st_size = 1024; } else { short magic; r = Fseek(0L, fd, SEEK_END); /* go to end of file */ st->st_size = r; (void)Fseek(0L, fd, SEEK_SET); /* go to start of file */ /* check for executable file */ if (_x_Bit_set_in_stat && Fread(fd, 2, (char *)&magic) == 2) { if (magic == 0x601a || magic == 0x2321) st->st_mode |= 0111; } (void)Fseek(oldplace, fd, SEEK_SET); } } /* all this stuff is likely bogus as well. sigh. */ st->st_dev = Dgetdrv(); st->st_rdev = 0; st->st_uid = getuid(); st->st_gid = getgid(); st->st_blksize = 1024; /* note: most Unixes measure st_blocks in 512 byte units */ st->st_blocks = (st->st_size + 511) / 512; st->st_ino = ++__inode; st->st_nlink = 1; return 0; } } errno = EBADF; return -1; } /* * "stat" that doesn't follow symbolic links * important exception: if _lAUTO is set and the link is an automatic * link, follow it anyways. This allows automatic links to be aliases * for real files (stat will correctly return the info about the * automatic link if the real file has been deleted). */ int lstat(_path, st) const char *_path; struct stat *st; { int r; /* _unx2dos returns _NM_LINK if the last path component was a symbolic link * in this case, __link_name and __link_path are set to the name and path * of the symbolic link file, __link_to to its contents, and __link_flags * its flags. */ r = _stat(_path, st); if (r < 0) return r; if (r == _NM_LINK && !(__link_flags & SD_AUTO)) { st->st_size = strlen(__link_to); st->st_blocks = 1; st->st_mode = ((st->st_mode & ~S_IFMT) | S_IFLNK); } return 0; }