tc.os.c revision 100616
1/* $Header: /src/pub/tcsh/tc.os.c,v 3.53 2002/03/08 17:36:47 christos Exp $ */
2/*
3 * tc.os.c: OS Dependent builtin functions
4 */
5/*-
6 * Copyright (c) 1980, 1991 The Regents of the University of California.
7 * All rights reserved.
8 *
9 * Redistribution and use in source and binary forms, with or without
10 * modification, are permitted provided that the following conditions
11 * are met:
12 * 1. Redistributions of source code must retain the above copyright
13 *    notice, this list of conditions and the following disclaimer.
14 * 2. Redistributions in binary form must reproduce the above copyright
15 *    notice, this list of conditions and the following disclaimer in the
16 *    documentation and/or other materials provided with the distribution.
17 * 3. Neither the name of the University nor the names of its contributors
18 *    may be used to endorse or promote products derived from this software
19 *    without specific prior written permission.
20 *
21 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
22 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
25 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31 * SUCH DAMAGE.
32 */
33#include "sh.h"
34
35RCSID("$Id: tc.os.c,v 3.53 2002/03/08 17:36:47 christos Exp $")
36
37#include "tw.h"
38#include "ed.h"
39#include "ed.defns.h"		/* for the function names */
40#include "sh.decls.h"
41
42#ifdef _UWIN
43#define TIOCGPGRP TIOCGETPGRP
44#define TIOCSPGRP TIOCSETPGRP
45#endif
46
47/***
48 *** MACH
49 ***/
50
51#ifdef MACH
52/* dosetpath -- setpath built-in command
53 *
54 **********************************************************************
55 * HISTORY
56 * 08-May-88  Richard Draves (rpd) at Carnegie-Mellon University
57 *	Major changes to remove artificial limits on sizes and numbers
58 *	of paths.
59 *
60 **********************************************************************
61 */
62
63#ifdef MACH
64static Char STRCPATH[] = {'C', 'P', 'A', 'T', 'H', '\0'};
65static Char STRLPATH[] = {'L', 'P', 'A', 'T', 'H', '\0'};
66static Char STRMPATH[] = {'M', 'P', 'A', 'T', 'H', '\0'};
67# if EPATH
68static Char STREPATH[] = {'E', 'P', 'A', 'T', 'H', '\0'};
69# endif
70#endif /* MACH */
71static Char *syspaths[] = {STRKPATH, STRCPATH, STRLPATH, STRMPATH,
72
73#if EPATH
74	STREPATH,
75#endif
76	 0};
77#define LOCALSYSPATH	"/usr/local"
78
79/*ARGSUSED*/
80void
81dosetpath(arglist, c)
82    Char  **arglist;
83    struct command *c;
84{
85    extern char *getenv();
86    sigmask_t omask;
87    Char  **pathvars, **cmdargs;
88    char  **spaths, **cpaths, **cmds;
89    char   *tcp;
90    unsigned int npaths, ncmds;
91    int     i, sysflag;
92
93    omask = sigsetmask(sigmask(SIGINT));
94
95    /*
96     * setpath(3) uses stdio and we want 0, 1, 2 to work...
97     */
98    if (!didfds) {
99	(void) dcopy(SHIN, 0);
100	(void) dcopy(SHOUT, 1);
101	(void) dcopy(SHDIAG, 2);
102	didfds = 1;
103    }
104
105    for (i = 1; arglist[i] && (arglist[i][0] != '-'); i++);
106    npaths = i - 1;
107
108    cmdargs = &arglist[i];
109    for (; arglist[i]; i++);
110    ncmds = i - npaths - 1;
111
112    if (npaths) {
113	sysflag = 0;
114	pathvars = &arglist[1];
115    }
116    else {
117	sysflag = 1;
118	npaths = (sizeof syspaths / sizeof *syspaths) - 1;
119	pathvars = syspaths;
120    }
121
122    /* note that npaths != 0 */
123
124    spaths = (char **) xmalloc((size_t) npaths * sizeof *spaths);
125    setzero((char *) spaths, npaths * sizeof *spaths);
126    cpaths = (char **) xmalloc((size_t) (npaths + 1) * sizeof *cpaths);
127    setzero((char *) cpaths, (npaths + 1) * sizeof *cpaths);
128    cmds = (char **) xmalloc((size_t) (ncmds + 1) * sizeof *cmds);
129    setzero((char *) cmds, (ncmds + 1) * sizeof *cmds);
130    for (i = 0; i < npaths; i++) {
131	char   *val = getenv(short2str(pathvars[i]));
132
133	if (val == NULL)
134	    val = "";
135
136	spaths[i] = (char *) xmalloc((size_t) (Strlen(pathvars[i]) +
137				      strlen(val) + 2) * sizeof **spaths);
138	(void) strcpy(spaths[i], short2str(pathvars[i]));
139	(void) strcat(spaths[i], "=");
140	(void) strcat(spaths[i], val);
141	cpaths[i] = spaths[i];
142    }
143
144    for (i = 0; i < ncmds; i++) {
145	Char   *val = globone(cmdargs[i], G_ERROR);
146
147	if (val == NULL)
148	    goto abortpath;
149	cmds[i] = (char *) xmalloc((size_t) Strlen(val) + 1);
150	(void) strcpy(cmds[i], short2str(val));
151    }
152
153
154    if (setpath(cpaths, cmds, LOCALSYSPATH, sysflag, 1) < 0) {
155abortpath:
156	if (spaths) {
157	    for (i = 0; i < npaths; i++)
158		if (spaths[i])
159		    xfree((ptr_t) spaths[i]);
160	    xfree((ptr_t) spaths);
161	}
162	if (cpaths)
163	    xfree((ptr_t) cpaths);
164	if (cmds) {
165	    for (i = 0; i < ncmds; i++)
166		if (cmds[i])
167		    xfree((ptr_t) cmds[i]);
168	    xfree((ptr_t) cmds);
169	}
170
171	(void) sigsetmask(omask);
172	donefds();
173	return;
174    }
175
176    for (i = 0; i < npaths; i++) {
177	Char	*val, *name;
178
179	name = str2short(cpaths[i]);
180	for (val = str2short(cpaths[i]); val && *val && *val != '='; val++);
181	if (val && *val == '=') {
182	    *val++ = '\0';
183
184	    tsetenv(name, val);
185	    if (Strcmp(name, STRKPATH) == 0) {
186		importpath(val);
187		if (havhash)
188		    dohash(NULL, NULL);
189	    }
190	    *--val = '=';
191	}
192    }
193    (void) sigsetmask(omask);
194    donefds();
195}
196#endif /* MACH */
197
198/***
199 *** AIX
200 ***/
201#ifdef TCF
202/* ARGSUSED */
203void
204dogetxvers(v, c)
205    Char  **v;
206    struct command *c;
207{
208    char    xvers[MAXPATHLEN];
209
210    if (getxvers(xvers, MAXPATHLEN) == -1)
211	stderror(ERR_SYSTEM, "getxvers", strerror(errno));
212    xprintf("%s\n", xvers);
213    flush();
214}
215
216/*ARGSUSED*/
217void
218dosetxvers(v, c)
219    Char  **v;
220    struct command *c;
221{
222    char   *xvers;
223
224    ++v;
225    if (!*v || *v[0] == '\0')
226	xvers = "";
227    else
228	xvers = short2str(*v);
229    if (setxvers(xvers) == -1)
230	stderror(ERR_SYSTEM, "setxvers", strerror(errno));
231}
232
233#include <sf.h>
234#ifdef _AIXPS2
235# define XC_PDP11	0x01
236# define XC_23		0x02
237# define XC_Z8K		0x03
238# define XC_8086	0x04
239# define XC_68K		0x05
240# define XC_Z80		0x06
241# define XC_VAX		0x07
242# define XC_16032	0x08
243# define XC_286		0x09
244# define XC_386		0x0a
245# define XC_S370	0x0b
246#else
247# include <sys/x.out.h>
248#endif /* _AIXPS2 */
249
250static struct xc_cpu_t {
251    short   xc_id;
252    char   *xc_name;
253}       xcpu[] =
254{
255    { XC_PDP11,	"pdp11"   },
256    { XC_23,	"i370"    },
257    { XC_Z8K,	"z8000"   },
258    { XC_8086,	"i86"	  },
259    { XC_68K,	"mc68000" },
260    { XC_Z80,	"x80"	  },
261    { XC_VAX,	"vax"	  },
262    { XC_16032,	"ns16032" },
263    { XC_286,	"i286"	  },
264    { XC_386,	"i386"	  },
265    { XC_S370,	"xa370"	  },
266    { 0,	NULL      }
267};
268
269/*
270 * our local hack table, stolen from x.out.h
271 */
272static char *
273getxcode(xcid)
274    short   xcid;
275{
276    int     i;
277
278    for (i = 0; xcpu[i].xc_name != NULL; i++)
279	if (xcpu[i].xc_id == xcid)
280	    return (xcpu[i].xc_name);
281    return (NULL);
282}
283
284static short
285getxid(xcname)
286    char   *xcname;
287{
288    int     i;
289
290    for (i = 0; xcpu[i].xc_name != NULL; i++)
291	if (strcmp(xcpu[i].xc_name, xcname) == 0)
292	    return (xcpu[i].xc_id);
293    return ((short) -1);
294}
295
296
297/*ARGSUSED*/
298void
299dogetspath(v, c)
300    Char  **v;
301    struct command *c;
302{
303    int     i, j;
304    sitepath_t p[MAXSITE];
305    struct sf *st;
306    static char *local = "LOCAL ";
307
308    if ((j = getspath(p, MAXSITE)) == -1)
309	stderror(ERR_SYSTEM, "getspath", strerror(errno));
310    for (i = 0; i < j && (p[i] & SPATH_CPU) != NOSITE; i++) {
311	if (p[i] & SPATH_CPU) {
312	    if ((p[i] & SPATH_MASK) == NULLSITE)
313		xprintf(local);
314	    else if ((st = sfxcode((short) (p[i] & SPATH_MASK))) != NULL)
315		xprintf("%s ", st->sf_ctype);
316	    else {
317		char   *xc = getxcode(p[i] & SPATH_MASK);
318
319		if (xc != NULL)
320		    xprintf("%s ", xc);
321		else
322		    xprintf("*cpu %d* ", (int) (p[i] & SPATH_MASK));
323		/*
324		 * BUG in the aix code... needs that cause if
325		 * sfxcode fails once it fails for ever
326		 */
327		endsf();
328	    }
329	}
330	else {
331	    if (p[i] == NULLSITE)
332		xprintf(local);
333	    else if ((st = sfnum(p[i])) != NULL)
334		xprintf("%s ", st->sf_sname);
335	    else
336		xprintf("*site %d* ", (int) (p[i] & SPATH_MASK));
337	}
338    }
339    xputchar('\n');
340    flush();
341}
342
343/*ARGSUSED*/
344void
345dosetspath(v, c)
346    Char  **v;
347    struct command *c;
348{
349    int     i;
350    short   j;
351    char   *s;
352    sitepath_t p[MAXSITE];
353    struct sf *st;
354
355    /*
356     * sfname() on AIX G9.9 at least, mallocs too pointers p, q
357     * then does the equivalent of while (*p++ == *q++) continue;
358     * and then tries to free(p,q) them! Congrats to the wizard who
359     * wrote that one. I bet he tested it really well too.
360     * Sooo, we set dont_free :-)
361     */
362    dont_free = 1;
363    for (i = 0, v++; *v && *v[0] != '\0'; v++, i++) {
364	s = short2str(*v);
365	if (Isdigit(*s))
366	    p[i] = atoi(s);
367	else if (strcmp(s, "LOCAL") == 0)
368	    p[i] = NULLSITE;
369	else if ((st = sfctype(s)) != NULL)
370	    p[i] = SPATH_CPU | st->sf_ccode;
371	else if ((j = getxid(s)) != -1)
372	    p[i] = SPATH_CPU | j;
373	else if ((st = sfname(s)) != NULL)
374	    p[i] = st->sf_id;
375	else {
376	    setname(s);
377	    stderror(ERR_NAME | ERR_STRING, CGETS(23, 1, "Bad cpu/site name"));
378	}
379	if (i == MAXSITE - 1)
380	    stderror(ERR_NAME | ERR_STRING, CGETS(23, 2, "Site path too long"));
381    }
382    if (setspath(p, i) == -1)
383	stderror(ERR_SYSTEM, "setspath", strerror(errno));
384    dont_free = 0;
385}
386
387/* sitename():
388 *	Return the site name where the process is running
389 */
390char   *
391sitename(pid)
392    pid_t   pid;
393{
394    siteno_t ss;
395    struct sf *st;
396
397    if ((ss = site(pid)) == -1 || (st = sfnum(ss)) == NULL)
398	return CGETS(23, 3, "unknown");
399    else
400	return st->sf_sname;
401}
402
403static int
404migratepid(pid, new_site)
405    pid_t   pid;
406    siteno_t new_site;
407{
408    struct sf *st;
409    int     need_local;
410
411    need_local = (pid == 0) || (pid == getpid());
412
413    if (kill3((pid_t) pid, SIGMIGRATE, new_site) < 0) {
414	xprintf("%d: %s\n", pid, strerror(errno));
415	return (-1);
416    }
417
418    if (need_local) {
419	if ((new_site = site(0)) == -1) {
420	    xprintf(CGETS(23, 4, "site: %s\n"), strerror(errno));
421	    return (-1);
422	}
423	if ((st = sfnum(new_site)) == NULL) {
424	    xprintf(CGETS(23, 5, "%d: Site not found\n"), new_site);
425	    return (-1);
426	}
427	if (setlocal(st->sf_local, strlen(st->sf_local)) == -1) {
428	    xprintf(CGETS(23, 6, "setlocal: %s: %s\n"),
429			  st->sf_local, strerror(errno));
430	    return (-1);
431	}
432    }
433    return (0);
434}
435
436/*ARGSUSED*/
437void
438domigrate(v, c)
439    Char  **v;
440    struct command *c;
441{
442    struct sf *st;
443    char   *s;
444    Char   *cp;
445    struct process *pp;
446    int    err1 = 0;
447    int    pid = 0;
448    siteno_t new_site = 0;
449    sigmask_t omask;
450
451#ifdef BSDSIGS
452    omask = sigmask(SIGCHLD);
453    if (setintr)
454	omask |= sigmask(SIGINT);
455    omask = sigblock(omask) & ~omask;
456#else
457    if (setintr)
458	(void) sighold(SIGINT);
459    (void) sighold(SIGCHLD);
460#endif /* BSDSIGS */
461
462    ++v;
463    if (*v[0] == '-') {
464	/*
465	 * Do the -site.
466	 */
467	s = short2str(&v[0][1]);
468	/*
469	 * see comment in setspath()
470	 */
471	dont_free = 1;
472	if ((st = sfname(s)) == NULL) {
473	    setname(s);
474	    stderror(ERR_NAME | ERR_STRING, CGETS(23, 7, "Site not found"));
475	}
476	dont_free = 0;
477	new_site = st->sf_id;
478	++v;
479    }
480
481    if (!*v || *v[0] == '\0') {
482	if (migratepid(0, new_site) == -1)
483	    err1++;
484    }
485    else {
486	gflag = 0, tglob(v);
487	if (gflag) {
488	    v = globall(v);
489	    if (v == 0)
490		stderror(ERR_NAME | ERR_NOMATCH);
491	}
492	else {
493	    v = gargv = saveblk(v);
494	    trim(v);
495	}
496
497	while (v && (cp = *v)) {
498	    if (*cp == '%') {
499		pp = pfind(cp);
500		if (kill3((pid_t) - pp->p_jobid, SIGMIGRATE, new_site) < 0) {
501		    xprintf("%S: %s\n", cp, strerror(errno));
502		    err1++;
503		}
504	    }
505	    else if (!(Isdigit(*cp) || *cp == '-'))
506		stderror(ERR_NAME | ERR_JOBARGS);
507	    else {
508		pid = atoi(short2str(cp));
509		if (migratepid(pid, new_site) == -1)
510		    err1++;
511	    }
512	    v++;
513	}
514	if (gargv)
515	    blkfree(gargv), gargv = 0;
516    }
517
518done:
519#ifdef BSDSIGS
520    (void) sigsetmask(omask);
521#else
522    (void) sigrelse(SIGCHLD);
523    if (setintr)
524	(void) sigrelse(SIGINT);
525#endif /* BSDSIGS */
526    if (err1)
527	stderror(ERR_SILENT);
528}
529
530#endif /* TCF */
531
532/***
533 *** CRAY ddmode <velo@sesun3.epfl.ch> (Martin Ouwehand EPFL-SIC/SE)
534 ***/
535#if defined(_CRAY) && !defined(_CRAYMPP)
536void
537dodmmode(v, c)
538    Char  **v;
539    struct command *c;
540{
541    Char *cp = v[1];
542
543    USE(c);
544
545    if ( !cp ) {
546	int mode;
547
548	mode = dmmode(0);
549	dmmode(mode);
550	xprintf("%d\n",mode);
551    }
552    else {
553	if (cp[1] != '\0')
554	    stderror(ERR_NAME | ERR_STRING,
555		     CGETS(23, 30, "Too many arguments"));
556	else
557	    switch(*cp) {
558	    case '0':
559		dmmode(0);
560		break;
561	    case '1':
562		dmmode(1);
563		break;
564	    default:
565		stderror(ERR_NAME | ERR_STRING,
566			 CGETS(23, 31, "Invalid argument"));
567	    }
568    }
569}
570#endif /* _CRAY && !_CRAYMPP */
571
572
573/***
574 *** CONVEX Warps.
575 ***/
576
577#ifdef WARP
578/*
579 * handle the funky warping of symlinks
580 */
581#include <warpdb.h>
582#include <sys/warp.h>
583
584static jmp_buf sigsys_buf;
585
586static  sigret_t
587catch_sigsys()
588{
589    longjmp(sigsys_buf, 1);
590}
591
592
593/*ARGSUSED*/
594void
595dowarp(v, c)
596    Char  **v;
597    struct command *c;
598{
599    int     warp, oldwarp;
600    struct warpent *we;
601    void    (*old_sigsys_handler) () = 0;
602    char   *newwarp;
603
604    if (setjmp(sigsys_buf)) {
605	signal(SIGSYS, old_sigsys_handler);
606	stderror(ERR_NAME | ERR_STRING,
607		 CGETS(23, 8, "You're trapped in a universe you never made"));
608	return;
609    }
610    old_sigsys_handler = signal(SIGSYS, catch_sigsys);
611
612    warp = getwarp();
613
614    v++;
615    if (*v == 0) {		/* display warp value */
616	if (warp < 0)
617	    stderror(ERR_NAME | ERR_STRING, CGETS(23, 9, "Getwarp failed"));
618	we = getwarpbyvalue(warp);
619	if (we)
620	    printf("%s\n", we->w_name);
621	else
622	    printf("%d\n", warp);
623    }
624    else {			/* set warp value */
625	oldwarp = warp;
626	newwarp = short2str(*v);
627	if (Isdigit(*v[0]))
628	    warp = atoi(newwarp);
629	else {
630	    we = getwarpbyname(newwarp);
631	    if (we)
632		warp = we->w_value;
633	    else
634		warp = -1;
635	}
636	if ((warp < 0) || (warp >= WARP_MAXLINK))
637	    stderror(ERR_NAME | ERR_STRING, CGETS(23, 10, "Invalid warp"));
638	if ((setwarp(warp) < 0) || (getwarp() != warp)) {
639	    (void) setwarp(oldwarp);
640	    stderror(ERR_NAME | ERR_STRING, CGETS(23, 11, "Setwarp failed"));
641	}
642    }
643    signal(SIGSYS, old_sigsys_handler);
644    return;
645}
646#endif /* WARP */
647
648/***
649 *** Masscomp or HCX
650 ***/
651/* Added, DAS DEC-90. */
652#if defined(masscomp) || defined(_CX_UX)
653/*ARGSUSED*/
654void
655douniverse(v, c)
656    register Char **v;
657    struct command *c;
658{
659    register Char *cp = v[1];
660    register Char *cp2;		/* dunno how many elements v comes in with */
661    char    ubuf[100];
662#ifdef BSDSIGS
663    register sigmask_t omask = 0;
664#endif /* BSDSIGS */
665
666    if (cp == 0) {
667	(void) getuniverse(ubuf);
668	xprintf("%s\n", ubuf);
669    }
670    else {
671	cp2 = v[2];
672	if (cp2 == 0) {
673	    if (*cp == '\0' || setuniverse(short2str(cp)) != 0)
674		stderror(ERR_NAME | ERR_STRING, CGETS(23, 12, "Illegal universe"));
675	    }
676	else {
677	    (void) getuniverse(ubuf);
678	    if (*cp == '\0' || setuniverse(short2str(cp)) != 0)
679	stderror(ERR_NAME | ERR_STRING, CGETS(23, 12, "Illegal universe"));
680	    if (setintr)
681#ifdef BSDSIGS
682		omask = sigblock(sigmask(SIGINT)) & ~sigmask(SIGINT);
683#else /* !BSDSIGS */
684		(void) sighold(SIGINT);
685#endif /* BSDSIGS */
686	    lshift(v, 2);
687	    if (setintr)
688#ifdef BSDSIGS
689		(void) sigsetmask(omask);
690#else /* !BSDSIGS */
691		(void) sigrelse (SIGINT);
692#endif /* BSDSIGS */
693	    reexecute(c);
694	    (void) setuniverse(ubuf);
695	}
696    }
697}
698#endif /* masscomp || _CX_UX */
699
700#if defined(_CX_UX)
701/*ARGSUSED*/
702void
703doatt(v, c)
704    register Char **v;
705    struct command *c;
706{
707    register Char *cp = v[1];
708    char    ubuf[100];
709#ifdef BSDSIGS
710    register sigmask_t omask = 0;
711#endif /* BSDSIGS */
712
713    if (cp == 0)
714	(void) setuniverse("att");
715    else {
716	(void) getuniverse(ubuf);
717	(void) setuniverse("att");
718	if (setintr)
719#ifdef BSDSIGS
720	    omask = sigblock(sigmask(SIGINT)) & ~sigmask(SIGINT);
721#else /* !BSDSIGS */
722	    (void) sighold(SIGINT);
723#endif /* BSDSIGS */
724	lshift(v, 1);
725	if (setintr)
726#ifdef BSDSIGS
727	    (void) sigsetmask(omask);
728#else /* !BSDSIGS */
729	    (void) sigrelse (SIGINT);
730#endif /* BSDSIGS */
731	reexecute(c);
732	(void) setuniverse(ubuf);
733    }
734}
735
736/*ARGSUSED*/
737void
738doucb(v, c)
739    register Char **v;
740    struct command *c;
741{
742    register Char *cp = v[1];
743    char    ubuf[100];
744#ifdef BSDSIGS
745    register sigmask_t omask = 0;
746#endif /* BSDSIGS */
747
748    if (cp == 0)
749	(void) setuniverse("ucb");
750    else {
751	(void) getuniverse(ubuf);
752	(void) setuniverse("ucb");
753	if (setintr)
754#ifdef BSDSIGS
755	    omask = sigblock(sigmask(SIGINT)) & ~sigmask(SIGINT);
756#else /* !BSDSIGS */
757	    (void) sighold(SIGINT);
758#endif /* BSDSIGS */
759	lshift(v, 1);
760	if (setintr)
761#ifdef BSDSIGS
762	    (void) sigsetmask(omask);
763#else /* !BSDSIGS */
764	    (void) sigrelse (SIGINT);
765#endif /* BSDSIGS */
766	reexecute(c);
767	(void) setuniverse(ubuf);
768    }
769}
770#endif /* _CX_UX */
771
772#ifdef _SEQUENT_
773/*
774 * Compute the difference in process stats.
775 */
776void
777pr_stat_sub(p2, p1, pr)
778    struct process_stats *p2, *p1, *pr;
779{
780    pr->ps_utime.tv_sec = p2->ps_utime.tv_sec - p1->ps_utime.tv_sec;
781    pr->ps_utime.tv_usec = p2->ps_utime.tv_usec - p1->ps_utime.tv_usec;
782    if (pr->ps_utime.tv_usec < 0) {
783	pr->ps_utime.tv_sec -= 1;
784	pr->ps_utime.tv_usec += 1000000;
785    }
786    pr->ps_stime.tv_sec = p2->ps_stime.tv_sec - p1->ps_stime.tv_sec;
787    pr->ps_stime.tv_usec = p2->ps_stime.tv_usec - p1->ps_stime.tv_usec;
788    if (pr->ps_stime.tv_usec < 0) {
789	pr->ps_stime.tv_sec -= 1;
790	pr->ps_stime.tv_usec += 1000000;
791    }
792
793    pr->ps_maxrss = p2->ps_maxrss - p1->ps_maxrss;
794    pr->ps_pagein = p2->ps_pagein - p1->ps_pagein;
795    pr->ps_reclaim = p2->ps_reclaim - p1->ps_reclaim;
796    pr->ps_zerofill = p2->ps_zerofill - p1->ps_zerofill;
797    pr->ps_pffincr = p2->ps_pffincr - p1->ps_pffincr;
798    pr->ps_pffdecr = p2->ps_pffdecr - p1->ps_pffdecr;
799    pr->ps_swap = p2->ps_swap - p1->ps_swap;
800    pr->ps_syscall = p2->ps_syscall - p1->ps_syscall;
801    pr->ps_volcsw = p2->ps_volcsw - p1->ps_volcsw;
802    pr->ps_involcsw = p2->ps_involcsw - p1->ps_involcsw;
803    pr->ps_signal = p2->ps_signal - p1->ps_signal;
804    pr->ps_lread = p2->ps_lread - p1->ps_lread;
805    pr->ps_lwrite = p2->ps_lwrite - p1->ps_lwrite;
806    pr->ps_bread = p2->ps_bread - p1->ps_bread;
807    pr->ps_bwrite = p2->ps_bwrite - p1->ps_bwrite;
808    pr->ps_phread = p2->ps_phread - p1->ps_phread;
809    pr->ps_phwrite = p2->ps_phwrite - p1->ps_phwrite;
810}
811
812#endif /* _SEQUENT_ */
813
814
815#ifdef NEEDmemset
816/* This is a replacement for a missing memset function */
817ptr_t xmemset(loc, value, len)
818    ptr_t loc;
819    int len;
820    size_t value;
821{
822    char *ptr = (char *) loc;
823
824    while (len--)
825	*ptr++ = value;
826    return loc;
827}
828#endif /* NEEDmemset */
829
830
831#ifdef NEEDmemmove
832/* memmove():
833 * 	This is the ANSI form of bcopy() with the arguments backwards...
834 *	Unlike memcpy(), it handles overlaps between source and
835 *	destination memory
836 */
837ptr_t
838xmemmove(vdst, vsrc, len)
839    ptr_t vdst;
840    const ptr_t vsrc;
841    size_t len;
842{
843    const char *src = (const char *) vsrc;
844    char *dst = (char *) vdst;
845
846    if (src == dst)
847	return vdst;
848
849    if (src > dst) {
850	while (len--)
851	    *dst++ = *src++;
852    }
853    else {
854	src += len;
855	dst += len;
856	while (len--)
857	    *--dst = *--src;
858    }
859    return vdst;
860}
861#endif /* NEEDmemmove */
862
863
864#ifndef WINNT_NATIVE
865#ifdef tcgetpgrp
866int
867xtcgetpgrp(fd)
868    int     fd;
869{
870    int     pgrp;
871
872    /* ioctl will handle setting errno correctly. */
873    if (ioctl(fd, TIOCGPGRP, (ioctl_t) & pgrp) < 0)
874	return (-1);
875    return (pgrp);
876}
877
878/*
879 * XXX: tcsetpgrp is not a macro any more cause on some systems,
880 * pid_t is a short, but the ioctl() takes a pointer to int (pyr)
881 * Thanks to Simon Day (simon@pharaoh.cyborg.bt.co.uk) for pointing
882 * this out.
883 */
884int
885xtcsetpgrp(fd, pgrp)
886    int fd, pgrp;
887{
888    return ioctl(fd, TIOCSPGRP, (ioctl_t) &pgrp);
889}
890
891#endif	/* tcgetpgrp */
892#endif /* WINNT_NATIVE */
893
894
895#ifdef YPBUGS
896void
897fix_yp_bugs()
898{
899    char   *mydomain;
900
901    extern int yp_get_default_domain __P((char **));
902    /*
903     * PWP: The previous version assumed that yp domain was the same as the
904     * internet name domain.  This isn't allways true. (Thanks to Mat Landau
905     * <mlandau@bbn.com> for the original version of this.)
906     */
907    if (yp_get_default_domain(&mydomain) == 0) {	/* if we got a name */
908	extern void yp_unbind __P((const char *));
909
910	yp_unbind(mydomain);
911    }
912}
913
914#endif /* YPBUGS */
915
916#ifdef STRCOLLBUG
917void
918fix_strcoll_bug()
919{
920#if defined(NLS) && !defined(NOSTRCOLL)
921    /*
922     * SunOS4 checks the file descriptor from openlocale() for <= 0
923     * instead of == -1. Someone should tell sun that file descriptor 0
924     * is valid! Our portable hack: open one so we call it with 0 used...
925     * We have to call this routine every time the locale changes...
926     *
927     * Of course it also tries to free the constant locale "C" it initially
928     * had allocated, with the sequence
929     * > setenv LANG "fr"
930     * > ls^D
931     * > unsetenv LANG
932     * But we are smarter than that and just print a warning message.
933     */
934    int fd = -1;
935    static char *root = "/";
936
937    if (!didfds)
938	fd = open(root, O_RDONLY);
939
940    (void) strcoll(root, root);
941
942    if (fd != -1)
943	(void) close(fd);
944#endif
945}
946#endif /* STRCOLLBUG */
947
948
949#ifdef OREO
950#include <compat.h>
951#endif /* OREO */
952
953void
954osinit()
955{
956#ifdef OREO
957    set42sig();
958    setcompat(getcompat() & ~COMPAT_EXEC);
959    sigignore(SIGIO);		/* ignore SIGIO */
960#endif /* OREO */
961
962#ifdef aiws
963    {
964	struct sigstack inst;
965	inst.ss_sp = (char *) xmalloc((size_t) 4192) + 4192;
966	inst.ss_onstack = 0;
967	sigstack(&inst, NULL);
968    }
969#endif /* aiws */
970
971#ifdef apollo
972    (void) isapad();
973#endif
974
975#ifdef _SX
976    /*
977     * kill(SIGCONT) problems, don't know what this syscall does
978     * [schott@rzg.mpg.de]
979     */
980    syscall(151, getpid(), getpid());
981#endif /* _SX */
982}
983
984#ifdef strerror
985char *
986xstrerror(i)
987    int i;
988{
989    static char errbuf[128];
990
991    if (i >= 0 && i < sys_nerr) {
992	return sys_errlist[i];
993    } else {
994	(void) xsnprintf(errbuf, sizeof(errbuf),
995	    CGETS(23, 13, "Unknown Error: %d"), i);
996	return errbuf;
997    }
998}
999#endif /* strerror */
1000
1001#ifdef gethostname
1002# if !defined(_MINIX) && !defined(__EMX__) && !defined(WINNT_NATIVE)
1003#  include <sys/utsname.h>
1004# endif /* !_MINIX && !__EMX__ && !WINNT_NATIVE */
1005
1006int
1007xgethostname(name, namlen)
1008    char   *name;
1009    int     namlen;
1010{
1011# if !defined(_MINIX) && !defined(__EMX__) && !defined(WINNT_NATIVE)
1012    int     i, retval;
1013    struct utsname uts;
1014
1015    retval = uname(&uts);
1016
1017#  ifdef DEBUG
1018    xprintf(CGETS(23, 14, "sysname:  %s\n"), uts.sysname);
1019    xprintf(CGETS(23, 15, "nodename: %s\n"), uts.nodename);
1020    xprintf(CGETS(23, 16, "release:  %s\n"), uts.release);
1021    xprintf(CGETS(23, 17, "version:  %s\n"), uts.version);
1022    xprintf(CGETS(23, 18, "machine:  %s\n"), uts.machine);
1023#  endif /* DEBUG */
1024    i = strlen(uts.nodename) + 1;
1025    (void) strncpy(name, uts.nodename, i < namlen ? i : namlen);
1026
1027    return retval;
1028# else /* !_MINIX && !__EMX__ */
1029    if (namlen > 0) {
1030#  ifdef __EMX__
1031	(void) strncpy(name, "OS/2", namlen);
1032#  else /* _MINIX */
1033	(void) strncpy(name, "minix", namlen);
1034#  endif /* __EMX__ */
1035	name[namlen-1] = '\0';
1036    }
1037    return(0);
1038#endif /* _MINIX && !__EMX__ */
1039} /* end xgethostname */
1040#endif /* gethostname */
1041
1042#ifdef nice
1043# if defined(_MINIX) && defined(NICE)
1044#  undef _POSIX_SOURCE	/* redefined in <lib.h> */
1045#  undef _MINIX		/* redefined in <lib.h> */
1046#  undef HZ		/* redefined in <minix/const.h> */
1047#  include <lib.h>
1048# endif /* _MINIX && NICE */
1049int
1050xnice(incr)
1051    int incr;
1052{
1053#if defined(_MINIX) && defined(NICE)
1054    return callm1(MM, NICE, incr, 0, 0, NIL_PTR, NIL_PTR, NIL_PTR);
1055#else
1056    return /* incr ? 0 : */ 0;
1057#endif /* _MINIX && NICE */
1058} /* end xnice */
1059#endif /* nice */
1060
1061#ifdef NEEDgetcwd
1062static char *strnrcpy __P((char *, char *, size_t));
1063
1064/* xgetcwd():
1065 *	Return the pathname of the current directory, or return
1066 *	an error message in pathname.
1067 */
1068
1069# ifdef hp9000s500
1070/*
1071 *  From: Bernd Mohr <mohr@faui77.informatik.uni-erlangen.de>
1072 *  I also ported the tcsh to the HP9000 Series 500. This computer
1073 *  is a little bit different than the other HP 9000 computer. It has
1074 *  a HP Chip instead of a Motorola CPU and it is no "real" UNIX. It runs
1075 *  HP-UX which is emulated in top of a HP operating system. So, the last
1076 *  supported version of HP-UX is 5.2 on the HP9000s500. This has two
1077 *  consequences: it supports no job control and it has a filesystem
1078 *  without "." and ".." !!!
1079 */
1080char *
1081xgetcwd(pathname, pathlen)
1082    char *pathname;
1083    size_t pathlen;
1084{
1085    char pathbuf[MAXNAMLEN];	/* temporary pathname buffer */
1086    char *pnptr = &pathbuf[(sizeof pathbuf)-1]; /* pathname pointer */
1087    dev_t rdev;			/* root device number */
1088    DIR *dirp = NULL;		/* directory stream */
1089    ino_t rino;			/* root inode number */
1090    off_t rsize;		/* root size */
1091    struct direct *dir;		/* directory entry struct */
1092    struct stat d, dd;		/* file status struct */
1093    int serrno;
1094
1095    *pnptr = '\0';
1096    (void) stat("/.", &d);
1097    rdev = d.st_dev;
1098    rino = d.st_ino;
1099    rsize = d.st_size;
1100    for (;;) {
1101	if (stat(".", &d) == -1) {
1102	    (void) xsnprintf(pathname, pathlen, CGETS(23, 24,
1103		"getcwd: Cannot stat \".\" (%s)"), strerror(errno));
1104	    goto fail;
1105	}
1106	if (d.st_ino == rino && d.st_dev == rdev && d.st_size == rsize)
1107	    break;		/* reached root directory */
1108	if ((dirp = opendir("..")) == NULL) {
1109	    (void) xsnprintf(pathname, pathlen, CGETS(23, 19,
1110		"getcwd: Cannot open \"..\" (%s)"), strerror(errno));
1111	    goto fail;
1112	}
1113	if (chdir("..") == -1) {
1114	    (void) xsnprintf(pathname, pathlen, CGETS(23, 20,
1115		"getcwd: Cannot chdir to \"..\" (%s)"), strerror(errno));
1116	    goto fail;
1117	}
1118	do {
1119	    if ((dir = readdir(dirp)) == NULL) {
1120		(void) xsnprintf(pathname, pathlen,
1121		    CGETS(23, 21, "getcwd: Read error in \"..\" (%s)"),
1122		    strerror(errno));
1123		goto fail;
1124	    }
1125	    if (stat(dir->d_name, &dd) == -1) {
1126		(void) xsnprintf(pathname, pathlen,
1127		    CGETS(23, 25, "getcwd: Cannot stat directory \"%s\" (%s)"),
1128		    dir->d_name, strerror(errno));
1129		goto fail;
1130	    }
1131	} while (dd.st_ino  != d.st_ino  ||
1132		 dd.st_dev  != d.st_dev  ||
1133		 dd.st_size != d.st_size);
1134	(void) closedir(dirp);
1135	dirp = NULL;
1136	pnptr = strnrcpy(dirp->d_name, pnptr, pnptr - pathbuf);
1137	pnptr = strnrcpy("/", pnptr, pnptr - pathbuf);
1138    }
1139
1140    if (*pnptr == '\0')		/* current dir == root dir */
1141	(void) strncpy(pathname, "/", pathlen);
1142    else {
1143	(void) strncpy(pathname, pnptr, pathlen);
1144	pathname[pathlen - 1] = '\0';
1145	if (chdir(pnptr) == -1) {
1146	    (void) xsnprintf(pathname, MAXPATHLEN, CGETS(23, 22,
1147		    "getcwd: Cannot change back to \".\" (%s)"),
1148		    strerror(errno));
1149	    return NULL;
1150	}
1151    }
1152    return pathname;
1153
1154fail:
1155    serrno = errno;
1156    (void) chdir(strnrcpy(".", pnptr, pnptr - pathbuf));
1157    errno = serrno;
1158    return NULL;
1159}
1160
1161# else /* ! hp9000s500 */
1162
1163#  if (SYSVREL != 0 && !defined(d_fileno)) || defined(_VMS_POSIX) || defined(WINNT) || defined(_MINIX_VMD)
1164#   define d_fileno d_ino
1165#  endif
1166
1167char *
1168xgetcwd(pathname, pathlen)
1169    char   *pathname;
1170    size_t pathlen;
1171{
1172    DIR    *dp;
1173    struct dirent *d;
1174
1175    struct stat st_root, st_cur, st_next, st_dotdot;
1176    char    pathbuf[MAXPATHLEN], nextpathbuf[MAXPATHLEN * 2];
1177    char   *pathptr, *nextpathptr, *cur_name_add;
1178    int	   save_errno = 0;
1179
1180    /* find the inode of root */
1181    if (stat("/", &st_root) == -1) {
1182	(void) xsnprintf(pathname, pathlen, CGETS(23, 23,
1183			"getcwd: Cannot stat \"/\" (%s)"),
1184			strerror(errno));
1185	return NULL;
1186    }
1187    pathbuf[MAXPATHLEN - 1] = '\0';
1188    pathptr = &pathbuf[MAXPATHLEN - 1];
1189    nextpathbuf[MAXPATHLEN - 1] = '\0';
1190    cur_name_add = nextpathptr = &nextpathbuf[MAXPATHLEN - 1];
1191
1192    /* find the inode of the current directory */
1193    if (lstat(".", &st_cur) == -1) {
1194	(void) xsnprintf(pathname, pathlen, CGETS(23, 24,
1195			 "getcwd: Cannot stat \".\" (%s)"),
1196			 strerror(errno));
1197	return NULL;
1198    }
1199    nextpathptr = strnrcpy(nextpathptr, "../", nextpathptr - nextpathbuf);
1200
1201    /* Descend to root */
1202    for (;;) {
1203
1204	/* look if we found root yet */
1205	if (st_cur.st_ino == st_root.st_ino &&
1206	    DEV_DEV_COMPARE(st_cur.st_dev, st_root.st_dev)) {
1207	    (void) strncpy(pathname, *pathptr != '/' ? "/" : pathptr, pathlen);
1208	    pathname[pathlen - 1] = '\0';
1209	    return pathname;
1210	}
1211
1212	/* open the parent directory */
1213	if (stat(nextpathptr, &st_dotdot) == -1) {
1214	    (void) xsnprintf(pathname, pathlen, CGETS(23, 25,
1215			     "getcwd: Cannot stat directory \"%s\" (%s)"),
1216			     nextpathptr, strerror(errno));
1217	    return NULL;
1218	}
1219	if ((dp = opendir(nextpathptr)) == NULL) {
1220	    (void) xsnprintf(pathname, pathlen, CGETS(23, 26,
1221			     "getcwd: Cannot open directory \"%s\" (%s)"),
1222			     nextpathptr, strerror(errno));
1223	    return NULL;
1224	}
1225
1226	/* look in the parent for the entry with the same inode */
1227	if (DEV_DEV_COMPARE(st_dotdot.st_dev, st_cur.st_dev)) {
1228	    /* Parent has same device. No need to stat every member */
1229	    for (d = readdir(dp); d != NULL; d = readdir(dp)) {
1230#ifdef __clipper__
1231		if (((unsigned long)d->d_fileno & 0xffff) == st_cur.st_ino)
1232		    break;
1233#else
1234		if (d->d_fileno == st_cur.st_ino)
1235		    break;
1236#endif
1237	    }
1238	}
1239	else {
1240	    /*
1241	     * Parent has a different device. This is a mount point so we
1242	     * need to stat every member
1243	     */
1244	    for (d = readdir(dp); d != NULL; d = readdir(dp)) {
1245		if (ISDOT(d->d_name) || ISDOTDOT(d->d_name))
1246		    continue;
1247		(void)strncpy(cur_name_add, d->d_name,
1248		    (size_t) (&nextpathbuf[sizeof(nextpathbuf) - 1] - cur_name_add));
1249		if (lstat(nextpathptr, &st_next) == -1) {
1250		    /*
1251		     * We might not be able to stat() some path components
1252		     * if we are using afs, but this is not an error as
1253		     * long as we find the one we need; we also save the
1254		     * first error to report it if we don't finally succeed.
1255		     */
1256		    if (save_errno == 0)
1257			save_errno = errno;
1258		    continue;
1259		}
1260		/* check if we found it yet */
1261		if (st_next.st_ino == st_cur.st_ino &&
1262		    DEV_DEV_COMPARE(st_next.st_dev, st_cur.st_dev))
1263		    break;
1264	    }
1265	}
1266	if (d == NULL) {
1267	    (void) xsnprintf(pathname, pathlen, CGETS(23, 27,
1268			     "getcwd: Cannot find \".\" in \"..\" (%s)"),
1269			     strerror(save_errno ? save_errno : ENOENT));
1270	    (void) closedir(dp);
1271	    return NULL;
1272	}
1273	else
1274	    save_errno = 0;
1275	st_cur = st_dotdot;
1276	pathptr = strnrcpy(pathptr, d->d_name, pathptr - pathbuf);
1277	pathptr = strnrcpy(pathptr, "/", pathptr - pathbuf);
1278	nextpathptr = strnrcpy(nextpathptr, "../", nextpathptr - nextpathbuf);
1279	*cur_name_add = '\0';
1280	(void) closedir(dp);
1281    }
1282} /* end getcwd */
1283# endif /* hp9000s500 */
1284
1285/* strnrcpy():
1286 *	Like strncpy, going backwards and returning the new pointer
1287 */
1288static char *
1289strnrcpy(ptr, str, siz)
1290    register char *ptr, *str;
1291    size_t siz;
1292{
1293    register int len = strlen(str);
1294    if (siz == 0)
1295	return ptr;
1296
1297    while (len && siz--)
1298	*--ptr = str[--len];
1299
1300    return (ptr);
1301} /* end strnrcpy */
1302#endif /* getcwd */
1303
1304#ifdef apollo
1305/***
1306 *** Domain/OS
1307 ***/
1308#include <apollo/base.h>
1309#include <apollo/loader.h>
1310#include <apollo/error.h>
1311
1312
1313static char *
1314apperr(st)
1315    status_$t *st;
1316{
1317    static char buf[BUFSIZE];
1318    short e_subl, e_modl, e_codel;
1319    error_$string_t e_sub, e_mod, e_code;
1320
1321    error_$get_text(*st, e_sub, &e_subl, e_mod, &e_modl, e_code, &e_codel);
1322    e_sub[e_subl] = '\0';
1323    e_code[e_codel] = '\0';
1324    e_mod[e_modl] = '\0';
1325    (void) xsnprintf(buf, sizeof(buf), "%s (%s/%s)", e_code, e_sub, e_mod);
1326
1327    return(buf);
1328}
1329
1330static int
1331llib(s)
1332    Char *s;
1333{
1334    short len = Strlen(s);
1335    status_$t st;
1336    char *t;
1337
1338    loader_$inlib(t = short2str(s), len, &st);
1339    if (st.all != status_$ok)
1340	stderror(ERR_SYSTEM, t, apperr(&st));
1341}
1342
1343/*ARGSUSED*/
1344void
1345doinlib(v, c)
1346    Char **v;
1347    struct command *c;
1348{
1349    setname(short2str(*v++));
1350    gflag = 0, tglob(v);
1351    if (gflag) {
1352	v = globall(v);
1353	if (v == 0)
1354	    stderror(ERR_NAME | ERR_NOMATCH);
1355    }
1356    else {
1357	v = gargv = saveblk(v);
1358	trim(v);
1359    }
1360
1361    while (v && *v)
1362	llib(*v++);
1363    if (gargv)
1364	blkfree(gargv), gargv = 0;
1365}
1366
1367int
1368getv(v)
1369    Char *v;
1370{
1371    if (eq(v, STRbsd43))
1372	return(1);
1373    else if (eq(v, STRsys53))
1374	return(0);
1375    else
1376	stderror(ERR_NAME | ERR_SYSTEM, short2str(v),
1377		 CGETS(23, 28, "Invalid system type"));
1378    /*NOTREACHED*/
1379    return(0);
1380}
1381
1382/*ARGSUSED*/
1383void
1384dover(v, c)
1385    Char **v;
1386    struct command *c;
1387{
1388    Char *p;
1389
1390    setname(short2str(*v++));
1391    if (!*v) {
1392	if (!(p = tgetenv(STRSYSTYPE)))
1393	    stderror(ERR_NAME | ERR_STRING,
1394		     CGETS(23, 29, "System type is not set"));
1395	xprintf("%S\n", p);
1396    }
1397    else {
1398	tsetenv(STRSYSTYPE, getv(*v) ? STRbsd43 : STRsys53);
1399	dohash(NULL, NULL);
1400    }
1401}
1402
1403/*
1404 * Many thanks to rees@citi.umich.edu (Jim Rees) and
1405 *                mathys@ssdt-tempe.sps.mot.com (Yves Mathys)
1406 * For figuring out how to do this... I could have never done
1407 * it without their help.
1408 */
1409typedef short enum {
1410	name_$wdir_type,
1411	name_$ndir_type,
1412	name_$node_dir_type,
1413} name_$dir_type_t;
1414
1415/*ARGSUSED*/
1416void
1417dorootnode(v, c)
1418    Char **v;
1419    struct command *c;
1420{
1421    name_$dir_type_t dirtype = name_$node_dir_type;
1422    uid_$t uid;
1423    status_$t st;
1424    char *name;
1425    short namelen;
1426
1427    setname(short2str(*v++));
1428
1429    name = short2str(*v);
1430    namelen = strlen(name);
1431
1432    name_$resolve(name, &namelen, &uid, &st);
1433    if (st.all != status_$ok)
1434	stderror(ERR_SYSTEM, name, apperr(&st));
1435    namelen = 0;
1436    name_$set_diru(&uid, "", &namelen, &dirtype, &st);
1437    if (st.all != status_$ok)
1438	stderror(ERR_SYSTEM, name, apperr(&st));
1439    dohash(NULL, NULL);
1440}
1441
1442int
1443isapad()
1444{
1445    static int res = -1;
1446    static status_$t st;
1447
1448    if (res == -1) {
1449	int strm;
1450	if (isatty(0))
1451	    strm = 0;
1452	if (isatty(1))
1453	    strm = 1;
1454	if (isatty(2))
1455	    strm = 2;
1456	else {
1457	    res = 0;
1458	    st.all = status_$ok;
1459	    return(res);
1460	}
1461	res = stream_$isavt(&strm, &st);
1462	res = res ? 1 : 0;
1463    }
1464    else {
1465	if (st.all != status_$ok)
1466	    stderror(ERR_SYSTEM, "stream_$isavt", apperr(&st));
1467    }
1468    return(res);
1469}
1470#endif
1471