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