/* $RCSfile: doio.c,v $$Revision: 4.0.1.4 $$Date: 91/11/05 16:51:43 $ * * Copyright (c) 1991, Larry Wall * * You may distribute under the terms of either the GNU General Public * License or the Artistic License, as specified in the README file. * * $Log: doio.c,v $ * Revision 4.0.1.4 91/11/05 16:51:43 lwall * patch11: prepared for ctype implementations that don't define isascii() * patch11: perl mistook some streams for sockets because they return mode 0 too * patch11: reopening STDIN, STDOUT and STDERR failed on some machines * patch11: certain perl errors should set EBADF so that $! looks better * patch11: truncate on a closed filehandle could dump * patch11: stats of _ forgot whether prior stat was actually lstat * patch11: -T returned true on NFS directory * * Revision 4.0.1.3 91/06/10 01:21:19 lwall * patch10: read didn't work from character special files open for writing * patch10: close-on-exec wrongly set on system file descriptors * * Revision 4.0.1.2 91/06/07 10:53:39 lwall * patch4: new copyright notice * patch4: system fd's are now treated specially * patch4: added $^F variable to specify maximum system fd, default 2 * patch4: character special files now opened with bidirectional stdio buffers * patch4: taintchecks could improperly modify parent in vfork() * patch4: many, many itty-bitty portability fixes * * Revision 4.0.1.1 91/04/11 17:41:06 lwall * patch1: hopefully straightened out some of the Xenix mess * * Revision 4.0 91/03/20 01:07:06 lwall * 4.0 baseline. * */ #include "EXTERN.h" #include "perl.h" #ifdef HAS_SOCKET #include #include #endif #ifdef HAS_SELECT #ifdef I_SYS_SELECT #ifndef I_SYS_TIME #include #endif #endif #endif #if defined(HAS_MSG) || defined(HAS_SEM) || defined(HAS_SHM) #include #ifdef HAS_MSG #include #endif #ifdef HAS_SEM #include #endif #ifdef HAS_SHM #include #endif #endif #ifdef I_PWD #include #endif #ifdef I_GRP #include #endif #ifdef I_UTIME #include #endif #ifdef I_FCNTL #include #endif #ifdef I_SYS_FILE #include #endif int laststatval = -1; int laststype = O_STAT; bool do_open(stab,name,len) STAB *stab; register char *name; int len; { FILE *fp; register STIO *stio = stab_io(stab); char *myname = savestr(name); int result; int fd; int writing = 0; char mode[3]; /* stdio file mode ("r\0" or "r+\0") */ FILE *saveifp = Nullfp; FILE *saveofp = Nullfp; char savetype = ' '; name = myname; forkprocess = 1; /* assume true if no fork */ while (len && isSPACE(name[len-1])) name[--len] = '\0'; if (!stio) stio = stab_io(stab) = stio_new(); else if (stio->ifp) { fd = fileno(stio->ifp); if (stio->type == '-') result = 0; else if (fd <= maxsysfd) { saveifp = stio->ifp; saveofp = stio->ofp; savetype = stio->type; result = 0; } else if (stio->type == '|') result = mypclose(stio->ifp); else if (stio->ifp != stio->ofp) { if (stio->ofp) { result = fclose(stio->ofp); fclose(stio->ifp); /* clear stdio, fd already closed */ } else result = fclose(stio->ifp); } else result = fclose(stio->ifp); if (result == EOF && fd > maxsysfd) fprintf(stderr,"Warning: unable to close filehandle %s properly.\n", stab_name(stab)); stio->ofp = stio->ifp = Nullfp; } if (*name == '+' && len > 1 && name[len-1] != '|') { /* scary */ mode[1] = *name++; mode[2] = '\0'; --len; writing = 1; } else { mode[1] = '\0'; } stio->type = *name; if (*name == '|') { /*SUPPRESS 530*/ for (name++; isSPACE(*name); name++) ; #ifdef TAINT taintenv(); taintproper("Insecure dependency in piped open"); #endif fp = mypopen(name,"w"); writing = 1; } else if (*name == '>') { #ifdef TAINT taintproper("Insecure dependency in open"); #endif name++; if (*name == '>') { mode[0] = stio->type = 'a'; name++; } else mode[0] = 'w'; writing = 1; if (*name == '&') { duplicity: name++; while (isSPACE(*name)) name++; if (isDIGIT(*name)) fd = atoi(name); else { stab = stabent(name,FALSE); if (!stab || !stab_io(stab)) { #ifdef EINVAL errno = EINVAL; #endif goto say_false; } if (stab_io(stab) && stab_io(stab)->ifp) { fd = fileno(stab_io(stab)->ifp); if (stab_io(stab)->type == 's') stio->type = 's'; } else fd = -1; } if (!(fp = fdopen(fd = dup(fd),mode))) { close(fd); } } else { while (isSPACE(*name)) name++; if (strEQ(name,"-")) { fp = stdout; stio->type = '-'; } else { fp = fopen(name,mode); } } } else { if (*name == '<') { mode[0] = 'r'; name++; while (isSPACE(*name)) name++; if (*name == '&') goto duplicity; if (strEQ(name,"-")) { fp = stdin; stio->type = '-'; } else fp = fopen(name,mode); } else if (name[len-1] == '|') { #ifdef TAINT taintenv(); taintproper("Insecure dependency in piped open"); #endif name[--len] = '\0'; while (len && isSPACE(name[len-1])) name[--len] = '\0'; /*SUPPRESS 530*/ for (; isSPACE(*name); name++) ; fp = mypopen(name,"r"); stio->type = '|'; } else { stio->type = '<'; /*SUPPRESS 530*/ for (; isSPACE(*name); name++) ; if (strEQ(name,"-")) { fp = stdin; stio->type = '-'; } else fp = fopen(name,"r"); } } Safefree(myname); if (!fp) goto say_false; if (stio->type && stio->type != '|' && stio->type != '-') { if (fstat(fileno(fp),&statbuf) < 0) { (void)fclose(fp); goto say_false; } if (S_ISSOCK(statbuf.st_mode)) stio->type = 's'; /* in case a socket was passed in to us */ #ifdef HAS_SOCKET else if ( #ifdef S_IFMT !(statbuf.st_mode & S_IFMT) #else !statbuf.st_mode #endif ) { if (getsockname(fileno(fp), tokenbuf, 0) >= 0 || errno != ENOTSOCK) stio->type = 's'; /* some OS's return 0 on fstat()ed socket */ /* but some return 0 for streams too, sigh */ } #endif } if (saveifp) { /* must use old fp? */ fd = fileno(saveifp); if (saveofp) { fflush(saveofp); /* emulate fclose() */ if (saveofp != saveifp) { /* was a socket? */ fclose(saveofp); if (fd > 2) Safefree(saveofp); } } if (fd != fileno(fp)) { dup2(fileno(fp), fd); fclose(fp); } fp = saveifp; } #if defined(HAS_FCNTL) && defined(F_SETFD) fd = fileno(fp); fcntl(fd,F_SETFD,fd > maxsysfd); #endif stio->ifp = fp; if (writing) { if (stio->type == 's' || (stio->type == '>' && S_ISCHR(statbuf.st_mode)) ) { if (!(stio->ofp = fdopen(fileno(fp),"w"))) { fclose(fp); stio->ifp = Nullfp; goto say_false; } } else stio->ofp = fp; } return TRUE; say_false: stio->ifp = saveifp; stio->ofp = saveofp; stio->type = savetype; return FALSE; } FILE * nextargv(stab) register STAB *stab; { register STR *str; #ifndef FLEXFILENAMES int filedev; int fileino; #endif int fileuid; int filegid; static int filemode = 0; static int lastfd; static char *oldname; if (!argvoutstab) argvoutstab = stabent("ARGVOUT",TRUE); if (filemode & (S_ISUID|S_ISGID)) { fflush(stab_io(argvoutstab)->ifp); /* chmod must follow last write */ #ifdef HAS_FCHMOD (void)fchmod(lastfd,filemode); #else (void)chmod(oldname,filemode); #endif } filemode = 0; while (alen(stab_xarray(stab)) >= 0) { str = ashift(stab_xarray(stab)); str_sset(stab_val(stab),str); STABSET(stab_val(stab)); oldname = str_get(stab_val(stab)); if (do_open(stab,oldname,stab_val(stab)->str_cur)) { if (inplace) { #ifdef TAINT taintproper("Insecure dependency in inplace open"); #endif if (strEQ(oldname,"-")) { str_free(str); defoutstab = stabent("STDOUT",TRUE); return stab_io(stab)->ifp; } #ifndef FLEXFILENAMES filedev = statbuf.st_dev; fileino = statbuf.st_ino; #endif filemode = statbuf.st_mode; fileuid = statbuf.st_uid; filegid = statbuf.st_gid; if (!S_ISREG(filemode)) { warn("Can't do inplace edit: %s is not a regular file", oldname ); do_close(stab,FALSE); str_free(str); continue; } if (*inplace) { #ifdef SUFFIX add_suffix(str,inplace); #else str_cat(str,inplace); #endif #ifndef FLEXFILENAMES if (stat(str->str_ptr,&statbuf) >= 0 && statbuf.st_dev == filedev && statbuf.st_ino == fileino ) { warn("Can't do inplace edit: %s > 14 characters", str->str_ptr ); do_close(stab,FALSE); str_free(str); continue; } #endif #ifdef HAS_RENAME #ifndef MSDOS if (rename(oldname,str->str_ptr) < 0) { warn("Can't rename %s to %s: %s, skipping file", oldname, str->str_ptr, strerror(errno) ); do_close(stab,FALSE); str_free(str); continue; } #else do_close(stab,FALSE); (void)unlink(str->str_ptr); (void)rename(oldname,str->str_ptr); do_open(stab,str->str_ptr,stab_val(stab)->str_cur); #endif /* MSDOS */ #else (void)UNLINK(str->str_ptr); if (link(oldname,str->str_ptr) < 0) { warn("Can't rename %s to %s: %s, skipping file", oldname, str->str_ptr, strerror(errno) ); do_close(stab,FALSE); str_free(str); continue; } (void)UNLINK(oldname); #endif } else { #ifndef MSDOS if (UNLINK(oldname) < 0) { warn("Can't rename %s to %s: %s, skipping file", oldname, str->str_ptr, strerror(errno) ); do_close(stab,FALSE); str_free(str); continue; } #else fatal("Can't do inplace edit without backup"); #endif } str_nset(str,">",1); str_cat(str,oldname); errno = 0; /* in case sprintf set errno */ if (!do_open(argvoutstab,str->str_ptr,str->str_cur)) { warn("Can't do inplace edit on %s: %s", oldname, strerror(errno) ); do_close(stab,FALSE); str_free(str); continue; } defoutstab = argvoutstab; lastfd = fileno(stab_io(argvoutstab)->ifp); (void)fstat(lastfd,&statbuf); #ifdef HAS_FCHMOD (void)fchmod(lastfd,filemode); #else (void)chmod(oldname,filemode); #endif if (fileuid != statbuf.st_uid || filegid != statbuf.st_gid) { #ifdef HAS_FCHOWN (void)fchown(lastfd,fileuid,filegid); #else #ifdef HAS_CHOWN (void)chown(oldname,fileuid,filegid); #endif #endif } } str_free(str); return stab_io(stab)->ifp; } else fprintf(stderr,"Can't open %s: %s\n",str_get(str), strerror(errno)); str_free(str); } if (inplace) { (void)do_close(argvoutstab,FALSE); defoutstab = stabent("STDOUT",TRUE); } return Nullfp; } #ifdef HAS_PIPE void do_pipe(str, rstab, wstab) STR *str; STAB *rstab; STAB *wstab; { register STIO *rstio; register STIO *wstio; int fd[2]; if (!rstab) goto badexit; if (!wstab) goto badexit; rstio = stab_io(rstab); wstio = stab_io(wstab); if (!rstio) rstio = stab_io(rstab) = stio_new(); else if (rstio->ifp) do_close(rstab,FALSE); if (!wstio) wstio = stab_io(wstab) = stio_new(); else if (wstio->ifp) do_close(wstab,FALSE); if (pipe(fd) < 0) goto badexit; rstio->ifp = fdopen(fd[0], "r"); wstio->ofp = fdopen(fd[1], "w"); wstio->ifp = wstio->ofp; rstio->type = '<'; wstio->type = '>'; if (!rstio->ifp || !wstio->ofp) { if (rstio->ifp) fclose(rstio->ifp); else close(fd[0]); if (wstio->ofp) fclose(wstio->ofp); else close(fd[1]); goto badexit; } str_sset(str,&str_yes); return; badexit: str_sset(str,&str_undef); return; } #endif bool do_close(stab,explicit) STAB *stab; bool explicit; { bool retval = FALSE; register STIO *stio; int status; if (!stab) stab = argvstab; if (!stab) { errno = EBADF; return FALSE; } stio = stab_io(stab); if (!stio) { /* never opened */ if (dowarn && explicit) warn("Close on unopened file <%s>",stab_name(stab)); return FALSE; } if (stio->ifp) { if (stio->type == '|') { status = mypclose(stio->ifp); retval = (status == 0); statusvalue = (unsigned short)status & 0xffff; } else if (stio->type == '-') retval = TRUE; else { if (stio->ofp && stio->ofp != stio->ifp) { /* a socket */ retval = (fclose(stio->ofp) != EOF); fclose(stio->ifp); /* clear stdio, fd already closed */ } else retval = (fclose(stio->ifp) != EOF); } stio->ofp = stio->ifp = Nullfp; } if (explicit) stio->lines = 0; stio->type = ' '; return retval; } bool do_eof(stab) STAB *stab; { register STIO *stio; int ch; if (!stab) { /* eof() */ if (argvstab) stio = stab_io(argvstab); else return TRUE; } else stio = stab_io(stab); if (!stio) return TRUE; while (stio->ifp) { #ifdef STDSTDIO /* (the code works without this) */ if (stio->ifp->_cnt > 0) /* cheat a little, since */ return FALSE; /* this is the most usual case */ #endif ch = getc(stio->ifp); if (ch != EOF) { (void)ungetc(ch, stio->ifp); return FALSE; } #ifdef STDSTDIO if (stio->ifp->_cnt < -1) stio->ifp->_cnt = -1; #endif if (!stab) { /* not necessarily a real EOF yet? */ if (!nextargv(argvstab)) /* get another fp handy */ return TRUE; } else return TRUE; /* normal fp, definitely end of file */ } return TRUE; } long do_tell(stab) STAB *stab; { register STIO *stio; if (!stab) goto phooey; stio = stab_io(stab); if (!stio || !stio->ifp) goto phooey; if (feof(stio->ifp)) (void)fseek (stio->ifp, 0L, 2); /* ultrix 1.2 workaround */ return ftell(stio->ifp); phooey: if (dowarn) warn("tell() on unopened file"); errno = EBADF; return -1L; } bool do_seek(stab, pos, whence) STAB *stab; long pos; int whence; { register STIO *stio; if (!stab) goto nuts; stio = stab_io(stab); if (!stio || !stio->ifp) goto nuts; if (feof(stio->ifp)) (void)fseek (stio->ifp, 0L, 2); /* ultrix 1.2 workaround */ return fseek(stio->ifp, pos, whence) >= 0; nuts: if (dowarn) warn("seek() on unopened file"); errno = EBADF; return FALSE; } int do_ctl(optype,stab,func,argstr) int optype; STAB *stab; int func; STR *argstr; { register STIO *stio; register char *s; int retval; if (!stab || !argstr || !(stio = stab_io(stab)) || !stio->ifp) { errno = EBADF; /* well, sort of... */ return -1; } if (argstr->str_pok || !argstr->str_nok) { if (!argstr->str_pok) s = str_get(argstr); #ifdef IOCPARM_MASK #ifndef IOCPARM_LEN #define IOCPARM_LEN(x) (((x) >> 16) & IOCPARM_MASK) #endif #endif #ifdef IOCPARM_LEN retval = IOCPARM_LEN(func); /* on BSDish systes we're safe */ #else retval = 256; /* otherwise guess at what's safe */ #endif if (argstr->str_cur < retval) { Str_Grow(argstr,retval+1); argstr->str_cur = retval; } s = argstr->str_ptr; s[argstr->str_cur] = 17; /* a little sanity check here */ } else { retval = (int)str_gnum(argstr); #ifdef MSDOS s = (char*)(long)retval; /* ouch */ #else s = (char*)retval; /* ouch */ #endif } #ifndef lint if (optype == O_IOCTL) retval = ioctl(fileno(stio->ifp), func, s); else #ifdef MSDOS fatal("fcntl is not implemented"); #else #ifdef HAS_FCNTL retval = fcntl(fileno(stio->ifp), func, s); #else fatal("fcntl is not implemented"); #endif #endif #else /* lint */ retval = 0; #endif /* lint */ if (argstr->str_pok) { if (s[argstr->str_cur] != 17) fatal("Return value overflowed string"); s[argstr->str_cur] = 0; /* put our null back */ } return retval; } int do_stat(str,arg,gimme,arglast) STR *str; register ARG *arg; int gimme; int *arglast; { register ARRAY *ary = stack; register int sp = arglast[0] + 1; int max = 13; if ((arg[1].arg_type & A_MASK) == A_WORD) { tmpstab = arg[1].arg_ptr.arg_stab; if (tmpstab != defstab) { laststype = O_STAT; statstab = tmpstab; str_set(statname,""); if (!stab_io(tmpstab) || !stab_io(tmpstab)->ifp || fstat(fileno(stab_io(tmpstab)->ifp),&statcache) < 0) { max = 0; laststatval = -1; } } else if (laststatval < 0) max = 0; } else { str_set(statname,str_get(ary->ary_array[sp])); statstab = Nullstab; #ifdef HAS_LSTAT laststype = arg->arg_type; if (arg->arg_type == O_LSTAT) laststatval = lstat(str_get(statname),&statcache); else #endif laststatval = stat(str_get(statname),&statcache); if (laststatval < 0) max = 0; } if (gimme != G_ARRAY) { if (max) str_sset(str,&str_yes); else str_sset(str,&str_undef); STABSET(str); ary->ary_array[sp] = str; return sp; } sp--; if (max) { #ifndef lint (void)astore(ary,++sp, str_2mortal(str_nmake((double)statcache.st_dev))); (void)astore(ary,++sp, str_2mortal(str_nmake((double)statcache.st_ino))); (void)astore(ary,++sp, str_2mortal(str_nmake((double)statcache.st_mode))); (void)astore(ary,++sp, str_2mortal(str_nmake((double)statcache.st_nlink))); (void)astore(ary,++sp, str_2mortal(str_nmake((double)statcache.st_uid))); (void)astore(ary,++sp, str_2mortal(str_nmake((double)statcache.st_gid))); (void)astore(ary,++sp, str_2mortal(str_nmake((double)statcache.st_rdev))); (void)astore(ary,++sp, str_2mortal(str_nmake((double)statcache.st_size))); (void)astore(ary,++sp, str_2mortal(str_nmake((double)statcache.st_atime))); (void)astore(ary,++sp, str_2mortal(str_nmake((double)statcache.st_mtime))); (void)astore(ary,++sp, str_2mortal(str_nmake((double)statcache.st_ctime))); #ifdef STATBLOCKS (void)astore(ary,++sp, str_2mortal(str_nmake((double)statcache.st_blksize))); (void)astore(ary,++sp, str_2mortal(str_nmake((double)statcache.st_blocks))); #else (void)astore(ary,++sp, str_2mortal(str_make("",0))); (void)astore(ary,++sp, str_2mortal(str_make("",0))); #endif #else /* lint */ (void)astore(ary,++sp,str_nmake(0.0)); #endif /* lint */ } return sp; } #if !defined(HAS_TRUNCATE) && !defined(HAS_CHSIZE) && defined(F_FREESP) /* code courtesy of William Kucharski */ #define HAS_CHSIZE int chsize(fd, length) int fd; /* file descriptor */ off_t length; /* length to set file to */ { extern long lseek(); struct flock fl; struct stat filebuf; if (fstat(fd, &filebuf) < 0) return -1; if (filebuf.st_size < length) { /* extend file length */ if ((lseek(fd, (length - 1), 0)) < 0) return -1; /* write a "0" byte */ if ((write(fd, "", 1)) != 1) return -1; } else { /* truncate length */ fl.l_whence = 0; fl.l_len = 0; fl.l_start = length; fl.l_type = F_WRLCK; /* write lock on file space */ /* * This relies on the UNDOCUMENTED F_FREESP argument to * fcntl(2), which truncates the file so that it ends at the * position indicated by fl.l_start. * * Will minor miracles never cease? */ if (fcntl(fd, F_FREESP, &fl) < 0) return -1; } return 0; } #endif /* F_FREESP */ int /*SUPPRESS 590*/ do_truncate(str,arg,gimme,arglast) STR *str; register ARG *arg; int gimme; int *arglast; { register ARRAY *ary = stack; register int sp = arglast[0] + 1; off_t len = (off_t)str_gnum(ary->ary_array[sp+1]); int result = 1; STAB *tmpstab; #if defined(HAS_TRUNCATE) || defined(HAS_CHSIZE) #ifdef HAS_TRUNCATE if ((arg[1].arg_type & A_MASK) == A_WORD) { tmpstab = arg[1].arg_ptr.arg_stab; if (!stab_io(tmpstab) || !stab_io(tmpstab)->ifp || ftruncate(fileno(stab_io(tmpstab)->ifp), len) < 0) result = 0; } else if (truncate(str_get(ary->ary_array[sp]), len) < 0) result = 0; #else if ((arg[1].arg_type & A_MASK) == A_WORD) { tmpstab = arg[1].arg_ptr.arg_stab; if (!stab_io(tmpstab) || !stab_io(tmpstab)->ifp || chsize(fileno(stab_io(tmpstab)->ifp), len) < 0) result = 0; } else { int tmpfd; if ((tmpfd = open(str_get(ary->ary_array[sp]), 0)) < 0) result = 0; else { if (chsize(tmpfd, len) < 0) result = 0; close(tmpfd); } } #endif if (result) str_sset(str,&str_yes); else str_sset(str,&str_undef); STABSET(str); ary->ary_array[sp] = str; return sp; #else fatal("truncate not implemented"); #endif } int looks_like_number(str) STR *str; { register char *s; register char *send; if (!str->str_pok) return TRUE; s = str->str_ptr; send = s + str->str_cur; while (isSPACE(*s)) s++; if (s >= send) return FALSE; if (*s == '+' || *s == '-') s++; while (isDIGIT(*s)) s++; if (s == send) return TRUE; if (*s == '.') s++; else if (s == str->str_ptr) return FALSE; while (isDIGIT(*s)) s++; if (s == send) return TRUE; if (*s == 'e' || *s == 'E') { s++; if (*s == '+' || *s == '-') s++; while (isDIGIT(*s)) s++; } while (isSPACE(*s)) s++; if (s >= send) return TRUE; return FALSE; } bool do_print(str,fp) register STR *str; FILE *fp; { register char *tmps; if (!fp) { if (dowarn) warn("print to unopened file"); errno = EBADF; return FALSE; } if (!str) return TRUE; if (ofmt && ((str->str_nok && str->str_u.str_nval != 0.0) || (looks_like_number(str) && str_gnum(str) != 0.0) ) ) { fprintf(fp, ofmt, str->str_u.str_nval); return !ferror(fp); } else { tmps = str_get(str); if (*tmps == 'S' && tmps[1] == 't' && tmps[2] == 'B' && tmps[3] == '\0' && str->str_cur == sizeof(STBP) && strlen(tmps) < str->str_cur) { STR *tmpstr = str_mortal(&str_undef); stab_fullname(tmpstr,((STAB*)str));/* a stab value, be nice */ str = tmpstr; tmps = str->str_ptr; putc('*',fp); } if (str->str_cur && (fwrite(tmps,1,str->str_cur,fp) == 0 || ferror(fp))) return FALSE; } return TRUE; } bool do_aprint(arg,fp,arglast) register ARG *arg; register FILE *fp; int *arglast; { register STR **st = stack->ary_array; register int sp = arglast[1]; register int retval; register int items = arglast[2] - sp; if (!fp) { if (dowarn) warn("print to unopened file"); errno = EBADF; return FALSE; } st += ++sp; if (arg->arg_type == O_PRTF) { do_sprintf(arg->arg_ptr.arg_str,items,st); retval = do_print(arg->arg_ptr.arg_str,fp); } else { retval = (items <= 0); for (; items > 0; items--,st++) { if (retval && ofslen) { if (fwrite(ofs, 1, ofslen, fp) == 0 || ferror(fp)) { retval = FALSE; break; } } if (!(retval = do_print(*st, fp))) break; } if (retval && orslen) if (fwrite(ors, 1, orslen, fp) == 0 || ferror(fp)) retval = FALSE; } return retval; } int mystat(arg,str) ARG *arg; STR *str; { STIO *stio; if (arg[1].arg_type & A_DONT) { stio = stab_io(arg[1].arg_ptr.arg_stab); if (stio && stio->ifp) { statstab = arg[1].arg_ptr.arg_stab; str_set(statname,""); laststype = O_STAT; return (laststatval = fstat(fileno(stio->ifp), &statcache)); } else { if (arg[1].arg_ptr.arg_stab == defstab) return laststatval; if (dowarn) warn("Stat on unopened file <%s>", stab_name(arg[1].arg_ptr.arg_stab)); statstab = Nullstab; str_set(statname,""); return (laststatval = -1); } } else { statstab = Nullstab; str_set(statname,str_get(str)); laststype = O_STAT; return (laststatval = stat(str_get(str),&statcache)); } } int mylstat(arg,str) ARG *arg; STR *str; { if (arg[1].arg_type & A_DONT) { if (arg[1].arg_ptr.arg_stab == defstab) { if (laststype != O_LSTAT) fatal("The stat preceding -l _ wasn't an lstat"); return laststatval; } fatal("You can't use -l on a filehandle"); } laststype = O_LSTAT; statstab = Nullstab; str_set(statname,str_get(str)); #ifdef HAS_LSTAT return (laststatval = lstat(str_get(str),&statcache)); #else return (laststatval = stat(str_get(str),&statcache)); #endif } STR * do_fttext(arg,str) register ARG *arg; STR *str; { int i; int len; int odd = 0; STDCHAR tbuf[512]; register STDCHAR *s; register STIO *stio; if (arg[1].arg_type & A_DONT) { if (arg[1].arg_ptr.arg_stab == defstab) { if (statstab) stio = stab_io(statstab); else { str = statname; goto really_filename; } } else { statstab = arg[1].arg_ptr.arg_stab; str_set(statname,""); stio = stab_io(statstab); } if (stio && stio->ifp) { #ifdef STDSTDIO fstat(fileno(stio->ifp),&statcache); if (S_ISDIR(statcache.st_mode)) /* handle NFS glitch */ return arg->arg_type == O_FTTEXT ? &str_no : &str_yes; if (stio->ifp->_cnt <= 0) { i = getc(stio->ifp); if (i != EOF) (void)ungetc(i,stio->ifp); } if (stio->ifp->_cnt <= 0) /* null file is anything */ return &str_yes; len = stio->ifp->_cnt + (stio->ifp->_ptr - stio->ifp->_base); s = stio->ifp->_base; #else fatal("-T and -B not implemented on filehandles"); #endif } else { if (dowarn) warn("Test on unopened file <%s>", stab_name(arg[1].arg_ptr.arg_stab)); errno = EBADF; return &str_undef; } } else { statstab = Nullstab; str_set(statname,str_get(str)); really_filename: i = open(str_get(str),0); if (i < 0) return &str_undef; fstat(i,&statcache); len = read(i,tbuf,512); (void)close(i); if (len <= 0) { if (S_ISDIR(statcache.st_mode) && arg->arg_type == O_FTTEXT) return &str_no; /* special case NFS directories */ return &str_yes; /* null file is anything */ } s = tbuf; } /* now scan s to look for textiness */ for (i = 0; i < len; i++,s++) { if (!*s) { /* null never allowed in text */ odd += len; break; } else if (*s & 128) odd++; else if (*s < 32 && *s != '\n' && *s != '\r' && *s != '\b' && *s != '\t' && *s != '\f' && *s != 27) odd++; } if ((odd * 10 > len) == (arg->arg_type == O_FTTEXT)) /* allow 10% odd */ return &str_no; else return &str_yes; } bool do_aexec(really,arglast) STR *really; int *arglast; { register STR **st = stack->ary_array; register int sp = arglast[1]; register int items = arglast[2] - sp; register char **a; char **argv; char *tmps; if (items) { New(401,argv, items+1, char*); a = argv; for (st += ++sp; items > 0; items--,st++) { if (*st) *a++ = str_get(*st); else *a++ = ""; } *a = Nullch; #ifdef TAINT if (*argv[0] != '/') /* will execvp use PATH? */ taintenv(); /* testing IFS here is overkill, probably */ #endif if (really && *(tmps = str_get(really))) execvp(tmps,argv); else execvp(argv[0],argv); Safefree(argv); } return FALSE; } static char **Argv = Null(char **); static char *Cmd = Nullch; void do_execfree() { if (Argv) { Safefree(Argv); Argv = Null(char **); } if (Cmd) { Safefree(Cmd); Cmd = Nullch; } } bool do_exec(cmd) char *cmd; { register char **a; register char *s; char flags[10]; /* save an extra exec if possible */ #ifdef CSH if (strnEQ(cmd,cshname,cshlen) && strnEQ(cmd+cshlen," -c",3)) { strcpy(flags,"-c"); s = cmd+cshlen+3; if (*s == 'f') { s++; strcat(flags,"f"); } if (*s == ' ') s++; if (*s++ == '\'') { char *ncmd = s; while (*s) s++; if (s[-1] == '\n') *--s = '\0'; if (s[-1] == '\'') { *--s = '\0'; execl(cshname,"csh", flags,ncmd,(char*)0); *s = '\''; return FALSE; } } } #endif /* CSH */ /* see if there are shell metacharacters in it */ /*SUPPRESS 530*/ for (s = cmd; *s && isALPHA(*s); s++) ; /* catch VAR=val gizmo */ if (*s == '=') goto doshell; for (s = cmd; *s; s++) { if (*s != ' ' && !isALPHA(*s) && index("$&*(){}[]'\";\\|?<>~`\n",*s)) { if (*s == '\n' && !s[1]) { *s = '\0'; break; } doshell: execl("/bin/sh","sh","-c",cmd,(char*)0); return FALSE; } } New(402,Argv, (s - cmd) / 2 + 2, char*); Cmd = nsavestr(cmd, s-cmd); a = Argv; for (s = Cmd; *s;) { while (*s && isSPACE(*s)) s++; if (*s) *(a++) = s; while (*s && !isSPACE(*s)) s++; if (*s) *s++ = '\0'; } *a = Nullch; if (Argv[0]) { execvp(Argv[0],Argv); if (errno == ENOEXEC) { /* for system V NIH syndrome */ do_execfree(); goto doshell; } } do_execfree(); return FALSE; } #ifdef HAS_SOCKET int do_socket(stab, arglast) STAB *stab; int *arglast; { register STR **st = stack->ary_array; register int sp = arglast[1]; register STIO *stio; int domain, type, protocol, fd; if (!stab) { errno = EBADF; return FALSE; } stio = stab_io(stab); if (!stio) stio = stab_io(stab) = stio_new(); else if (stio->ifp) do_close(stab,FALSE); domain = (int)str_gnum(st[++sp]); type = (int)str_gnum(st[++sp]); protocol = (int)str_gnum(st[++sp]); #ifdef TAINT taintproper("Insecure dependency in socket"); #endif fd = socket(domain,type,protocol); if (fd < 0) return FALSE; stio->ifp = fdopen(fd, "r"); /* stdio gets confused about sockets */ stio->ofp = fdopen(fd, "w"); stio->type = 's'; if (!stio->ifp || !stio->ofp) { if (stio->ifp) fclose(stio->ifp); if (stio->ofp) fclose(stio->ofp); if (!stio->ifp && !stio->ofp) close(fd); return FALSE; } return TRUE; } int do_bind(stab, arglast) STAB *stab; int *arglast; { register STR **st = stack->ary_array; register int sp = arglast[1]; register STIO *stio; char *addr; if (!stab) goto nuts; stio = stab_io(stab); if (!stio || !stio->ifp) goto nuts; addr = str_get(st[++sp]); #ifdef TAINT taintproper("Insecure dependency in bind"); #endif return bind(fileno(stio->ifp), addr, st[sp]->str_cur) >= 0; nuts: if (dowarn) warn("bind() on closed fd"); errno = EBADF; return FALSE; } int do_connect(stab, arglast) STAB *stab; int *arglast; { register STR **st = stack->ary_array; register int sp = arglast[1]; register STIO *stio; char *addr; if (!stab) goto nuts; stio = stab_io(stab); if (!stio || !stio->ifp) goto nuts; addr = str_get(st[++sp]); #ifdef TAINT taintproper("Insecure dependency in connect"); #endif return connect(fileno(stio->ifp), addr, st[sp]->str_cur) >= 0; nuts: if (dowarn) warn("connect() on closed fd"); errno = EBADF; return FALSE; } int do_listen(stab, arglast) STAB *stab; int *arglast; { register STR **st = stack->ary_array; register int sp = arglast[1]; register STIO *stio; int backlog; if (!stab) goto nuts; stio = stab_io(stab); if (!stio || !stio->ifp) goto nuts; backlog = (int)str_gnum(st[++sp]); return listen(fileno(stio->ifp), backlog) >= 0; nuts: if (dowarn) warn("listen() on closed fd"); errno = EBADF; return FALSE; } void do_accept(str, nstab, gstab) STR *str; STAB *nstab; STAB *gstab; { register STIO *nstio; register STIO *gstio; int len = sizeof buf; int fd; if (!nstab) goto badexit; if (!gstab) goto nuts; gstio = stab_io(gstab); nstio = stab_io(nstab); if (!gstio || !gstio->ifp) goto nuts; if (!nstio) nstio = stab_io(nstab) = stio_new(); else if (nstio->ifp) do_close(nstab,FALSE); fd = accept(fileno(gstio->ifp),(struct sockaddr *)buf,&len); if (fd < 0) goto badexit; nstio->ifp = fdopen(fd, "r"); nstio->ofp = fdopen(fd, "w"); nstio->type = 's'; if (!nstio->ifp || !nstio->ofp) { if (nstio->ifp) fclose(nstio->ifp); if (nstio->ofp) fclose(nstio->ofp); if (!nstio->ifp && !nstio->ofp) close(fd); goto badexit; } str_nset(str, buf, len); return; nuts: if (dowarn) warn("accept() on closed fd"); errno = EBADF; badexit: str_sset(str,&str_undef); return; } int do_shutdown(stab, arglast) STAB *stab; int *arglast; { register STR **st = stack->ary_array; register int sp = arglast[1]; register STIO *stio; int how; if (!stab) goto nuts; stio = stab_io(stab); if (!stio || !stio->ifp) goto nuts; how = (int)str_gnum(st[++sp]); return shutdown(fileno(stio->ifp), how) >= 0; nuts: if (dowarn) warn("shutdown() on closed fd"); errno = EBADF; return FALSE; } int do_sopt(optype, stab, arglast) int optype; STAB *stab; int *arglast; { register STR **st = stack->ary_array; register int sp = arglast[1]; register STIO *stio; int fd; int lvl; int optname; if (!stab) goto nuts; stio = stab_io(stab); if (!stio || !stio->ifp) goto nuts; fd = fileno(stio->ifp); lvl = (int)str_gnum(st[sp+1]); optname = (int)str_gnum(st[sp+2]); switch (optype) { case O_GSOCKOPT: st[sp] = str_2mortal(Str_new(22,257)); st[sp]->str_cur = 256; st[sp]->str_pok = 1; if (getsockopt(fd, lvl, optname, st[sp]->str_ptr, &st[sp]->str_cur) < 0) goto nuts; break; case O_SSOCKOPT: st[sp] = st[sp+3]; if (setsockopt(fd, lvl, optname, st[sp]->str_ptr, st[sp]->str_cur) < 0) goto nuts; st[sp] = &str_yes; break; } return sp; nuts: if (dowarn) warn("[gs]etsockopt() on closed fd"); st[sp] = &str_undef; errno = EBADF; return sp; } int do_getsockname(optype, stab, arglast) int optype; STAB *stab; int *arglast; { register STR **st = stack->ary_array; register int sp = arglast[1]; register STIO *stio; int fd; if (!stab) goto nuts; stio = stab_io(stab); if (!stio || !stio->ifp) goto nuts; st[sp] = str_2mortal(Str_new(22,257)); st[sp]->str_cur = 256; st[sp]->str_pok = 1; fd = fileno(stio->ifp); switch (optype) { case O_GETSOCKNAME: if (getsockname(fd, st[sp]->str_ptr, &st[sp]->str_cur) < 0) goto nuts2; break; case O_GETPEERNAME: if (getpeername(fd, st[sp]->str_ptr, &st[sp]->str_cur) < 0) goto nuts2; break; } return sp; nuts: if (dowarn) warn("get{sock,peer}name() on closed fd"); errno = EBADF; nuts2: st[sp] = &str_undef; return sp; } int do_ghent(which,gimme,arglast) int which; int gimme; int *arglast; { register ARRAY *ary = stack; register int sp = arglast[0]; register char **elem; register STR *str; struct hostent *gethostbyname(); struct hostent *gethostbyaddr(); #ifdef HAS_GETHOSTENT struct hostent *gethostent(); #endif struct hostent *hent; unsigned long len; if (gimme != G_ARRAY) { astore(ary, ++sp, str_mortal(&str_undef)); return sp; } if (which == O_GHBYNAME) { char *name = str_get(ary->ary_array[sp+1]); hent = gethostbyname(name); } else if (which == O_GHBYADDR) { STR *addrstr = ary->ary_array[sp+1]; int addrtype = (int)str_gnum(ary->ary_array[sp+2]); char *addr = str_get(addrstr); hent = gethostbyaddr(addr,addrstr->str_cur,addrtype); } else #ifdef HAS_GETHOSTENT hent = gethostent(); #else fatal("gethostent not implemented"); #endif if (hent) { #ifndef lint (void)astore(ary, ++sp, str = str_mortal(&str_no)); str_set(str, hent->h_name); (void)astore(ary, ++sp, str = str_mortal(&str_no)); for (elem = hent->h_aliases; *elem; elem++) { str_cat(str, *elem); if (elem[1]) str_ncat(str," ",1); } (void)astore(ary, ++sp, str = str_mortal(&str_no)); str_numset(str, (double)hent->h_addrtype); (void)astore(ary, ++sp, str = str_mortal(&str_no)); len = hent->h_length; str_numset(str, (double)len); #ifdef h_addr for (elem = hent->h_addr_list; *elem; elem++) { (void)astore(ary, ++sp, str = str_mortal(&str_no)); str_nset(str, *elem, len); } #else (void)astore(ary, ++sp, str = str_mortal(&str_no)); str_nset(str, hent->h_addr, len); #endif /* h_addr */ #else /* lint */ elem = Nullch; elem = elem; (void)astore(ary, ++sp, str_mortal(&str_no)); #endif /* lint */ } return sp; } int do_gnent(which,gimme,arglast) int which; int gimme; int *arglast; { register ARRAY *ary = stack; register int sp = arglast[0]; register char **elem; register STR *str; struct netent *getnetbyname(); struct netent *getnetbyaddr(); struct netent *getnetent(); struct netent *nent; if (gimme != G_ARRAY) { astore(ary, ++sp, str_mortal(&str_undef)); return sp; } if (which == O_GNBYNAME) { char *name = str_get(ary->ary_array[sp+1]); nent = getnetbyname(name); } else if (which == O_GNBYADDR) { unsigned long addr = U_L(str_gnum(ary->ary_array[sp+1])); int addrtype = (int)str_gnum(ary->ary_array[sp+2]); nent = getnetbyaddr((long)addr,addrtype); } else nent = getnetent(); if (nent) { #ifndef lint (void)astore(ary, ++sp, str = str_mortal(&str_no)); str_set(str, nent->n_name); (void)astore(ary, ++sp, str = str_mortal(&str_no)); for (elem = nent->n_aliases; *elem; elem++) { str_cat(str, *elem); if (elem[1]) str_ncat(str," ",1); } (void)astore(ary, ++sp, str = str_mortal(&str_no)); str_numset(str, (double)nent->n_addrtype); (void)astore(ary, ++sp, str = str_mortal(&str_no)); str_numset(str, (double)nent->n_net); #else /* lint */ elem = Nullch; elem = elem; (void)astore(ary, ++sp, str_mortal(&str_no)); #endif /* lint */ } return sp; } int do_gpent(which,gimme,arglast) int which; int gimme; int *arglast; { register ARRAY *ary = stack; register int sp = arglast[0]; register char **elem; register STR *str; struct protoent *getprotobyname(); struct protoent *getprotobynumber(); struct protoent *getprotoent(); struct protoent *pent; if (gimme != G_ARRAY) { astore(ary, ++sp, str_mortal(&str_undef)); return sp; } if (which == O_GPBYNAME) { char *name = str_get(ary->ary_array[sp+1]); pent = getprotobyname(name); } else if (which == O_GPBYNUMBER) { int proto = (int)str_gnum(ary->ary_array[sp+1]); pent = getprotobynumber(proto); } else pent = getprotoent(); if (pent) { #ifndef lint (void)astore(ary, ++sp, str = str_mortal(&str_no)); str_set(str, pent->p_name); (void)astore(ary, ++sp, str = str_mortal(&str_no)); for (elem = pent->p_aliases; *elem; elem++) { str_cat(str, *elem); if (elem[1]) str_ncat(str," ",1); } (void)astore(ary, ++sp, str = str_mortal(&str_no)); str_numset(str, (double)pent->p_proto); #else /* lint */ elem = Nullch; elem = elem; (void)astore(ary, ++sp, str_mortal(&str_no)); #endif /* lint */ } return sp; } int do_gsent(which,gimme,arglast) int which; int gimme; int *arglast; { register ARRAY *ary = stack; register int sp = arglast[0]; register char **elem; register STR *str; struct servent *getservbyname(); struct servent *getservbynumber(); struct servent *getservent(); struct servent *sent; if (gimme != G_ARRAY) { astore(ary, ++sp, str_mortal(&str_undef)); return sp; } if (which == O_GSBYNAME) { char *name = str_get(ary->ary_array[sp+1]); char *proto = str_get(ary->ary_array[sp+2]); if (proto && !*proto) proto = Nullch; sent = getservbyname(name,proto); } else if (which == O_GSBYPORT) { int port = (int)str_gnum(ary->ary_array[sp+1]); char *proto = str_get(ary->ary_array[sp+2]); sent = getservbyport(port,proto); } else sent = getservent(); if (sent) { #ifndef lint (void)astore(ary, ++sp, str = str_mortal(&str_no)); str_set(str, sent->s_name); (void)astore(ary, ++sp, str = str_mortal(&str_no)); for (elem = sent->s_aliases; *elem; elem++) { str_cat(str, *elem); if (elem[1]) str_ncat(str," ",1); } (void)astore(ary, ++sp, str = str_mortal(&str_no)); #ifdef HAS_NTOHS str_numset(str, (double)ntohs(sent->s_port)); #else str_numset(str, (double)(sent->s_port)); #endif (void)astore(ary, ++sp, str = str_mortal(&str_no)); str_set(str, sent->s_proto); #else /* lint */ elem = Nullch; elem = elem; (void)astore(ary, ++sp, str_mortal(&str_no)); #endif /* lint */ } return sp; } #endif /* HAS_SOCKET */ #ifdef HAS_SELECT int do_select(gimme,arglast) int gimme; int *arglast; { register STR **st = stack->ary_array; register int sp = arglast[0]; register int i; register int j; register char *s; register STR *str; double value; int maxlen = 0; int nfound; struct timeval timebuf; struct timeval *tbuf = &timebuf; int growsize; #if BYTEORDER != 0x1234 && BYTEORDER != 0x12345678 int masksize; int offset; char *fd_sets[4]; int k; #if BYTEORDER & 0xf0000 #define ORDERBYTE (0x88888888 - BYTEORDER) #else #define ORDERBYTE (0x4444 - BYTEORDER) #endif #endif for (i = 1; i <= 3; i++) { j = st[sp+i]->str_cur; if (maxlen < j) maxlen = j; } #if BYTEORDER == 0x1234 || BYTEORDER == 0x12345678 growsize = maxlen; /* little endians can use vecs directly */ #else #ifdef NFDBITS #ifndef NBBY #define NBBY 8 #endif masksize = NFDBITS / NBBY; #else masksize = sizeof(long); /* documented int, everyone seems to use long */ #endif growsize = maxlen + (masksize - (maxlen % masksize)); Zero(&fd_sets[0], 4, char*); #endif for (i = 1; i <= 3; i++) { str = st[sp+i]; j = str->str_len; if (j < growsize) { if (str->str_pok) { Str_Grow(str,growsize); s = str_get(str) + j; while (++j <= growsize) { *s++ = '\0'; } } else if (str->str_ptr) { Safefree(str->str_ptr); str->str_ptr = Nullch; } } #if BYTEORDER != 0x1234 && BYTEORDER != 0x12345678 s = str->str_ptr; if (s) { New(403, fd_sets[i], growsize, char); for (offset = 0; offset < growsize; offset += masksize) { for (j = 0, k=ORDERBYTE; j < masksize; j++, (k >>= 4)) fd_sets[i][j+offset] = s[(k % masksize) + offset]; } } #endif } str = st[sp+4]; if (str->str_nok || str->str_pok) { value = str_gnum(str); if (value < 0.0) value = 0.0; timebuf.tv_sec = (long)value; value -= (double)timebuf.tv_sec; timebuf.tv_usec = (long)(value * 1000000.0); } else tbuf = Null(struct timeval*); #if BYTEORDER == 0x1234 || BYTEORDER == 0x12345678 nfound = select( maxlen * 8, st[sp+1]->str_ptr, st[sp+2]->str_ptr, st[sp+3]->str_ptr, tbuf); #else nfound = select( maxlen * 8, fd_sets[1], fd_sets[2], fd_sets[3], tbuf); for (i = 1; i <= 3; i++) { if (fd_sets[i]) { str = st[sp+i]; s = str->str_ptr; for (offset = 0; offset < growsize; offset += masksize) { for (j = 0, k=ORDERBYTE; j < masksize; j++, (k >>= 4)) s[(k % masksize) + offset] = fd_sets[i][j+offset]; } } } #endif st[++sp] = str_mortal(&str_no); str_numset(st[sp], (double)nfound); if (gimme == G_ARRAY && tbuf) { value = (double)(timebuf.tv_sec) + (double)(timebuf.tv_usec) / 1000000.0; st[++sp] = str_mortal(&str_no); str_numset(st[sp], value); } return sp; } #endif /* SELECT */ #ifdef HAS_SOCKET int do_spair(stab1, stab2, arglast) STAB *stab1; STAB *stab2; int *arglast; { register STR **st = stack->ary_array; register int sp = arglast[2]; register STIO *stio1; register STIO *stio2; int domain, type, protocol, fd[2]; if (!stab1 || !stab2) return FALSE; stio1 = stab_io(stab1); stio2 = stab_io(stab2); if (!stio1) stio1 = stab_io(stab1) = stio_new(); else if (stio1->ifp) do_close(stab1,FALSE); if (!stio2) stio2 = stab_io(stab2) = stio_new(); else if (stio2->ifp) do_close(stab2,FALSE); domain = (int)str_gnum(st[++sp]); type = (int)str_gnum(st[++sp]); protocol = (int)str_gnum(st[++sp]); #ifdef TAINT taintproper("Insecure dependency in socketpair"); #endif #ifdef HAS_SOCKETPAIR if (socketpair(domain,type,protocol,fd) < 0) return FALSE; #else fatal("Socketpair unimplemented"); #endif stio1->ifp = fdopen(fd[0], "r"); stio1->ofp = fdopen(fd[0], "w"); stio1->type = 's'; stio2->ifp = fdopen(fd[1], "r"); stio2->ofp = fdopen(fd[1], "w"); stio2->type = 's'; if (!stio1->ifp || !stio1->ofp || !stio2->ifp || !stio2->ofp) { if (stio1->ifp) fclose(stio1->ifp); if (stio1->ofp) fclose(stio1->ofp); if (!stio1->ifp && !stio1->ofp) close(fd[0]); if (stio2->ifp) fclose(stio2->ifp); if (stio2->ofp) fclose(stio2->ofp); if (!stio2->ifp && !stio2->ofp) close(fd[1]); return FALSE; } return TRUE; } #endif /* HAS_SOCKET */ int do_gpwent(which,gimme,arglast) int which; int gimme; int *arglast; { #ifdef I_PWD register ARRAY *ary = stack; register int sp = arglast[0]; register STR *str; struct passwd *getpwnam(); struct passwd *getpwuid(); struct passwd *getpwent(); struct passwd *pwent; if (gimme != G_ARRAY) { astore(ary, ++sp, str_mortal(&str_undef)); return sp; } if (which == O_GPWNAM) { char *name = str_get(ary->ary_array[sp+1]); pwent = getpwnam(name); } else if (which == O_GPWUID) { int uid = (int)str_gnum(ary->ary_array[sp+1]); pwent = getpwuid(uid); } else pwent = getpwent(); if (pwent) { (void)astore(ary, ++sp, str = str_mortal(&str_no)); str_set(str, pwent->pw_name); (void)astore(ary, ++sp, str = str_mortal(&str_no)); str_set(str, pwent->pw_passwd); (void)astore(ary, ++sp, str = str_mortal(&str_no)); str_numset(str, (double)pwent->pw_uid); (void)astore(ary, ++sp, str = str_mortal(&str_no)); str_numset(str, (double)pwent->pw_gid); (void)astore(ary, ++sp, str = str_mortal(&str_no)); #ifdef PWCHANGE str_numset(str, (double)pwent->pw_change); #else #ifdef PWQUOTA str_numset(str, (double)pwent->pw_quota); #else #ifdef PWAGE str_set(str, pwent->pw_age); #endif #endif #endif (void)astore(ary, ++sp, str = str_mortal(&str_no)); #ifdef PWCLASS str_set(str,pwent->pw_class); #else #ifdef PWCOMMENT str_set(str, pwent->pw_comment); #endif #endif (void)astore(ary, ++sp, str = str_mortal(&str_no)); str_set(str, pwent->pw_gecos); (void)astore(ary, ++sp, str = str_mortal(&str_no)); str_set(str, pwent->pw_dir); (void)astore(ary, ++sp, str = str_mortal(&str_no)); str_set(str, pwent->pw_shell); #ifdef PWEXPIRE (void)astore(ary, ++sp, str = str_mortal(&str_no)); str_numset(str, (double)pwent->pw_expire); #endif } return sp; #else fatal("password routines not implemented"); #endif } int do_ggrent(which,gimme,arglast) int which; int gimme; int *arglast; { #ifdef I_GRP register ARRAY *ary = stack; register int sp = arglast[0]; register char **elem; register STR *str; struct group *getgrnam(); struct group *getgrgid(); struct group *getgrent(); struct group *grent; if (gimme != G_ARRAY) { astore(ary, ++sp, str_mortal(&str_undef)); return sp; } if (which == O_GGRNAM) { char *name = str_get(ary->ary_array[sp+1]); grent = getgrnam(name); } else if (which == O_GGRGID) { int gid = (int)str_gnum(ary->ary_array[sp+1]); grent = getgrgid(gid); } else grent = getgrent(); if (grent) { (void)astore(ary, ++sp, str = str_mortal(&str_no)); str_set(str, grent->gr_name); (void)astore(ary, ++sp, str = str_mortal(&str_no)); str_set(str, grent->gr_passwd); (void)astore(ary, ++sp, str = str_mortal(&str_no)); str_numset(str, (double)grent->gr_gid); (void)astore(ary, ++sp, str = str_mortal(&str_no)); for (elem = grent->gr_mem; *elem; elem++) { str_cat(str, *elem); if (elem[1]) str_ncat(str," ",1); } } return sp; #else fatal("group routines not implemented"); #endif } int do_dirop(optype,stab,gimme,arglast) int optype; STAB *stab; int gimme; int *arglast; { #if defined(DIRENT) && defined(HAS_READDIR) register ARRAY *ary = stack; register STR **st = ary->ary_array; register int sp = arglast[1]; register STIO *stio; long along; #ifndef telldir long telldir(); #endif #ifndef apollo struct DIRENT *readdir(); #endif register struct DIRENT *dp; if (!stab) goto nope; if (!(stio = stab_io(stab))) stio = stab_io(stab) = stio_new(); if (!stio->dirp && optype != O_OPEN_DIR) goto nope; st[sp] = &str_yes; switch (optype) { case O_OPEN_DIR: if (stio->dirp) closedir(stio->dirp); if (!(stio->dirp = opendir(str_get(st[sp+1])))) goto nope; break; case O_READDIR: if (gimme == G_ARRAY) { --sp; /*SUPPRESS 560*/ while (dp = readdir(stio->dirp)) { #ifdef DIRNAMLEN (void)astore(ary,++sp, str_2mortal(str_make(dp->d_name,dp->d_namlen))); #else (void)astore(ary,++sp, str_2mortal(str_make(dp->d_name,0))); #endif } } else { if (!(dp = readdir(stio->dirp))) goto nope; st[sp] = str_mortal(&str_undef); #ifdef DIRNAMLEN str_nset(st[sp], dp->d_name, dp->d_namlen); #else str_set(st[sp], dp->d_name); #endif } break; #if MACH case O_TELLDIR: case O_SEEKDIR: goto nope; #else case O_TELLDIR: st[sp] = str_mortal(&str_undef); str_numset(st[sp], (double)telldir(stio->dirp)); break; case O_SEEKDIR: st[sp] = str_mortal(&str_undef); along = (long)str_gnum(st[sp+1]); (void)seekdir(stio->dirp,along); break; #endif case O_REWINDDIR: st[sp] = str_mortal(&str_undef); (void)rewinddir(stio->dirp); break; case O_CLOSEDIR: st[sp] = str_mortal(&str_undef); (void)closedir(stio->dirp); stio->dirp = 0; break; } return sp; nope: st[sp] = &str_undef; if (!errno) errno = EBADF; return sp; #else fatal("Unimplemented directory operation"); #endif } apply(type,arglast) int type; int *arglast; { register STR **st = stack->ary_array; register int sp = arglast[1]; register int items = arglast[2] - sp; register int val; register int val2; register int tot = 0; char *s; #ifdef TAINT for (st += ++sp; items--; st++) tainted |= (*st)->str_tainted; st = stack->ary_array; sp = arglast[1]; items = arglast[2] - sp; #endif switch (type) { case O_CHMOD: #ifdef TAINT taintproper("Insecure dependency in chmod"); #endif if (--items > 0) { tot = items; val = (int)str_gnum(st[++sp]); while (items--) { if (chmod(str_get(st[++sp]),val)) tot--; } } break; #ifdef HAS_CHOWN case O_CHOWN: #ifdef TAINT taintproper("Insecure dependency in chown"); #endif if (items > 2) { items -= 2; tot = items; val = (int)str_gnum(st[++sp]); val2 = (int)str_gnum(st[++sp]); while (items--) { if (chown(str_get(st[++sp]),val,val2)) tot--; } } break; #endif #ifdef HAS_KILL case O_KILL: #ifdef TAINT taintproper("Insecure dependency in kill"); #endif if (--items > 0) { tot = items; s = str_get(st[++sp]); if (isUPPER(*s)) { if (*s == 'S' && s[1] == 'I' && s[2] == 'G') s += 3; if (!(val = whichsig(s))) fatal("Unrecognized signal name \"%s\"",s); } else val = (int)str_gnum(st[sp]); if (val < 0) { val = -val; while (items--) { int proc = (int)str_gnum(st[++sp]); #ifdef HAS_KILLPG if (killpg(proc,val)) /* BSD */ #else if (kill(-proc,val)) /* SYSV */ #endif tot--; } } else { while (items--) { if (kill((int)(str_gnum(st[++sp])),val)) tot--; } } } break; #endif case O_UNLINK: #ifdef TAINT taintproper("Insecure dependency in unlink"); #endif tot = items; while (items--) { s = str_get(st[++sp]); if (euid || unsafe) { if (UNLINK(s)) tot--; } else { /* don't let root wipe out directories without -U */ #ifdef HAS_LSTAT if (lstat(s,&statbuf) < 0 || S_ISDIR(statbuf.st_mode)) #else if (stat(s,&statbuf) < 0 || S_ISDIR(statbuf.st_mode)) #endif tot--; else { if (UNLINK(s)) tot--; } } } break; case O_UTIME: #ifdef TAINT taintproper("Insecure dependency in utime"); #endif if (items > 2) { #ifdef I_UTIME struct utimbuf utbuf; #else struct { long actime; long modtime; } utbuf; #endif Zero(&utbuf, sizeof utbuf, char); utbuf.actime = (long)str_gnum(st[++sp]); /* time accessed */ utbuf.modtime = (long)str_gnum(st[++sp]); /* time modified */ items -= 2; #ifndef lint tot = items; while (items--) { if (utime(str_get(st[++sp]),&utbuf)) tot--; } #endif } else items = 0; break; } return tot; } /* Do the permissions allow some operation? Assumes statcache already set. */ int cando(bit, effective, statbufp) int bit; int effective; register struct stat *statbufp; { #ifdef MSDOS /* [Comments and code from Len Reed] * MS-DOS "user" is similar to UNIX's "superuser," but can't write * to write-protected files. The execute permission bit is set * by the Miscrosoft C library stat() function for the following: * .exe files * .com files * .bat files * directories * All files and directories are readable. * Directories and special files, e.g. "CON", cannot be * write-protected. * [Comment by Tom Dinger -- a directory can have the write-protect * bit set in the file system, but DOS permits changes to * the directory anyway. In addition, all bets are off * here for networked software, such as Novell and * Sun's PC-NFS.] */ return (bit & statbufp->st_mode) ? TRUE : FALSE; #else /* ! MSDOS */ if ((effective ? euid : uid) == 0) { /* root is special */ if (bit == S_IXUSR) { if (statbufp->st_mode & 0111 || S_ISDIR(statbufp->st_mode)) return TRUE; } else return TRUE; /* root reads and writes anything */ return FALSE; } if (statbufp->st_uid == (effective ? euid : uid) ) { if (statbufp->st_mode & bit) return TRUE; /* ok as "user" */ } else if (ingroup((int)statbufp->st_gid,effective)) { if (statbufp->st_mode & bit >> 3) return TRUE; /* ok as "group" */ } else if (statbufp->st_mode & bit >> 6) return TRUE; /* ok as "other" */ return FALSE; #endif /* ! MSDOS */ } int ingroup(testgid,effective) int testgid; int effective; { if (testgid == (effective ? egid : gid)) return TRUE; #ifdef HAS_GETGROUPS #ifndef NGROUPS #define NGROUPS 32 #endif { GROUPSTYPE gary[NGROUPS]; int anum; anum = getgroups(NGROUPS,gary); while (--anum >= 0) if (gary[anum] == testgid) return TRUE; } #endif return FALSE; } #if defined(HAS_MSG) || defined(HAS_SEM) || defined(HAS_SHM) int do_ipcget(optype, arglast) int optype; int *arglast; { register STR **st = stack->ary_array; register int sp = arglast[0]; key_t key; int n, flags; key = (key_t)str_gnum(st[++sp]); n = (optype == O_MSGGET) ? 0 : (int)str_gnum(st[++sp]); flags = (int)str_gnum(st[++sp]); errno = 0; switch (optype) { #ifdef HAS_MSG case O_MSGGET: return msgget(key, flags); #endif #ifdef HAS_SEM case O_SEMGET: return semget(key, n, flags); #endif #ifdef HAS_SHM case O_SHMGET: return shmget(key, n, flags); #endif #if !defined(HAS_MSG) || !defined(HAS_SEM) || !defined(HAS_SHM) default: fatal("%s not implemented", opname[optype]); #endif } return -1; /* should never happen */ } int do_ipcctl(optype, arglast) int optype; int *arglast; { register STR **st = stack->ary_array; register int sp = arglast[0]; STR *astr; char *a; int id, n, cmd, infosize, getinfo, ret; id = (int)str_gnum(st[++sp]); n = (optype == O_SEMCTL) ? (int)str_gnum(st[++sp]) : 0; cmd = (int)str_gnum(st[++sp]); astr = st[++sp]; infosize = 0; getinfo = (cmd == IPC_STAT); switch (optype) { #ifdef HAS_MSG case O_MSGCTL: if (cmd == IPC_STAT || cmd == IPC_SET) infosize = sizeof(struct msqid_ds); break; #endif #ifdef HAS_SHM case O_SHMCTL: if (cmd == IPC_STAT || cmd == IPC_SET) infosize = sizeof(struct shmid_ds); break; #endif #ifdef HAS_SEM case O_SEMCTL: if (cmd == IPC_STAT || cmd == IPC_SET) infosize = sizeof(struct semid_ds); else if (cmd == GETALL || cmd == SETALL) { struct semid_ds semds; if (semctl(id, 0, IPC_STAT, &semds) == -1) return -1; getinfo = (cmd == GETALL); infosize = semds.sem_nsems * sizeof(short); /* "short" is technically wrong but much more portable than guessing about u_?short(_t)? */ } break; #endif #if !defined(HAS_MSG) || !defined(HAS_SEM) || !defined(HAS_SHM) default: fatal("%s not implemented", opname[optype]); #endif } if (infosize) { if (getinfo) { STR_GROW(astr, infosize+1); a = str_get(astr); } else { a = str_get(astr); if (astr->str_cur != infosize) { errno = EINVAL; return -1; } } } else { int i = (int)str_gnum(astr); a = (char *)i; /* ouch */ } errno = 0; switch (optype) { #ifdef HAS_MSG case O_MSGCTL: ret = msgctl(id, cmd, a); break; #endif #ifdef HAS_SEM case O_SEMCTL: ret = semctl(id, n, cmd, a); break; #endif #ifdef HAS_SHM case O_SHMCTL: ret = shmctl(id, cmd, a); break; #endif } if (getinfo && ret >= 0) { astr->str_cur = infosize; astr->str_ptr[infosize] = '\0'; } return ret; } int do_msgsnd(arglast) int *arglast; { #ifdef HAS_MSG register STR **st = stack->ary_array; register int sp = arglast[0]; STR *mstr; char *mbuf; int id, msize, flags; id = (int)str_gnum(st[++sp]); mstr = st[++sp]; flags = (int)str_gnum(st[++sp]); mbuf = str_get(mstr); if ((msize = mstr->str_cur - sizeof(long)) < 0) { errno = EINVAL; return -1; } errno = 0; return msgsnd(id, mbuf, msize, flags); #else fatal("msgsnd not implemented"); #endif } int do_msgrcv(arglast) int *arglast; { #ifdef HAS_MSG register STR **st = stack->ary_array; register int sp = arglast[0]; STR *mstr; char *mbuf; long mtype; int id, msize, flags, ret; id = (int)str_gnum(st[++sp]); mstr = st[++sp]; msize = (int)str_gnum(st[++sp]); mtype = (long)str_gnum(st[++sp]); flags = (int)str_gnum(st[++sp]); mbuf = str_get(mstr); if (mstr->str_cur < sizeof(long)+msize+1) { STR_GROW(mstr, sizeof(long)+msize+1); mbuf = str_get(mstr); } errno = 0; ret = msgrcv(id, mbuf, msize, mtype, flags); if (ret >= 0) { mstr->str_cur = sizeof(long)+ret; mstr->str_ptr[sizeof(long)+ret] = '\0'; } return ret; #else fatal("msgrcv not implemented"); #endif } int do_semop(arglast) int *arglast; { #ifdef HAS_SEM register STR **st = stack->ary_array; register int sp = arglast[0]; STR *opstr; char *opbuf; int id, opsize; id = (int)str_gnum(st[++sp]); opstr = st[++sp]; opbuf = str_get(opstr); opsize = opstr->str_cur; if (opsize < sizeof(struct sembuf) || (opsize % sizeof(struct sembuf)) != 0) { errno = EINVAL; return -1; } errno = 0; return semop(id, (struct sembuf *)opbuf, opsize/sizeof(struct sembuf)); #else fatal("semop not implemented"); #endif } int do_shmio(optype, arglast) int optype; int *arglast; { #ifdef HAS_SHM register STR **st = stack->ary_array; register int sp = arglast[0]; STR *mstr; char *mbuf, *shm; int id, mpos, msize; struct shmid_ds shmds; #ifndef VOIDSHMAT extern char *shmat(); #endif id = (int)str_gnum(st[++sp]); mstr = st[++sp]; mpos = (int)str_gnum(st[++sp]); msize = (int)str_gnum(st[++sp]); errno = 0; if (shmctl(id, IPC_STAT, &shmds) == -1) return -1; if (mpos < 0 || msize < 0 || mpos + msize > shmds.shm_segsz) { errno = EFAULT; /* can't do as caller requested */ return -1; } shm = (char*)shmat(id, (char*)NULL, (optype == O_SHMREAD) ? SHM_RDONLY : 0); if (shm == (char *)-1) /* I hate System V IPC, I really do */ return -1; mbuf = str_get(mstr); if (optype == O_SHMREAD) { if (mstr->str_cur < msize) { STR_GROW(mstr, msize+1); mbuf = str_get(mstr); } bcopy(shm + mpos, mbuf, msize); mstr->str_cur = msize; mstr->str_ptr[msize] = '\0'; } else { int n; if ((n = mstr->str_cur) > msize) n = msize; bcopy(mbuf, shm + mpos, n); if (n < msize) bzero(shm + mpos + n, msize - n); } return shmdt(shm); #else fatal("shm I/O not implemented"); #endif } #endif /* SYSV IPC */