perl 4.0.00: (no release announcement available)
[p5sagit/p5-mst-13.2.git] / doio.c
1 /* $Header: doio.c,v 4.0 91/03/20 01:07:06 lwall Locked $
2  *
3  *    Copyright (c) 1989, Larry Wall
4  *
5  *    You may distribute under the terms of the GNU General Public License
6  *    as specified in the README file that comes with the perl 3.0 kit.
7  *
8  * $Log:        doio.c,v $
9  * Revision 4.0  91/03/20  01:07:06  lwall
10  * 4.0 baseline.
11  * 
12  */
13
14 #include "EXTERN.h"
15 #include "perl.h"
16
17 #ifdef HAS_SOCKET
18 #include <sys/socket.h>
19 #include <netdb.h>
20 #endif
21
22 #ifdef M_UNIX
23 #if defined(HAS_SELECT) && !defined(I_SYS_TIME)
24 #include <sys/select.h>
25 #endif
26 #endif
27
28 #ifdef M_XENIX
29 #ifdef HAS_SELECT
30 #include <sys/select.h>
31 #endif
32 #endif
33
34 #if defined(HAS_MSG) || defined(HAS_SEM) || defined(HAS_SHM)
35 #include <sys/ipc.h>
36 #ifdef HAS_MSG
37 #include <sys/msg.h>
38 #endif
39 #ifdef HAS_SEM
40 #include <sys/sem.h>
41 #endif
42 #ifdef HAS_SHM
43 #include <sys/shm.h>
44 #endif
45 #endif
46
47 #ifdef I_PWD
48 #include <pwd.h>
49 #endif
50 #ifdef I_GRP
51 #include <grp.h>
52 #endif
53 #ifdef I_UTIME
54 #include <utime.h>
55 #endif
56 #ifdef I_FCNTL
57 #include <fcntl.h>
58 #endif
59 #ifdef I_SYS_FILE
60 #include <sys/file.h>
61 #endif
62
63 int laststatval = -1;
64 int laststype = O_STAT;
65
66 bool
67 do_open(stab,name,len)
68 STAB *stab;
69 register char *name;
70 int len;
71 {
72     FILE *fp;
73     register STIO *stio = stab_io(stab);
74     char *myname = savestr(name);
75     int result;
76     int fd;
77     int writing = 0;
78     char mode[3];               /* stdio file mode ("r\0" or "r+\0") */
79
80     name = myname;
81     forkprocess = 1;            /* assume true if no fork */
82     while (len && isspace(name[len-1]))
83         name[--len] = '\0';
84     if (!stio)
85         stio = stab_io(stab) = stio_new();
86     else if (stio->ifp) {
87         fd = fileno(stio->ifp);
88         if (stio->type == '|')
89             result = mypclose(stio->ifp);
90         else if (stio->type == '-')
91             result = 0;
92         else if (stio->ifp != stio->ofp) {
93             if (stio->ofp) {
94                 result = fclose(stio->ofp);
95                 fclose(stio->ifp);      /* clear stdio, fd already closed */
96             }
97             else
98                 result = fclose(stio->ifp);
99         }
100         else
101             result = fclose(stio->ifp);
102         if (result == EOF && fd > 2)
103             fprintf(stderr,"Warning: unable to close filehandle %s properly.\n",
104               stab_name(stab));
105         stio->ofp = stio->ifp = Nullfp;
106     }
107     if (*name == '+' && len > 1 && name[len-1] != '|') {        /* scary */
108         mode[1] = *name++;
109         mode[2] = '\0';
110         --len;
111         writing = 1;
112     }
113     else  {
114         mode[1] = '\0';
115     }
116     stio->type = *name;
117     if (*name == '|') {
118         for (name++; isspace(*name); name++) ;
119 #ifdef TAINT
120         taintenv();
121         taintproper("Insecure dependency in piped open");
122 #endif
123         fp = mypopen(name,"w");
124         writing = 1;
125     }
126     else if (*name == '>') {
127 #ifdef TAINT
128         taintproper("Insecure dependency in open");
129 #endif
130         name++;
131         if (*name == '>') {
132             mode[0] = stio->type = 'a';
133             name++;
134         }
135         else
136             mode[0] = 'w';
137         writing = 1;
138         if (*name == '&') {
139           duplicity:
140             name++;
141             while (isspace(*name))
142                 name++;
143             if (isdigit(*name))
144                 fd = atoi(name);
145             else {
146                 stab = stabent(name,FALSE);
147                 if (!stab || !stab_io(stab))
148                     return FALSE;
149                 if (stab_io(stab) && stab_io(stab)->ifp) {
150                     fd = fileno(stab_io(stab)->ifp);
151                     if (stab_io(stab)->type == 's')
152                         stio->type = 's';
153                 }
154                 else
155                     fd = -1;
156             }
157             if (!(fp = fdopen(fd = dup(fd),mode))) {
158                 close(fd);
159             }
160         }
161         else {
162             while (isspace(*name))
163                 name++;
164             if (strEQ(name,"-")) {
165                 fp = stdout;
166                 stio->type = '-';
167             }
168             else  {
169                 fp = fopen(name,mode);
170             }
171         }
172     }
173     else {
174         if (*name == '<') {
175             mode[0] = 'r';
176             name++;
177             while (isspace(*name))
178                 name++;
179             if (*name == '&')
180                 goto duplicity;
181             if (strEQ(name,"-")) {
182                 fp = stdin;
183                 stio->type = '-';
184             }
185             else
186                 fp = fopen(name,mode);
187         }
188         else if (name[len-1] == '|') {
189 #ifdef TAINT
190             taintenv();
191             taintproper("Insecure dependency in piped open");
192 #endif
193             name[--len] = '\0';
194             while (len && isspace(name[len-1]))
195                 name[--len] = '\0';
196             for (; isspace(*name); name++) ;
197             fp = mypopen(name,"r");
198             stio->type = '|';
199         }
200         else {
201             stio->type = '<';
202             for (; isspace(*name); name++) ;
203             if (strEQ(name,"-")) {
204                 fp = stdin;
205                 stio->type = '-';
206             }
207             else
208                 fp = fopen(name,"r");
209         }
210     }
211     Safefree(myname);
212     if (!fp)
213         return FALSE;
214     if (stio->type &&
215       stio->type != '|' && stio->type != '-') {
216         if (fstat(fileno(fp),&statbuf) < 0) {
217             (void)fclose(fp);
218             return FALSE;
219         }
220         if (S_ISSOCK(statbuf.st_mode))
221             stio->type = 's';   /* in case a socket was passed in to us */
222 #ifdef S_IFMT
223         else if (!(statbuf.st_mode & S_IFMT))
224             stio->type = 's';   /* some OS's return 0 on fstat()ed socket */
225 #endif
226     }
227 #if defined(HAS_FCNTL) && defined(F_SETFD)
228     fd = fileno(fp);
229     fcntl(fd,F_SETFD,fd >= 3);
230 #endif
231     stio->ifp = fp;
232     if (writing) {
233         if (stio->type != 's')
234             stio->ofp = fp;
235         else
236             if (!(stio->ofp = fdopen(fileno(fp),"w"))) {
237                 fclose(fp);
238                 stio->ifp = Nullfp;
239             }
240     }
241     return TRUE;
242 }
243
244 FILE *
245 nextargv(stab)
246 register STAB *stab;
247 {
248     register STR *str;
249     int filedev;
250     int fileino;
251     int fileuid;
252     int filegid;
253     static int filemode = 0;
254     static int lastfd;
255     static char *oldname;
256
257     if (!argvoutstab)
258         argvoutstab = stabent("ARGVOUT",TRUE);
259     if (filemode & (S_ISUID|S_ISGID)) {
260         fflush(stab_io(argvoutstab)->ifp);  /* chmod must follow last write */
261 #ifdef HAS_FCHMOD
262         (void)fchmod(lastfd,filemode);
263 #else
264         (void)chmod(oldname,filemode);
265 #endif
266     }
267     filemode = 0;
268     while (alen(stab_xarray(stab)) >= 0) {
269         str = ashift(stab_xarray(stab));
270         str_sset(stab_val(stab),str);
271         STABSET(stab_val(stab));
272         oldname = str_get(stab_val(stab));
273         if (do_open(stab,oldname,stab_val(stab)->str_cur)) {
274             if (inplace) {
275 #ifdef TAINT
276                 taintproper("Insecure dependency in inplace open");
277 #endif
278                 if (strEQ(oldname,"-")) {
279                     str_free(str);
280                     defoutstab = stabent("STDOUT",TRUE);
281                     return stab_io(stab)->ifp;
282                 }
283                 filedev = statbuf.st_dev;
284                 fileino = statbuf.st_ino;
285                 filemode = statbuf.st_mode;
286                 fileuid = statbuf.st_uid;
287                 filegid = statbuf.st_gid;
288                 if (!S_ISREG(filemode)) {
289                     warn("Can't do inplace edit: %s is not a regular file",
290                       oldname );
291                     do_close(stab,FALSE);
292                     str_free(str);
293                     continue;
294                 }
295                 if (*inplace) {
296 #ifdef SUFFIX
297                     add_suffix(str,inplace);
298 #else
299                     str_cat(str,inplace);
300 #endif
301 #ifndef FLEXFILENAMES
302                     if (stat(str->str_ptr,&statbuf) >= 0
303                       && statbuf.st_dev == filedev
304                       && statbuf.st_ino == fileino ) {
305                         warn("Can't do inplace edit: %s > 14 characters",
306                           str->str_ptr );
307                         do_close(stab,FALSE);
308                         str_free(str);
309                         continue;
310                     }
311 #endif
312 #ifdef HAS_RENAME
313 #ifndef MSDOS
314                     if (rename(oldname,str->str_ptr) < 0) {
315                         warn("Can't rename %s to %s: %s, skipping file",
316                           oldname, str->str_ptr, strerror(errno) );
317                         do_close(stab,FALSE);
318                         str_free(str);
319                         continue;
320                     }
321 #else
322                     do_close(stab,FALSE);
323                     (void)unlink(str->str_ptr);
324                     (void)rename(oldname,str->str_ptr);
325                     do_open(stab,str->str_ptr,stab_val(stab)->str_cur);
326 #endif /* MSDOS */
327 #else
328                     (void)UNLINK(str->str_ptr);
329                     if (link(oldname,str->str_ptr) < 0) {
330                         warn("Can't rename %s to %s: %s, skipping file",
331                           oldname, str->str_ptr, strerror(errno) );
332                         do_close(stab,FALSE);
333                         str_free(str);
334                         continue;
335                     }
336                     (void)UNLINK(oldname);
337 #endif
338                 }
339                 else {
340 #ifndef MSDOS
341                     if (UNLINK(oldname) < 0) {
342                         warn("Can't rename %s to %s: %s, skipping file",
343                           oldname, str->str_ptr, strerror(errno) );
344                         do_close(stab,FALSE);
345                         str_free(str);
346                         continue;
347                     }
348 #else
349                     fatal("Can't do inplace edit without backup");
350 #endif
351                 }
352
353                 str_nset(str,">",1);
354                 str_cat(str,oldname);
355                 errno = 0;              /* in case sprintf set errno */
356                 if (!do_open(argvoutstab,str->str_ptr,str->str_cur)) {
357                     warn("Can't do inplace edit on %s: %s",
358                       oldname, strerror(errno) );
359                     do_close(stab,FALSE);
360                     str_free(str);
361                     continue;
362                 }
363                 defoutstab = argvoutstab;
364                 lastfd = fileno(stab_io(argvoutstab)->ifp);
365                 (void)fstat(lastfd,&statbuf);
366 #ifdef HAS_FCHMOD
367                 (void)fchmod(lastfd,filemode);
368 #else
369                 (void)chmod(oldname,filemode);
370 #endif
371                 if (fileuid != statbuf.st_uid || filegid != statbuf.st_gid) {
372 #ifdef HAS_FCHOWN
373                     (void)fchown(lastfd,fileuid,filegid);
374 #else
375 #ifdef HAS_CHOWN
376                     (void)chown(oldname,fileuid,filegid);
377 #endif
378 #endif
379                 }
380             }
381             str_free(str);
382             return stab_io(stab)->ifp;
383         }
384         else
385             fprintf(stderr,"Can't open %s: %s\n",str_get(str), strerror(errno));
386         str_free(str);
387     }
388     if (inplace) {
389         (void)do_close(argvoutstab,FALSE);
390         defoutstab = stabent("STDOUT",TRUE);
391     }
392     return Nullfp;
393 }
394
395 #ifdef HAS_PIPE
396 void
397 do_pipe(str, rstab, wstab)
398 STR *str;
399 STAB *rstab;
400 STAB *wstab;
401 {
402     register STIO *rstio;
403     register STIO *wstio;
404     int fd[2];
405
406     if (!rstab)
407         goto badexit;
408     if (!wstab)
409         goto badexit;
410
411     rstio = stab_io(rstab);
412     wstio = stab_io(wstab);
413
414     if (!rstio)
415         rstio = stab_io(rstab) = stio_new();
416     else if (rstio->ifp)
417         do_close(rstab,FALSE);
418     if (!wstio)
419         wstio = stab_io(wstab) = stio_new();
420     else if (wstio->ifp)
421         do_close(wstab,FALSE);
422
423     if (pipe(fd) < 0)
424         goto badexit;
425     rstio->ifp = fdopen(fd[0], "r");
426     wstio->ofp = fdopen(fd[1], "w");
427     wstio->ifp = wstio->ofp;
428     rstio->type = '<';
429     wstio->type = '>';
430     if (!rstio->ifp || !wstio->ofp) {
431         if (rstio->ifp) fclose(rstio->ifp);
432         else close(fd[0]);
433         if (wstio->ofp) fclose(wstio->ofp);
434         else close(fd[1]);
435         goto badexit;
436     }
437
438     str_sset(str,&str_yes);
439     return;
440
441 badexit:
442     str_sset(str,&str_undef);
443     return;
444 }
445 #endif
446
447 bool
448 do_close(stab,explicit)
449 STAB *stab;
450 bool explicit;
451 {
452     bool retval = FALSE;
453     register STIO *stio;
454     int status;
455
456     if (!stab)
457         stab = argvstab;
458     if (!stab)
459         return FALSE;
460     stio = stab_io(stab);
461     if (!stio) {                /* never opened */
462         if (dowarn && explicit)
463             warn("Close on unopened file <%s>",stab_name(stab));
464         return FALSE;
465     }
466     if (stio->ifp) {
467         if (stio->type == '|') {
468             status = mypclose(stio->ifp);
469             retval = (status == 0);
470             statusvalue = (unsigned short)status & 0xffff;
471         }
472         else if (stio->type == '-')
473             retval = TRUE;
474         else {
475             if (stio->ofp && stio->ofp != stio->ifp) {          /* a socket */
476                 retval = (fclose(stio->ofp) != EOF);
477                 fclose(stio->ifp);      /* clear stdio, fd already closed */
478             }
479             else
480                 retval = (fclose(stio->ifp) != EOF);
481         }
482         stio->ofp = stio->ifp = Nullfp;
483     }
484     if (explicit)
485         stio->lines = 0;
486     stio->type = ' ';
487     return retval;
488 }
489
490 bool
491 do_eof(stab)
492 STAB *stab;
493 {
494     register STIO *stio;
495     int ch;
496
497     if (!stab) {                        /* eof() */
498         if (argvstab)
499             stio = stab_io(argvstab);
500         else
501             return TRUE;
502     }
503     else
504         stio = stab_io(stab);
505
506     if (!stio)
507         return TRUE;
508
509     while (stio->ifp) {
510
511 #ifdef STDSTDIO                 /* (the code works without this) */
512         if (stio->ifp->_cnt > 0)        /* cheat a little, since */
513             return FALSE;               /* this is the most usual case */
514 #endif
515
516         ch = getc(stio->ifp);
517         if (ch != EOF) {
518             (void)ungetc(ch, stio->ifp);
519             return FALSE;
520         }
521 #ifdef STDSTDIO
522         if (stio->ifp->_cnt < -1)
523             stio->ifp->_cnt = -1;
524 #endif
525         if (!stab) {                    /* not necessarily a real EOF yet? */
526             if (!nextargv(argvstab))    /* get another fp handy */
527                 return TRUE;
528         }
529         else
530             return TRUE;                /* normal fp, definitely end of file */
531     }
532     return TRUE;
533 }
534
535 long
536 do_tell(stab)
537 STAB *stab;
538 {
539     register STIO *stio;
540
541     if (!stab)
542         goto phooey;
543
544     stio = stab_io(stab);
545     if (!stio || !stio->ifp)
546         goto phooey;
547
548     if (feof(stio->ifp))
549         (void)fseek (stio->ifp, 0L, 2);         /* ultrix 1.2 workaround */
550
551     return ftell(stio->ifp);
552
553 phooey:
554     if (dowarn)
555         warn("tell() on unopened file");
556     return -1L;
557 }
558
559 bool
560 do_seek(stab, pos, whence)
561 STAB *stab;
562 long pos;
563 int whence;
564 {
565     register STIO *stio;
566
567     if (!stab)
568         goto nuts;
569
570     stio = stab_io(stab);
571     if (!stio || !stio->ifp)
572         goto nuts;
573
574     if (feof(stio->ifp))
575         (void)fseek (stio->ifp, 0L, 2);         /* ultrix 1.2 workaround */
576
577     return fseek(stio->ifp, pos, whence) >= 0;
578
579 nuts:
580     if (dowarn)
581         warn("seek() on unopened file");
582     return FALSE;
583 }
584
585 int
586 do_ctl(optype,stab,func,argstr)
587 int optype;
588 STAB *stab;
589 int func;
590 STR *argstr;
591 {
592     register STIO *stio;
593     register char *s;
594     int retval;
595
596     if (!stab || !argstr)
597         return -1;
598     stio = stab_io(stab);
599     if (!stio)
600         return -1;
601
602     if (argstr->str_pok || !argstr->str_nok) {
603         if (!argstr->str_pok)
604             s = str_get(argstr);
605
606 #ifdef IOCPARM_MASK
607 #ifndef IOCPARM_LEN
608 #define IOCPARM_LEN(x)  (((x) >> 16) & IOCPARM_MASK)
609 #endif
610 #endif
611 #ifdef IOCPARM_LEN
612         retval = IOCPARM_LEN(func);     /* on BSDish systes we're safe */
613 #else
614         retval = 256;                   /* otherwise guess at what's safe */
615 #endif
616         if (argstr->str_cur < retval) {
617             Str_Grow(argstr,retval+1);
618             argstr->str_cur = retval;
619         }
620
621         s = argstr->str_ptr;
622         s[argstr->str_cur] = 17;        /* a little sanity check here */
623     }
624     else {
625         retval = (int)str_gnum(argstr);
626 #ifdef MSDOS
627         s = (char*)(long)retval;                /* ouch */
628 #else
629         s = (char*)retval;              /* ouch */
630 #endif
631     }
632
633 #ifndef lint
634     if (optype == O_IOCTL)
635         retval = ioctl(fileno(stio->ifp), func, s);
636     else
637 #ifdef MSDOS
638         fatal("fcntl is not implemented");
639 #else
640 #ifdef HAS_FCNTL
641         retval = fcntl(fileno(stio->ifp), func, s);
642 #else
643         fatal("fcntl is not implemented");
644 #endif
645 #endif
646 #else /* lint */
647     retval = 0;
648 #endif /* lint */
649
650     if (argstr->str_pok) {
651         if (s[argstr->str_cur] != 17)
652             fatal("Return value overflowed string");
653         s[argstr->str_cur] = 0;         /* put our null back */
654     }
655     return retval;
656 }
657
658 int
659 do_stat(str,arg,gimme,arglast)
660 STR *str;
661 register ARG *arg;
662 int gimme;
663 int *arglast;
664 {
665     register ARRAY *ary = stack;
666     register int sp = arglast[0] + 1;
667     int max = 13;
668
669     if ((arg[1].arg_type & A_MASK) == A_WORD) {
670         tmpstab = arg[1].arg_ptr.arg_stab;
671         if (tmpstab != defstab) {
672             laststype = O_STAT;
673             statstab = tmpstab;
674             str_set(statname,"");
675             if (!stab_io(tmpstab) || !stab_io(tmpstab)->ifp ||
676               fstat(fileno(stab_io(tmpstab)->ifp),&statcache) < 0) {
677                 max = 0;
678                 laststatval = -1;
679             }
680         }
681         else if (laststatval < 0)
682             max = 0;
683     }
684     else {
685         str_set(statname,str_get(ary->ary_array[sp]));
686         statstab = Nullstab;
687 #ifdef HAS_LSTAT
688         laststype = arg->arg_type;
689         if (arg->arg_type == O_LSTAT)
690             laststatval = lstat(str_get(statname),&statcache);
691         else
692 #endif
693             laststatval = stat(str_get(statname),&statcache);
694         if (laststatval < 0)
695             max = 0;
696     }
697
698     if (gimme != G_ARRAY) {
699         if (max)
700             str_sset(str,&str_yes);
701         else
702             str_sset(str,&str_undef);
703         STABSET(str);
704         ary->ary_array[sp] = str;
705         return sp;
706     }
707     sp--;
708     if (max) {
709 #ifndef lint
710         (void)astore(ary,++sp,
711           str_2mortal(str_nmake((double)statcache.st_dev)));
712         (void)astore(ary,++sp,
713           str_2mortal(str_nmake((double)statcache.st_ino)));
714         (void)astore(ary,++sp,
715           str_2mortal(str_nmake((double)statcache.st_mode)));
716         (void)astore(ary,++sp,
717           str_2mortal(str_nmake((double)statcache.st_nlink)));
718         (void)astore(ary,++sp,
719           str_2mortal(str_nmake((double)statcache.st_uid)));
720         (void)astore(ary,++sp,
721           str_2mortal(str_nmake((double)statcache.st_gid)));
722         (void)astore(ary,++sp,
723           str_2mortal(str_nmake((double)statcache.st_rdev)));
724         (void)astore(ary,++sp,
725           str_2mortal(str_nmake((double)statcache.st_size)));
726         (void)astore(ary,++sp,
727           str_2mortal(str_nmake((double)statcache.st_atime)));
728         (void)astore(ary,++sp,
729           str_2mortal(str_nmake((double)statcache.st_mtime)));
730         (void)astore(ary,++sp,
731           str_2mortal(str_nmake((double)statcache.st_ctime)));
732 #ifdef STATBLOCKS
733         (void)astore(ary,++sp,
734           str_2mortal(str_nmake((double)statcache.st_blksize)));
735         (void)astore(ary,++sp,
736           str_2mortal(str_nmake((double)statcache.st_blocks)));
737 #else
738         (void)astore(ary,++sp,
739           str_2mortal(str_make("",0)));
740         (void)astore(ary,++sp,
741           str_2mortal(str_make("",0)));
742 #endif
743 #else /* lint */
744         (void)astore(ary,++sp,str_nmake(0.0));
745 #endif /* lint */
746     }
747     return sp;
748 }
749
750 #if !defined(HAS_TRUNCATE) && !defined(HAS_CHSIZE) && defined(F_FREESP)
751         /* code courtesy of William Kucharski */
752 #define HAS_CHSIZE
753
754 int chsize(fd, length)
755 int fd;                 /* file descriptor */
756 off_t length;           /* length to set file to */
757 {
758     extern long lseek();
759     struct flock fl;
760     struct stat filebuf;
761
762     if (fstat(fd, &filebuf) < 0)
763         return -1;
764
765     if (filebuf.st_size < length) {
766
767         /* extend file length */
768
769         if ((lseek(fd, (length - 1), 0)) < 0)
770             return -1;
771
772         /* write a "0" byte */
773
774         if ((write(fd, "", 1)) != 1)
775             return -1;
776     }
777     else {
778         /* truncate length */
779
780         fl.l_whence = 0;
781         fl.l_len = 0;
782         fl.l_start = length;
783         fl.l_type = F_WRLCK;    /* write lock on file space */
784
785         /*
786         * This relies on the UNDOCUMENTED F_FREESP argument to
787         * fcntl(2), which truncates the file so that it ends at the
788         * position indicated by fl.l_start.
789         *
790         * Will minor miracles never cease?
791         */
792
793         if (fcntl(fd, F_FREESP, &fl) < 0)
794             return -1;
795
796     }
797
798     return 0;
799 }
800 #endif /* F_FREESP */
801
802 int
803 do_truncate(str,arg,gimme,arglast)
804 STR *str;
805 register ARG *arg;
806 int gimme;
807 int *arglast;
808 {
809     register ARRAY *ary = stack;
810     register int sp = arglast[0] + 1;
811     off_t len = (off_t)str_gnum(ary->ary_array[sp+1]);
812     int result = 1;
813     STAB *tmpstab;
814
815 #if defined(HAS_TRUNCATE) || defined(HAS_CHSIZE)
816 #ifdef HAS_TRUNCATE
817     if ((arg[1].arg_type & A_MASK) == A_WORD) {
818         tmpstab = arg[1].arg_ptr.arg_stab;
819         if (!stab_io(tmpstab) ||
820           ftruncate(fileno(stab_io(tmpstab)->ifp), len) < 0)
821             result = 0;
822     }
823     else if (truncate(str_get(ary->ary_array[sp]), len) < 0)
824         result = 0;
825 #else
826     if ((arg[1].arg_type & A_MASK) == A_WORD) {
827         tmpstab = arg[1].arg_ptr.arg_stab;
828         if (!stab_io(tmpstab) ||
829           chsize(fileno(stab_io(tmpstab)->ifp), len) < 0)
830             result = 0;
831     }
832     else {
833         int tmpfd;
834
835         if ((tmpfd = open(str_get(ary->ary_array[sp]), 0)) < 0)
836             result = 0;
837         else {
838             if (chsize(tmpfd, len) < 0)
839                 result = 0;
840             close(tmpfd);
841         }
842     }
843 #endif
844
845     if (result)
846         str_sset(str,&str_yes);
847     else
848         str_sset(str,&str_undef);
849     STABSET(str);
850     ary->ary_array[sp] = str;
851     return sp;
852 #else
853     fatal("truncate not implemented");
854 #endif
855 }
856
857 int
858 looks_like_number(str)
859 STR *str;
860 {
861     register char *s;
862     register char *send;
863
864     if (!str->str_pok)
865         return TRUE;
866     s = str->str_ptr; 
867     send = s + str->str_cur;
868     while (isspace(*s))
869         s++;
870     if (s >= send)
871         return FALSE;
872     if (*s == '+' || *s == '-')
873         s++;
874     while (isdigit(*s))
875         s++;
876     if (s == send)
877         return TRUE;
878     if (*s == '.') 
879         s++;
880     else if (s == str->str_ptr)
881         return FALSE;
882     while (isdigit(*s))
883         s++;
884     if (s == send)
885         return TRUE;
886     if (*s == 'e' || *s == 'E') {
887         s++;
888         if (*s == '+' || *s == '-')
889             s++;
890         while (isdigit(*s))
891             s++;
892     }
893     while (isspace(*s))
894         s++;
895     if (s >= send)
896         return TRUE;
897     return FALSE;
898 }
899
900 bool
901 do_print(str,fp)
902 register STR *str;
903 FILE *fp;
904 {
905     register char *tmps;
906
907     if (!fp) {
908         if (dowarn)
909             warn("print to unopened file");
910         return FALSE;
911     }
912     if (!str)
913         return TRUE;
914     if (ofmt &&
915       ((str->str_nok && str->str_u.str_nval != 0.0)
916        || (looks_like_number(str) && str_gnum(str) != 0.0) ) ) {
917         fprintf(fp, ofmt, str->str_u.str_nval);
918         return !ferror(fp);
919     }
920     else {
921         tmps = str_get(str);
922         if (*tmps == 'S' && tmps[1] == 't' && tmps[2] == 'B' && tmps[3] == '\0'
923           && str->str_cur == sizeof(STBP) && strlen(tmps) < str->str_cur) {
924             STR *tmpstr = str_mortal(&str_undef);
925             stab_fullname(tmpstr,((STAB*)str));/* a stab value, be nice */
926             str = tmpstr;
927             tmps = str->str_ptr;
928             putc('*',fp);
929         }
930         if (str->str_cur && (fwrite(tmps,1,str->str_cur,fp) == 0 || ferror(fp)))
931             return FALSE;
932     }
933     return TRUE;
934 }
935
936 bool
937 do_aprint(arg,fp,arglast)
938 register ARG *arg;
939 register FILE *fp;
940 int *arglast;
941 {
942     register STR **st = stack->ary_array;
943     register int sp = arglast[1];
944     register int retval;
945     register int items = arglast[2] - sp;
946
947     if (!fp) {
948         if (dowarn)
949             warn("print to unopened file");
950         return FALSE;
951     }
952     st += ++sp;
953     if (arg->arg_type == O_PRTF) {
954         do_sprintf(arg->arg_ptr.arg_str,items,st);
955         retval = do_print(arg->arg_ptr.arg_str,fp);
956     }
957     else {
958         retval = (items <= 0);
959         for (; items > 0; items--,st++) {
960             if (retval && ofslen) {
961                 if (fwrite(ofs, 1, ofslen, fp) == 0 || ferror(fp)) {
962                     retval = FALSE;
963                     break;
964                 }
965             }
966             if (!(retval = do_print(*st, fp)))
967                 break;
968         }
969         if (retval && orslen)
970             if (fwrite(ors, 1, orslen, fp) == 0 || ferror(fp))
971                 retval = FALSE;
972     }
973     return retval;
974 }
975
976 int
977 mystat(arg,str)
978 ARG *arg;
979 STR *str;
980 {
981     STIO *stio;
982
983     laststype = O_STAT;
984     if (arg[1].arg_type & A_DONT) {
985         stio = stab_io(arg[1].arg_ptr.arg_stab);
986         if (stio && stio->ifp) {
987             statstab = arg[1].arg_ptr.arg_stab;
988             str_set(statname,"");
989             return (laststatval = fstat(fileno(stio->ifp), &statcache));
990         }
991         else {
992             if (arg[1].arg_ptr.arg_stab == defstab)
993                 return laststatval;
994             if (dowarn)
995                 warn("Stat on unopened file <%s>",
996                   stab_name(arg[1].arg_ptr.arg_stab));
997             statstab = Nullstab;
998             str_set(statname,"");
999             return (laststatval = -1);
1000         }
1001     }
1002     else {
1003         statstab = Nullstab;
1004         str_set(statname,str_get(str));
1005         return (laststatval = stat(str_get(str),&statcache));
1006     }
1007 }
1008
1009 int
1010 mylstat(arg,str)
1011 ARG *arg;
1012 STR *str;
1013 {
1014     if (arg[1].arg_type & A_DONT) {
1015         if (arg[1].arg_ptr.arg_stab == defstab) {
1016             if (laststype != O_LSTAT)
1017                 fatal("The stat preceding -l _ wasn't an lstat");
1018             return laststatval;
1019         }
1020         fatal("You can't use -l on a filehandle");
1021     }
1022
1023     laststype = O_LSTAT;
1024     statstab = Nullstab;
1025     str_set(statname,str_get(str));
1026 #ifdef HAS_LSTAT
1027     return (laststatval = lstat(str_get(str),&statcache));
1028 #else
1029     return (laststatval = stat(str_get(str),&statcache));
1030 #endif
1031 }
1032
1033 STR *
1034 do_fttext(arg,str)
1035 register ARG *arg;
1036 STR *str;
1037 {
1038     int i;
1039     int len;
1040     int odd = 0;
1041     STDCHAR tbuf[512];
1042     register STDCHAR *s;
1043     register STIO *stio;
1044
1045     if (arg[1].arg_type & A_DONT) {
1046         if (arg[1].arg_ptr.arg_stab == defstab) {
1047             if (statstab)
1048                 stio = stab_io(statstab);
1049             else {
1050                 str = statname;
1051                 goto really_filename;
1052             }
1053         }
1054         else {
1055             statstab = arg[1].arg_ptr.arg_stab;
1056             str_set(statname,"");
1057             stio = stab_io(statstab);
1058         }
1059         if (stio && stio->ifp) {
1060 #ifdef STDSTDIO
1061             fstat(fileno(stio->ifp),&statcache);
1062             if (stio->ifp->_cnt <= 0) {
1063                 i = getc(stio->ifp);
1064                 if (i != EOF)
1065                     (void)ungetc(i,stio->ifp);
1066             }
1067             if (stio->ifp->_cnt <= 0)   /* null file is anything */
1068                 return &str_yes;
1069             len = stio->ifp->_cnt + (stio->ifp->_ptr - stio->ifp->_base);
1070             s = stio->ifp->_base;
1071 #else
1072             fatal("-T and -B not implemented on filehandles\n");
1073 #endif
1074         }
1075         else {
1076             if (dowarn)
1077                 warn("Test on unopened file <%s>",
1078                   stab_name(arg[1].arg_ptr.arg_stab));
1079             return &str_undef;
1080         }
1081     }
1082     else {
1083         statstab = Nullstab;
1084         str_set(statname,str_get(str));
1085       really_filename:
1086         i = open(str_get(str),0);
1087         if (i < 0)
1088             return &str_undef;
1089         fstat(i,&statcache);
1090         len = read(i,tbuf,512);
1091         (void)close(i);
1092         if (len <= 0)           /* null file is anything */
1093             return &str_yes;
1094         s = tbuf;
1095     }
1096
1097     /* now scan s to look for textiness */
1098
1099     for (i = 0; i < len; i++,s++) {
1100         if (!*s) {                      /* null never allowed in text */
1101             odd += len;
1102             break;
1103         }
1104         else if (*s & 128)
1105             odd++;
1106         else if (*s < 32 &&
1107           *s != '\n' && *s != '\r' && *s != '\b' &&
1108           *s != '\t' && *s != '\f' && *s != 27)
1109             odd++;
1110     }
1111
1112     if ((odd * 10 > len) == (arg->arg_type == O_FTTEXT)) /* allow 10% odd */
1113         return &str_no;
1114     else
1115         return &str_yes;
1116 }
1117
1118 bool
1119 do_aexec(really,arglast)
1120 STR *really;
1121 int *arglast;
1122 {
1123     register STR **st = stack->ary_array;
1124     register int sp = arglast[1];
1125     register int items = arglast[2] - sp;
1126     register char **a;
1127     char **argv;
1128     char *tmps;
1129
1130     if (items) {
1131         New(401,argv, items+1, char*);
1132         a = argv;
1133         for (st += ++sp; items > 0; items--,st++) {
1134             if (*st)
1135                 *a++ = str_get(*st);
1136             else
1137                 *a++ = "";
1138         }
1139         *a = Nullch;
1140 #ifdef TAINT
1141         if (*argv[0] != '/')    /* will execvp use PATH? */
1142             taintenv();         /* testing IFS here is overkill, probably */
1143 #endif
1144         if (really && *(tmps = str_get(really)))
1145             execvp(tmps,argv);
1146         else
1147             execvp(argv[0],argv);
1148         Safefree(argv);
1149     }
1150     return FALSE;
1151 }
1152
1153 static char **Argv = Null(char **);
1154 static char *Cmd = Nullch;
1155
1156 void
1157 do_execfree()
1158 {
1159     if (Argv) {
1160         Safefree(Argv);
1161         Argv = Null(char **);
1162     }
1163     if (Cmd) {
1164         Safefree(Cmd);
1165         Cmd = Nullch;
1166     }
1167 }
1168
1169 bool
1170 do_exec(cmd)
1171 char *cmd;
1172 {
1173     register char **a;
1174     register char *s;
1175     char flags[10];
1176
1177 #ifdef TAINT
1178     taintenv();
1179     taintproper("Insecure dependency in exec");
1180 #endif
1181
1182     /* save an extra exec if possible */
1183
1184 #ifdef CSH
1185     if (strnEQ(cmd,cshname,cshlen) && strnEQ(cmd+cshlen," -c",3)) {
1186         strcpy(flags,"-c");
1187         s = cmd+cshlen+3;
1188         if (*s == 'f') {
1189             s++;
1190             strcat(flags,"f");
1191         }
1192         if (*s == ' ')
1193             s++;
1194         if (*s++ == '\'') {
1195             char *ncmd = s;
1196
1197             while (*s)
1198                 s++;
1199             if (s[-1] == '\n')
1200                 *--s = '\0';
1201             if (s[-1] == '\'') {
1202                 *--s = '\0';
1203                 execl(cshname,"csh", flags,ncmd,(char*)0);
1204                 *s = '\'';
1205                 return FALSE;
1206             }
1207         }
1208     }
1209 #endif /* CSH */
1210
1211     /* see if there are shell metacharacters in it */
1212
1213     for (s = cmd; *s && isalpha(*s); s++) ;     /* catch VAR=val gizmo */
1214     if (*s == '=')
1215         goto doshell;
1216     for (s = cmd; *s; s++) {
1217         if (*s != ' ' && !isalpha(*s) && index("$&*(){}[]'\";\\|?<>~`\n",*s)) {
1218             if (*s == '\n' && !s[1]) {
1219                 *s = '\0';
1220                 break;
1221             }
1222           doshell:
1223             execl("/bin/sh","sh","-c",cmd,(char*)0);
1224             return FALSE;
1225         }
1226     }
1227     New(402,Argv, (s - cmd) / 2 + 2, char*);
1228     Cmd = nsavestr(cmd, s-cmd);
1229     a = Argv;
1230     for (s = Cmd; *s;) {
1231         while (*s && isspace(*s)) s++;
1232         if (*s)
1233             *(a++) = s;
1234         while (*s && !isspace(*s)) s++;
1235         if (*s)
1236             *s++ = '\0';
1237     }
1238     *a = Nullch;
1239     if (Argv[0]) {
1240         execvp(Argv[0],Argv);
1241         if (errno == ENOEXEC) {         /* for system V NIH syndrome */
1242             do_execfree();
1243             goto doshell;
1244         }
1245     }
1246     do_execfree();
1247     return FALSE;
1248 }
1249
1250 #ifdef HAS_SOCKET
1251 int
1252 do_socket(stab, arglast)
1253 STAB *stab;
1254 int *arglast;
1255 {
1256     register STR **st = stack->ary_array;
1257     register int sp = arglast[1];
1258     register STIO *stio;
1259     int domain, type, protocol, fd;
1260
1261     if (!stab)
1262         return FALSE;
1263
1264     stio = stab_io(stab);
1265     if (!stio)
1266         stio = stab_io(stab) = stio_new();
1267     else if (stio->ifp)
1268         do_close(stab,FALSE);
1269
1270     domain = (int)str_gnum(st[++sp]);
1271     type = (int)str_gnum(st[++sp]);
1272     protocol = (int)str_gnum(st[++sp]);
1273 #ifdef TAINT
1274     taintproper("Insecure dependency in socket");
1275 #endif
1276     fd = socket(domain,type,protocol);
1277     if (fd < 0)
1278         return FALSE;
1279     stio->ifp = fdopen(fd, "r");        /* stdio gets confused about sockets */
1280     stio->ofp = fdopen(fd, "w");
1281     stio->type = 's';
1282     if (!stio->ifp || !stio->ofp) {
1283         if (stio->ifp) fclose(stio->ifp);
1284         if (stio->ofp) fclose(stio->ofp);
1285         if (!stio->ifp && !stio->ofp) close(fd);
1286         return FALSE;
1287     }
1288
1289     return TRUE;
1290 }
1291
1292 int
1293 do_bind(stab, arglast)
1294 STAB *stab;
1295 int *arglast;
1296 {
1297     register STR **st = stack->ary_array;
1298     register int sp = arglast[1];
1299     register STIO *stio;
1300     char *addr;
1301
1302     if (!stab)
1303         goto nuts;
1304
1305     stio = stab_io(stab);
1306     if (!stio || !stio->ifp)
1307         goto nuts;
1308
1309     addr = str_get(st[++sp]);
1310 #ifdef TAINT
1311     taintproper("Insecure dependency in bind");
1312 #endif
1313     return bind(fileno(stio->ifp), addr, st[sp]->str_cur) >= 0;
1314
1315 nuts:
1316     if (dowarn)
1317         warn("bind() on closed fd");
1318     return FALSE;
1319
1320 }
1321
1322 int
1323 do_connect(stab, arglast)
1324 STAB *stab;
1325 int *arglast;
1326 {
1327     register STR **st = stack->ary_array;
1328     register int sp = arglast[1];
1329     register STIO *stio;
1330     char *addr;
1331
1332     if (!stab)
1333         goto nuts;
1334
1335     stio = stab_io(stab);
1336     if (!stio || !stio->ifp)
1337         goto nuts;
1338
1339     addr = str_get(st[++sp]);
1340 #ifdef TAINT
1341     taintproper("Insecure dependency in connect");
1342 #endif
1343     return connect(fileno(stio->ifp), addr, st[sp]->str_cur) >= 0;
1344
1345 nuts:
1346     if (dowarn)
1347         warn("connect() on closed fd");
1348     return FALSE;
1349
1350 }
1351
1352 int
1353 do_listen(stab, arglast)
1354 STAB *stab;
1355 int *arglast;
1356 {
1357     register STR **st = stack->ary_array;
1358     register int sp = arglast[1];
1359     register STIO *stio;
1360     int backlog;
1361
1362     if (!stab)
1363         goto nuts;
1364
1365     stio = stab_io(stab);
1366     if (!stio || !stio->ifp)
1367         goto nuts;
1368
1369     backlog = (int)str_gnum(st[++sp]);
1370     return listen(fileno(stio->ifp), backlog) >= 0;
1371
1372 nuts:
1373     if (dowarn)
1374         warn("listen() on closed fd");
1375     return FALSE;
1376 }
1377
1378 void
1379 do_accept(str, nstab, gstab)
1380 STR *str;
1381 STAB *nstab;
1382 STAB *gstab;
1383 {
1384     register STIO *nstio;
1385     register STIO *gstio;
1386     int len = sizeof buf;
1387     int fd;
1388
1389     if (!nstab)
1390         goto badexit;
1391     if (!gstab)
1392         goto nuts;
1393
1394     gstio = stab_io(gstab);
1395     nstio = stab_io(nstab);
1396
1397     if (!gstio || !gstio->ifp)
1398         goto nuts;
1399     if (!nstio)
1400         nstio = stab_io(nstab) = stio_new();
1401     else if (nstio->ifp)
1402         do_close(nstab,FALSE);
1403
1404     fd = accept(fileno(gstio->ifp),buf,&len);
1405     if (fd < 0)
1406         goto badexit;
1407     nstio->ifp = fdopen(fd, "r");
1408     nstio->ofp = fdopen(fd, "w");
1409     nstio->type = 's';
1410     if (!nstio->ifp || !nstio->ofp) {
1411         if (nstio->ifp) fclose(nstio->ifp);
1412         if (nstio->ofp) fclose(nstio->ofp);
1413         if (!nstio->ifp && !nstio->ofp) close(fd);
1414         goto badexit;
1415     }
1416
1417     str_nset(str, buf, len);
1418     return;
1419
1420 nuts:
1421     if (dowarn)
1422         warn("accept() on closed fd");
1423 badexit:
1424     str_sset(str,&str_undef);
1425     return;
1426 }
1427
1428 int
1429 do_shutdown(stab, arglast)
1430 STAB *stab;
1431 int *arglast;
1432 {
1433     register STR **st = stack->ary_array;
1434     register int sp = arglast[1];
1435     register STIO *stio;
1436     int how;
1437
1438     if (!stab)
1439         goto nuts;
1440
1441     stio = stab_io(stab);
1442     if (!stio || !stio->ifp)
1443         goto nuts;
1444
1445     how = (int)str_gnum(st[++sp]);
1446     return shutdown(fileno(stio->ifp), how) >= 0;
1447
1448 nuts:
1449     if (dowarn)
1450         warn("shutdown() on closed fd");
1451     return FALSE;
1452
1453 }
1454
1455 int
1456 do_sopt(optype, stab, arglast)
1457 int optype;
1458 STAB *stab;
1459 int *arglast;
1460 {
1461     register STR **st = stack->ary_array;
1462     register int sp = arglast[1];
1463     register STIO *stio;
1464     int fd;
1465     int lvl;
1466     int optname;
1467
1468     if (!stab)
1469         goto nuts;
1470
1471     stio = stab_io(stab);
1472     if (!stio || !stio->ifp)
1473         goto nuts;
1474
1475     fd = fileno(stio->ifp);
1476     lvl = (int)str_gnum(st[sp+1]);
1477     optname = (int)str_gnum(st[sp+2]);
1478     switch (optype) {
1479     case O_GSOCKOPT:
1480         st[sp] = str_2mortal(str_new(257));
1481         st[sp]->str_cur = 256;
1482         st[sp]->str_pok = 1;
1483         if (getsockopt(fd, lvl, optname, st[sp]->str_ptr, &st[sp]->str_cur) < 0)
1484             goto nuts;
1485         break;
1486     case O_SSOCKOPT:
1487         st[sp] = st[sp+3];
1488         if (setsockopt(fd, lvl, optname, st[sp]->str_ptr, st[sp]->str_cur) < 0)
1489             goto nuts;
1490         st[sp] = &str_yes;
1491         break;
1492     }
1493     
1494     return sp;
1495
1496 nuts:
1497     if (dowarn)
1498         warn("[gs]etsockopt() on closed fd");
1499     st[sp] = &str_undef;
1500     return sp;
1501
1502 }
1503
1504 int
1505 do_getsockname(optype, stab, arglast)
1506 int optype;
1507 STAB *stab;
1508 int *arglast;
1509 {
1510     register STR **st = stack->ary_array;
1511     register int sp = arglast[1];
1512     register STIO *stio;
1513     int fd;
1514
1515     if (!stab)
1516         goto nuts;
1517
1518     stio = stab_io(stab);
1519     if (!stio || !stio->ifp)
1520         goto nuts;
1521
1522     st[sp] = str_2mortal(str_new(257));
1523     st[sp]->str_cur = 256;
1524     st[sp]->str_pok = 1;
1525     fd = fileno(stio->ifp);
1526     switch (optype) {
1527     case O_GETSOCKNAME:
1528         if (getsockname(fd, st[sp]->str_ptr, &st[sp]->str_cur) < 0)
1529             goto nuts2;
1530         break;
1531     case O_GETPEERNAME:
1532         if (getpeername(fd, st[sp]->str_ptr, &st[sp]->str_cur) < 0)
1533             goto nuts2;
1534         break;
1535     }
1536     
1537     return sp;
1538
1539 nuts:
1540     if (dowarn)
1541         warn("get{sock,peer}name() on closed fd");
1542 nuts2:
1543     st[sp] = &str_undef;
1544     return sp;
1545
1546 }
1547
1548 int
1549 do_ghent(which,gimme,arglast)
1550 int which;
1551 int gimme;
1552 int *arglast;
1553 {
1554     register ARRAY *ary = stack;
1555     register int sp = arglast[0];
1556     register char **elem;
1557     register STR *str;
1558     struct hostent *gethostbyname();
1559     struct hostent *gethostbyaddr();
1560 #ifdef HAS_GETHOSTENT
1561     struct hostent *gethostent();
1562 #endif
1563     struct hostent *hent;
1564     unsigned long len;
1565
1566     if (gimme != G_ARRAY) {
1567         astore(ary, ++sp, str_mortal(&str_undef));
1568         return sp;
1569     }
1570
1571     if (which == O_GHBYNAME) {
1572         char *name = str_get(ary->ary_array[sp+1]);
1573
1574         hent = gethostbyname(name);
1575     }
1576     else if (which == O_GHBYADDR) {
1577         STR *addrstr = ary->ary_array[sp+1];
1578         int addrtype = (int)str_gnum(ary->ary_array[sp+2]);
1579         char *addr = str_get(addrstr);
1580
1581         hent = gethostbyaddr(addr,addrstr->str_cur,addrtype);
1582     }
1583     else
1584 #ifdef HAS_GETHOSTENT
1585         hent = gethostent();
1586 #else
1587         fatal("gethostent not implemented");
1588 #endif
1589     if (hent) {
1590 #ifndef lint
1591         (void)astore(ary, ++sp, str = str_mortal(&str_no));
1592         str_set(str, hent->h_name);
1593         (void)astore(ary, ++sp, str = str_mortal(&str_no));
1594         for (elem = hent->h_aliases; *elem; elem++) {
1595             str_cat(str, *elem);
1596             if (elem[1])
1597                 str_ncat(str," ",1);
1598         }
1599         (void)astore(ary, ++sp, str = str_mortal(&str_no));
1600         str_numset(str, (double)hent->h_addrtype);
1601         (void)astore(ary, ++sp, str = str_mortal(&str_no));
1602         len = hent->h_length;
1603         str_numset(str, (double)len);
1604 #ifdef h_addr
1605         for (elem = hent->h_addr_list; *elem; elem++) {
1606             (void)astore(ary, ++sp, str = str_mortal(&str_no));
1607             str_nset(str, *elem, len);
1608         }
1609 #else
1610         (void)astore(ary, ++sp, str = str_mortal(&str_no));
1611         str_nset(str, hent->h_addr, len);
1612 #endif /* h_addr */
1613 #else /* lint */
1614         elem = Nullch;
1615         elem = elem;
1616         (void)astore(ary, ++sp, str_mortal(&str_no));
1617 #endif /* lint */
1618     }
1619
1620     return sp;
1621 }
1622
1623 int
1624 do_gnent(which,gimme,arglast)
1625 int which;
1626 int gimme;
1627 int *arglast;
1628 {
1629     register ARRAY *ary = stack;
1630     register int sp = arglast[0];
1631     register char **elem;
1632     register STR *str;
1633     struct netent *getnetbyname();
1634     struct netent *getnetbyaddr();
1635     struct netent *getnetent();
1636     struct netent *nent;
1637
1638     if (gimme != G_ARRAY) {
1639         astore(ary, ++sp, str_mortal(&str_undef));
1640         return sp;
1641     }
1642
1643     if (which == O_GNBYNAME) {
1644         char *name = str_get(ary->ary_array[sp+1]);
1645
1646         nent = getnetbyname(name);
1647     }
1648     else if (which == O_GNBYADDR) {
1649         unsigned long addr = U_L(str_gnum(ary->ary_array[sp+1]));
1650         int addrtype = (int)str_gnum(ary->ary_array[sp+2]);
1651
1652         nent = getnetbyaddr((long)addr,addrtype);
1653     }
1654     else
1655         nent = getnetent();
1656
1657     if (nent) {
1658 #ifndef lint
1659         (void)astore(ary, ++sp, str = str_mortal(&str_no));
1660         str_set(str, nent->n_name);
1661         (void)astore(ary, ++sp, str = str_mortal(&str_no));
1662         for (elem = nent->n_aliases; *elem; elem++) {
1663             str_cat(str, *elem);
1664             if (elem[1])
1665                 str_ncat(str," ",1);
1666         }
1667         (void)astore(ary, ++sp, str = str_mortal(&str_no));
1668         str_numset(str, (double)nent->n_addrtype);
1669         (void)astore(ary, ++sp, str = str_mortal(&str_no));
1670         str_numset(str, (double)nent->n_net);
1671 #else /* lint */
1672         elem = Nullch;
1673         elem = elem;
1674         (void)astore(ary, ++sp, str_mortal(&str_no));
1675 #endif /* lint */
1676     }
1677
1678     return sp;
1679 }
1680
1681 int
1682 do_gpent(which,gimme,arglast)
1683 int which;
1684 int gimme;
1685 int *arglast;
1686 {
1687     register ARRAY *ary = stack;
1688     register int sp = arglast[0];
1689     register char **elem;
1690     register STR *str;
1691     struct protoent *getprotobyname();
1692     struct protoent *getprotobynumber();
1693     struct protoent *getprotoent();
1694     struct protoent *pent;
1695
1696     if (gimme != G_ARRAY) {
1697         astore(ary, ++sp, str_mortal(&str_undef));
1698         return sp;
1699     }
1700
1701     if (which == O_GPBYNAME) {
1702         char *name = str_get(ary->ary_array[sp+1]);
1703
1704         pent = getprotobyname(name);
1705     }
1706     else if (which == O_GPBYNUMBER) {
1707         int proto = (int)str_gnum(ary->ary_array[sp+1]);
1708
1709         pent = getprotobynumber(proto);
1710     }
1711     else
1712         pent = getprotoent();
1713
1714     if (pent) {
1715 #ifndef lint
1716         (void)astore(ary, ++sp, str = str_mortal(&str_no));
1717         str_set(str, pent->p_name);
1718         (void)astore(ary, ++sp, str = str_mortal(&str_no));
1719         for (elem = pent->p_aliases; *elem; elem++) {
1720             str_cat(str, *elem);
1721             if (elem[1])
1722                 str_ncat(str," ",1);
1723         }
1724         (void)astore(ary, ++sp, str = str_mortal(&str_no));
1725         str_numset(str, (double)pent->p_proto);
1726 #else /* lint */
1727         elem = Nullch;
1728         elem = elem;
1729         (void)astore(ary, ++sp, str_mortal(&str_no));
1730 #endif /* lint */
1731     }
1732
1733     return sp;
1734 }
1735
1736 int
1737 do_gsent(which,gimme,arglast)
1738 int which;
1739 int gimme;
1740 int *arglast;
1741 {
1742     register ARRAY *ary = stack;
1743     register int sp = arglast[0];
1744     register char **elem;
1745     register STR *str;
1746     struct servent *getservbyname();
1747     struct servent *getservbynumber();
1748     struct servent *getservent();
1749     struct servent *sent;
1750
1751     if (gimme != G_ARRAY) {
1752         astore(ary, ++sp, str_mortal(&str_undef));
1753         return sp;
1754     }
1755
1756     if (which == O_GSBYNAME) {
1757         char *name = str_get(ary->ary_array[sp+1]);
1758         char *proto = str_get(ary->ary_array[sp+2]);
1759
1760         if (proto && !*proto)
1761             proto = Nullch;
1762
1763         sent = getservbyname(name,proto);
1764     }
1765     else if (which == O_GSBYPORT) {
1766         int port = (int)str_gnum(ary->ary_array[sp+1]);
1767         char *proto = str_get(ary->ary_array[sp+2]);
1768
1769         sent = getservbyport(port,proto);
1770     }
1771     else
1772         sent = getservent();
1773     if (sent) {
1774 #ifndef lint
1775         (void)astore(ary, ++sp, str = str_mortal(&str_no));
1776         str_set(str, sent->s_name);
1777         (void)astore(ary, ++sp, str = str_mortal(&str_no));
1778         for (elem = sent->s_aliases; *elem; elem++) {
1779             str_cat(str, *elem);
1780             if (elem[1])
1781                 str_ncat(str," ",1);
1782         }
1783         (void)astore(ary, ++sp, str = str_mortal(&str_no));
1784 #ifdef HAS_NTOHS
1785         str_numset(str, (double)ntohs(sent->s_port));
1786 #else
1787         str_numset(str, (double)(sent->s_port));
1788 #endif
1789         (void)astore(ary, ++sp, str = str_mortal(&str_no));
1790         str_set(str, sent->s_proto);
1791 #else /* lint */
1792         elem = Nullch;
1793         elem = elem;
1794         (void)astore(ary, ++sp, str_mortal(&str_no));
1795 #endif /* lint */
1796     }
1797
1798     return sp;
1799 }
1800
1801 #endif /* HAS_SOCKET */
1802
1803 #ifdef HAS_SELECT
1804 int
1805 do_select(gimme,arglast)
1806 int gimme;
1807 int *arglast;
1808 {
1809     register STR **st = stack->ary_array;
1810     register int sp = arglast[0];
1811     register int i;
1812     register int j;
1813     register char *s;
1814     register STR *str;
1815     double value;
1816     int maxlen = 0;
1817     int nfound;
1818     struct timeval timebuf;
1819     struct timeval *tbuf = &timebuf;
1820     int growsize;
1821 #if BYTEORDER != 0x1234 && BYTEORDER != 0x12345678
1822     int masksize;
1823     int offset;
1824     char *fd_sets[4];
1825     int k;
1826
1827 #if BYTEORDER & 0xf0000
1828 #define ORDERBYTE (0x88888888 - BYTEORDER)
1829 #else
1830 #define ORDERBYTE (0x4444 - BYTEORDER)
1831 #endif
1832
1833 #endif
1834
1835     for (i = 1; i <= 3; i++) {
1836         j = st[sp+i]->str_cur;
1837         if (maxlen < j)
1838             maxlen = j;
1839     }
1840
1841 #if BYTEORDER == 0x1234 || BYTEORDER == 0x12345678
1842     growsize = maxlen;          /* little endians can use vecs directly */
1843 #else
1844 #ifdef NFDBITS
1845
1846 #ifndef NBBY
1847 #define NBBY 8
1848 #endif
1849
1850     masksize = NFDBITS / NBBY;
1851 #else
1852     masksize = sizeof(long);    /* documented int, everyone seems to use long */
1853 #endif
1854     growsize = maxlen + (masksize - (maxlen % masksize));
1855     Zero(&fd_sets[0], 4, char*);
1856 #endif
1857
1858     for (i = 1; i <= 3; i++) {
1859         str = st[sp+i];
1860         j = str->str_len;
1861         if (j < growsize) {
1862             if (str->str_pok) {
1863                 Str_Grow(str,growsize);
1864                 s = str_get(str) + j;
1865                 while (++j <= growsize) {
1866                     *s++ = '\0';
1867                 }
1868             }
1869             else if (str->str_ptr) {
1870                 Safefree(str->str_ptr);
1871                 str->str_ptr = Nullch;
1872             }
1873         }
1874 #if BYTEORDER != 0x1234 && BYTEORDER != 0x12345678
1875         s = str->str_ptr;
1876         if (s) {
1877             New(403, fd_sets[i], growsize, char);
1878             for (offset = 0; offset < growsize; offset += masksize) {
1879                 for (j = 0, k=ORDERBYTE; j < masksize; j++, (k >>= 4))
1880                     fd_sets[i][j+offset] = s[(k % masksize) + offset];
1881             }
1882         }
1883 #endif
1884     }
1885     str = st[sp+4];
1886     if (str->str_nok || str->str_pok) {
1887         value = str_gnum(str);
1888         if (value < 0.0)
1889             value = 0.0;
1890         timebuf.tv_sec = (long)value;
1891         value -= (double)timebuf.tv_sec;
1892         timebuf.tv_usec = (long)(value * 1000000.0);
1893     }
1894     else
1895         tbuf = Null(struct timeval*);
1896
1897 #if BYTEORDER == 0x1234 || BYTEORDER == 0x12345678
1898     nfound = select(
1899         maxlen * 8,
1900         st[sp+1]->str_ptr,
1901         st[sp+2]->str_ptr,
1902         st[sp+3]->str_ptr,
1903         tbuf);
1904 #else
1905     nfound = select(
1906         maxlen * 8,
1907         fd_sets[1],
1908         fd_sets[2],
1909         fd_sets[3],
1910         tbuf);
1911     for (i = 1; i <= 3; i++) {
1912         if (fd_sets[i]) {
1913             str = st[sp+i];
1914             s = str->str_ptr;
1915             for (offset = 0; offset < growsize; offset += masksize) {
1916                 for (j = 0, k=ORDERBYTE; j < masksize; j++, (k >>= 4))
1917                     s[(k % masksize) + offset] = fd_sets[i][j+offset];
1918             }
1919         }
1920     }
1921 #endif
1922
1923     st[++sp] = str_mortal(&str_no);
1924     str_numset(st[sp], (double)nfound);
1925     if (gimme == G_ARRAY && tbuf) {
1926         value = (double)(timebuf.tv_sec) +
1927                 (double)(timebuf.tv_usec) / 1000000.0;
1928         st[++sp] = str_mortal(&str_no);
1929         str_numset(st[sp], value);
1930     }
1931     return sp;
1932 }
1933 #endif /* SELECT */
1934
1935 #ifdef HAS_SOCKET
1936 int
1937 do_spair(stab1, stab2, arglast)
1938 STAB *stab1;
1939 STAB *stab2;
1940 int *arglast;
1941 {
1942     register STR **st = stack->ary_array;
1943     register int sp = arglast[2];
1944     register STIO *stio1;
1945     register STIO *stio2;
1946     int domain, type, protocol, fd[2];
1947
1948     if (!stab1 || !stab2)
1949         return FALSE;
1950
1951     stio1 = stab_io(stab1);
1952     stio2 = stab_io(stab2);
1953     if (!stio1)
1954         stio1 = stab_io(stab1) = stio_new();
1955     else if (stio1->ifp)
1956         do_close(stab1,FALSE);
1957     if (!stio2)
1958         stio2 = stab_io(stab2) = stio_new();
1959     else if (stio2->ifp)
1960         do_close(stab2,FALSE);
1961
1962     domain = (int)str_gnum(st[++sp]);
1963     type = (int)str_gnum(st[++sp]);
1964     protocol = (int)str_gnum(st[++sp]);
1965 #ifdef TAINT
1966     taintproper("Insecure dependency in socketpair");
1967 #endif
1968 #ifdef HAS_SOCKETPAIR
1969     if (socketpair(domain,type,protocol,fd) < 0)
1970         return FALSE;
1971 #else
1972     fatal("Socketpair unimplemented");
1973 #endif
1974     stio1->ifp = fdopen(fd[0], "r");
1975     stio1->ofp = fdopen(fd[0], "w");
1976     stio1->type = 's';
1977     stio2->ifp = fdopen(fd[1], "r");
1978     stio2->ofp = fdopen(fd[1], "w");
1979     stio2->type = 's';
1980     if (!stio1->ifp || !stio1->ofp || !stio2->ifp || !stio2->ofp) {
1981         if (stio1->ifp) fclose(stio1->ifp);
1982         if (stio1->ofp) fclose(stio1->ofp);
1983         if (!stio1->ifp && !stio1->ofp) close(fd[0]);
1984         if (stio2->ifp) fclose(stio2->ifp);
1985         if (stio2->ofp) fclose(stio2->ofp);
1986         if (!stio2->ifp && !stio2->ofp) close(fd[1]);
1987         return FALSE;
1988     }
1989
1990     return TRUE;
1991 }
1992
1993 #endif /* HAS_SOCKET */
1994
1995 int
1996 do_gpwent(which,gimme,arglast)
1997 int which;
1998 int gimme;
1999 int *arglast;
2000 {
2001 #ifdef I_PWD
2002     register ARRAY *ary = stack;
2003     register int sp = arglast[0];
2004     register STR *str;
2005     struct passwd *getpwnam();
2006     struct passwd *getpwuid();
2007     struct passwd *getpwent();
2008     struct passwd *pwent;
2009
2010     if (gimme != G_ARRAY) {
2011         astore(ary, ++sp, str_mortal(&str_undef));
2012         return sp;
2013     }
2014
2015     if (which == O_GPWNAM) {
2016         char *name = str_get(ary->ary_array[sp+1]);
2017
2018         pwent = getpwnam(name);
2019     }
2020     else if (which == O_GPWUID) {
2021         int uid = (int)str_gnum(ary->ary_array[sp+1]);
2022
2023         pwent = getpwuid(uid);
2024     }
2025     else
2026         pwent = getpwent();
2027
2028     if (pwent) {
2029         (void)astore(ary, ++sp, str = str_mortal(&str_no));
2030         str_set(str, pwent->pw_name);
2031         (void)astore(ary, ++sp, str = str_mortal(&str_no));
2032         str_set(str, pwent->pw_passwd);
2033         (void)astore(ary, ++sp, str = str_mortal(&str_no));
2034         str_numset(str, (double)pwent->pw_uid);
2035         (void)astore(ary, ++sp, str = str_mortal(&str_no));
2036         str_numset(str, (double)pwent->pw_gid);
2037         (void)astore(ary, ++sp, str = str_mortal(&str_no));
2038 #ifdef PWCHANGE
2039         str_numset(str, (double)pwent->pw_change);
2040 #else
2041 #ifdef PWQUOTA
2042         str_numset(str, (double)pwent->pw_quota);
2043 #else
2044 #ifdef PWAGE
2045         str_set(str, pwent->pw_age);
2046 #endif
2047 #endif
2048 #endif
2049         (void)astore(ary, ++sp, str = str_mortal(&str_no));
2050 #ifdef PWCLASS
2051         str_set(str,pwent->pw_class);
2052 #else
2053 #ifdef PWCOMMENT
2054         str_set(str, pwent->pw_comment);
2055 #endif
2056 #endif
2057         (void)astore(ary, ++sp, str = str_mortal(&str_no));
2058         str_set(str, pwent->pw_gecos);
2059         (void)astore(ary, ++sp, str = str_mortal(&str_no));
2060         str_set(str, pwent->pw_dir);
2061         (void)astore(ary, ++sp, str = str_mortal(&str_no));
2062         str_set(str, pwent->pw_shell);
2063 #ifdef PWEXPIRE
2064         (void)astore(ary, ++sp, str = str_mortal(&str_no));
2065         str_numset(str, (double)pwent->pw_expire);
2066 #endif
2067     }
2068
2069     return sp;
2070 #else
2071     fatal("password routines not implemented");
2072 #endif
2073 }
2074
2075 int
2076 do_ggrent(which,gimme,arglast)
2077 int which;
2078 int gimme;
2079 int *arglast;
2080 {
2081 #ifdef I_GRP
2082     register ARRAY *ary = stack;
2083     register int sp = arglast[0];
2084     register char **elem;
2085     register STR *str;
2086     struct group *getgrnam();
2087     struct group *getgrgid();
2088     struct group *getgrent();
2089     struct group *grent;
2090
2091     if (gimme != G_ARRAY) {
2092         astore(ary, ++sp, str_mortal(&str_undef));
2093         return sp;
2094     }
2095
2096     if (which == O_GGRNAM) {
2097         char *name = str_get(ary->ary_array[sp+1]);
2098
2099         grent = getgrnam(name);
2100     }
2101     else if (which == O_GGRGID) {
2102         int gid = (int)str_gnum(ary->ary_array[sp+1]);
2103
2104         grent = getgrgid(gid);
2105     }
2106     else
2107         grent = getgrent();
2108
2109     if (grent) {
2110         (void)astore(ary, ++sp, str = str_mortal(&str_no));
2111         str_set(str, grent->gr_name);
2112         (void)astore(ary, ++sp, str = str_mortal(&str_no));
2113         str_set(str, grent->gr_passwd);
2114         (void)astore(ary, ++sp, str = str_mortal(&str_no));
2115         str_numset(str, (double)grent->gr_gid);
2116         (void)astore(ary, ++sp, str = str_mortal(&str_no));
2117         for (elem = grent->gr_mem; *elem; elem++) {
2118             str_cat(str, *elem);
2119             if (elem[1])
2120                 str_ncat(str," ",1);
2121         }
2122     }
2123
2124     return sp;
2125 #else
2126     fatal("group routines not implemented");
2127 #endif
2128 }
2129
2130 int
2131 do_dirop(optype,stab,gimme,arglast)
2132 int optype;
2133 STAB *stab;
2134 int gimme;
2135 int *arglast;
2136 {
2137 #if defined(DIRENT) && defined(HAS_READDIR)
2138     register ARRAY *ary = stack;
2139     register STR **st = ary->ary_array;
2140     register int sp = arglast[1];
2141     register STIO *stio;
2142     long along;
2143 #ifndef telldir
2144     long telldir();
2145 #endif
2146     struct DIRENT *readdir();
2147     register struct DIRENT *dp;
2148
2149     if (!stab)
2150         goto nope;
2151     if (!(stio = stab_io(stab)))
2152         stio = stab_io(stab) = stio_new();
2153     if (!stio->dirp && optype != O_OPENDIR)
2154         goto nope;
2155     st[sp] = &str_yes;
2156     switch (optype) {
2157     case O_OPENDIR:
2158         if (stio->dirp)
2159             closedir(stio->dirp);
2160         if (!(stio->dirp = opendir(str_get(st[sp+1]))))
2161             goto nope;
2162         break;
2163     case O_READDIR:
2164         if (gimme == G_ARRAY) {
2165             --sp;
2166             while (dp = readdir(stio->dirp)) {
2167 #ifdef DIRNAMLEN
2168                 (void)astore(ary,++sp,
2169                   str_2mortal(str_make(dp->d_name,dp->d_namlen)));
2170 #else
2171                 (void)astore(ary,++sp,
2172                   str_2mortal(str_make(dp->d_name,0)));
2173 #endif
2174             }
2175         }
2176         else {
2177             if (!(dp = readdir(stio->dirp)))
2178                 goto nope;
2179             st[sp] = str_mortal(&str_undef);
2180 #ifdef DIRNAMLEN
2181             str_nset(st[sp], dp->d_name, dp->d_namlen);
2182 #else
2183             str_set(st[sp], dp->d_name);
2184 #endif
2185         }
2186         break;
2187 #if MACH
2188     case O_TELLDIR:
2189     case O_SEEKDIR:
2190         goto nope;
2191 #else
2192     case O_TELLDIR:
2193         st[sp] = str_mortal(&str_undef);
2194         str_numset(st[sp], (double)telldir(stio->dirp));
2195         break;
2196     case O_SEEKDIR:
2197         st[sp] = str_mortal(&str_undef);
2198         along = (long)str_gnum(st[sp+1]);
2199         (void)seekdir(stio->dirp,along);
2200         break;
2201 #endif
2202     case O_REWINDDIR:
2203         st[sp] = str_mortal(&str_undef);
2204         (void)rewinddir(stio->dirp);
2205         break;
2206     case O_CLOSEDIR:
2207         st[sp] = str_mortal(&str_undef);
2208         (void)closedir(stio->dirp);
2209         stio->dirp = 0;
2210         break;
2211     }
2212     return sp;
2213
2214 nope:
2215     st[sp] = &str_undef;
2216     return sp;
2217
2218 #else
2219     fatal("Unimplemented directory operation");
2220 #endif
2221 }
2222
2223 apply(type,arglast)
2224 int type;
2225 int *arglast;
2226 {
2227     register STR **st = stack->ary_array;
2228     register int sp = arglast[1];
2229     register int items = arglast[2] - sp;
2230     register int val;
2231     register int val2;
2232     register int tot = 0;
2233     char *s;
2234
2235 #ifdef TAINT
2236     for (st += ++sp; items--; st++)
2237         tainted |= (*st)->str_tainted;
2238     st = stack->ary_array;
2239     sp = arglast[1];
2240     items = arglast[2] - sp;
2241 #endif
2242     switch (type) {
2243     case O_CHMOD:
2244 #ifdef TAINT
2245         taintproper("Insecure dependency in chmod");
2246 #endif
2247         if (--items > 0) {
2248             tot = items;
2249             val = (int)str_gnum(st[++sp]);
2250             while (items--) {
2251                 if (chmod(str_get(st[++sp]),val))
2252                     tot--;
2253             }
2254         }
2255         break;
2256 #ifdef HAS_CHOWN
2257     case O_CHOWN:
2258 #ifdef TAINT
2259         taintproper("Insecure dependency in chown");
2260 #endif
2261         if (items > 2) {
2262             items -= 2;
2263             tot = items;
2264             val = (int)str_gnum(st[++sp]);
2265             val2 = (int)str_gnum(st[++sp]);
2266             while (items--) {
2267                 if (chown(str_get(st[++sp]),val,val2))
2268                     tot--;
2269             }
2270         }
2271         break;
2272 #endif
2273 #ifdef HAS_KILL
2274     case O_KILL:
2275 #ifdef TAINT
2276         taintproper("Insecure dependency in kill");
2277 #endif
2278         if (--items > 0) {
2279             tot = items;
2280             s = str_get(st[++sp]);
2281             if (isupper(*s)) {
2282                 if (*s == 'S' && s[1] == 'I' && s[2] == 'G')
2283                     s += 3;
2284                 if (!(val = whichsig(s)))
2285                     fatal("Unrecognized signal name \"%s\"",s);
2286             }
2287             else
2288                 val = (int)str_gnum(st[sp]);
2289             if (val < 0) {
2290                 val = -val;
2291                 while (items--) {
2292                     int proc = (int)str_gnum(st[++sp]);
2293 #ifdef HAS_KILLPG
2294                     if (killpg(proc,val))       /* BSD */
2295 #else
2296                     if (kill(-proc,val))        /* SYSV */
2297 #endif
2298                         tot--;
2299                 }
2300             }
2301             else {
2302                 while (items--) {
2303                     if (kill((int)(str_gnum(st[++sp])),val))
2304                         tot--;
2305                 }
2306             }
2307         }
2308         break;
2309 #endif
2310     case O_UNLINK:
2311 #ifdef TAINT
2312         taintproper("Insecure dependency in unlink");
2313 #endif
2314         tot = items;
2315         while (items--) {
2316             s = str_get(st[++sp]);
2317             if (euid || unsafe) {
2318                 if (UNLINK(s))
2319                     tot--;
2320             }
2321             else {      /* don't let root wipe out directories without -U */
2322 #ifdef HAS_LSTAT
2323                 if (lstat(s,&statbuf) < 0 || S_ISDIR(statbuf.st_mode))
2324 #else
2325                 if (stat(s,&statbuf) < 0 || S_ISDIR(statbuf.st_mode))
2326 #endif
2327                     tot--;
2328                 else {
2329                     if (UNLINK(s))
2330                         tot--;
2331                 }
2332             }
2333         }
2334         break;
2335     case O_UTIME:
2336 #ifdef TAINT
2337         taintproper("Insecure dependency in utime");
2338 #endif
2339         if (items > 2) {
2340 #ifdef I_UTIME
2341             struct utimbuf utbuf;
2342 #else
2343             struct {
2344                 long    actime;
2345                 long    modtime;
2346             } utbuf;
2347 #endif
2348
2349             Zero(&utbuf, sizeof utbuf, char);
2350             utbuf.actime = (long)str_gnum(st[++sp]);    /* time accessed */
2351             utbuf.modtime = (long)str_gnum(st[++sp]);    /* time modified */
2352             items -= 2;
2353 #ifndef lint
2354             tot = items;
2355             while (items--) {
2356                 if (utime(str_get(st[++sp]),&utbuf))
2357                     tot--;
2358             }
2359 #endif
2360         }
2361         else
2362             items = 0;
2363         break;
2364     }
2365     return tot;
2366 }
2367
2368 /* Do the permissions allow some operation?  Assumes statcache already set. */
2369
2370 int
2371 cando(bit, effective, statbufp)
2372 int bit;
2373 int effective;
2374 register struct stat *statbufp;
2375 {
2376 #ifdef MSDOS
2377     /* [Comments and code from Len Reed]
2378      * MS-DOS "user" is similar to UNIX's "superuser," but can't write
2379      * to write-protected files.  The execute permission bit is set
2380      * by the Miscrosoft C library stat() function for the following:
2381      *          .exe files
2382      *          .com files
2383      *          .bat files
2384      *          directories
2385      * All files and directories are readable.
2386      * Directories and special files, e.g. "CON", cannot be
2387      * write-protected.
2388      * [Comment by Tom Dinger -- a directory can have the write-protect
2389      *          bit set in the file system, but DOS permits changes to
2390      *          the directory anyway.  In addition, all bets are off
2391      *          here for networked software, such as Novell and
2392      *          Sun's PC-NFS.]
2393      */
2394
2395      return (bit & statbufp->st_mode) ? TRUE : FALSE;
2396
2397 #else /* ! MSDOS */
2398     if ((effective ? euid : uid) == 0) {        /* root is special */
2399         if (bit == S_IXUSR) {
2400             if (statbufp->st_mode & 0111 || S_ISDIR(statbufp->st_mode))
2401                 return TRUE;
2402         }
2403         else
2404             return TRUE;                /* root reads and writes anything */
2405         return FALSE;
2406     }
2407     if (statbufp->st_uid == (effective ? euid : uid) ) {
2408         if (statbufp->st_mode & bit)
2409             return TRUE;        /* ok as "user" */
2410     }
2411     else if (ingroup((int)statbufp->st_gid,effective)) {
2412         if (statbufp->st_mode & bit >> 3)
2413             return TRUE;        /* ok as "group" */
2414     }
2415     else if (statbufp->st_mode & bit >> 6)
2416         return TRUE;    /* ok as "other" */
2417     return FALSE;
2418 #endif /* ! MSDOS */
2419 }
2420
2421 int
2422 ingroup(testgid,effective)
2423 int testgid;
2424 int effective;
2425 {
2426     if (testgid == (effective ? egid : gid))
2427         return TRUE;
2428 #ifdef HAS_GETGROUPS
2429 #ifndef NGROUPS
2430 #define NGROUPS 32
2431 #endif
2432     {
2433         GIDTYPE gary[NGROUPS];
2434         int anum;
2435
2436         anum = getgroups(NGROUPS,gary);
2437         while (--anum >= 0)
2438             if (gary[anum] == testgid)
2439                 return TRUE;
2440     }
2441 #endif
2442     return FALSE;
2443 }
2444
2445 #if defined(HAS_MSG) || defined(HAS_SEM) || defined(HAS_SHM)
2446
2447 int
2448 do_ipcget(optype, arglast)
2449 int optype;
2450 int *arglast;
2451 {
2452     register STR **st = stack->ary_array;
2453     register int sp = arglast[0];
2454     key_t key;
2455     int n, flags;
2456
2457     key = (key_t)str_gnum(st[++sp]);
2458     n = (optype == O_MSGGET) ? 0 : (int)str_gnum(st[++sp]);
2459     flags = (int)str_gnum(st[++sp]);
2460     errno = 0;
2461     switch (optype)
2462     {
2463 #ifdef HAS_MSG
2464     case O_MSGGET:
2465         return msgget(key, flags);
2466 #endif
2467 #ifdef HAS_SEM
2468     case O_SEMGET:
2469         return semget(key, n, flags);
2470 #endif
2471 #ifdef HAS_SHM
2472     case O_SHMGET:
2473         return shmget(key, n, flags);
2474 #endif
2475 #if !defined(HAS_MSG) || !defined(HAS_SEM) || !defined(HAS_SHM)
2476     default:
2477         fatal("%s not implemented", opname[optype]);
2478 #endif
2479     }
2480     return -1;                  /* should never happen */
2481 }
2482
2483 int
2484 do_ipcctl(optype, arglast)
2485 int optype;
2486 int *arglast;
2487 {
2488     register STR **st = stack->ary_array;
2489     register int sp = arglast[0];
2490     STR *astr;
2491     char *a;
2492     int id, n, cmd, infosize, getinfo, ret;
2493
2494     id = (int)str_gnum(st[++sp]);
2495     n = (optype == O_SEMCTL) ? (int)str_gnum(st[++sp]) : 0;
2496     cmd = (int)str_gnum(st[++sp]);
2497     astr = st[++sp];
2498
2499     infosize = 0;
2500     getinfo = (cmd == IPC_STAT);
2501
2502     switch (optype)
2503     {
2504 #ifdef HAS_MSG
2505     case O_MSGCTL:
2506         if (cmd == IPC_STAT || cmd == IPC_SET)
2507             infosize = sizeof(struct msqid_ds);
2508         break;
2509 #endif
2510 #ifdef HAS_SHM
2511     case O_SHMCTL:
2512         if (cmd == IPC_STAT || cmd == IPC_SET)
2513             infosize = sizeof(struct shmid_ds);
2514         break;
2515 #endif
2516 #ifdef HAS_SEM
2517     case O_SEMCTL:
2518         if (cmd == IPC_STAT || cmd == IPC_SET)
2519             infosize = sizeof(struct semid_ds);
2520         else if (cmd == GETALL || cmd == SETALL)
2521         {
2522             struct semid_ds semds;
2523             if (semctl(id, 0, IPC_STAT, &semds) == -1)
2524                 return -1;
2525             getinfo = (cmd == GETALL);
2526 #ifdef _POSIX_SOURCE
2527             infosize = semds.sem_nsems * sizeof(ushort_t);
2528 #else
2529             infosize = semds.sem_nsems * sizeof(ushort);
2530 #endif
2531         }
2532         break;
2533 #endif
2534 #if !defined(HAS_MSG) || !defined(HAS_SEM) || !defined(HAS_SHM)
2535     default:
2536         fatal("%s not implemented", opname[optype]);
2537 #endif
2538     }
2539
2540     if (infosize)
2541     {
2542         if (getinfo)
2543         {
2544             STR_GROW(astr, infosize+1);
2545             a = str_get(astr);
2546         }
2547         else
2548         {
2549             a = str_get(astr);
2550             if (astr->str_cur != infosize)
2551             {
2552                 errno = EINVAL;
2553                 return -1;
2554             }
2555         }
2556     }
2557     else
2558     {
2559         int i = (int)str_gnum(astr);
2560         a = (char *)i;          /* ouch */
2561     }
2562     errno = 0;
2563     switch (optype)
2564     {
2565 #ifdef HAS_MSG
2566     case O_MSGCTL:
2567         ret = msgctl(id, cmd, a);
2568         break;
2569 #endif
2570 #ifdef HAS_SEM
2571     case O_SEMCTL:
2572         ret = semctl(id, n, cmd, a);
2573         break;
2574 #endif
2575 #ifdef HAS_SHM
2576     case O_SHMCTL:
2577         ret = shmctl(id, cmd, a);
2578         break;
2579 #endif
2580     }
2581     if (getinfo && ret >= 0) {
2582         astr->str_cur = infosize;
2583         astr->str_ptr[infosize] = '\0';
2584     }
2585     return ret;
2586 }
2587
2588 int
2589 do_msgsnd(arglast)
2590 int *arglast;
2591 {
2592 #ifdef HAS_MSG
2593     register STR **st = stack->ary_array;
2594     register int sp = arglast[0];
2595     STR *mstr;
2596     char *mbuf;
2597     int id, msize, flags;
2598
2599     id = (int)str_gnum(st[++sp]);
2600     mstr = st[++sp];
2601     flags = (int)str_gnum(st[++sp]);
2602     mbuf = str_get(mstr);
2603     if ((msize = mstr->str_cur - sizeof(long)) < 0) {
2604         errno = EINVAL;
2605         return -1;
2606     }
2607     errno = 0;
2608     return msgsnd(id, mbuf, msize, flags);
2609 #else
2610     fatal("msgsnd not implemented");
2611 #endif
2612 }
2613
2614 int
2615 do_msgrcv(arglast)
2616 int *arglast;
2617 {
2618 #ifdef HAS_MSG
2619     register STR **st = stack->ary_array;
2620     register int sp = arglast[0];
2621     STR *mstr;
2622     char *mbuf;
2623     long mtype;
2624     int id, msize, flags, ret;
2625
2626     id = (int)str_gnum(st[++sp]);
2627     mstr = st[++sp];
2628     msize = (int)str_gnum(st[++sp]);
2629     mtype = (long)str_gnum(st[++sp]);
2630     flags = (int)str_gnum(st[++sp]);
2631     mbuf = str_get(mstr);
2632     if (mstr->str_cur < sizeof(long)+msize+1) {
2633         STR_GROW(mstr, sizeof(long)+msize+1);
2634         mbuf = str_get(mstr);
2635     }
2636     errno = 0;
2637     ret = msgrcv(id, mbuf, msize, mtype, flags);
2638     if (ret >= 0) {
2639         mstr->str_cur = sizeof(long)+ret;
2640         mstr->str_ptr[sizeof(long)+ret] = '\0';
2641     }
2642     return ret;
2643 #else
2644     fatal("msgrcv not implemented");
2645 #endif
2646 }
2647
2648 int
2649 do_semop(arglast)
2650 int *arglast;
2651 {
2652 #ifdef HAS_SEM
2653     register STR **st = stack->ary_array;
2654     register int sp = arglast[0];
2655     STR *opstr;
2656     char *opbuf;
2657     int id, opsize;
2658
2659     id = (int)str_gnum(st[++sp]);
2660     opstr = st[++sp];
2661     opbuf = str_get(opstr);
2662     opsize = opstr->str_cur;
2663     if (opsize < sizeof(struct sembuf)
2664         || (opsize % sizeof(struct sembuf)) != 0) {
2665         errno = EINVAL;
2666         return -1;
2667     }
2668     errno = 0;
2669     return semop(id, opbuf, opsize/sizeof(struct sembuf));
2670 #else
2671     fatal("semop not implemented");
2672 #endif
2673 }
2674
2675 int
2676 do_shmio(optype, arglast)
2677 int optype;
2678 int *arglast;
2679 {
2680 #ifdef HAS_SHM
2681     register STR **st = stack->ary_array;
2682     register int sp = arglast[0];
2683     STR *mstr;
2684     char *mbuf, *shm;
2685     int id, mpos, msize;
2686     struct shmid_ds shmds;
2687     extern char *shmat();
2688
2689     id = (int)str_gnum(st[++sp]);
2690     mstr = st[++sp];
2691     mpos = (int)str_gnum(st[++sp]);
2692     msize = (int)str_gnum(st[++sp]);
2693     errno = 0;
2694     if (shmctl(id, IPC_STAT, &shmds) == -1)
2695         return -1;
2696     if (mpos < 0 || msize < 0 || mpos + msize > shmds.shm_segsz) {
2697         errno = EFAULT;         /* can't do as caller requested */
2698         return -1;
2699     }
2700     shm = shmat(id, (char *)NULL, (optype == O_SHMREAD) ? SHM_RDONLY : 0);
2701     if (shm == (char *)-1)      /* I hate System V IPC, I really do */
2702         return -1;
2703     mbuf = str_get(mstr);
2704     if (optype == O_SHMREAD) {
2705         if (mstr->str_cur < msize) {
2706             STR_GROW(mstr, msize+1);
2707             mbuf = str_get(mstr);
2708         }
2709         bcopy(shm + mpos, mbuf, msize);
2710         mstr->str_cur = msize;
2711         mstr->str_ptr[msize] = '\0';
2712     }
2713     else {
2714         int n;
2715
2716         if ((n = mstr->str_cur) > msize)
2717             n = msize;
2718         bcopy(mbuf, shm + mpos, n);
2719         if (n < msize)
2720             bzero(shm + mpos + n, msize - n);
2721     }
2722     return shmdt(shm);
2723 #else
2724     fatal("shm I/O not implemented");
2725 #endif
2726 }
2727
2728 #endif /* SYSV IPC */