/* * stat, fstat, lstat emulation for TOS * written by Eric R. Smith and placed in the public domain */ #include #include #include #include #include #include #include #include #include #include #include /* for FSTAT */ #include "lib.h" extern int __mint; ino_t __inode = 32; /* used in readdir also */ /* for backwards compatibilty: if nonzero, files are checked to see if * they have the TOS executable magic number in them */ int _x_Bit_set_in_stat = 0; /* date for files (like root directories) that don't have one */ #define OLDDATE _unixtime(0,0) /* * macro for converting a long in DOS format to one in Unix format. "x" * _must_ be an lvalue! */ #define CONVERT(x) (x = _unixtime( ((short *)&x)[0], ((short *)&x)[1] )) /* * common routine for stat() and lstat(); if "lflag" is 0, then symbolic * links are automatically followed (like stat), if 1 then they are not * (like lstat) */ static int do_stat(_path, st, lflag) const char *_path; struct stat *st; int lflag; { long r, olddta; int nval; char path[PATH_MAX]; char *ext, drv; int fd; short magic; _DTA d; int isdot = 0; if (!_path) { errno = EFAULT; return -1; } /* * _unx2dos returns 1 for device names (like /dev/con) */ nval = _unx2dos(_path, path); /* for MiNT 0.9 and up, we use the built in stat() call */ if (__mint >= 9) { r = Fxattr(lflag, path, st); if (r) { errno = -r; return -1; } CONVERT(st->st_mtime); CONVERT(st->st_atime); CONVERT(st->st_ctime); /* Most versions of Unix count in 512 byte blocks */ st->st_blocks = (st->st_blocks * st->st_blksize) / 512; return 0; } /* otherwise, check to see if we have a name like CON: or AUX: */ if (nval == 1) { st->st_mode = S_IFCHR | 0600; st->st_attr = 0; st->st_ino = ++__inode; st->st_rdev = 0; st->st_mtime = st->st_ctime = st->st_atime = time((time_t *)0) - 2; st->st_dev = 0; st->st_nlink = 1; st->st_uid = geteuid(); st->st_gid = getegid(); st->st_size = st->st_blocks = 0; st->st_blksize = 1024; return 0; } /* A file name: check for root directory of a 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_dev = isupper(drv) ? drv - 'A' : drv - 'a'; st->st_ino = 2; 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; } /* OK, here we're going to have to do an Fsfirst to get the date */ /* NOTE: Fsfirst(".",-1) or Fsfirst("..",-1) both fail under TOS, * so we kludge around this by using the fact that Fsfirst(".\*.*" * or "..\*.*" will return the correct file first (except, of course, * in root directories :-( ). */ /* find the end of the string */ for (ext = path; ext[0] && ext[1]; ext++) ; /* add appropriate kludge if necessary */ if (*ext == '.' && (ext == path || ext[-1] == '\\' || ext[-1] == '.')) { isdot = 1; strcat(path, "\\*.*"); } olddta = Fgetdta(); Fsetdta(&d); r = Fsfirst(path, 0xff); Fsetdta(olddta); if (r < 0) { if (isdot && r == -ENOENT) goto rootdir; errno = -r; return -1; } if (isdot && ((d.dta_name[0] != '.') || (d.dta_name[1]))) { goto rootdir; } st->st_mtime = st->st_ctime = st->st_atime = _unixtime(d.dta_time, d.dta_date); if ((drv = *path) && path[1] == ':') st->st_dev = toupper(drv) - 'A'; else st->st_dev = Dgetdrv(); st->st_ino = __inode++; st->st_attr = d.dta_attribute; if (__mint && st->st_dev == ('Q' - 'A')) st->st_mode = 0644 | S_IFIFO; else { st->st_mode = 0644 | (st->st_attr & FA_DIR ? S_IFDIR | 0111 : S_IFREG); } if (st->st_attr & FA_RDONLY) st->st_mode &= ~0222; /* no write permission */ if (st->st_attr & 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 = -fd; return -1; } magic = 0; (void)Fread(fd,2,(char *)&magic); (void)Fclose(fd); if (magic == 0x601A /* TOS executable */ || magic == 0x2321) /* "#!" shell file */ st->st_mode |= 0111; } st->st_size = d.dta_size; /* in Unix, blocks are measured in 512 bytes */ st->st_blocks = (st->st_size + 511) / 512; st->st_nlink = 1; /* we dont have hard links */ } else { fill_dir: st->st_size = 1024; st->st_blocks = 2; st->st_nlink = 2; /* "foo" && "foo/.." */ } st->st_rdev = 0; st->st_uid = geteuid(); /* the current user owns every file */ st->st_gid = getegid(); st->st_blksize = 1024; return 0; } /* * fstat: if we're not running under MiNT, this is pretty bogus. * what we can really find is: * modification time: via Fdatime() * file size: via Fseek() * fortunately, these are the things most programs are interested in. * BUG: passing an invalid file descriptor gets back a stat structure for * a tty. */ int fstat(fd, st) int fd; struct stat *st; { long oldplace, r; short timeptr[2]; short magic; if (__mint >= 9) { /* use FSTAT Fcntl */ r = Fcntl(fd, st, (long)FSTAT); if (r) { errno = -r; return -1; } CONVERT(st->st_mtime); CONVERT(st->st_atime); CONVERT(st->st_ctime); st->st_blocks = (st->st_blocks * st->st_blksize) / 512; return 0; } r = Fdatime(timeptr, 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 { st->st_mtime = st->st_atime = st->st_ctime = _unixtime(timeptr[0], timeptr[1]); 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 { 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 (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; } int lstat(path, st) const char *path; struct stat *st; { return do_stat(path, st, 1); } int stat(path, st) const char *path; struct stat *st; { return do_stat(path, st, 0); }