#include #include #include #include #include #include "defs.h" #include "prims.h" #include "externs.h" #define MAXLINE 2048 /* * All primitives that take ofile arguments have to be careful when * they POP() arguments, in case the POP() frees the last reference to * the ofile. This means they have to have copied any values out of * the ofile before they POP(). */ #ifndef NO_MUF_FILES /* not used if sys-open disabled! */ static int badpath(const char *path) { const char *slash; if (! path) return(1); if (path[0] == '/') return(1); if ( (path[0] == '.') && (path[1] == '.') && ( (path[2] == '\0') || (path[2] == '/') ) ) return(1); while (1) { slash = index(path,'/'); if (! slash) return(0); if ( (slash[1] == '.') && (slash[2] == '.') && ( (slash[3] == '\0') || (slash[3] == '/') ) ) return(1); path = slash + 1; } } #endif PRIM(sys_open) { #ifdef NO_MUF_FILES ABORT_INTERP("Disabled in this build."); #else struct inst *v; char *realpath; char *path; int how; int fd; OFILE *o; struct stat stb; dbref who; const char *fdhow; FILE *f; NARGS(2); v = TOS(0); if (v->type != PROG_INTEGER) ABORT_INTERP("Non-integer flags."); how = v->data.number; if (how & ~(O_ACCMODE|O_CREAT|O_TRUNC|O_EXCL)) ABORT_INTERP("Bad flag bit(s)."); switch (how & O_ACCMODE) { case O_RDONLY: fdhow = "r"; break; case O_WRONLY: fdhow = "w"; break; case O_RDWR: fdhow = "r+"; break; default: ABORT_INTERP("Bad access mode"); break; } v = TOS(1); if (v->type != PROG_STRING) ABORT_INTERP("Non-string pathname."); path = v->data.string; if (badpath(path)) ABORT_INTERP("Bad pathname."); realpath = malloc(64+strlen(path)); who = UID; sprintf(realpath,"files/%d/%s",(int)who,path); fd = open(realpath,how,0600); if (fd >= 0) { fstat(fd,&stb); if ((stb.st_mode & S_IFMT) == S_IFDIR) { close(fd); fd = -1; errno = EISDIR; } } if (fd < 0) { char errbuf[1024]; sprintf(&errbuf[0],"open: %s",strerror(errno)); free(realpath); ABORT_INTERP(&errbuf[0]); } free(realpath); f = fdopen(fd,fdhow); if (f == 0) { char errbuf[1024]; sprintf(&errbuf[0],"fdopen %s: %s",fdhow,strerror(errno)); close(fd); free(realpath); ABORT_INTERP(&errbuf[0]); } o = malloc(sizeof(OFILE)); o->refcnt = 1; o->who = who; o->how = how; o->f = f; o->path = dup_string(path); POP(2); MPUSH(PROG_OFILE,o); #endif } PRIM(sys_read) { #ifdef NO_MUF_FILES ABORT_INTERP("Disabled in this build."); #else struct inst *v; OFILE *o; char buf[MAXLINE+1]; int len; int c; NARGS(1); v = TOS(0); if (v->type != PROG_OFILE) ABORT_INTERP("Non-file argument."); o = v->data.ofile; if ((o->how & O_ACCMODE) == O_WRONLY) ABORT_INTERP("File is write-only."); len = 0; while (1) { c = getc(o->f); if (c == EOF) { if (len == 0) { POP(1); MPUSH(PROG_INTEGER,0); return; } break; } if (c == '\n') break; if (c) { if (len >= MAXLINE) { ungetc(c,o->f); break; } buf[len++] = c; } } buf[len] = '\0'; POP(1); MPUSH(PROG_STRING,dup_string(&buf[0])); #endif } PRIM(sys_write) { #ifdef NO_MUF_FILES ABORT_INTERP("Disabled in this build."); #else struct inst *v; char *str; OFILE *o; int n; NARGS(2); v = TOS(0); if (v->type != PROG_STRING) ABORT_INTERP("Non-string argument (2)."); str = v->data.string; v = TOS(1); if (v->type != PROG_OFILE) ABORT_INTERP("Non-file argument (1)."); o = v->data.ofile; if (str) { n = strlen(str); if (n > MAXLINE) ABORT_INTERP("Line too long."); } if ((o->how & O_ACCMODE) == O_RDONLY) ABORT_INTERP("File is read-only."); if (str) fwrite(str,1,n,o->f); putc('\n',o->f); POP(2); #endif } PRIM(sys_lseek) { #ifdef NO_MUF_FILES ABORT_INTERP("Disabled in this build."); #else struct inst *v; int whence; int off; OFILE *o; int n; NARGS(3); v = TOS(0); if (v->type != PROG_INTEGER) ABORT_INTERP("Non-integer argument (3)."); whence = v->data.number; v = TOS(1); if (v->type != PROG_INTEGER) ABORT_INTERP("Non-integer argument (2)."); off = v->data.number; v = TOS(2); if (v->type != PROG_OFILE) ABORT_INTERP("Non-file argument (1)."); o = v->data.ofile; switch (whence) { case SEEK_SET: case SEEK_CUR: case SEEK_END: break; default: ABORT_INTERP("Bad whence."); break; } n = fseek(o->f,off,whence); POP(3); MPUSH(PROG_INTEGER,n); #endif } /* This doesn't work, even with the concatenation to keep it from expanding the argument macro too early. Growl. */ /* #define foo(x) PRIM(x) { MPUSH(PROG_INTEGER,x); (void)prims_##x; } */ PRIM(O_RDONLY) { MPUSH(PROG_INTEGER,O_RDONLY); } PRIM(O_RDWR) { MPUSH(PROG_INTEGER,O_RDWR); } PRIM(O_WRONLY) { MPUSH(PROG_INTEGER,O_WRONLY); } PRIM(O_CREAT) { MPUSH(PROG_INTEGER,O_CREAT); } PRIM(O_TRUNC) { MPUSH(PROG_INTEGER,O_TRUNC); } PRIM(O_EXCL) { MPUSH(PROG_INTEGER,O_EXCL); } PRIM(SEEK_SET) { MPUSH(PROG_INTEGER,SEEK_SET); } PRIM(SEEK_CUR) { MPUSH(PROG_INTEGER,SEEK_CUR); } PRIM(SEEK_END) { MPUSH(PROG_INTEGER,SEEK_END); }