# Copyright 2006 Sun Microsystems, Inc. All rights reserved. # Use is subject to license terms. # Copyright (c) 1983, 1984, 1985, 1986, 1987, 1988, 1989 AT&T # All Rights Reserved # Copyright (c) 1980 Regents of the University of California. # All rights reserved. The Berkeley Software License Agreement # specifies the terms and conditions for redistribution. # # C Shell with process control; VM/UNIX VAX Makefile # Bill Joy UC Berkeley; Jim Kulp IIASA, Austria # include ../Makefile.cmd # # For message catalogue files # POFILE= csh.po CLOBBERFILES += sh.tconst.h CLEANFILES += sh.tconst.h all : TARGET = all install : TARGET = install clean : TARGET = clean clobber : TARGET = clobber lint : TARGET = lint POBJS = \ sh.o sh.char.o \ sh.dir.o sh.dol.o sh.err.o sh.exec.o sh.exp.o\ sh.file.o sh.func.o sh.glob.o sh.hist.o\ sh.init.o sh.lex.o sh.misc.o sh.parse.o\ sh.print.o sh.proc.o sh.sem.o sh.set.o\ sh.tchar.o sh.time.o wait3.o printf.o CPPFLAGS += -D_FILE_OFFSET_BITS=64 CPPFLAGS += -I ../sh .KEEP_STATE: # Hammerhead: amd64-only all install clean clobber lint: $(MACH64) POFILES= $(POBJS:%.o=%.po) XGETFLAGS += -a -x csh.xcl # # For message catalogue files # _msg: $(MSGDOMAIN) sh.tconst.h .WAIT $(POFILE) $(RM) $(POFILE) $(CAT) $(POFILES) > $(POFILE) $(RM) $(MSGDOMAIN)/$(POFILE) $(CP) $(POFILE) $(MSGDOMAIN) $(MSGDOMAIN): $(INS.dir) $(POFILE): $(POFILES) $(RM) $@; cat $(POFILES) > $@ sh.tconst.h: sh.tconst.c make.sh.tconst.h.ed $(RM) $@; $(ED) sh.tconst.c < make.sh.tconst.h.ed clobber: local_clobber $(MACH64) local_clobber: $(RM) $(CLOBBERFILES) clean: local_clean local_clean: $(RM) $(CLEANFILES) $(MACH64): sh.tconst.h .WAIT FRC @cd $@; pwd; $(MAKE) $(TARGET) FRC: # # CDDL HEADER START # # The contents of this file are subject to the terms of the # Common Development and Distribution License (the "License"). # You may not use this file except in compliance with the License. # # You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE # or http://www.opensolaris.org/os/licensing. # See the License for the specific language governing permissions # and limitations under the License. # # When distributing Covered Code, include this CDDL HEADER in each # file and include the License file at usr/src/OPENSOLARIS.LICENSE. # If applicable, add the following below this CDDL HEADER, with the # fields enclosed by brackets "[]" replaced with your own identifying # information: Portions Copyright [yyyy] [name of copyright owner] # # CDDL HEADER END # # Copyright 2006 Sun Microsystems, Inc. All rights reserved. # Use is subject to license terms. # # Copyright (c) 1983, 1984, 1985, 1986, 1987, 1988, 1989 AT&T # All Rights Reserved # # Copyright (c) 1980 Regents of the University of California. # All rights reserved. The Berkeley Software License Agreement # specifies the terms and conditions for redistribution. # # C Shell with process control; VM/UNIX VAX Makefile # Bill Joy UC Berkeley; Jim Kulp IIASA, Austria # PROG = csh OBJS = \ sh.o sh.char.o \ sh.dir.o sh.dol.o sh.err.o sh.exec.o sh.exp.o \ sh.file.o sh.func.o sh.glob.o sh.hist.o \ sh.init.o sh.lex.o sh.misc.o sh.parse.o \ sh.print.o sh.proc.o sh.sem.o sh.set.o \ sh.tchar.o sh.time.o wait3.o printf.o SRCS = $(OBJS:%.o=../%.c) include ../../Makefile.cmd CPPFLAGS += -D_FILE_OFFSET_BITS=64 CPPFLAGS += -I../../sh -I.. LDLIBS += -lcurses -lmapmalloc MAPFILES = $(MAPFILE.INT) $(MAPFILE.NGB) LDFLAGS += $(MAPFILES:%=-Wl,-M%) .KEEP_STATE: all: $(PROG) $(PROG): $(OBJS) $(MAPFILES) $(LINK.c) $(OBJS) -o $@ $(LDLIBS) $(POST_PROCESS) clean: $(RM) $(OBJS) include ../../Makefile.targ %.o: ../%.c $(COMPILE.c) $< $(POST_PROCESS_O) [seizo:09/09/92] To fix #1098866, the directory structure is modified. This directory contains machine independent source codes of csh. Two files are currently machine dependent. These are signal.c and signal.h Machine dependent files are moved under $(MACH) directory. So, if you are going to port csh for other architecture, you have to do: 1) create a directory for the new architecture. (Say, intel) 2) create following files. (You can use sparc/{Makefile, signal.c, signal.h} as templates. intel/Makefile intel/signal.c You need to modify sigvechandler() intel/signal.h You need to modify struct sigcontext. In future, I am planning to rewrite csh so it uses modern signal interface so csh will not have any machine dependent portion. usr/src/cmd/csh/SCCS/{s.signal.c, s.signal.h} are move under usr/src/cmd/csh/sparc/SCCS. So, if you wish to see sccs histories of signal.c and signal.h, please refer to usr/src/cmd/csh/sparc/SCCS. Copyright (c) 1980, Regents of the University of California All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 3. All advertising materials mentioning features or use of this software must display the following acknowledgement: This product includes software developed by the University of California, Berkeley and its contributors. 4. Neither the name of the University nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. CSH SOFTWARE # # CDDL HEADER START # # The contents of this file are subject to the terms of the # Common Development and Distribution License (the "License"). # You may not use this file except in compliance with the License. # # You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE # or http://www.opensolaris.org/os/licensing. # See the License for the specific language governing permissions # and limitations under the License. # # When distributing Covered Code, include this CDDL HEADER in each # file and include the License file at usr/src/OPENSOLARIS.LICENSE. # If applicable, add the following below this CDDL HEADER, with the # fields enclosed by brackets "[]" replaced with your own identifying # information: Portions Copyright [yyyy] [name of copyright owner] # # CDDL HEADER END # # Copyright 2006 Sun Microsystems, Inc. All rights reserved. # Use is subject to license terms. # include ../Makefile.com include ../../Makefile.cmd.64 install: all $(ROOTPROG64) msgid " " msgid " " msgid " \b\b" msgid "" msgid "%ld %t" msgid "&" msgid "(null)" msgid "+" msgid "-" msgid "../" msgid "./" msgid "/" msgid "/.login" msgid "/bin/login" msgid "/dev/null" msgid "/etc" msgid "0123456789ABCDEF" msgid "0123456789abcdef" msgid "0X" msgid "0x" msgid ":" msgid "? " msgid "HOME" msgid "LC_CTYPE" msgid "LC_MESSAGES" msgid "Mail" msgid "PATH" msgid "Signal 32" msgid "TERM" msgid "TS" msgid "USER" msgid "\015\n" msgid "\07" msgid "\b\b \b\b" msgid "\b\b" msgid "\r" msgid "^R\n" msgid "login" msgid "vb" msgid "getwd: can't stat /" msgid "getwd: can't stat ." msgid "getwd: can't open .." msgid "getwd: read error in .." msgid "%s\n" msgid "\n" msgid "%s" msgid "/dev/fd/%d" msgid "%s: " msgid "%t" msgid "~%t" msgid "%t: " msgid "%o\n" msgid "%t \t" msgid "%s %t" msgid "%6d\t" msgid "%llu:" msgid "%llu" msgid "%d:" msgid "%d" msgid "%llu%llu" msgid "%d%d" msgid "[%d]" msgid " %d" msgid "[%d]%s %c " msgid "%5d " msgid "%-21s" msgid " |" msgid " &" msgid "\n\t" msgid "%s " msgid "%d: " msgid "%d%%" msgid "%d.%01d" !#ident "%Z%%M% %I% %E% SMI" v/^tchar/d 1,$s/=.*/;/ 1,$s/^tchar/extern tchar/ 1i /* DON'T EDIT THIS FILE. * This file was made from sh.tconst by the ed script make.sh.tconst.h.ed. */ . w sh.tconst.h q # # CDDL HEADER START # # The contents of this file are subject to the terms of the # Common Development and Distribution License (the "License"). # You may not use this file except in compliance with the License. # # You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE # or http://www.opensolaris.org/os/licensing. # See the License for the specific language governing permissions # and limitations under the License. # # When distributing Covered Code, include this CDDL HEADER in each # file and include the License file at usr/src/OPENSOLARIS.LICENSE. # If applicable, add the following below this CDDL HEADER, with the # fields enclosed by brackets "[]" replaced with your own identifying # information: Portions Copyright [yyyy] [name of copyright owner] # # CDDL HEADER END # # # Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved. # # # MAPFILE HEADER START # # WARNING: STOP NOW. DO NOT MODIFY THIS FILE. # Object versioning must comply with the rules detailed in # # usr/src/lib/README.mapfiles # # You should not be making modifications here until you've read the most current # copy of that file. If you need help, contact a gatekeeper for guidance. # # MAPFILE HEADER END # $mapfile_version 2 # csh interposes on a couple of libc functions (printf and signal). SYMBOL_SCOPE { global: printf { FLAGS = INTERPOSE }; signal { FLAGS = INTERPOSE }; }; /* * Copyright 2005 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ /* Copyright (c) 1983, 1984, 1985, 1986, 1987, 1988, 1989 AT&T */ /* All Rights Reserved */ /* * Copyright (c) 1980 Regents of the University of California. * All rights reserved. The Berkeley Software License Agreement * specifies the terms and conditions for redistribution. */ /* * Hacked "printf" which prints through putbyte and Putchar. * putbyte() is used to send a pure byte, which might be a part * of a mutlibyte character, mainly for %s. A control character * for putbyte() may be QUOTE'd meaning not to convert it to ^x * sequence. In all other cases Putchar() is used to send a character * in tchar (== wchar_t + * optional QUOE.) * DONT USE WITH STDIO! * This printf has been hacked again so that it understands tchar string * when the format specifier %t is used. Also %c has been expanded * to take a tchar character as well as normal int. * %t is supported in its simplest form; no width or precision will * be understood. * Assumption here is that sizeof(tchar)<=sizeof(int) so that tchar is * passed as int. Otherwise, %T must be specified instead of %c to * print a character in tchar. */ #include #include #include "sh.h" /* For tchar. */ #define HIBITLL (1ULL << 63) void _print(char *format, va_list *args); static char *p; int printf(const char *format, ...) { va_list stupid; p = (char *)gettext(format); va_start(stupid, format); _print(p, &stupid); va_end(stupid); return (0); } /* * Floating-point code is included or not, depending * on whether the preprocessor variable FLOAT is 1 or 0. */ /* Maximum number of digits in any integer (long) representation */ #define MAXDIGS 20 /* Convert a digit character to the corresponding number */ #define tonumber(x) ((x) - '0') /* Convert a number between 0 and 9 to the corresponding digit */ #define todigit(x) ((x) + '0') /* Maximum total number of digits in E format */ #define MAXECVT 17 /* Maximum number of digits after decimal point in F format */ #define MAXFCVT 60 /* Maximum significant figures in a floating-point number */ #define MAXFSIG 17 /* Maximum number of characters in an exponent */ #define MAXESIZ 4 /* Maximum (positive) exponent or greater */ #define MAXEXP 40 #define max(a, b) ((a) > (b) ? (a) : (b)) #define min(a, b) ((a) < (b) ? (a) : (b)) /* If this symbol is nonzero, allow '0' as a flag */ #define FZERO 1 #if FLOAT /* * System-supplied routines for floating conversion */ char *fcvt(); char *ecvt(); #endif void _print(char *format, va_list *args) { /* Current position in format */ char *cp; /* Starting and ending points for value to be printed */ char *bp, *p; tchar *tbp, *tep; /* For "%t". */ tchar tcbuf[2]; /* For "%c" or "%T". */ /* Field width and precision */ int width, prec; /* Format code */ char fcode; /* Number of padding zeroes required on the left */ int lzero; /* Flags - nonzero if corresponding character appears in format */ bool length; /* l */ bool double_length; /* ll */ bool fplus; /* + */ bool fminus; /* - */ bool fblank; /* blank */ bool fsharp; /* # */ #if FZERO bool fzero; /* 0 */ #endif /* Pointer to sign, "0x", "0X", or empty */ char *prefix; #if FLOAT /* Exponent or empty */ char *suffix; /* Buffer to create exponent */ char expbuf[MAXESIZ + 1]; /* Number of padding zeroes required on the right */ int rzero; /* The value being converted, if real */ double dval; /* Output values from fcvt and ecvt */ int decpt, sign; /* Scratch */ int k; /* Values are developed in this buffer */ char buf[max(MAXDIGS, max(MAXFCVT + DMAXEXP, MAXECVT) + 1)]; #else char buf[MAXDIGS]; #endif /* The value being converted, if integer */ long long val; /* Set to point to a translate table for digits of whatever radix */ char *tab; /* Work variables */ int n, hradix, lowbit; cp = format; /* * The main loop -- this loop goes through one iteration * for each ordinary character or format specification. */ while (*cp) if (*cp != '%') { /* Ordinary (non-%) character */ putbyte (*cp++); } else { /* * % has been found. * First, parse the format specification. */ /* Scan the */ fplus = fminus = fblank = fsharp = 0; #if FZERO fzero = 0; #endif scan: switch (*++cp) { case '+': fplus = 1; goto scan; case '-': fminus = 1; goto scan; case ' ': fblank = 1; goto scan; case '#': fsharp = 1; goto scan; #if FZERO case '0': fzero = 1; goto scan; #endif } /* Scan the field width */ if (*cp == '*') { width = va_arg (*args, int); if (width < 0) { width = -width; fminus = 1; } cp++; } else { width = 0; while (isdigit(*cp)) { n = tonumber(*cp++); width = width * 10 + n; } } /* Scan the precision */ if (*cp == '.') { /* '*' instead of digits? */ if (*++cp == '*') { prec = va_arg(*args, int); cp++; } else { prec = 0; while (isdigit(*cp)) { n = tonumber(*cp++); prec = prec * 10 + n; } } } else { prec = -1; } /* Scan the length modifier */ double_length = length = 0; switch (*cp) { case 'l': if (*(cp + 1) == 'l') { cp++; double_length = 1; } else { length = 1; } /* No break */ case 'h': cp++; } /* * The character addressed by cp must be the * format letter -- there is nothing left for * it to be. * * The status of the +, -, #, blank, and 0 * flags are reflected in the variables * "fplus", "fminus", "fsharp", "fblank", * and "fzero", respectively. * "width" and "prec" contain numbers * corresponding to the digit strings * before and after the decimal point, * respectively. If there was no decimal * point, "prec" is -1. * * The following switch sets things up * for printing. What ultimately gets * printed will be padding blanks, a prefix, * left padding zeroes, a value, right padding * zeroes, a suffix, and more padding * blanks. Padding blanks will not appear * simultaneously on both the left and the * right. Each case in this switch will * compute the value, and leave in several * variables the information necessary to * construct what is to be printed. * * The prefix is a sign, a blank, "0x", "0X", * or null, and is addressed by "prefix". * * The suffix is either null or an exponent, * and is addressed by "suffix". * * The value to be printed starts at "bp" * and continues up to and not including "p". * * "lzero" and "rzero" will contain the number * of padding zeroes required on the left * and right, respectively. If either of * these variables is negative, it will be * treated as if it were zero. * * The number of padding blanks, and whether * they go on the left or the right, will be * computed on exit from the switch. */ lzero = 0; prefix = ""; #if FLOAT rzero = lzero; suffix = prefix; #endif switch (fcode = *cp++) { /* * fixed point representations * * "hradix" is half the radix for the conversion. * Conversion is unsigned unless fcode is 'd'. * HIBITLL is 1000...000 binary, and is equal to * the maximum negative number. * We assume a 2's complement machine */ case 'D': case 'U': length = 1; case 'd': case 'u': hradix = 5; goto fixed; case 'O': length = 1; case 'o': hradix = 4; goto fixed; case 'X': case 'x': hradix = 8; fixed: /* Establish default precision */ if (prec < 0) { prec = 1; } /* Fetch the argument to be printed */ if (double_length) { val = va_arg(*args, long long); } else if (length) { val = va_arg(*args, long); } else if (fcode == 'd') { val = va_arg(*args, int); } else { val = va_arg(*args, unsigned); } /* If signed conversion, establish sign */ if (fcode == 'd' || fcode == 'D') { if (val < 0) { prefix = "-"; /* * Negate, checking in * advance for possible * overflow. */ if (val != HIBITLL) { val = -val; } } else if (fplus) { prefix = "+"; } else if (fblank) { prefix = " "; } } #if FZERO if (fzero) { int n = width - strlen(prefix); if (n > prec) { prec = n; } } #endif /* Set translate table for digits */ if (fcode == 'X') { tab = "0123456789ABCDEF"; } else { tab = "0123456789abcdef"; } /* Develop the digits of the value */ p = bp = buf + MAXDIGS; while (val) { lowbit = val & 1; val = (val >> 1) & ~HIBITLL; *--bp = tab[val % hradix * 2 + lowbit]; val /= hradix; } /* Calculate padding zero requirement */ lzero = bp - p + prec; /* Handle the # flag */ if (fsharp && bp != p) { switch (fcode) { case 'o': if (lzero < 1) lzero = 1; break; case 'x': prefix = "0x"; break; case 'X': prefix = "0X"; break; } } break; #if FLOAT case 'E': case 'e': /* * E-format. The general strategy * here is fairly easy: we take * what ecvt gives us and re-format it. */ /* Establish default precision */ if (prec < 0) { prec = 6; } /* Fetch the value */ dval = va_arg(*args, double); /* Develop the mantissa */ bp = ecvt(dval, min(prec + 1, MAXECVT), &decpt, &sign); /* Determine the prefix */ e_merge: if (sign) { prefix = "-"; } else if (fplus) { prefix = "+"; } else if (fblank) { prefix = " "; } /* Place the first digit in the buffer */ p = &buf[0]; *p++ = *bp != '\0' ? *bp++ : '0'; /* Put in a decimal point if needed */ if (prec != 0 || fsharp) { *p++ = '.'; } /* Create the rest of the mantissa */ rzero = prec; while (rzero > 0 && *bp != '\0') { --rzero; *p++ = *bp++; } bp = &buf[0]; /* Create the exponent */ suffix = &expbuf[MAXESIZ]; *suffix = '\0'; if (dval != 0) { n = decpt - 1; if (n < 0) { n = -n; } while (n != 0) { *--suffix = todigit(n % 10); n /= 10; } } /* Prepend leading zeroes to the exponent */ while (suffix > &expbuf[MAXESIZ - 2]) { *--suffix = '0'; } /* Put in the exponent sign */ *--suffix = (decpt > 0 || dval == 0) ? '+' : '-'; /* Put in the e */ *--suffix = isupper(fcode) ? 'E' : 'e'; break; case 'f': /* * F-format floating point. This is * a good deal less simple than E-format. * The overall strategy will be to call * fcvt, reformat its result into buf, * and calculate how many trailing * zeroes will be required. There will * never be any leading zeroes needed. */ /* Establish default precision */ if (prec < 0) { prec = 6; } /* Fetch the value */ dval = va_arg(*args, double); /* Do the conversion */ bp = fcvt(dval, min(prec, MAXFCVT), &decpt, &sign); /* Determine the prefix */ f_merge: if (sign && decpt > -prec && *bp != '\0' && *bp != '0') { prefix = "-"; } else if (fplus) { prefix = "+"; } else if (fblank) { prefix = " "; } /* Initialize buffer pointer */ p = &buf[0]; /* Emit the digits before the decimal point */ n = decpt; k = 0; if (n <= 0) { *p++ = '0'; } else { do { if (*bp == '\0' || k >= MAXFSIG) { *p++ = '0'; } else { *p++ = *bp++; ++k; } } while (--n != 0); } /* Decide whether we need a decimal point */ if (fsharp || prec > 0) { *p++ = '.'; } /* Digits (if any) after the decimal point */ n = min(prec, MAXFCVT); rzero = prec - n; while (--n >= 0) { if (++decpt <= 0 || *bp == '\0' || k >= MAXFSIG) { *p++ = '0'; } else { *p++ = *bp++; ++k; } } bp = &buf[0]; break; case 'G': case 'g': /* * g-format. We play around a bit * and then jump into e or f, as needed. */ /* Establish default precision */ if (prec < 0) { prec = 6; } /* Fetch the value */ dval = va_arg(*args, double); /* Do the conversion */ bp = ecvt(dval, min(prec, MAXECVT), &decpt, &sign); if (dval == 0) { decpt = 1; } k = prec; if (!fsharp) { n = strlen(bp); if (n < k) { k = n; } while (k >= 1 && bp[k-1] == '0') { --k; } } if (decpt < -3 || decpt > prec) { prec = k - 1; goto e_merge; } else { prec = k - decpt; goto f_merge; } #endif case 'c': #ifdef MBCHAR_1 /* sizeof(int)>=sizeof(tchar) */ /* * A tchar arg is passed as int so we used the normal %c to specify * such an arugument. */ tcbuf[0] = va_arg(*args, int); tbp = &tcbuf[0]; tep = tbp + 1; fcode = 't'; /* Fake the rest of code. */ break; #else /* * We would have to invent another new format speficier such as "%T" to * take a tchar arg. Let's worry about when that time comes. */ /* * Following code take care of a char arg * only. */ buf[0] = va_arg(*args, int); bp = &buf[0]; p = bp + 1; break; case 'T': /* Corresponding arg is tchar. */ tcbuf[0] = va_arg(*args, tchar); tbp = &tcbuf[0]; tep = tbp + 1; fcode = 't'; /* Fake the rest of code. */ break; #endif case 's': bp = va_arg(*args, char *); if (bp == 0) { nullstr: bp = "(null)"; p = bp + strlen("(null)"); break; } if (prec < 0) { prec = MAXINT; } for (n = 0; *bp++ != '\0' && n < prec; n++) ; p = --bp; bp -= n; break; case 't': /* * Special format specifier "%t" tells * printf() to print char strings written * as tchar string. */ tbp = va_arg(*args, tchar *); if (tbp == 0) { fcode = 's'; /* Act as if it were %s. */ goto nullstr; } if (prec < 0) { prec = MAXINT; } for (n = 0; *tbp++ != 0 && n < prec; n++) ; tep = --tbp; tbp -= n; /* * Just to make the following padding * calculation not to go very crazy... */ bp = NULL; p = bp + n; break; case '\0': cp--; break; default: p = bp = &fcode; p++; break; } if (fcode != '\0') { /* Calculate number of padding blanks */ int nblank; nblank = width #if FLOAT - (rzero < 0 ? 0: rzero) - strlen(suffix) #endif - (p - bp) - (lzero < 0 ? 0 : lzero) - strlen(prefix); /* Blanks on left if required */ if (!fminus) { while (--nblank >= 0) { Putchar(' '); } } /* Prefix, if any */ while (*prefix != '\0') { Putchar(*prefix++); } /* Zeroes on the left */ while (--lzero >= 0) { Putchar('0'); } /* The value itself */ if (fcode == 't') { /* %t is special. */ while (tbp < tep) { Putchar(*tbp++); } } else { /* For rest of the cases. */ while (bp < p) { putbyte(*bp++); } } #if FLOAT /* Zeroes on the right */ while (--rzero >= 0) Putchar('0'); /* The suffix */ while (*suffix != '\0') { Putchar(*suffix++); } #endif /* Blanks on the right if required */ if (fminus) { while (--nblank >= 0) { Putchar(' '); } } } } } /* * Copyright (c) 1989, 2010, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2016 by Delphix. All rights reserved. */ /* Copyright (c) 1983, 1984, 1985, 1986, 1987, 1988, 1989 AT&T */ /* All Rights Reserved */ /* * Copyright (c) 1980 Regents of the University of California. * All rights reserved. The Berkeley Software License Agreement * specifies the terms and conditions for redistribution. */ #include #include "sh.h" /* #include */ #include #include #include "sh.tconst.h" #include #include #ifdef TRACE #include #endif /* * We use these csh(1) private versions of the select macros, (see select(3C)) * so as not to be limited by the size of struct fd_set (ie 1024). */ #define CSH_FD_SET(n, p) ((*((p) + ((n)/NFDBITS))) |= (1 << ((n) % NFDBITS))) #define CSH_FD_CLR(n, p) ((*((p) + ((n)/NFDBITS))) &= ~(1 << ((n) % NFDBITS))) #define CSH_FD_ISSET(n, p) ((*((p) + ((n)/NFDBITS))) & (1 << ((n) % NFDBITS))) #define CSH_FD_ZERO(p, n) memset((void *)(p), 0, (n)) tchar *pathlist[] = { S_usrbin /* "/usr/bin" */, S_DOT /* "." */, 0 }; tchar *dumphist[] = { S_history /* "history" */, S_h /* "-h" */, 0, 0 }; tchar *loadhist[] = { S_source /* "source" */, S_h /* "-h" */, S_NDOThistory /* "~/.history" */, 0 }; tchar HIST = '!'; tchar HISTSUB = '^'; int nofile; bool reenter; bool nverbose; bool nexececho; bool quitit; bool fast; bool batch; bool prompt = 1; bool enterhist = 0; static time_t chktim; char *err_msg; /* Error message from scanner/parser */ extern gid_t getegid(), getgid(); extern uid_t geteuid(), getuid(); extern tchar **strblktotsblk(/* char **, int */); extern void hupforegnd(void); void interactive_hup(void); void interactive_login_hup(void); void importpath(tchar *); void srccat(tchar *, tchar *); void srccat_inlogin(tchar *, tchar *); void srcunit(int, bool, bool); void rechist(void); void goodbye(void); void pintr1(bool); void process(bool); void dosource(tchar **); void mailchk(void); void printprompt(void); void sigwaiting(void); void siglwp(void); void initdesc(int, char *[]); void initdesc_x(int, char *[], int); void closem(void); void unsetfd(int); void phup(void); #ifdef TRACE FILE *trace; /* * Trace routines */ #define TRACEFILE "/tmp/trace.XXXXXX" /* * Initialize trace file. * Called from main. */ void trace_init(void) { char name[128]; char *p; strcpy(name, TRACEFILE); p = mktemp(name); trace = fopen(p, "w"); } /* * write message to trace file */ /*VARARGS1*/ void tprintf(fmt, a, b, c, d, e, f, g, h, i, j) char *fmt; { if (trace) { fprintf(trace, fmt, a, b, c, d, e, f, g, h, i, j); fflush(trace); } } #endif int main(int c, char **av) { tchar **v, *cp, *r; int f; struct sigvec osv; struct sigaction sa; tchar s_prompt[MAXHOSTNAMELEN+3]; char *c_max_var_len; int c_max_var_len_size; bool intact = 0; /* * set up the error exit, if there is an error before * this is done, it will core dump, and we don't * tolerate core dumps */ haderr = 0; setexit(); if (haderr) { /* * if were here, there was an error in the csh * startup so just punt */ printf("csh startup error, csh exiting...\n"); flush(); exitstat(); } (void) setlocale(LC_ALL, ""); #if !defined(TEXT_DOMAIN) /* Should be defined by cc -D */ #define TEXT_DOMAIN "SYS_TEST" /* Use this only if it weren't */ #endif (void) textdomain(TEXT_DOMAIN); /* Copy arguments */ v = strblktotsblk(av, c); /* * Initialize paraml list */ paraml.next = paraml.prev = ¶ml; settimes(); /* Immed. estab. timing base */ if (eq(v[0], S_aout /* "a.out" */)) /* A.out's are quittable */ quitit = 1; uid = getuid(); loginsh = **v == '-'; if (loginsh) (void) time(&chktim); /* * Move the descriptors to safe places. * The variable didfds is 0 while we have only FSH* to work with. * When didfds is true, we have 0,1,2 and prefer to use these. * * Also, setup data for csh internal file descriptor book keeping. */ initdesc(c, av); /* * Initialize the shell variables. * ARGV and PROMPT are initialized later. * STATUS is also munged in several places. * CHILD is munged when forking/waiting */ c_max_var_len_size = snprintf(NULL, 0, "%ld", MAX_VAR_LEN); c_max_var_len = (char *)xalloc(c_max_var_len_size + 1); (void) snprintf(c_max_var_len, (c_max_var_len_size + 1), "%ld", MAX_VAR_LEN); set(S_SUNW_VARLEN, strtots(NOSTR, c_max_var_len)); xfree(c_max_var_len); /* don't do globbing here, just set exact copies */ setNS(S_noglob); set(S_status /* "status" */, S_0 /* "0" */); dinit(cp = getenvs_("HOME")); /* dinit thinks that HOME==cwd in a */ /* login shell */ if (cp == NOSTR) fast++; /* No home -> can't read scripts */ else { if (strlen_(cp) >= BUFSIZ - 10) { cp = NOSTR; fast++; printf("%s\n", gettext("Pathname too long")); set(S_home /* "home" */, savestr(cp)); local_setenv(S_HOME, savestr(cp)); } set(S_home /* "home" */, savestr(cp)); } /* * Grab other useful things from the environment. * Should we grab everything?? */ if ((cp = getenvs_("USER")) != NOSTR) set(S_user /* "user" */, savestr(cp)); else { /* * If USER is not defined, set it here. */ struct passwd *pw; pw = getpwuid(getuid()); if (pw != NULL) { set(S_user, strtots((tchar *)0, pw->pw_name)); local_setenv(S_USER, strtots((tchar *)0, pw->pw_name)); } else if (loginsh) { /* Give up setting USER variable. */ printf("Warning: USER environment variable could not be set.\n"); } } if ((cp = getenvs_("TERM")) != NOSTR) set(S_term /* "term" */, savestr(cp)); /* * Re-initialize path if set in environment */ if ((cp = getenvs_("PATH")) == NOSTR) set1(S_path /* "path" */, saveblk(pathlist), &shvhed); else importpath(cp); set(S_shell /* "shell" */, S_SHELLPATH); doldol = putn(getpid()); /* For $$ */ /* restore globbing until the user says otherwise */ unsetv(S_noglob); /* * Record the interrupt states from the parent process. * If the parent is non-interruptible our hand must be forced * or we (and our children) won't be either. * Our children inherit termination from our parent. * We catch it only if we are the login shell. */ /* parents interruptibility */ (void) sigvec(SIGINT, (struct sigvec *)0, &osv); parintr = osv.sv_handler; /* parents terminability */ (void) sigvec(SIGTERM, (struct sigvec *)0, &osv); parterm = osv.sv_handler; _signal(SIGLWP, siglwp); _signal(SIGWAITING, sigwaiting); if (loginsh) { (void) signal(SIGHUP, phup); /* exit processing on HUP */ (void) signal(SIGXCPU, phup); /* ...and on XCPU */ (void) signal(SIGXFSZ, phup); /* ...and on XFSZ */ } /* * Process the arguments. * * Note that processing of -v/-x is actually delayed till after * script processing. */ c--, v++; while (c > 0 && (cp = v[0])[0] == '-' && *++cp != '\0' && !batch) { do switch (*cp++) { case 'b': /* -b Next arg is input file */ batch++; break; case 'c': /* -c Command input from arg */ if (c == 1) exit(0); c--, v++; arginp = v[0]; prompt = 0; nofile++; cflg++; break; case 'e': /* -e Exit on any error */ exiterr++; break; case 'f': /* -f Fast start */ fast++; break; case 'i': /* -i Interactive, even if !intty */ intact++; nofile++; break; case 'n': /* -n Don't execute */ noexec++; break; case 'q': /* -q (Undoc'd) ... die on quit */ quitit = 1; break; case 's': /* -s Read from std input */ nofile++; break; case 't': /* -t Read one line from input */ onelflg = 2; prompt = 0; nofile++; break; #ifdef TRACE case 'T': /* -T trace switch on */ trace_init(); break; #endif case 'v': /* -v Echo hist expanded input */ nverbose = 1; /* ... later */ break; case 'x': /* -x Echo just before execution */ nexececho = 1; /* ... later */ break; case 'V': /* -V Echo hist expanded input */ setNS(S_verbose /* "verbose" */); /* NOW! */ break; case 'X': /* -X Echo just before execution */ setNS(S_echo /* "echo" */); /* NOW! */ break; } while (*cp); v++, c--; } if (quitit) /* With all due haste, for debugging */ (void) signal(SIGQUIT, SIG_DFL); /* * Unless prevented by -c, -i, -s, or -t, if there * are remaining arguments the first of them is the name * of a shell file from which to read commands. */ if (!batch && (uid != geteuid() || getgid() != getegid())) { errno = EACCES; child++; /* So this ... */ Perror(S_csh /* "csh" */); /* ... doesn't return */ } if (nofile == 0 && c > 0) { nofile = open_(v[0], 0); if (nofile < 0) { child++; /* So this ... */ Perror(v[0]); /* ... doesn't return */ } file = v[0]; SHIN = dmove(nofile, FSHIN); /* Replace FSHIN */ (void) fcntl(SHIN, F_SETFD, 1); prompt = 0; c--, v++; } /* * Consider input a tty if it really is or we are interactive. */ intty = intact || isatty(SHIN); /* * Decide whether we should play with signals or not. * If we are explicitly told (via -i, or -) or we are a login * shell (arg0 starts with -) or the input and output are both * the ttys("csh", or "csh/dev/ttyx") * Note that in only the login shell is it likely that parent * may have set signals to be ignored */ if (loginsh || intact || intty && isatty(SHOUT)) setintr = 1; #ifdef TELL settell(); #endif /* * Save the remaining arguments in argv. */ setq(S_argv /* "argv" */, copyblk(v), &shvhed); /* * Set up the prompt. */ if (prompt) { gethostname_(s_prompt, MAXHOSTNAMELEN); strcat_(s_prompt, uid == 0 ? S_SHARPSP /* "# " */ : S_PERSENTSP /* "% " */); set(S_prompt /* "prompt" */, s_prompt); } /* * If we are an interactive shell, then start fiddling * with the signals; this is a tricky game. */ shpgrp = getpgid(0); opgrp = tpgrp = -1; if (setintr) { **av = '-'; if (!quitit) /* Wary! */ (void) signal(SIGQUIT, SIG_IGN); (void) signal(SIGINT, pintr); (void) sigblock(sigmask(SIGINT)); (void) signal(SIGTERM, SIG_IGN); /* * Explicitly terminate foreground jobs and exit if we are * interactive shell */ if (loginsh) { (void) signal(SIGHUP, interactive_login_hup); } else { (void) signal(SIGHUP, interactive_hup); } if (quitit == 0 && arginp == 0) { (void) signal(SIGTSTP, SIG_IGN); (void) signal(SIGTTIN, SIG_IGN); (void) signal(SIGTTOU, SIG_IGN); /* * Wait till in foreground, in case someone * stupidly runs * csh & * dont want to try to grab away the tty. */ if (isatty(FSHDIAG)) f = FSHDIAG; else if (isatty(FSHOUT)) f = FSHOUT; else if (isatty(OLDSTD)) f = OLDSTD; else f = -1; retry: if (ioctl(f, TIOCGPGRP, (char *)&tpgrp) == 0 && tpgrp != -1) { if (tpgrp != shpgrp) { void (*old)() = (void (*)()) signal(SIGTTIN, SIG_DFL); (void) kill(0, SIGTTIN); (void) signal(SIGTTIN, old); goto retry; } opgrp = shpgrp; shpgrp = getpid(); tpgrp = shpgrp; (void) setpgid(0, shpgrp); (void) ioctl(f, TIOCSPGRP, (char *)&shpgrp); (void) fcntl(dcopy(f, FSHTTY), F_SETFD, 1); } else { printf("Warning: no access to tty; thus no job control in this shell...\n"); tpgrp = -1; } } } if (setintr == 0 && parintr == SIG_DFL) setintr++; /* * Set SIGCHLD handler, making sure that reads restart after it runs. */ sigemptyset(&sa.sa_mask); sa.sa_handler = pchild; sa.sa_flags = SA_RESTART; (void) sigaction(SIGCHLD, &sa, (struct sigaction *)NULL); /* * Set an exit here in case of an interrupt or error reading * the shell start-up scripts. */ setexit(); haderr = 0; /* In case second time through */ if (!fast && reenter == 0) { reenter++; /* * If this is a login csh, and /etc/.login exists, * source /etc/.login first. */ if (loginsh) { tchar tmp_etc[4+1]; /* strlen("/etc")+1 */ tchar tmp_login[7+1]; /* strlen("/.login")+1 */ strtots(tmp_etc, "/etc"); strtots(tmp_login, "/.login"); srccat_inlogin(tmp_etc, tmp_login); } /* Will have value("home") here because set fast if don't */ srccat(value(S_home /* "home" */), S_SLADOTcshrc /* "/.cshrc" */); /* Hash path */ if (!fast && !arginp && !onelflg && !havhash) dohash(xhash); /* * Reconstruct the history list now, so that it's * available from within .login. */ dosource(loadhist); if (loginsh) { srccat_inlogin(value(S_home /* "home" */), S_SLADOTlogin /* "/.login" */); } /* * To get cdpath hashing $cdpath must have a * value, not $CDPATH. So if after reading * the startup files ( .cshrc ), and * user has specified a value for cdpath, then * cache $cdpath paths. xhash2 is global array * for $cdpath caching. */ if (!fast && !arginp && !onelflg && !havhash2) dohash(xhash2); } /* * Now are ready for the -v and -x flags */ if (nverbose) setNS(S_verbose /* "verbose" */); if (nexececho) setNS(S_echo /* "echo" */); /* * All the rest of the world is inside this call. * The argument to process indicates whether it should * catch "error unwinds". Thus if we are a interactive shell * our call here will never return by being blown past on an error. */ process(setintr); /* * Mop-up. */ if (loginsh) { printf("logout\n"); (void) close(SHIN); /* No need for unsetfd(). */ child++; goodbye(); } rechist(); exitstat(); } void untty(void) { if (tpgrp > 0) { (void) setpgid(0, opgrp); (void) ioctl(FSHTTY, TIOCSPGRP, (char *)&opgrp); } } void importpath(tchar *cp) { int i = 0; tchar *dp; tchar **pv; int c; static tchar dot[2] = {'.', 0}; for (dp = cp; *dp; dp++) if (*dp == ':') i++; /* * i+2 where i is the number of colons in the path. * There are i+1 directories in the path plus we need * room for a zero terminator. */ pv = (tchar **)xcalloc((unsigned)(i + 2), sizeof (tchar **)); dp = cp; i = 0; if (*dp) for (;;) { if ((c = *dp) == ':' || c == 0) { *dp = 0; pv[i++] = savestr(*cp ? cp : dot); if (c) { cp = dp + 1; *dp = ':'; } else break; } dp++; } pv[i] = 0; set1(S_path /* "path" */, pv, &shvhed); } /* * Source to the file which is the catenation of the argument names. */ void srccat(tchar *cp, tchar *dp) { tchar *ep = strspl(cp, dp); int unit = dmove(open_(ep, 0), -1); (void) fcntl(unit, F_SETFD, 1); xfree(ep); #ifdef INGRES srcunit(unit, 0, 0); #else srcunit(unit, 1, 0); #endif } /* * Source to the file which is the catenation of the argument names. * This one does not check the ownership. */ void srccat_inlogin(tchar *cp, tchar *dp) { tchar *ep = strspl(cp, dp); int unit = dmove(open_(ep, 0), -1); (void) fcntl(unit, F_SETFD, 1); xfree(ep); srcunit(unit, 0, 0); } /* * Source to a unit. If onlyown it must be our file or our group or * we don't chance it. This occurs on ".cshrc"s and the like. */ void srcunit(int unit, bool onlyown, bool hflg) { /* We have to push down a lot of state here */ /* All this could go into a structure */ int oSHIN = -1, oldintty = intty; struct whyle *oldwhyl = whyles; tchar *ogointr = gointr, *oarginp = arginp; tchar *oevalp = evalp, **oevalvec = evalvec; int oonelflg = onelflg; bool oenterhist = enterhist; tchar OHIST = HIST; #ifdef TELL bool otell = cantell; #endif struct Bin saveB; /* The (few) real local variables */ jmp_buf oldexit; int reenter, omask; if (unit < 0) return; if (didfds) donefds(); if (onlyown) { struct stat stb; if (fstat(unit, &stb) < 0 || (stb.st_uid != uid && stb.st_gid != getgid())) { (void) close(unit); unsetfd(unit); return; } } /* * There is a critical section here while we are pushing down the * input stream since we have stuff in different structures. * If we weren't careful an interrupt could corrupt SHIN's Bin * structure and kill the shell. * * We could avoid the critical region by grouping all the stuff * in a single structure and pointing at it to move it all at * once. This is less efficient globally on many variable references * however. */ getexit(oldexit); reenter = 0; if (setintr) omask = sigblock(sigmask(SIGINT)); setexit(); reenter++; if (reenter == 1) { /* Setup the new values of the state stuff saved above */ copy((char *)&saveB, (char *)&B, sizeof (saveB)); fbuf = (tchar **) 0; fseekp = feobp = fblocks = 0; oSHIN = SHIN, SHIN = unit, arginp = 0, onelflg = 0; intty = isatty(SHIN), whyles = 0, gointr = 0; evalvec = 0; evalp = 0; enterhist = hflg; if (enterhist) HIST = '\0'; /* * Now if we are allowing commands to be interrupted, * we let ourselves be interrupted. */ if (setintr) (void) sigsetmask(omask); #ifdef TELL settell(); #endif process(0); /* 0 -> blow away on errors */ } if (setintr) (void) sigsetmask(omask); if (oSHIN >= 0) { int i; /* We made it to the new state... free up its storage */ /* This code could get run twice but xfree doesn't care */ for (i = 0; i < fblocks; i++) xfree(fbuf[i]); xfree((char *)fbuf); /* Reset input arena */ copy((char *)&B, (char *)&saveB, sizeof (B)); (void) close(SHIN), SHIN = oSHIN; unsetfd(SHIN); arginp = oarginp, onelflg = oonelflg; evalp = oevalp, evalvec = oevalvec; intty = oldintty, whyles = oldwhyl, gointr = ogointr; if (enterhist) HIST = OHIST; enterhist = oenterhist; #ifdef TELL cantell = otell; #endif } resexit(oldexit); /* * If process reset() (effectively an unwind) then * we must also unwind. */ if (reenter >= 2) error(NULL); } void rechist(void) { tchar buf[BUFSIZ]; int fp, ftmp, oldidfds; if (!fast) { if (value(S_savehist /* "savehist" */)[0] == '\0') return; (void) strcpy_(buf, value(S_home /* "home" */)); (void) strcat_(buf, S_SLADOThistory /* "/.history" */); fp = creat_(buf, 0666); if (fp == -1) return; oldidfds = didfds; didfds = 0; ftmp = SHOUT; SHOUT = fp; (void) strcpy_(buf, value(S_savehist /* "savehist" */)); dumphist[2] = buf; dohist(dumphist); (void) close(fp); unsetfd(fp); SHOUT = ftmp; didfds = oldidfds; } } void goodbye(void) { if (loginsh) { (void) signal(SIGQUIT, SIG_IGN); (void) signal(SIGINT, SIG_IGN); (void) signal(SIGTERM, SIG_IGN); setintr = 0; /* No interrupts after "logout" */ if (adrof(S_home /* "home" */)) srccat(value(S_home /* "home" */), S_SLADOTlogout /* "/.logout" */); } rechist(); exitstat(); } void exitstat(void) { #ifdef PROF monitor(0); #endif /* * Note that if STATUS is corrupted (i.e. getn bombs) * then error will exit directly because we poke child here. * Otherwise we might continue unwarrantedly (sic). */ child++; untty(); exit(getn(value(S_status /* "status" */))); } /* * in the event of a HUP we want to save the history */ void phup(void) { rechist(); exit(1); } void interactive_hup(void) { hupforegnd(); exit(1); } void interactive_login_hup(void) { rechist(); hupforegnd(); exit(1); } tchar *jobargv[2] = { S_jobs /* "jobs" */, 0 }; /* * Catch an interrupt, e.g. during lexical input. * If we are an interactive shell, we reset the interrupt catch * immediately. In any case we drain the shell output, * and finally go through the normal error mechanism, which * gets a chance to make the shell go away. */ void pintr(void) { pintr1(1); } void pintr1(bool wantnl) { tchar **v; int omask; omask = sigblock(0); if (setintr) { (void) sigsetmask(omask & ~sigmask(SIGINT)); if (pjobs) { pjobs = 0; printf("\n"); dojobs(jobargv); bferr("Interrupted"); } } (void) sigsetmask(omask & ~sigmask(SIGCHLD)); draino(); /* * If we have an active "onintr" then we search for the label. * Note that if one does "onintr -" then we shan't be interruptible * so we needn't worry about that here. */ if (gointr) { search(ZGOTO, 0, gointr); if (v = pargv) pargv = 0, blkfree(v); if (v = gargv) gargv = 0, blkfree(v); reset(); } else if (intty && wantnl) printf("\n"); /* Some like this, others don't */ error(NULL); } /* * Process is the main driving routine for the shell. * It runs all command processing, except for those within { ... } * in expressions (which is run by a routine evalav in sh.exp.c which * is a stripped down process), and `...` evaluation which is run * also by a subset of this code in sh.glob.c in the routine backeval. * * The code here is a little strange because part of it is interruptible * and hence freeing of structures appears to occur when none is necessary * if this is ignored. * * Note that if catch is not set then we will unwind on any error. * If an end-of-file occurs, we return. */ void process(bool catch) { jmp_buf osetexit; struct command *t; getexit(osetexit); for (;;) { pendjob(); freelex(¶ml); paraml.next = paraml.prev = ¶ml; paraml.word = S_ /* "" */; t = 0; setexit(); justpr = enterhist; /* execute if not entering history */ /* * Interruptible during interactive reads */ if (setintr) (void) sigsetmask(sigblock(0) & ~sigmask(SIGINT)); /* * For the sake of reset() */ freelex(¶ml), freesyn(t), t = 0; if (haderr) { if (!catch) { /* unwind */ doneinp = 0; resexit(osetexit); reset(); } haderr = 0; /* * Every error is eventually caught here or * the shell dies. It is at this * point that we clean up any left-over open * files, by closing all but a fixed number * of pre-defined files. Thus routines don't * have to worry about leaving files open due * to deeper errors... they will get closed here. */ closem(); continue; } if (doneinp) { doneinp = 0; break; } if (chkstop) chkstop--; if (neednote) pnote(); if (intty && prompt && evalvec == 0) { mailchk(); /* * If we are at the end of the input buffer * then we are going to read fresh stuff. * Otherwise, we are rereading input and don't * need or want to prompt. */ if (fseekp == feobp) printprompt(); } err_msg = NULL; /* * Echo not only on VERBOSE, but also with history expansion. */ if (lex(¶ml) && intty || adrof(S_verbose /* "verbose" */)) { haderr = 1; prlex(¶ml); haderr = 0; } /* * The parser may lose space if interrupted. */ if (setintr) (void) sigblock(sigmask(SIGINT)); /* * Save input text on the history list if * reading in old history, or it * is from the terminal at the top level and not * in a loop. */ if (enterhist || catch && intty && !whyles) savehist(¶ml); /* * Print lexical error messages, except when sourcing * history lists. */ if (!enterhist && err_msg) error("%s", gettext(err_msg)); /* * If had a history command :p modifier then * this is as far as we should go */ if (justpr) reset(); alias(¶ml); /* * Parse the words of the input into a parse tree. */ t = syntax(paraml.next, ¶ml, 0); if (err_msg) error("%s", gettext(err_msg)); /* * Execute the parse tree */ { /* * POSIX requires SIGCHLD to be held * until all processes have joined the * process group in order to avoid race * condition. */ int omask; omask = sigblock(sigmask(SIGCHLD)); execute(t, tpgrp); (void) sigsetmask(omask &~ sigmask(SIGCHLD)); } if (err_msg) error("%s", gettext(err_msg)); /* * Made it! */ freelex(¶ml), freesyn(t); } resexit(osetexit); } void dosource(tchar **t) { tchar *f; int u; bool hflg = 0; tchar buf[BUFSIZ]; t++; if (*t && eq(*t, S_h /* "-h" */)) { if (*++t == NOSTR) bferr("Too few arguments."); hflg++; } (void) strcpy_(buf, *t); f = globone(buf); u = dmove(open_(f, 0), -1); xfree(f); freelex(¶ml); if (u < 0 && !hflg) Perror(f); (void) fcntl(u, F_SETFD, 1); srcunit(u, 0, hflg); } /* * Check for mail. * If we are a login shell, then we don't want to tell * about any mail file unless its been modified * after the time we started. * This prevents us from telling the user things they already * know, since the login program insists on saying * "You have mail." */ void mailchk(void) { struct varent *v; tchar **vp; time_t t; int intvl, cnt; struct stat stb; bool new; v = adrof(S_mail /* "mail" */); if (v == 0) return; (void) time(&t); vp = v->vec; cnt = blklen(vp); intvl = (cnt && number(*vp)) ? (--cnt, getn(*vp++)) : MAILINTVL; if (intvl < 1) intvl = 1; if (chktim + intvl > t) return; for (; *vp; vp++) { if (stat_(*vp, &stb) < 0) continue; new = stb.st_mtime > time0.tv_sec; if (stb.st_size == 0 || stb.st_atime >= stb.st_mtime || (stb.st_atime <= chktim && stb.st_mtime <= chktim) || loginsh && !new) continue; if (cnt == 1) printf("You have %smail.\n", new ? "new " : ""); else printf("%s in %t.\n", new ? "New mail" : "Mail", *vp); } chktim = t; } /* * Extract a home directory from the password file * The argument points to a buffer where the name of the * user whose home directory is sought is currently. * We write the home directory of the user back there. */ int gethdir(tchar *home) { /* getpwname will not be modified, so we need temp. buffer */ char home_str[BUFSIZ]; tchar home_ts[BUFSIZ]; struct passwd *pp /* = getpwnam(home) */; pp = getpwnam(tstostr(home_str, home)); if (pp == 0) return (1); (void) strcpy_(home, strtots(home_ts, pp->pw_dir)); return (0); } #if 0 void #ifdef PROF done(int i) #else exit(int i) #endif { untty(); _exit(i); } #endif void printprompt(void) { tchar *cp; if (!whyles) { /* * Print the prompt string */ for (cp = value(S_prompt /* "prompt" */); *cp; cp++) if (*cp == HIST) printf("%d", eventno + 1); else { if (*cp == '\\' && cp[1] == HIST) cp++; Putchar(*cp | QUOTE); } } else /* * Prompt for forward reading loop * body content. */ printf("? "); flush(); } /* * Save char * block. */ tchar ** strblktotsblk(char **v, int num) { tchar **newv = (tchar **)xcalloc((unsigned)(num+ 1), sizeof (tchar **)); tchar **onewv = newv; while (*v && num--) *newv++ = strtots(NOSTR, *v++); *newv = 0; return (onewv); } void sigwaiting(void) { _signal(SIGWAITING, sigwaiting); } void siglwp(void) { _signal(SIGLWP, siglwp); } /* * Following functions and data are used for csh to do its * file descriptors book keeping. */ static int *fdinuse = NULL; /* The list of files opened by csh */ static int nbytesused = 0; /* no of bytes allocated to fdinuse */ static int max_fd = 0; /* The maximum descriptor in fdinuse */ static int my_pid; /* The process id set in initdesc() */ static int NoFile = NOFILE; /* The number of files I can use. */ /* * Get the number of files this csh can use. * * Move the initial descriptors to their eventual * resting places, closing all other units. * * Also, reserve 0/1/2, so NIS+ routines do not get * hold of them. And initialize fdinuse list and set * the current process id. * * If this csh was invoked from setuid'ed script file, * do not close the third argument passed. The file * must be one of /dev/fd/0,1,2,,, * (execv() always passes three arguments when it execs a script * file in a form of #! /bin/csh -b.) * * If is_reinit is set in initdesc_x(), then we only close the file * descriptors that we actually opened (as recorded in fdinuse). */ void initdesc(int argc, char *argv[]) { initdesc_x(argc, argv, 0); } void reinitdesc(int argc, char *argv[]) { initdesc_x(argc, argv, 1); } /* * Callback functions for closing all file descriptors. */ static int close_except(void *cd, int fd) { int script_fd = *(int *)cd; if (fd >= 3 && fd < NoFile && fd != script_fd) (void) close(fd); return (0); } static int close_inuse(void *cd, int fd) { int script_fd = *(int *)cd; if (fd >= 3 && fd < NoFile && fd != script_fd && CSH_FD_ISSET(fd, fdinuse)) { (void) close(fd); unsetfd(fd); } return (0); } void initdesc_x(int argc, char *argv[], int is_reinit) { int script_fd = -1; struct stat buf; struct rlimit rlp; /* * Get pid of this shell */ my_pid = getpid(); /* * Get the hard limit numbers of descriptors * this csh can use. */ if (getrlimit(RLIMIT_NOFILE, &rlp) == 0) NoFile = rlp.rlim_cur; /* * If this csh was invoked for executing setuid script file, * the third argument passed is the special file name * which should not be closed. This special file name is * in the form /dev/fd/X. */ if (argc >= 3) if (sscanf(argv[2], "/dev/fd/%d", &script_fd) != 1) script_fd = -1; else /* Make sure to close this file on exec. */ fcntl(script_fd, F_SETFD, 1); if (fdinuse == NULL) { nbytesused = sizeof (int) * howmany(NoFile, sizeof (int) * NBBY); fdinuse = (int *)xalloc(nbytesused); } /* * Close all files except 0/1/2 to get a clean * file descritor space. */ if (!is_reinit) (void) fdwalk(close_except, &script_fd); else (void) fdwalk(close_inuse, &script_fd); didfds = 0; /* 0, 1, 2 aren't set up */ if (fstat(0, &buf) < 0) open("/dev/null", 0); (void) fcntl(SHIN = dcopy(0, FSHIN), F_SETFD, 1); (void) fcntl(SHOUT = dcopy(1, FSHOUT), F_SETFD, 1); (void) fcntl(SHDIAG = dcopy(2, FSHDIAG), F_SETFD, 1); (void) fcntl(OLDSTD = dcopy(SHIN, FOLDSTD), F_SETFD, 1); /* * Open 0/1/2 to avoid Nis+ functions to pick them up. * Now, 0/1/2 are saved, close them and open them. */ close(0); close(1); close(2); open("/dev/null", 0); dup(0); dup(0); /* * Clear fd_set mask */ if (!is_reinit) CSH_FD_ZERO(fdinuse, nbytesused); } /* * This routine is called after an error to close up * any units which may have been left open accidentally. * * You only need to remove files in fdinuse list. * After you have removed the files, you can clear the * list and max_fd. */ void closem(void) { int f; for (f = 3; f <= max_fd; f++) { if (CSH_FD_ISSET(f, fdinuse) && f != SHIN && f != SHOUT && f != SHDIAG && f != OLDSTD && f != FSHTTY) close(f); } CSH_FD_ZERO(fdinuse, nbytesused); max_fd = 0; } /* * Reset my_pid when a new process is created. Only call this * if you want the process to affect fdinuse (e.g., fork, but * not vfork). */ void new_process(void) { my_pid = getpid(); } /* * Whenever Csh open/create/dup/pipe a file or files, * Csh keeps track of its open files. The open files * are kept in "fdinuse, Fd In Use" list. * * When a file descriptor is newly allocated, setfd() is * used to mark the fact in "fdinuse" list. * For example, * fd = open("newfile", 0); * setfd(fd); * * When a file is freed by close() function, unsetfd() is * used to remove the fd from "fdinuse" list. * For example, * close(fd); * unsetfd(fd); */ void setfd(int fd) { /* * Because you want to avoid * conflict due to vfork(). */ if (my_pid != getpid()) return; if (fd >= NoFile || fd < 0) return; if (fd > max_fd) max_fd = fd; CSH_FD_SET(fd, fdinuse); } void unsetfd(int fd) { int i; /* * Because you want to avoid * conflict due to vfork(). */ if (my_pid != getpid()) return; if (fd >= NoFile || fd < 0) return; CSH_FD_CLR(fd, fdinuse); if (fd == max_fd) { for (i = max_fd-1; i >= 3; i--) if (CSH_FD_ISSET(i, fdinuse)) { max_fd = i; return; } max_fd = 0; } } /* * Copyright 1990 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ /* Copyright (c) 1983, 1984, 1985, 1986, 1987, 1988, 1989 AT&T */ /* All Rights Reserved */ /* * Copyright (c) 1980 Regents of the University of California. * All rights reserved. The Berkeley Software License Agreement * specifies the terms and conditions for redistribution. */ #include "sh.char.h" unsigned int Z; unsigned short _cmap[128] = { /* nul soh stx etx */ 0, 0, 0, 0, /* eot enq ack bel */ 0, 0, 0, 0, /* bs ht nl vt */ 0, _SP|_META, _NL|_META, 0, /* np cr so si */ 0, 0, 0, 0, /* dle dc1 dc2 dc3 */ 0, 0, 0, 0, /* dc4 nak syn etb */ 0, 0, 0, 0, /* can em sub esc */ 0, 0, 0, 0, /* fs gs rs us */ 0, 0, 0, 0, /* sp ! " # */ _SP|_META, 0, _Q, _META, /* $ % & ' */ _DOL, 0, _META, _Q, /* ( ) * + */ _META, _META, _GLOB, 0, /* , - . / */ 0, 0, 0, 0, /* 0 1 2 3 */ _DIG, _DIG, _DIG, _DIG, /* 4 5 6 7 */ _DIG, _DIG, _DIG, _DIG, /* 8 9 : ; */ _DIG, _DIG, 0, _META, /* < = > ? */ _META, 0, _META, _GLOB, /* @ A B C */ 0, _LET, _LET, _LET, /* D E F G */ _LET, _LET, _LET, _LET, /* H I J K */ _LET, _LET, _LET, _LET, /* L M N O */ _LET, _LET, _LET, _LET, /* P Q R S */ _LET, _LET, _LET, _LET, /* T U V W */ _LET, _LET, _LET, _LET, /* X Y Z [ */ _LET, _LET, _LET, _GLOB, /* \ ] ^ _ */ _ESC, 0, 0, _LET, /* ` a b c */ _Q1|_GLOB, _LET, _LET, _LET, /* d e f g */ _LET, _LET, _LET, _LET, /* h i j k */ _LET, _LET, _LET, _LET, /* l m n o */ _LET, _LET, _LET, _LET, /* p q r s */ _LET, _LET, _LET, _LET, /* t u v w */ _LET, _LET, _LET, _LET, /* x y z { */ _LET, _LET, _LET, _GLOB, /* | } ~ del */ _META, 0, 0, 0, }; /* * Copyright 1997 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ /* Copyright (c) 1983, 1984, 1985, 1986, 1987, 1988, 1989 AT&T */ /* All Rights Reserved */ /* * Copyright (c) 1980 Regents of the University of California. * All rights reserved. The Berkeley Software License Agreement * specifies the terms and conditions for redistribution. */ /* * Macros to classify characters. */ #ifdef MBCHAR #include #include #define isauxspZ (!isascii(Z)&&!(Z"E)&&iswspace(Z)) #define isauxsp(c) (Z=((unsigned)(c)), isauxspZ) /* Regocnizes non-ASCII space characters. */ #else #include /* macros of macros to reduce further #ifdef MBCHAR later. Please be patient!*/ #define iswdigit(c) isdigit(c) #define iswalpha(c) isalpha(c) #define isphonogram(c) 0 #define isideogram(c) 0 #define isauxsp(c) 0 #define isauxspZ 0 #endif extern unsigned short _cmap[];/* Defined in sh.char.c */ extern unsigned int Z; /* A place to save macro arg to avoid side-effect!*/ #define _Q 0x01 /* '" */ #define _Q1 0x02 /* ` */ #define _SP 0x04 /* space and tab */ #define _NL 0x08 /* \n */ #define _META 0x10 /* lex meta characters, sp #'`";&<>()|\t\n */ #define _GLOB 0x20 /* glob characters, *?{[` */ #define _ESC 0x40 /* \ */ #define _DOL 0x80 /* $ */ #define _DIG 0x100 /* 0-9 */ #define _LET 0x200 /* a-z, A-Z, _ NO LONGER OF REAL USE. */ #define quoted(c) ((unsigned)(c) & QUOTE) #define cmapZ(bits) (isascii(Z)?(_cmap[Z] & (bits)):0) #define cmap(c, bits) (Z=((unsigned)(c)), cmapZ(bits)) #define isglob(c) cmap(c, _GLOB) #define ismeta(c) cmap(c, _META) #define digit(c) cmap(c, _DIG) #define issp(c) (Z=((unsigned)(c)), cmapZ( _SP)||isauxspZ) /*WAS isspace(c)*/ #define isspnl(c) (Z=((unsigned)(c)), cmapZ( _SP|_NL)||isauxspZ) #define letter(c) \ (Z=((unsigned)(c)), !quoted(Z)&&(iswalpha(Z)||((Z)=='_')\ ||isphonogram(Z)||isideogram(Z))) #define alnum(c) \ (Z=((unsigned)(c)), !quoted(Z)&&(iswalpha(Z)||((Z)=='_')\ ||iswdigit(Z)||isphonogram(Z)||isideogram(Z))) /* * Copyright 2005 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. * Copyright (c) 2016 by Delphix. All rights reserved. */ /* Copyright (c) 1983, 1984, 1985, 1986, 1987, 1988, 1989 AT&T */ /* All Rights Reserved */ /* * Copyright (c) 1980 Regents of the University of California. * All rights reserved. The Berkeley Software License Agreement * specifies the terms and conditions for redistribution. */ #include "sh.h" #include "sh.dir.h" #include "sh.tconst.h" /* * C Shell - directory management */ struct directory *dfind(tchar *); tchar *dfollow(tchar *); tchar *dcanon(tchar *, tchar *); void dtildepr(tchar *, tchar *); void dfree(struct directory *); void dnewcwd(struct directory *); int didchdir; bool loginsh; bool havhash2; struct varent shvhed; struct directory *dcwd; struct directory dhead; /* "head" of loop */ int printd; /* force name to be printed */ static tchar *fakev[] = { S_dirs, NOSTR }; char xhash2[HSHSIZ / 8]; /* * dinit - initialize current working directory */ void dinit(tchar *hp) { tchar *cp; struct directory *dp; tchar path[MAXPATHLEN]; #ifdef TRACE tprintf("TRACE- dinit()\n"); #endif /* * If this is a login shell, we should have a home directory. But, * if we got here via 'su - ' where the user has no directory * in their passwd file, then su has passed HOME=, so hp is * non-null, but has zero length. Thus, we do not know the current * working directory based on the home directory. */ if (loginsh && hp && *hp) cp = hp; else { cp = getwd_(path); if (cp == NULL) { printf("Warning: cannot determine current directory\n"); cp = S_DOT; } } dp = (struct directory *)xcalloc(sizeof (struct directory), 1); dp->di_name = savestr(cp); dp->di_count = 0; dhead.di_next = dhead.di_prev = dp; dp->di_next = dp->di_prev = &dhead; printd = 0; dnewcwd(dp); } /* * dodirs - list all directories in directory loop */ void dodirs(tchar **v) { struct directory *dp; bool lflag; tchar *hp = value(S_home); #ifdef TRACE tprintf("TRACE- dodirs()\n"); #endif if (*hp == '\0') hp = NOSTR; if (*++v != NOSTR) if (eq(*v, S_MINl /* "-l" */) && *++v == NOSTR) lflag = 1; else error("Usage: dirs [ -l ]"); else lflag = 0; dp = dcwd; do { if (dp == &dhead) continue; if (!lflag && hp != NOSTR) { dtildepr(hp, dp->di_name); } else printf("%t", dp->di_name); printf(" "); } while ((dp = dp->di_prev) != dcwd); printf("\n"); } void dtildepr(tchar *home, tchar *dir) { #ifdef TRACE tprintf("TRACE- dtildepr()\n"); #endif if (!eq(home, S_SLASH /* "/" */) && prefix(home, dir)) printf("~%t", dir + strlen_(home)); else printf("%t", dir); } /* * dochngd - implement chdir command. */ void dochngd(tchar **v) { tchar *cp; struct directory *dp; #ifdef TRACE tprintf("TRACE- dochngd()\n"); #endif printd = 0; if (*++v == NOSTR) { if ((cp = value(S_home)) == NOSTR || *cp == 0) bferr("No home directory"); if (chdir_(cp) < 0) bferr("Can't change to home directory"); cp = savestr(cp); } else if ((dp = dfind(*v)) != 0) { printd = 1; if (chdir_(dp->di_name) < 0) Perror(dp->di_name); dcwd->di_prev->di_next = dcwd->di_next; dcwd->di_next->di_prev = dcwd->di_prev; goto flushcwd; } else cp = dfollow(*v); dp = (struct directory *)xcalloc(sizeof (struct directory), 1); dp->di_name = cp; dp->di_count = 0; dp->di_next = dcwd->di_next; dp->di_prev = dcwd->di_prev; dp->di_prev->di_next = dp; dp->di_next->di_prev = dp; flushcwd: dfree(dcwd); dnewcwd(dp); } /* * dfollow - change to arg directory; fall back on cdpath if not valid */ tchar * dfollow(tchar *cp) { tchar *dp; struct varent *c; int cdhashval, cdhashval1; int index; int slash; /* slashes in the argument */ tchar *fullpath; tchar *slashcp; /* cp string prepended with a slash */ #ifdef TRACE tprintf("TRACE- dfollow()\n"); #endif cp = globone(cp); if (chdir_(cp) >= 0) goto gotcha; /* * If the directory argument has a slash in it, * for example, directory/directory, then can't * find that in the cache table. */ slash = any('/', cp); /* * Try interpreting wrt successive components of cdpath. * cdpath caching is turned off or directory argument * has a slash in it. */ if (cp[0] != '/' && !prefix(S_DOTSLA /* "./" */, cp) && !prefix(S_DOTDOTSLA /* "../" */, cp) && (c = adrof(S_cdpath)) && (!havhash2 || slash)) { tchar **cdp; tchar *p; tchar buf[MAXPATHLEN]; for (cdp = c->vec; *cdp; cdp++) { for (dp = buf, p = *cdp; *dp++ = *p++; ) ; dp[-1] = '/'; for (p = cp; *dp++ = *p++; ) ; if (chdir_(buf) >= 0) { printd = 1; xfree(cp); cp = savestr(buf); goto gotcha; } } } /* cdpath caching turned on */ if (cp[0] != '/' && !prefix(S_DOTSLA /* "./" */, cp) && !prefix(S_DOTDOTSLA /* "../" */, cp) && (c = adrof(S_cdpath)) && havhash2 && !slash) { tchar **pv; /* If no cdpath or no paths in cdpath, leave */ if (c == 0 || c->vec[0] == 0) pv = justabs; else pv = c->vec; slashcp = strspl(S_SLASH, cp); cdhashval = hashname(cp); /* index points to next path component to test */ index = 0; /* * Look at each path in cdpath until get a match. * Only look at those path beginning with a slash */ do { /* only check cache for absolute pathnames */ if (pv[0][0] == '/') { cdhashval1 = hash(cdhashval, index); if (bit(xhash2, cdhashval1)) { /* * concatenate found path with * arg directory */ fullpath = strspl(*pv, slashcp); if (chdir_(fullpath) >= 0) { printd = 1; xfree(cp); cp = savestr(fullpath); xfree(slashcp); xfree(fullpath); goto gotcha; } } } /* * relative pathnames are not cached, and must be * checked manually */ else { tchar *p; tchar buf[MAXPATHLEN]; for (dp = buf, p = *pv; *dp++ = *p++; ) ; dp[-1] = '/'; for (p = cp; *dp++ = *p++; ) ; if (chdir_(buf) >= 0) { printd = 1; xfree(cp); cp = savestr(buf); xfree(slashcp); goto gotcha; } } pv++; index++; } while (*pv); } /* * Try dereferencing the variable named by the argument. */ dp = value(cp); if ((dp[0] == '/' || dp[0] == '.') && chdir_(dp) >= 0) { xfree(cp); cp = savestr(dp); printd = 1; goto gotcha; } xfree(cp); /* XXX, use after free */ Perror(cp); gotcha: if (*cp != '/') { tchar *p, *q; int cwdlen; int len; /* * All in the name of efficiency? */ if ((cwdlen = (strlen_(dcwd->di_name))) == 1) { if (*dcwd->di_name == '/') /* root */ cwdlen = 0; else { /* * if we are here, when the shell started * it was unable to getwd(), lets try it again */ tchar path[MAXPATHLEN]; p = getwd_(path); if (p == NULL) error("cannot determine current directory"); else { xfree(dcwd->di_name); dcwd->di_name = savestr(p); xfree(cp); cp = savestr(p); return dcanon(cp, cp); } } } /* * * for (p = cp; *p++;) * ; * dp = (tchar *)xalloc((unsigned) (cwdlen + (p - cp) + 1)*sizeof (tchar)) */ len = strlen_(cp); dp = (tchar *)xalloc((unsigned)(cwdlen + len + 2) * sizeof (tchar)); for (p = dp, q = dcwd->di_name; *p++ = *q++; ) ; if (cwdlen) p[-1] = '/'; else p--; /* don't add a / after root */ for (q = cp; *p++ = *q++; ) ; xfree(cp); cp = dp; dp += cwdlen; } else dp = cp; return dcanon(cp, dp); } /* * dopushd - push new directory onto directory stack. * with no arguments exchange top and second. * with numeric argument (+n) bring it to top. */ void dopushd(tchar **v) { struct directory *dp; #ifdef TRACE tprintf("TRACE- dopushd()\n"); #endif printd = 1; if (*++v == NOSTR) { if ((dp = dcwd->di_prev) == &dhead) dp = dhead.di_prev; if (dp == dcwd) bferr("No other directory"); if (chdir_(dp->di_name) < 0) Perror(dp->di_name); dp->di_prev->di_next = dp->di_next; dp->di_next->di_prev = dp->di_prev; dp->di_next = dcwd->di_next; dp->di_prev = dcwd; dcwd->di_next->di_prev = dp; dcwd->di_next = dp; } else if (dp = dfind(*v)) { if (chdir_(dp->di_name) < 0) Perror(dp->di_name); } else { tchar *cp; cp = dfollow(*v); dp = (struct directory *)xcalloc(sizeof (struct directory), 1); dp->di_name = cp; dp->di_count = 0; dp->di_prev = dcwd; dp->di_next = dcwd->di_next; dcwd->di_next = dp; dp->di_next->di_prev = dp; } dnewcwd(dp); } /* * dfind - find a directory if specified by numeric (+n) argument */ struct directory * dfind(tchar *cp) { struct directory *dp; int i; tchar *ep; #ifdef TRACE tprintf("TRACE- dfind()\n"); #endif if (*cp++ != '+') return (0); for (ep = cp; digit(*ep); ep++) continue; if (*ep) return (0); i = getn(cp); if (i <= 0) return (0); for (dp = dcwd; i != 0; i--) { if ((dp = dp->di_prev) == &dhead) dp = dp->di_prev; if (dp == dcwd) bferr("Directory stack not that deep"); } return (dp); } /* * dopopd - pop a directory out of the directory stack * with a numeric argument just discard it. */ void dopopd(tchar **v) { struct directory *dp, *p; #ifdef TRACE tprintf("TRACE- dopopd()\n"); #endif printd = 1; if (*++v == NOSTR) dp = dcwd; else if ((dp = dfind(*v)) == 0) bferr("Invalid argument"); if (dp->di_prev == &dhead && dp->di_next == &dhead) bferr("Directory stack empty"); if (dp == dcwd) { if ((p = dp->di_prev) == &dhead) p = dhead.di_prev; if (chdir_(p->di_name) < 0) Perror(p->di_name); } dp->di_prev->di_next = dp->di_next; dp->di_next->di_prev = dp->di_prev; if (dp == dcwd) dnewcwd(p); else dodirs(fakev); dfree(dp); } /* * dfree - free the directory (or keep it if it still has ref count) */ void dfree(struct directory *dp) { #ifdef TRACE tprintf("TRACE- dfree()\n"); #endif if (dp->di_count != 0) dp->di_next = dp->di_prev = 0; else xfree(dp->di_name), xfree((tchar *)dp); } /* * dcanon - canonicalize the pathname, removing excess ./ and ../ etc. * We are of course assuming that the file system is standardly * constructed (always have ..'s, directories have links). * * If the hardpaths shell variable is set, resolve the * resulting pathname to contain no symbolic link components. */ tchar * dcanon(tchar *cp, tchar *p) { tchar *sp; /* rightmost component currently under consideration */ tchar *p1, /* general purpose */ *p2; bool slash, dotdot, hardpaths; #ifdef TRACE tprintf("TRACE- dcannon()\n"); #endif if (*cp != '/') abort(); if (hardpaths = (adrof(S_hardpaths) != NULL)) { /* * Be paranoid: don't trust the initial prefix * to be symlink-free. */ p = cp; } /* * Loop invariant: cp points to the overall path start, * p to its as yet uncanonicalized trailing suffix. */ while (*p) { /* for each component */ sp = p; /* save slash address */ while (*++p == '/') /* flush extra slashes */ ; if (p != ++sp) for (p1 = sp, p2 = p; *p1++ = *p2++; ) ; p = sp; /* save start of component */ slash = 0; if (*p) while (*++p) /* find next slash or end of path */ if (*p == '/') { slash = 1; *p = '\0'; break; } if (*sp == '\0') { /* component is null */ if (--sp == cp) /* if path is one tchar (i.e. /) */ break; else *sp = '\0'; continue; } if (sp[0] == '.' && sp[1] == '\0') { /* Squeeze out component consisting of "." */ if (slash) { for (p1 = sp, p2 = p + 1; *p1++ = *p2++; ) ; p = --sp; } else if (--sp != cp) *sp = '\0'; continue; } /* * At this point we have a path of the form "x/yz", * where "x" is null or rooted at "/", "y" is a single * component, and "z" is possibly null. The pointer cp * points to the start of "x", sp to the start of "y", * and p to the beginning of "z", which has been forced * to a null. */ /* * Process symbolic link component. Provided that either * the hardpaths shell variable is set or "y" is really * ".." we replace the symlink with its contents. The * second condition for replacement is necessary to make * the command "cd x/.." produce the same results as the * sequence "cd x; cd ..". * * Note that the two conditions correspond to different * potential symlinks. When hardpaths is set, we must * check "x/y"; otherwise, when "y" is known to be "..", * we check "x". */ dotdot = sp[0] == '.' && sp[1] == '.' && sp[2] == '\0'; if (hardpaths || dotdot) { tchar link[MAXPATHLEN]; int cc; tchar *newcp; /* * Isolate the end of the component that is to * be checked for symlink-hood. */ sp--; if (! hardpaths) *sp = '\0'; /* * See whether the component is really a symlink by * trying to read it. If the read succeeds, it is. */ if ((hardpaths || sp > cp) && (cc = readlink_(cp, link, MAXPATHLEN)) >= 0) { /* * readlink_ put null, so we don't need this. */ /* link[cc] = '\0'; */ /* Restore path. */ if (slash) *p = '/'; /* * Point p at the start of the trailing * path following the symlink component. * It's already there is hardpaths is set. */ if (! hardpaths) { /* Restore path as well. */ *(p = sp) = '/'; } /* * Find length of p. */ for (p1 = p; *p1++; ) ; if (*link != '/') { /* * Relative path: replace the symlink * component with its value. First, * set sp to point to the slash at * its beginning. If hardpaths is * set, this is already the case. */ if (! hardpaths) { while (*--sp != '/') ; } /* * Terminate the leading part of the * path, including trailing slash. */ sp++; *sp = '\0'; /* * New length is: "x/" + link + "z" */ p1 = newcp = (tchar *)xalloc((unsigned) ((sp - cp) + cc + (p1 - p)) * sizeof (tchar)); /* * Copy new path into newcp */ for (p2 = cp; *p1++ = *p2++; ) ; for (p1--, p2 = link; *p1++ = *p2++; ) ; for (p1--, p2 = p; *p1++ = *p2++; ) ; /* * Restart canonicalization at * expanded "/y". */ p = sp - cp - 1 + newcp; } else { /* * New length is: link + "z" */ p1 = newcp = (tchar *)xalloc((unsigned) (cc + (p1 - p))*sizeof (tchar)); /* * Copy new path into newcp */ for (p2 = link; *p1++ = *p2++; ) ; for (p1--, p2 = p; *p1++ = *p2++; ) ; /* * Restart canonicalization at beginning */ p = newcp; } xfree(cp); cp = newcp; continue; /* canonicalize the link */ } /* The component wasn't a symlink after all. */ if (! hardpaths) *sp = '/'; } if (dotdot) { if (sp != cp) while (*--sp != '/') ; if (slash) { for (p1 = sp + 1, p2 = p + 1; *p1++ = *p2++; ) ; p = sp; } else if (cp == sp) *++sp = '\0'; else *sp = '\0'; continue; } if (slash) *p = '/'; } return cp; } /* * dnewcwd - make a new directory in the loop the current one * and export its name to the PWD environment variable. */ void dnewcwd(struct directory *dp) { #ifdef TRACE tprintf("TRACE- dnewcwd()\n"); #endif dcwd = dp; #ifdef notdef /* * If we have a fast version of getwd available * and hardpaths is set, it would be reasonable * here to verify that dcwd->di_name really does * name the current directory. Later... */ #endif /* notdef */ didchdir = 1; set(S_cwd, savestr(dcwd->di_name)); didchdir = 0; local_setenv(S_PWD, dcwd->di_name); if (printd) dodirs(fakev); } /* * Copyright 1990 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ /* Copyright (c) 1983, 1984, 1985, 1986, 1987, 1988, 1989 AT&T */ /* All Rights Reserved */ /* * Copyright (c) 1980 Regents of the University of California. * All rights reserved. The Berkeley Software License Agreement * specifies the terms and conditions for redistribution. */ /* * Structure for entries in directory stack. */ struct directory { struct directory *di_next; /* next in loop */ struct directory *di_prev; /* prev in loop */ unsigned short *di_count; /* refcount of processes */ tchar *di_name; /* actual name */ }; extern struct directory *dcwd; /* the one we are in now */ /* * Copyright 2005 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ /* Copyright (c) 1983, 1984, 1985, 1986, 1987, 1988, 1989 AT&T */ /* All Rights Reserved */ /* * Copyright (c) 1980 Regents of the University of California. * All rights reserved. The Berkeley Software License Agreement * specifies the terms and conditions for redistribution. */ #include /* for lseek prototype */ #include "sh.h" #include "sh.tconst.h" /* * C shell */ bool noexec; long gargc; short OLDSTD; short gflag; tchar *bname; tchar *file; tchar **gargv; tchar *doldol; tchar *lap; tchar **pargv; /* * These routines perform variable substitution and quoting via ' and ". * To this point these constructs have been preserved in the divided * input words. Here we expand variables and turn quoting via ' and " into * QUOTE bits on characters (which prevent further interpretation). * If the `:q' modifier was applied during history expansion, then * some QUOTEing may have occurred already, so we dont "trim()" here. */ int Dpeekc, Dpeekrd; /* Peeks for DgetC and Dreadc */ tchar *Dcp, **Dvp; /* Input vector for Dreadc */ #define DEOF -1 #define unDgetC(c) Dpeekc = c #define QUOTES (_Q|_Q1|_ESC) /* \ ' " ` */ /* * The following variables give the information about the current * $ expansion, recording the current word position, the remaining * words within this expansion, the count of remaining words, and the * information about any : modifier which is being applied. */ tchar *dolp; /* Remaining chars from this word */ tchar **dolnxt; /* Further words */ int dolcnt; /* Count of further words */ tchar dolmod; /* : modifier character */ int dolmcnt; /* :gx -> 10000, else 1 */ void Dfix2(tchar **); void Dgetdol(void); void setDolp(tchar *); void unDredc(int); /* * Fix up the $ expansions and quotations in the * argument list to command t. */ void Dfix(struct command *t) { tchar **pp; tchar *p; #ifdef TRACE tprintf("TRACE- Dfix()\n"); #endif if (noexec) return; /* Note that t_dcom isn't trimmed thus !...:q's aren't lost */ for (pp = t->t_dcom; p = *pp++; ) while (*p) if (cmap(*p++, _DOL|QUOTES)) { /* $, \, ', ", ` */ Dfix2(t->t_dcom); /* found one */ blkfree(t->t_dcom); t->t_dcom = gargv; gargv = 0; return; } } /* * $ substitute one word, for i/o redirection */ tchar * Dfix1(tchar *cp) { tchar *Dv[2]; #ifdef TRACE tprintf("TRACE- Dfix1()\n"); #endif if (noexec) return (0); Dv[0] = cp; Dv[1] = NOSTR; Dfix2(Dv); if (gargc != 1) { setname(cp); bferr("Ambiguous"); } cp = savestr(gargv[0]); blkfree(gargv), gargv = 0; return (cp); } /* * Subroutine to do actual fixing after state initialization. */ void Dfix2(tchar **v) { tchar *agargv[GAVSIZ]; #ifdef TRACE tprintf("TRACE- Dfix2()\n"); #endif ginit(agargv); /* Initialize glob's area pointers */ Dvp = v; Dcp = S_ /* "" */; /* Setup input vector for Dreadc */ unDgetC(0); unDredc(0); /* Clear out any old peeks (at error) */ dolp = 0; dolcnt = 0; /* Clear out residual $ expands (...) */ while (Dword()) continue; gargv = copyblk(gargv); } /* * Get a word. This routine is analogous to the routine * word() in sh.lex.c for the main lexical input. One difference * here is that we don't get a newline to terminate our expansion. * Rather, DgetC will return a DEOF when we hit the end-of-input. */ int Dword(void) { int c, c1; static tchar *wbuf = NULL; static int wbufsiz = BUFSIZ; int wp = 0; bool dolflg; bool sofar = 0; #define DYNAMICBUFFER() \ do { \ if (wp >= wbufsiz) { \ wbufsiz += BUFSIZ; \ wbuf = xrealloc(wbuf, (wbufsiz+1) * sizeof (tchar)); \ } \ } while (0) #ifdef TRACE tprintf("TRACE- Dword()\n"); #endif if (wbuf == NULL) wbuf = xalloc((wbufsiz+1) * sizeof (tchar)); loop: c = DgetC(DODOL); switch (c) { case DEOF: deof: if (sofar == 0) return (0); /* finish this word and catch the code above the next time */ unDredc(c); /* fall into ... */ case '\n': wbuf[wp] = 0; goto ret; case ' ': case '\t': goto loop; case '`': /* We preserve ` quotations which are done yet later */ wbuf[wp++] = c; case '\'': case '"': /* * Note that DgetC never returns a QUOTES character * from an expansion, so only true input quotes will * get us here or out. */ c1 = c; dolflg = c1 == '"' ? DODOL : 0; for (;;) { c = DgetC(dolflg); if (c == c1) break; if (c == '\n' || c == DEOF) error("Unmatched %c", (tchar) c1); if ((c & (QUOTE|TRIM)) == ('\n' | QUOTE)) --wp; DYNAMICBUFFER(); switch (c1) { case '"': /* * Leave any `s alone for later. * Other chars are all quoted, thus `...` * can tell it was within "...". */ wbuf[wp++] = c == '`' ? '`' : c | QUOTE; break; case '\'': /* Prevent all further interpretation */ wbuf[wp++] = c | QUOTE; break; case '`': /* Leave all text alone for later */ wbuf[wp++] = c; break; } } if (c1 == '`') { DYNAMICBUFFER(); wbuf[wp++] = '`'; } goto pack; /* continue the word */ case '\\': c = DgetC(0); /* No $ subst! */ if (c == '\n' || c == DEOF) goto loop; c |= QUOTE; break; #ifdef MBCHAR /* Could be a space char from aux. codeset. */ default: if (isauxsp(c)) goto loop; #endif /* MBCHAR */ } unDgetC(c); pack: sofar = 1; /* pack up more characters in this word */ for (;;) { c = DgetC(DODOL); if (c == '\\') { c = DgetC(0); if (c == DEOF) goto deof; if (c == '\n') c = ' '; else c |= QUOTE; } if (c == DEOF) goto deof; if (cmap(c, _SP|_NL|_Q|_Q1) || isauxsp(c)) { /* sp \t\n'"` or aux. sp */ unDgetC(c); if (cmap(c, QUOTES)) goto loop; DYNAMICBUFFER(); wbuf[wp++] = 0; goto ret; } DYNAMICBUFFER(); wbuf[wp++] = c; } ret: Gcat(S_ /* "" */, wbuf); return (1); } /* * Get a character, performing $ substitution unless flag is 0. * Any QUOTES character which is returned from a $ expansion is * QUOTEd so that it will not be recognized above. */ int DgetC(int flag) { int c; top: if (c = Dpeekc) { Dpeekc = 0; return (c); } if (lap) { c = *lap++ & (QUOTE|TRIM); if (c == 0) { lap = 0; goto top; } quotspec: /* * don't quote things if there was an error (err!=0) * the input is original, not from a substitution and * therefore should not be quoted */ if (!err_msg && cmap(c, QUOTES)) return (c | QUOTE); return (c); } if (dolp) { if (c = *dolp++ & (QUOTE|TRIM)) goto quotspec; if (dolcnt > 0) { setDolp(*dolnxt++); --dolcnt; return (' '); } dolp = 0; } if (dolcnt > 0) { setDolp(*dolnxt++); --dolcnt; goto top; } c = Dredc(); if (c == '$' && flag) { Dgetdol(); goto top; } return (c); } tchar *nulvec[] = { 0 }; struct varent nulargv = { nulvec, S_argv, 0 }; /* * Handle the multitudinous $ expansion forms. * Ugh. */ void Dgetdol(void) { tchar *np; struct varent *vp; tchar name[MAX_VREF_LEN]; int c, sc; int subscr = 0, lwb = 1, upb = 0; bool dimen = 0, bitset = 0; tchar wbuf[BUFSIZ + MB_LEN_MAX]; /* read_ may return extra bytes */ #ifdef TRACE tprintf("TRACE- Dgetdol()\n"); #endif dolmod = dolmcnt = 0; c = sc = DgetC(0); if (c == '{') c = DgetC(0); /* sc is { to take } later */ if ((c & TRIM) == '#') dimen++, c = DgetC(0); /* $# takes dimension */ else if (c == '?') bitset++, c = DgetC(0); /* $? tests existence */ switch (c) { case '$': if (dimen || bitset) syntax: error("Variable syntax"); /* No $?$, $#$ */ setDolp(doldol); goto eatbrac; case '<'|QUOTE: if (dimen || bitset) goto syntax; /* No $?<, $#< */ for (np = wbuf; read_(OLDSTD, np, 1) == 1; np++) { if (np >= &wbuf[BUFSIZ-1]) error("$< line too long"); if (*np <= 0 || *np == '\n') break; } *np = 0; /* * KLUDGE: dolmod is set here because it will * cause setDolp to call domod and thus to copy wbuf. * Otherwise setDolp would use it directly. If we saved * it ourselves, no one would know when to free it. * The actual function of the 'q' causes filename * expansion not to be done on the interpolated value. */ dolmod = 'q'; dolmcnt = 10000; setDolp(wbuf); goto eatbrac; case DEOF: case '\n': goto syntax; case '*': (void) strcpy_(name, S_argv); vp = adrof(S_argv); subscr = -1; /* Prevent eating [...] */ break; default: np = name; if (digit(c)) { if (dimen) goto syntax; /* No $#1, e.g. */ subscr = 0; do { subscr = subscr * 10 + c - '0'; c = DgetC(0); } while (digit(c)); unDredc(c); if (subscr < 0) error("Subscript out of range"); if (subscr == 0) { if (bitset) { dolp = file ? S_1 /* "1" */ : S_0 /* "0" */; goto eatbrac; } if (file == 0) error("No file for $0"); setDolp(file); goto eatbrac; } if (bitset) goto syntax; vp = adrof(S_argv); if (vp == 0) { vp = &nulargv; goto eatmod; } break; } if (!alnum(c)) goto syntax; for (;;) { *np++ = c; c = DgetC(0); if (!alnum(c)) break; /* if variable name is > 20, complain */ if (np >= &name[MAX_VAR_LEN]) error("Variable name too long"); } *np++ = 0; unDredc(c); vp = adrof(name); } if (bitset) { /* * getenv() to getenv_(), because 'name''s type is now tchar * * no need to xalloc */ dolp = (vp || getenv_(name)) ? S_1 /* "1" */ : S_0 /* "0" */; goto eatbrac; } if (vp == 0) { /* * getenv() to getenv_(), because 'name''s type is now tchar * * no need to xalloc */ np = getenv_(name); if (np) { addla(np); goto eatbrac; } udvar(name); /*NOTREACHED*/ } c = DgetC(0); upb = blklen(vp->vec); if (dimen == 0 && subscr == 0 && c == '[') { np = name; for (;;) { c = DgetC(DODOL); /* Allow $ expand within [ ] */ if (c == ']') break; if (c == '\n' || c == DEOF) goto syntax; if (np >= &name[MAX_VREF_LEN]) error("Variable reference too long"); *np++ = c; } *np = 0, np = name; if (dolp || dolcnt) /* $ exp must end before ] */ goto syntax; if (!*np) goto syntax; if (digit(*np)) { int i = 0; while (digit(*np)) i = i * 10 + *np++ - '0'; /* if ((i < 0 || i > upb) && !any(*np, "-*")) { */ if ((i < 0 || i > upb) && (*np != '-') && (*np != '*')) { oob: setname(vp->v_name); error("Subscript out of range"); } lwb = i; if (!*np) upb = lwb, np = S_AST /* "*" */; } if (*np == '*') np++; else if (*np != '-') goto syntax; else { int i = upb; np++; if (digit(*np)) { i = 0; while (digit(*np)) i = i * 10 + *np++ - '0'; if (i < 0 || i > upb) goto oob; } if (i < lwb) upb = lwb - 1; else upb = i; } if (lwb == 0) { if (upb != 0) goto oob; upb = -1; } if (*np) goto syntax; } else { if (subscr > 0) if (subscr > upb) lwb = 1, upb = 0; else lwb = upb = subscr; unDredc(c); } if (dimen) { tchar *cp = putn(upb - lwb + 1); addla(cp); xfree(cp); } else { eatmod: c = DgetC(0); if (c == ':') { c = DgetC(0), dolmcnt = 1; if (c == 'g') c = DgetC(0), dolmcnt = 10000; if (!any(c, S_htrqxe)) error("Bad : mod in $"); dolmod = c; if (c == 'q') dolmcnt = 10000; } else unDredc(c); dolnxt = &vp->vec[lwb - 1]; dolcnt = upb - lwb + 1; } eatbrac: if (sc == '{') { c = Dredc(); if (c != '}') goto syntax; } } void setDolp(tchar *cp) { tchar *dp; #ifdef TRACE tprintf("TRACE- setDolp()\n"); #endif if (dolmod == 0 || dolmcnt == 0) { dolp = cp; return; } dp = domod(cp, dolmod); if (dp) { dolmcnt--; addla(dp); xfree(dp); } else addla(cp); dolp = S_ /* "" */; } void unDredc(int c) { Dpeekrd = c; } int Dredc() { int c; if (c = Dpeekrd) { Dpeekrd = 0; return (c); } if (Dcp && (c = *Dcp++)) return (c&(QUOTE|TRIM)); if (*Dvp == 0) { Dcp = 0; return (DEOF); } Dcp = *Dvp++; return (' '); } void Dtestq(int c) { if (cmap(c, QUOTES)) gflag = 1; } /* * Form a shell temporary file (in unit 0) from the words * of the shell input up to a line the same as "term". * Unit 0 should have been closed before this call. */ void heredoc(tchar *term) { int c; tchar *Dv[2]; tchar obuf[BUFSIZ], lbuf[BUFSIZ], mbuf[BUFSIZ]; int ocnt, lcnt, mcnt; tchar *lbp, *obp, *mbp; tchar **vp; bool quoted; tchar shtemp[] = {'/', 't', 'm', 'p', '/', 's', 'h', 'X', 'X', 'X', 'X', 'X', 'X', 0}; int fd1; #ifdef TRACE tprintf("TRACE- heredoc()\n"); #endif if ((fd1 = mkstemp_(shtemp)) < 0) Perror(shtemp); (void) unlink_(shtemp); /* 0 0 inode! */ unsetfd(fd1); Dv[0] = term; Dv[1] = NOSTR; gflag = 0; trim(Dv); rscan(Dv, Dtestq); quoted = gflag; ocnt = BUFSIZ; obp = obuf; for (;;) { /* * Read up a line */ lbp = lbuf; lcnt = BUFSIZ - 4; for (;;) { c = readc(1); /* 1 -> Want EOF returns */ if (c < 0) { setname(term); bferr("<< terminator not found"); } if (c == '\n') break; if (c &= TRIM) { *lbp++ = c; if (--lcnt < 0) { setname(S_LESLES /* "<<" */); error("Line overflow"); } } } *lbp = 0; /* * Compare to terminator -- before expansion */ if (eq(lbuf, term)) { (void) write_(0, obuf, BUFSIZ - ocnt); (void) lseek(0, (off_t)0, 0); return; } /* * If term was quoted or -n just pass it on */ if (quoted || noexec) { *lbp++ = '\n'; *lbp = 0; for (lbp = lbuf; c = *lbp++; ) { *obp++ = c; if (--ocnt == 0) { (void) write_(0, obuf, BUFSIZ); obp = obuf; ocnt = BUFSIZ; } } continue; } /* * Term wasn't quoted so variable and then command * expand the input line */ Dcp = lbuf; Dvp = Dv + 1; mbp = mbuf; mcnt = BUFSIZ - 4; for (;;) { c = DgetC(DODOL); if (c == DEOF) break; if ((c &= TRIM) == 0) continue; /* \ quotes \ $ ` here */ if (c == '\\') { c = DgetC(0); /* if (!any(c, "$\\`")) */ if ((c != '$') && (c != '\\') && (c != '`')) unDgetC(c | QUOTE), c = '\\'; else c |= QUOTE; } *mbp++ = c; if (--mcnt == 0) { setname(S_LESLES /* "<<" */); bferr("Line overflow"); } } *mbp++ = 0; /* * If any ` in line do command substitution */ mbp = mbuf; if (any('`', mbp)) { /* * 1 arg to dobackp causes substitution to be literal. * Words are broken only at newlines so that all blanks * and tabs are preserved. Blank lines (null words) * are not discarded. */ vp = dobackp(mbuf, 1); } else /* Setup trivial vector similar to return of dobackp */ Dv[0] = mbp, Dv[1] = NOSTR, vp = Dv; /* * Resurrect the words from the command substitution * each separated by a newline. Note that the last * newline of a command substitution will have been * discarded, but we put a newline after the last word * because this represents the newline after the last * input line! */ for (; *vp; vp++) { for (mbp = *vp; *mbp; mbp++) { *obp++ = *mbp & TRIM; if (--ocnt == 0) { (void) write_(0, obuf, BUFSIZ); obp = obuf; ocnt = BUFSIZ; } } *obp++ = '\n'; if (--ocnt == 0) { (void) write_(0, obuf, BUFSIZ); obp = obuf; ocnt = BUFSIZ; } } if (pargv) blkfree(pargv), pargv = 0; } } /* * Copyright 2005 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ /* Copyright (c) 1983, 1984, 1985, 1986, 1987, 1988, 1989 AT&T */ /* All Rights Reserved */ /* * Copyright (c) 1980 Regents of the University of California. * All rights reserved. The Berkeley Software License Agreement * specifies the terms and conditions for redistribution. */ #include "sh.h" #include #include #include /* * #include * #include */ #include "sh.tconst.h" /* * C Shell */ bool child; bool didfds; bool exiterr; bool errspl; /* Argument to error was spliced by seterr2 */ bool haderr; jmp_buf reslab; tchar one[2] = { '1', 0 }; tchar *onev[2] = { one, NOSTR }; short SHDIAG; int tpgrp; /* * contains DIR * for last opendir_(), its left open if an error * longjmp (reset) occurs before it gets closed via closedir. * if its not null in the error handler, then closedir it. */ DIR *Dirp = NULL; /* * Print error string s with optional argument arg. * This routine always resets or exits. The flag haderr * is set so the routine who catches the unwind can propogate * it if they want. * * Note that any open files at the point of error will eventually * be closed in the routine process in sh.c which is the only * place error unwinds are ever caught. */ /*VARARGS1*/ void error(s, a1, a2) char *s; { tchar **v; char *ep; /* * Must flush before we print as we wish output before the error * to go on (some form of) standard output, while output after * goes on (some form of) diagnostic output. * If didfds then output will go to 1/2 else to FSHOUT/FSHDIAG. * See flush in sh.print.c. */ flush(); haderr = 1; /* Now to diagnostic output */ if (v = pargv) pargv = 0, blkfree(v); if (v = gargv) gargv = 0, blkfree(v); /* * A zero arguments causes no printing, else print * an error diagnostic here. */ if (s) { printf(s, a1, a2), printf("\n"); } didfds = 0; /* Forget about 0,1,2 */ if ((ep = err_msg) && errspl) { errspl = 0; xfree(ep); } errspl = 0; if ( Dirp ){ closedir(Dirp); Dirp = NULL; } /* * Go away if -e or we are a child shell */ if (exiterr || child) { exit(1); } /* * Reset the state of the input. * This buffered seek to end of file will also * clear the while/foreach stack. */ btoeof(); setq(S_status, onev, &shvhed); if (tpgrp > 0) (void) ioctl(FSHTTY, TIOCSPGRP, (char *)&tpgrp); reset(); /* Unwind */ } /* * Perror is the shells version of perror which should otherwise * never be called. */ void Perror(tchar *s) { char chbuf[BUFSIZ]; /* * Perror uses unit 2, thus if we didn't set up the fd's * we must set up unit 2 now else the diagnostic will disappear */ if (!didfds) { int oerrno = errno; (void) dcopy(SHDIAG, 2); errno = oerrno; } tstostr(chbuf, s); perror(chbuf); error(NULL); /* To exit or unwind */ } void bferr(char *cp) { flush(); haderr = 1; if( bname) printf("%t: ", bname); error("%s", gettext(cp)); } /* * The parser and scanner set up errors for later by calling seterr, * which sets the variable err as a side effect; later to be tested, * e.g. in process. */ void seterr(char *s) { if (err_msg == NULL) err_msg = s, errspl = 0; } /* Set err to a splice of cp and dp, to be freed later in error() */ void seterr2(tchar *cp, char *dp) { char chbuf[BUFSIZ]; char *gdp; if (err_msg) return; /* Concatinate cp and dp in the allocated space. */ tstostr(chbuf, cp); gdp = gettext(dp); err_msg = (char *)xalloc(strlen(chbuf)+strlen(gdp)+1); strcpy(err_msg, chbuf); strcat(err_msg, gdp); errspl++;/* Remember to xfree(err_msg). */ } /* Set err to a splice of cp with a string form of character d */ void seterrc(char *cp, tchar d) { char chbuf[MB_LEN_MAX+1]; /* don't overwrite an existing error message */ if (err_msg) return; #ifdef MBCHAR { wchar_t wcd=(wchar_t)(d&TRIM); int i; i = wctomb(chbuf, wcd); /* chbuf holds d in multibyte representation. */ chbuf[(i>0)?i:0] = (char) 0; } #else chbuf[0]=(char)(d&TRIM); chbuf[1]=(char)0; #endif /* Concatinate cp and d in the allocated space. */ err_msg = (char *)xalloc(strlen(cp)+strlen(chbuf)+1); strcpy(err_msg, cp); strcat(err_msg, chbuf); errspl++; /* Remember to xfree(err_msg). */ } /* * Copyright (c) 1989, 2010, Oracle and/or its affiliates. All rights reserved. */ /* Copyright (c) 1983, 1984, 1985, 1986, 1987, 1988, 1989 AT&T */ /* All Rights Reserved */ /* * Copyright (c) 1980 Regents of the University of California. * All rights reserved. The Berkeley Software License Agreement * specifies the terms and conditions for redistribution. */ #include "sh.h" #include #include #include "sh.tconst.h" /* * C shell */ /* * System level search and execute of a command. * We look in each directory for the specified command name. * If the name contains a '/' then we execute only the full path name. * If there is no search path then we execute only full path names. */ char xhash[HSHSIZ / 8]; tchar **Vav; tchar *Vdp; tchar *Vsav; struct varent aliases; bool havhash; static int hits; static int misses; short SHOUT; short SHIN; void (*parintr)(); void (*parterm)(); /* * As we search for the command we note the first non-trivial error * message for presentation to the user. This allows us often * to show that a file has the wrong mode/no access when the file * is not in the last component of the search path, so we must * go on after first detecting the error. */ char *exerr; /* Execution error message */ void pexerr(void); void texec(struct command *, tchar *, tchar **); void xechoit(tchar **); void dohash(char []); static void tconvert(struct command *, tchar *, tchar **); extern DIR *opendir_(tchar *); void doexec(struct command *t) { tchar *sav; tchar *dp, **pv, **av; struct varent *v; bool slash; int hashval, hashval1, i; tchar *blk[2]; #ifdef TRACE tprintf("TRACE- doexec()\n"); #endif /* * Glob the command name. If this does anything, then we * will execute the command only relative to ".". One special * case: if there is no PATH, then we execute only commands * which start with '/'. */ dp = globone(t->t_dcom[0]); sav = t->t_dcom[0]; exerr = 0; t->t_dcom[0] = dp; setname(dp); xfree(sav); v = adrof(S_path /* "path" */); if (v == 0 && dp[0] != '/') { pexerr(); } slash = gflag; /* * Glob the argument list, if necessary. * Otherwise trim off the quote bits. */ gflag = 0; av = &t->t_dcom[1]; tglob(av); if (gflag) { av = glob(av); if (av == 0) error("No match"); } blk[0] = t->t_dcom[0]; blk[1] = 0; av = blkspl(blk, av); #ifdef VFORK Vav = av; #endif trim(av); slash |= any('/', av[0]); xechoit(av); /* Echo command if -x */ /* * Since all internal file descriptors are set to close on exec, * we don't need to close them explicitly here. Just reorient * ourselves for error messages. */ SHIN = 0; SHOUT = 1; SHDIAG = 2; OLDSTD = 0; /* * We must do this AFTER any possible forking (like `foo` * in glob) so that this shell can still do subprocesses. */ (void) sigsetmask(0); /* * If no path, no words in path, or a / in the filename * then restrict the command search. */ if (v == 0 || v->vec[0] == 0 || slash) pv = justabs; else pv = v->vec; /* / command name for postpending */ sav = strspl(S_SLASH /* "/" */, *av); #ifdef VFORK Vsav = sav; #endif if (havhash) hashval = hashname(*av); i = 0; #ifdef VFORK hits++; #endif do { if (!slash && pv[0][0] == '/' && havhash) { hashval1 = hash(hashval, i); if (!bit(xhash, hashval1)) goto cont; } /* don't make ./xxx */ if (pv[0][0] == 0 || eq(pv[0], S_DOT /* "." */)) { texec(t, *av, av); } else { dp = strspl(*pv, sav); #ifdef VFORK Vdp = dp; #endif texec(t, dp, av); #ifdef VFORK Vdp = 0; #endif xfree(dp); } #ifdef VFORK misses++; #endif cont: pv++; i++; } while (*pv); #ifdef VFORK hits--; #endif #ifdef VFORK Vsav = 0; Vav = 0; #endif xfree(sav); xfree((char *)av); pexerr(); } void pexerr(void) { #ifdef TRACE tprintf("TRACE- pexerr()\n"); #endif /* Couldn't find the damn thing */ if (exerr) bferr(exerr); bferr("Command not found"); } /* * Execute command f, arg list t. * Record error message if not found. * Also do shell scripts here. */ void texec(struct command *cmd, tchar *f, tchar **t) { struct varent *v; tchar **vp; tchar *lastsh[2]; #ifdef TRACE tprintf("TRACE- texec()\n"); #endif /* convert cfname and cargs from tchar to char */ tconvert(cmd, f, t); execv(cmd->cfname, cmd->cargs); /* * exec returned, free up allocations from above * tconvert(), zero cfname and cargs to prevent * duplicate free() in freesyn() */ xfree(cmd->cfname); chr_blkfree(cmd->cargs); cmd->cfname = (char *)0; cmd->cargs = (char **)0; switch (errno) { case ENOEXEC: /* check that this is not a binary file */ { int ff = open_(f, 0); tchar ch[MB_LEN_MAX]; if (ff != -1 && read_(ff, ch, 1) == 1 && !isprint(ch[0]) && !isspace(ch[0])) { printf("Cannot execute binary file.\n"); Perror(f); (void) close(ff); unsetfd(ff); return; } (void) close(ff); unsetfd(ff); } /* * If there is an alias for shell, then * put the words of the alias in front of the * argument list replacing the command name. * Note no interpretation of the words at this point. */ v = adrof1(S_shell /* "shell" */, &aliases); if (v == 0) { #ifdef OTHERSH int ff = open_(f, 0); tchar ch[MB_LEN_MAX]; #endif vp = lastsh; vp[0] = adrof(S_shell /* "shell" */) ? value(S_shell /* "shell" */) : S_SHELLPATH /* SHELLPATH */; vp[1] = (tchar *) NULL; #ifdef OTHERSH if (ff != -1 && read_(ff, ch, 1) == 1 && ch[0] != '#') vp[0] = S_OTHERSH /* OTHERSH */; (void) close(ff); unsetfd(ff); #endif } else vp = v->vec; t[0] = f; t = blkspl(vp, t); /* Splice up the new arglst */ f = *t; tconvert(cmd, f, t); /* convert tchar to char */ /* * now done with tchar arg list t, * free the space calloc'd by above blkspl() */ xfree((char *)t); execv(cmd->cfname, cmd->cargs); /* exec the command */ /* exec returned, same free'ing as above */ xfree(cmd->cfname); chr_blkfree(cmd->cargs); cmd->cfname = (char *)0; cmd->cargs = (char **)0; /* The sky is falling, the sky is falling! */ case ENOMEM: Perror(f); case ENOENT: break; default: if (exerr == 0) { exerr = strerror(errno); setname(f); } } } static void tconvert(struct command *cmd, tchar *fname, tchar **list) { char **rc; int len; cmd->cfname = tstostr(NULL, fname); len = blklen(list); rc = cmd->cargs = (char **) xcalloc((uint_t)(len + 1), sizeof (char **)); while (len--) *rc++ = tstostr(NULL, *list++); *rc = NULL; } /*ARGSUSED*/ void execash(tchar **t, struct command *kp) { #ifdef TRACE tprintf("TRACE- execash()\n"); #endif rechist(); (void) signal(SIGINT, parintr); (void) signal(SIGQUIT, parintr); (void) signal(SIGTERM, parterm); /* if doexec loses, screw */ lshift(kp->t_dcom, 1); exiterr++; doexec(kp); /*NOTREACHED*/ } void xechoit(tchar **t) { #ifdef TRACE tprintf("TRACE- xechoit()\n"); #endif if (adrof(S_echo /* "echo" */)) { flush(); haderr = 1; blkpr(t), Putchar('\n'); haderr = 0; } } /* * This routine called when user enters "rehash". * Both the path and cdpath caching arrays will * be rehashed, via calling dohash. If either * variable is not set with a value, then dohash * just exits. */ void dorehash(void) { dohash(xhash); dohash(xhash2); } /* * Fill up caching arrays for path and cdpath */ void dohash(char cachearray[]) { struct stat stb; DIR *dirp; struct dirent *dp; int cnt; int i = 0; struct varent *v; tchar **pv; int hashval; tchar curdir_[MAXNAMLEN+1]; #ifdef TRACE tprintf("TRACE- dohash()\n"); #endif /* Caching $path */ if (cachearray == xhash) { havhash = 1; v = adrof(S_path /* "path" */); } else { /* Caching $cdpath */ havhash2 = 1; v = adrof(S_cdpath /* "cdpath" */); } for (cnt = 0; cnt < (HSHSIZ / 8); cnt++) cachearray[cnt] = 0; if (v == 0) return; for (pv = v->vec; *pv; pv++, i++) { if (pv[0][0] != '/') continue; dirp = opendir_(*pv); if (dirp == NULL) continue; if (fstat(dirp->dd_fd, &stb) < 0 || !isdir(stb)) { unsetfd(dirp->dd_fd); closedir_(dirp); continue; } while ((dp = readdir(dirp)) != NULL) { if (dp->d_ino == 0) continue; if (dp->d_name[0] == '.' && (dp->d_name[1] == '\0' || dp->d_name[1] == '.' && dp->d_name[2] == '\0')) continue; hashval = hash(hashname(strtots(curdir_, dp->d_name)), i); bis(cachearray, hashval); } unsetfd(dirp->dd_fd); closedir_(dirp); } } void dounhash(void) { #ifdef TRACE tprintf("TRACE- dounhash()\n"); #endif havhash = 0; havhash2 = 0; } #ifdef VFORK void hashstat(void) { #ifdef TRACE tprintf("TRACE- hashstat_()\n"); #endif if (hits+misses) printf("%d hits, %d misses, %d%%\n", hits, misses, 100 * hits / (hits + misses)); } #endif /* * Hash a command name. */ int hashname(tchar *cp) { long h = 0; #ifdef TRACE tprintf("TRACE- hashname()\n"); #endif while (*cp) h = hash(h, *cp++); return ((int)h); } /* * Copyright 2006 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ /* Copyright (c) 1983, 1984, 1985, 1986, 1987, 1988, 1989 AT&T */ /* All Rights Reserved */ /* * Copyright (c) 1980 Regents of the University of California. * All rights reserved. The Berkeley Software License Agreement * specifies the terms and conditions for redistribution. */ #include "sh.h" #include "sh.tconst.h" /* * C shell */ #define IGNORE 1 /* in ignore, it means to ignore value, just parse */ #define NOGLOB 2 /* in ignore, it means not to globone */ #define ADDOP 1 #define MULOP 2 #define EQOP 4 #define RELOP 8 #define RESTOP 16 #define ANYOP 31 #define EQEQ 1 #define GTR 2 #define LSS 4 #define NOTEQ 6 #define EQMATCH 7 #define NOTEQMATCH 8 int uid; int exp0(tchar ***, bool); int exp1(tchar ***, bool); int exp2(tchar ***, bool); int exp2a(tchar ***, bool); int exp2b(tchar ***, bool); int exp2c(tchar ***, bool); tchar *exp3(tchar ***, bool); tchar *exp3a(tchar ***, bool); tchar *exp4(tchar ***, bool); tchar *exp5(tchar ***, bool); tchar *exp6(tchar ***, bool); void evalav(tchar **); /* * Determine if file given by name is accessible with permissions * given by mode. * * Borrowed from the Bourne sh, and modified a bit * * If the requested access is permitted, a value of 0 is * returned. Otherwise, a value of -1 is returned and errno is * set to indicate the error */ int chk_access(tchar *path, mode_t mode) { static int flag; static uid_t euid; struct stat statb; mode_t ftype; unsigned char name[MAXPATHLEN*MB_LEN_MAX]; /* General use buffer. */ /* convert tchar * to char * */ tstostr((char *)name, path); if (flag == 0) { euid = geteuid(); flag = 1; } if (stat((char *)name, &statb) == 0) { ftype = statb.st_mode & S_IFMT; if (access((char *)name, 010|(mode>>6)) == 0) { if (euid == 0) { if (ftype != S_IFREG || mode != S_IEXEC) return (0); /* root can execute file as long as it has execute permission for someone */ if (statb.st_mode & (S_IEXEC|(S_IEXEC>>3)|(S_IEXEC>>6))) return (0); return (-1); } return (0); } } return (-1); } int exp(tchar ***vp) { #ifdef TRACE tprintf("TRACE- exp()\n"); #endif return (exp0(vp, 0)); } int exp0(tchar ***vp, bool ignore) { int p1 = exp1(vp, ignore); #ifdef TRACE tprintf("TRACE- exp0()\n"); #endif #ifdef EDEBUG etraci("exp0 p1", p1, vp); #endif if (**vp && eq(**vp, S_BARBAR /* "||" */)) { int p2; (*vp)++; p2 = exp0(vp, (ignore&IGNORE) || p1); #ifdef EDEBUG etraci("exp0 p2", p2, vp); #endif return (p1 || p2); } return (p1); } int exp1(tchar ***vp, bool ignore) { int p1 = exp2(vp, ignore); #ifdef TRACE tprintf("TRACE- exp1()\n"); #endif #ifdef EDEBUG etraci("exp1 p1", p1, vp); #endif if (**vp && eq(**vp, S_ANDAND /* "&&" */)) { int p2; (*vp)++; p2 = exp1(vp, (ignore&IGNORE) || !p1); #ifdef EDEBUG etraci("exp1 p2", p2, vp); #endif return (p1 && p2); } return (p1); } int exp2(tchar ***vp, bool ignore) { int p1 = exp2a(vp, ignore); #ifdef TRACE tprintf("TRACE- exp2()\n"); #endif #ifdef EDEBUG etraci("exp3 p1", p1, vp); #endif if (**vp && eq(**vp, S_BAR /* "|" */)) { int p2; (*vp)++; p2 = exp2(vp, ignore); #ifdef EDEBUG etraci("exp3 p2", p2, vp); #endif return (p1 | p2); } return (p1); } int exp2a(tchar ***vp, bool ignore) { int p1 = exp2b(vp, ignore); #ifdef TRACE tprintf("TRACE- exp2a()\n"); #endif #ifdef EDEBUG etraci("exp2a p1", p1, vp); #endif if (**vp && eq(**vp, S_HAT /* "^" */)) { int p2; (*vp)++; p2 = exp2a(vp, ignore); #ifdef EDEBUG etraci("exp2a p2", p2, vp); #endif return (p1 ^ p2); } return (p1); } int exp2b(tchar ***vp, bool ignore) { int p1 = exp2c(vp, ignore); #ifdef TRACE tprintf("TRACE- exp2b()\n"); #endif #ifdef EDEBUG etraci("exp2b p1", p1, vp); #endif if (**vp && eq(**vp, S_AND /* "&" */)) { int p2; (*vp)++; p2 = exp2b(vp, ignore); #ifdef EDEBUG etraci("exp2b p2", p2, vp); #endif return (p1 & p2); } return (p1); } int exp2c(tchar ***vp, bool ignore) { tchar *p1 = exp3(vp, ignore); tchar *p2; int i; #ifdef TRACE tprintf("TRACE- exp2c()\n"); #endif #ifdef EDEBUG etracc("exp2c p1", p1, vp); #endif if (i = isa(**vp, EQOP)) { (*vp)++; if (i == EQMATCH || i == NOTEQMATCH) ignore |= NOGLOB; p2 = exp3(vp, ignore); #ifdef EDEBUG etracc("exp2c p2", p2, vp); #endif if (!(ignore&IGNORE)) switch (i) { case EQEQ: i = eq(p1, p2); break; case NOTEQ: i = !eq(p1, p2); break; case EQMATCH: i = Gmatch(p1, p2); break; case NOTEQMATCH: i = !Gmatch(p1, p2); break; } xfree(p1), xfree(p2); return (i); } i = egetn(p1); xfree(p1); return (i); } tchar * exp3(tchar ***vp, bool ignore) { tchar *p1, *p2; int i; #ifdef TRACE tprintf("TRACE- exp3()\n"); #endif p1 = exp3a(vp, ignore); #ifdef EDEBUG etracc("exp3 p1", p1, vp); #endif if (i = isa(**vp, RELOP)) { (*vp)++; if (**vp && eq(**vp, S_EQ /* "=" */)) i |= 1, (*vp)++; p2 = exp3(vp, ignore); #ifdef EDEBUG etracc("exp3 p2", p2, vp); #endif if (!(ignore&IGNORE)) switch (i) { case GTR: i = egetn(p1) > egetn(p2); break; case GTR|1: i = egetn(p1) >= egetn(p2); break; case LSS: i = egetn(p1) < egetn(p2); break; case LSS|1: i = egetn(p1) <= egetn(p2); break; } xfree(p1), xfree(p2); return (putn(i)); } return (p1); } tchar * exp3a(tchar ***vp, bool ignore) { tchar *p1, *p2, *op; int i; #ifdef TRACE tprintf("TRACE- exp3a()\n"); #endif p1 = exp4(vp, ignore); #ifdef EDEBUG etracc("exp3a p1", p1, vp); #endif op = **vp; /* if (op && any(op[0], "<>") && op[0] == op[1]) { */ if (op && (op[0] == '<' || op[0] == '>') && op[0] == op[1]) { (*vp)++; p2 = exp3a(vp, ignore); #ifdef EDEBUG etracc("exp3a p2", p2, vp); #endif if (op[0] == '<') i = egetn(p1) << egetn(p2); else i = egetn(p1) >> egetn(p2); xfree(p1), xfree(p2); return (putn(i)); } return (p1); } tchar * exp4(tchar ***vp, bool ignore) { tchar *p1, *p2; int i = 0; #ifdef TRACE tprintf("TRACE- exp4()\n"); #endif p1 = exp5(vp, ignore); #ifdef EDEBUG etracc("exp4 p1", p1, vp); #endif if (isa(**vp, ADDOP)) { tchar *op = *(*vp)++; p2 = exp4(vp, ignore); #ifdef EDEBUG etracc("exp4 p2", p2, vp); #endif if (!(ignore&IGNORE)) switch (op[0]) { case '+': i = egetn(p1) + egetn(p2); break; case '-': i = egetn(p1) - egetn(p2); break; } xfree(p1), xfree(p2); return (putn(i)); } return (p1); } tchar * exp5(tchar ***vp, bool ignore) { tchar *p1, *p2; int i = 0; #ifdef TRACE tprintf("TRACE- exp5()\n"); #endif p1 = exp6(vp, ignore); #ifdef EDEBUG etracc("exp5 p1", p1, vp); #endif if (isa(**vp, MULOP)) { tchar *op = *(*vp)++; p2 = exp5(vp, ignore); #ifdef EDEBUG etracc("exp5 p2", p2, vp); #endif if (!(ignore&IGNORE)) switch (op[0]) { case '*': i = egetn(p1) * egetn(p2); break; case '/': i = egetn(p2); if (i == 0) error("Divide by 0"); i = egetn(p1) / i; break; case '%': i = egetn(p2); if (i == 0) error("Mod by 0"); i = egetn(p1) % i; break; } xfree(p1), xfree(p2); return (putn(i)); } return (p1); } tchar * exp6(tchar ***vp, bool ignore) { int ccode, i; tchar *cp, *dp, *ep; #ifdef TRACE tprintf("TRACE- exp6()\n"); #endif if (**vp == 0) bferr("Expression syntax"); if (eq(**vp, S_EXAS /* "!" */)) { (*vp)++; cp = exp6(vp, ignore); #ifdef EDEBUG etracc("exp6 ! cp", cp, vp); #endif i = egetn(cp); xfree(cp); return (putn(!i)); } if (eq(**vp, S_TIL /* "~" */)) { (*vp)++; cp = exp6(vp, ignore); #ifdef EDEBUG etracc("exp6 ~ cp", cp, vp); #endif i = egetn(cp); xfree(cp); return (putn(~i)); } if (eq(**vp, S_LPAR /* "(" */)) { (*vp)++; ccode = exp0(vp, ignore); #ifdef EDEBUG etraci("exp6 () ccode", ccode, vp); #endif if (*vp == 0 || **vp == 0 || ***vp != ')') bferr("Expression syntax"); (*vp)++; return (putn(ccode)); } if (eq(**vp, S_LBRA /* "{" */)) { tchar **v; struct command faket; tchar *fakecom[2]; faket.t_dtyp = TCOM; faket.t_dflg = 0; faket.t_dcar = faket.t_dcdr = faket.t_dspr = (struct command *)0; faket.t_dcom = fakecom; fakecom[0] = S_BRAPPPBRA /* "{ ... }" */; fakecom[1] = NOSTR; (*vp)++; v = *vp; for (;;) { if (!**vp) bferr("Missing }"); if (eq(*(*vp)++, S_RBRA /* "}" */)) break; } if (ignore&IGNORE) return (S_ /* "" */); psavejob(); if (pfork(&faket, -1) == 0) { *--(*vp) = 0; evalav(v); exitstat(); } pwait(); prestjob(); #ifdef EDEBUG etraci("exp6 {} status", egetn(value("status")), vp); #endif return (putn(egetn(value(S_status /* "status" */)) == 0)); } if (isa(**vp, ANYOP)) return (S_ /* "" */); cp = *(*vp)++; if (*cp == '-' && any(cp[1], S_erwxfdzo /* "erwxfdzo" */)) { struct stat stb; if (cp[2] != '\0') bferr("Malformed file inquiry"); /* * Detect missing file names by checking for operator * in the file name position. However, if an operator * name appears there, we must make sure that there's * no file by that name (e.g., "/") before announcing * an error. Even this check isn't quite right, since * it doesn't take globbing into account. */ if ((**vp == NULL) || isa(**vp, ANYOP) && stat_(**vp, &stb)) bferr("Missing file name"); dp = *(*vp)++; if (ignore&IGNORE) return (S_ /* "" */); ep = globone(dp); switch (cp[1]) { case 'r': i = !chk_access(ep, S_IREAD); break; case 'w': i = !chk_access(ep, S_IWRITE); break; case 'x': i = !chk_access(ep, S_IEXEC); break; default: if (stat_(ep, &stb)) { xfree(ep); return (S_0 /* "0" */); } switch (cp[1]) { case 'f': i = (stb.st_mode & S_IFMT) == S_IFREG; break; case 'd': i = (stb.st_mode & S_IFMT) == S_IFDIR; break; case 'z': i = stb.st_size == 0; break; case 'e': i = 1; break; case 'o': i = stb.st_uid == uid; break; } } #ifdef EDEBUG etraci("exp6 -? i", i, vp); #endif xfree(ep); return (putn(i)); } #ifdef EDEBUG etracc("exp6 default", cp, vp); #endif return (ignore&NOGLOB ? savestr(cp) : globone(cp)); } void evalav(tchar **v) { struct wordent paraml; struct wordent *hp = ¶ml; struct command *t; struct wordent *wdp = hp; #ifdef TRACE tprintf("TRACE- evalav()\n"); #endif set(S_status /* "status" */, S_0 /* "0" */); hp->prev = hp->next = hp; hp->word = S_ /* "" */; while (*v) { struct wordent *new = (struct wordent *)xcalloc(1, sizeof *wdp); new->prev = wdp; new->next = hp; wdp->next = new; wdp = new; wdp->word = savestr(*v++); } hp->prev = wdp; alias(¶ml); t = syntax(paraml.next, ¶ml, 0); if (err_msg) error("%s", gettext(err_msg)); execute(t, -1); freelex(¶ml), freesyn(t); } int isa(tchar *cp, int what) { #ifdef TRACE tprintf("TRACE- isa()\n"); #endif if (cp == 0) return ((what & RESTOP) != 0); if (cp[1] == 0) { if (what & ADDOP && (*cp == '+' || *cp == '-')) return (1); if (what & MULOP && (*cp == '*' || *cp == '/' || *cp == '%')) return (1); if (what & RESTOP && (*cp == '(' || *cp == ')' || *cp == '!' || *cp == '~' || *cp == '^' || *cp == '"')) return (1); } else if (cp[2] == 0) { if (what & RESTOP) { if (cp[0] == '|' && cp[1] == '&') return (1); if (cp[0] == '<' && cp[1] == '<') return (1); if (cp[0] == '>' && cp[1] == '>') return (1); } if (what & EQOP) { if (cp[0] == '=') { if (cp[1] == '=') return (EQEQ); if (cp[1] == '~') return (EQMATCH); } else if (cp[0] == '!') { if (cp[1] == '=') return (NOTEQ); if (cp[1] == '~') return (NOTEQMATCH); } } } if (what & RELOP) { if (*cp == '<') return (LSS); if (*cp == '>') return (GTR); } return (0); } int egetn(tchar *cp) { #ifdef TRACE tprintf("TRACE- egetn()\n"); #endif if (*cp && *cp != '-' && !digit(*cp)) bferr("Expression syntax"); return (getn(cp)); } /* Phew! */ #ifdef EDEBUG void etraci(tchar *str, int i, tchar ***vp) { printf("%s=%d\t", str, i); blkpr(*vp); printf("\n"); } void etracc(tchar *str, tchar *cp, tchar ***vp) { printf("%s=%s\t", str, cp); blkpr(*vp); printf("\n"); } #endif /* * Copyright 2005 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ /* Copyright (c) 1983, 1984, 1985, 1986, 1987, 1988, 1989 AT&T */ /* All Rights Reserved */ /* * Copyright (c) 1980 Regents of the University of California. * All rights reserved. The Berkeley Software License Agreement * specifies the terms and conditions for redistribution. */ #ifdef FILEC /* * Tenex style file name recognition, .. and more. * History: * Author: Ken Greer, Sept. 1975, CMU. * Finally got around to adding to the Cshell., Ken Greer, Dec. 1981. */ #include "sh.h" #include #include #include #include "sh.tconst.h" #define TRUE 1 #define FALSE 0 #define ON 1 #define OFF 0 #define ESC '\033' extern DIR *opendir_(tchar *); static char *BELL = "\07"; static char *CTRLR = "^R\n"; typedef enum {LIST, RECOGNIZE} COMMAND; static jmp_buf osetexit; /* saved setexit() state */ static struct termios tty_save; /* saved terminal state */ static struct termios tty_new; /* new terminal state */ static int is_prefix(tchar *, tchar *); static int is_suffix(tchar *, tchar *); static int ignored(tchar *); /* * Put this here so the binary can be patched with adb to enable file * completion by default. Filec controls completion, nobeep controls * ringing the terminal bell on incomplete expansions. */ bool filec = 0; static void setup_tty(int on) { int omask; #ifdef TRACE tprintf("TRACE- setup_tty()\n"); #endif omask = sigblock(sigmask(SIGINT)); if (on) { /* * The shell makes sure that the tty is not in some weird state * and fixes it if it is. But it should be noted that the * tenex routine will not work correctly in CBREAK or RAW mode * so this code below is, therefore, mandatory. * * Also, in order to recognize the ESC (filename-completion) * character, set EOL to ESC. This way, ESC will terminate * the line, but still be in the input stream. * EOT (filename list) will also terminate the line, * but will not appear in the input stream. * * The getexit/setexit contortions ensure that the * tty state will be restored if the user types ^C. */ (void) ioctl(SHIN, TCGETS, (char *)&tty_save); getexit(osetexit); if (setjmp(reslab)) { (void) ioctl(SHIN, TCSETSW, (char *)&tty_save); resexit(osetexit); reset(); } tty_new = tty_save; tty_new.c_cc[VEOL] = ESC; tty_new.c_iflag |= IMAXBEL | BRKINT | IGNPAR; tty_new.c_lflag |= ICANON; tty_new.c_lflag |= ECHOCTL; tty_new.c_oflag &= ~OCRNL; (void) ioctl(SHIN, TCSETSW, (char *)&tty_new); } else { /* * Reset terminal state to what user had when invoked */ (void) ioctl(SHIN, TCSETSW, (char *)&tty_save); resexit(osetexit); } (void) sigsetmask(omask); } static void termchars(void) { extern char *tgetstr(); char bp[1024]; static char area[256]; static int been_here = 0; char *ap = area; char *s; char *term; #ifdef TRACE tprintf("TRACE- termchars()\n"); #endif if (been_here) return; been_here = TRUE; if ((term = getenv("TERM")) == NULL) return; if (tgetent(bp, term) != 1) return; if (s = tgetstr("vb", &ap)) /* Visible Bell */ BELL = s; } /* * Move back to beginning of current line */ static void back_to_col_1(void) { int omask; #ifdef TRACE tprintf("TRACE- back_to_col_1()\n"); #endif omask = sigblock(sigmask(SIGINT)); (void) write(SHOUT, "\r", 1); (void) sigsetmask(omask); } /* * Push string contents back into tty queue */ static void pushback(tchar *string, int echoflag) { tchar *p; struct termios tty; int omask, retry = 0; #ifdef TRACE tprintf("TRACE- pushback()\n"); #endif omask = sigblock(sigmask(SIGINT)); tty = tty_new; if (!echoflag) tty.c_lflag &= ~ECHO; again: (void) ioctl(SHIN, TCSETSF, (char *)&tty); for (p = string; *p; p++) { char mbc[MB_LEN_MAX]; int i, j = wctomb(mbc, (wchar_t)*p); if (j < 0) { /* Error! But else what can we do? */ continue; } for (i = 0; i < j; ++i) { if (ioctl(SHIN, TIOCSTI, mbc + i) != 0 && errno == EAGAIN) { if (retry++ < 5) goto again; /* probably no worth retrying any more */ } } } if (tty.c_lflag != tty_new.c_lflag) (void) ioctl(SHIN, TCSETS, (char *)&tty_new); (void) sigsetmask(omask); } /* * Concatenate src onto tail of des. * Des is a string whose maximum length is count. * Always null terminate. */ void catn(tchar *des, tchar *src, int count) { #ifdef TRACE tprintf("TRACE- catn()\n"); #endif while (--count >= 0 && *des) des++; while (--count >= 0) if ((*des++ = *src++) == '\0') return; *des = '\0'; } static int max(a, b) { return (a > b ? a : b); } /* * Like strncpy but always leave room for trailing \0 * and always null terminate. */ void copyn(tchar *des, tchar *src, int count) { #ifdef TRACE tprintf("TRACE- copyn()\n"); #endif while (--count >= 0) if ((*des++ = *src++) == '\0') return; *des = '\0'; } /* * For qsort() */ static int fcompare(tchar **file1, tchar **file2) { #ifdef TRACE tprintf("TRACE- fcompare()\n"); #endif return (strcoll_(*file1, *file2)); } static char filetype(tchar *dir, tchar *file, int nosym) { tchar path[MAXPATHLEN + 1]; struct stat statb; #ifdef TRACE tprintf("TRACE- filetype()\n"); #endif if (dir) { catn(strcpy_(path, dir), file, MAXPATHLEN); if (nosym) { if (stat_(path, &statb) < 0) return (' '); } else { if (lstat_(path, &statb) < 0) return (' '); } if ((statb.st_mode & S_IFMT) == S_IFLNK) return ('@'); if ((statb.st_mode & S_IFMT) == S_IFDIR) return ('/'); if (((statb.st_mode & S_IFMT) == S_IFREG) && (statb.st_mode & 011)) return ('*'); } return (' '); } /* * Print sorted down columns */ static void print_by_column(tchar *dir, tchar *items[], int count, int looking_for_command) { int i, rows, r, c, maxwidth = 0, columns; #ifdef TRACE tprintf("TRACE- print_by_column()\n"); #endif for (i = 0; i < count; i++) maxwidth = max(maxwidth, tswidth(items[i])); /* for the file tag and space */ maxwidth += looking_for_command ? 1 : 2; columns = max(78 / maxwidth, 1); rows = (count + (columns - 1)) / columns; for (r = 0; r < rows; r++) { for (c = 0; c < columns; c++) { i = c * rows + r; if (i < count) { int w; /* * Print filename followed by * '@' or '/' or '*' or ' ' */ printf("%t", items[i]); w = tswidth(items[i]); if (!looking_for_command) { printf("%c", (tchar) filetype(dir, items[i], 0)); w++; } if (c < columns - 1) /* last column? */ for (; w < maxwidth; w++) printf(" "); } } printf("\n"); } } /* * Expand file name with possible tilde usage * ~person/mumble * expands to * home_directory_of_person/mumble */ tchar * tilde(tchar *new, tchar *old) { tchar *o, *p; struct passwd *pw; static tchar person[40]; char person_[40]; /* work */ tchar *pw_dir; /* work */ #ifdef TRACE tprintf("TRACE- tilde()\n"); #endif if (old[0] != '~') return (strcpy_(new, old)); for (p = person, o = &old[1]; *o && *o != '/'; *p++ = *o++) ; *p = '\0'; if (person[0] == '\0') (void) strcpy_(new, value(S_home /* "home" */)); else { pw = getpwnam(tstostr(person_, person)); if (pw == NULL) return (NULL); pw_dir = strtots((tchar *)NULL, pw->pw_dir); /* allocate */ (void) strcpy_(new, pw_dir); xfree(pw_dir); /* free it */ } (void) strcat_(new, o); return (new); } /* * Cause pending line to be printed */ static void sim_retype(void) { #ifdef notdef struct termios tty_pending; #ifdef TRACE tprintf("TRACE- sim_retypr()\n"); #endif tty_pending = tty_new; tty_pending.c_lflag |= PENDIN; (void) ioctl(SHIN, TCSETS, (char *)&tty_pending); #else #ifdef TRACE tprintf("TRACE- sim_retype()\n"); #endif (void) write(SHOUT, CTRLR, strlen(CTRLR)); printprompt(); #endif } static int beep_outc(int c) { char buf[1]; buf[0] = c; (void) write(SHOUT, buf, 1); return 0; } static void beep(void) { #ifdef TRACE tprintf("TRACE- beep()\n"); #endif if (adrof(S_nobeep /* "nobeep" */) == 0) (void) tputs(BELL, 0, beep_outc); } /* * Erase that silly ^[ and print the recognized part of the string. */ static void print_recognized_stuff(tchar *recognized_part) { int unit = didfds ? 1 : SHOUT; #ifdef TRACE tprintf("TRACE- print_recognized_stuff()\n"); #endif /* * An optimized erasing of that silly ^[ * * One would think that line speeds have become fast enough that this * isn't necessary, but it turns out that the visual difference is * quite noticeable. */ flush(); switch (tswidth(recognized_part)) { case 0: /* erase two characters: ^[ */ write(unit, "\b\b \b\b", sizeof "\b\b \b\b" - 1); break; case 1: /* overstrike the ^, erase the [ */ write(unit, "\b\b", 2); printf("%t", recognized_part); write(unit, " \b\b", 4); break; default: /* overstrike both characters ^[ */ write(unit, "\b\b", 2); printf("%t", recognized_part); break; } flush(); } /* * Parse full path in file into 2 parts: directory and file names * Should leave final slash (/) at end of dir. */ static void extract_dir_and_name(tchar *path, tchar *dir, tchar *name) { tchar *p; #ifdef TRACE tprintf("TRACE- extract_dir_and_name()\n"); #endif p = rindex_(path, '/'); if (p == NOSTR) { copyn(name, path, MAXNAMLEN); dir[0] = '\0'; } else { copyn(name, ++p, MAXNAMLEN); copyn(dir, path, p - path); } } tchar * getentry(DIR *dir_fd, int looking_for_lognames) { struct passwd *pw; struct dirent *dirp; /* * For char * -> tchar * Conversion */ static tchar strbuf[MAXNAMLEN+1]; #ifdef TRACE tprintf("TRACE- getentry()\n"); #endif if (looking_for_lognames) { if ((pw = getpwent()) == NULL) return (NULL); return (strtots(strbuf, pw->pw_name)); } if (dirp = readdir(dir_fd)) return (strtots(strbuf, dirp->d_name)); return (NULL); } static void free_items(tchar **items) { int i; #ifdef TRACE tprintf("TRACE- free_items()\n"); #endif for (i = 0; items[i]; i++) xfree(items[i]); xfree((char *)items); } #define FREE_ITEMS(items) { \ int omask;\ \ omask = sigblock(sigmask(SIGINT));\ free_items(items);\ items = NULL;\ (void) sigsetmask(omask);\ } /* * Perform a RECOGNIZE or LIST command on string "word". */ static int search2(tchar *word, COMMAND command, int max_word_length) { static tchar **items = NULL; DIR *dir_fd; int numitems = 0, ignoring = TRUE, nignored = 0; int name_length, looking_for_lognames; tchar tilded_dir[MAXPATHLEN + 1], dir[MAXPATHLEN + 1]; tchar name[MAXNAMLEN + 1], extended_name[MAXNAMLEN+1]; tchar *entry; #define MAXITEMS 1024 #ifdef TRACE tprintf("TRACE- search2()\n"); #endif if (items != NULL) FREE_ITEMS(items); looking_for_lognames = (*word == '~') && (index_(word, '/') == NULL); if (looking_for_lognames) { (void) setpwent(); copyn(name, &word[1], MAXNAMLEN); /* name sans ~ */ } else { extract_dir_and_name(word, dir, name); if (tilde(tilded_dir, dir) == 0) return (0); dir_fd = opendir_(*tilded_dir ? tilded_dir : S_DOT /* "." */); if (dir_fd == NULL) return (0); } again: /* search for matches */ name_length = strlen_(name); for (numitems = 0; entry = getentry(dir_fd, looking_for_lognames); ) { if (!is_prefix(name, entry)) continue; /* Don't match . files on null prefix match */ if (name_length == 0 && entry[0] == '.' && !looking_for_lognames) continue; if (command == LIST) { if (numitems >= MAXITEMS) { printf("\nYikes!! Too many %s!!\n", looking_for_lognames ? "names in password file":"files"); break; } if (items == NULL) items = (tchar **)xcalloc(sizeof (items[1]), MAXITEMS+1); items[numitems] = (tchar *)xalloc((unsigned)(strlen_(entry) + 1) * sizeof (tchar)); copyn(items[numitems], entry, MAXNAMLEN); numitems++; } else { /* RECOGNIZE command */ if (ignoring && ignored(entry)) nignored++; else if (recognize(extended_name, entry, name_length, ++numitems)) break; } } if (ignoring && numitems == 0 && nignored > 0) { ignoring = FALSE; nignored = 0; if (looking_for_lognames) (void) setpwent(); else rewinddir(dir_fd); goto again; } if (looking_for_lognames) (void) endpwent(); else { unsetfd(dir_fd->dd_fd); closedir_(dir_fd); } if (command == RECOGNIZE && numitems > 0) { if (looking_for_lognames) copyn(word, S_TIL /* "~" */, 1); else /* put back dir part */ copyn(word, dir, max_word_length); /* add extended name */ catn(word, extended_name, max_word_length); return (numitems); } if (command == LIST) { qsort((char *)items, numitems, sizeof (items[1]), (int (*)(const void *, const void *))fcompare); /* * Never looking for commands in this version, so final * argument forced to 0. If command name completion is * reinstated, this must change. */ print_by_column(looking_for_lognames ? NULL : tilded_dir, items, numitems, 0); if (items != NULL) FREE_ITEMS(items); } return (0); } /* * Object: extend what user typed up to an ambiguity. * Algorithm: * On first match, copy full entry (assume it'll be the only match) * On subsequent matches, shorten extended_name to the first * character mismatch between extended_name and entry. * If we shorten it back to the prefix length, stop searching. */ int recognize(tchar *extended_name, tchar *entry, int name_length, int numitems) { #ifdef TRACE tprintf("TRACE- recognize()\n"); #endif if (numitems == 1) /* 1st match */ copyn(extended_name, entry, MAXNAMLEN); else { /* 2nd and subsequent matches */ tchar *x, *ent; int len = 0; x = extended_name; for (ent = entry; *x && *x == *ent++; x++, len++) ; *x = '\0'; /* Shorten at 1st char diff */ if (len == name_length) /* Ambiguous to prefix? */ return (-1); /* So stop now and save time */ } return (0); } /* * Return true if check items initial chars in template * This differs from PWB imatch in that if check is null * it items anything */ static int is_prefix(tchar *check, tchar *template) { #ifdef TRACE tprintf("TRACE- is_prefix()\n"); #endif do if (*check == 0) return (TRUE); while (*check++ == *template++); return (FALSE); } /* * Return true if the chars in template appear at the * end of check, i.e., are its suffix. */ static int is_suffix(tchar *check, tchar *template) { tchar *c, *t; #ifdef TRACE tprintf("TRACE- is_suffix()\n"); #endif for (c = check; *c++; ) ; for (t = template; *t++; ) ; for (;;) { if (t == template) return (TRUE); if (c == check || *--t != *--c) return (FALSE); } } int tenex(tchar *inputline, int inputline_size) { int numitems, num_read, should_retype; int i; #ifdef TRACE tprintf("TRACE- tenex()\n"); #endif setup_tty(ON); termchars(); num_read = 0; should_retype = FALSE; while ((i = read_(SHIN, inputline+num_read, inputline_size-num_read)) > 0) { static tchar *delims = S_DELIM /* " '\"\t;&<>()|`" */; tchar *str_end, *word_start, last_char; int space_left; struct termios tty; COMMAND command; num_read += i; inputline[num_read] = '\0'; last_char = inputline[num_read - 1] & TRIM; /* * read_() can return more than requested size if there * is multibyte character at the end. */ if ((num_read >= inputline_size) || (last_char == '\n')) break; str_end = &inputline[num_read]; if (last_char == ESC) { command = RECOGNIZE; *--str_end = '\0'; /* wipe out trailing ESC */ } else command = LIST; tty = tty_new; tty.c_lflag &= ~ECHO; (void) ioctl(SHIN, TCSETSF, (char *)&tty); if (command == LIST) printf("\n"); /* * Find LAST occurence of a delimiter in the inputline. * The word start is one character past it. */ for (word_start = str_end; word_start > inputline; --word_start) { if (index_(delims, word_start[-1]) || isauxsp(word_start[-1])) break; } space_left = inputline_size - (word_start - inputline) - 1; numitems = search2(word_start, command, space_left); /* * Tabs in the input line cause trouble after a pushback. * tty driver won't backspace over them because column * positions are now incorrect. This is solved by retyping * over current line. */ if (index_(inputline, '\t')) { /* tab tchar in input line? */ back_to_col_1(); should_retype = TRUE; } if (command == LIST) /* Always retype after a LIST */ should_retype = TRUE; if (should_retype) printprompt(); pushback(inputline, should_retype); num_read = 0; /* chars will be reread */ should_retype = FALSE; /* * Avoid a race condition by echoing what we're recognized * _after_ pushing back the command line. This way, if the * user waits until seeing this output before typing more * stuff, the resulting keystrokes won't race with the STIed * input we've pushed back. (Of course, if the user types * ahead, the race still exists and it's quite possible that * the pushed back input line will interleave with the * keystrokes in unexpected ways.) */ if (command == RECOGNIZE) { /* print from str_end on */ print_recognized_stuff(str_end); if (numitems != 1) /* Beep = No match/ambiguous */ beep(); } } setup_tty(OFF); return (num_read); } static int ignored(tchar *entry) { struct varent *vp; tchar **cp; #ifdef TRACE tprintf("TRACE- ignored()\n"); #endif if ((vp = adrof(S_fignore /* "fignore" */)) == NULL || (cp = vp->vec) == NULL) return (FALSE); for (; *cp != NULL; cp++) if (is_suffix(entry, *cp)) return (TRUE); return (FALSE); } #endif /* FILEC */ /* * Copyright 2006 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ /* Copyright (c) 1983, 1984, 1985, 1986, 1987, 1988, 1989 AT&T */ /* All Rights Reserved */ /* * Copyright (c) 1980 Regents of the University of California. * All rights reserved. The Berkeley Software License Agreement * specifies the terms and conditions for redistribution. */ #include "sh.h" #include /* For LC_ALL */ #include "sh.tconst.h" #include #include /* * N.B.: Some of the limits change from SunOS 4.x to SunOS 5.0. In * particular, RLIMIT_RSS is gone and RLIMIT_VMEM is new. Beware of consusing * the keywords that the command prints for these two. The old one was * "memoryuse" and the new one is "memorysize". Note also that a given limit * doesn't necessarily appear in the same position in the two releases. */ struct limits { int limconst; tchar *limname; int limdiv; tchar *limscale; } limits[] = { RLIMIT_CPU, S_cputime, /* "cputime" */ 1, S_seconds, /* "seconds" */ RLIMIT_FSIZE, S_filesize, /* "filesize" */ 1024, S_kbytes, /* "kbytes" */ RLIMIT_DATA, S_datasize, /* "datasize" */ 1024, S_kbytes, /* "kbytes" */ RLIMIT_STACK, S_stacksize, /* "stacksize" */ 1024, S_kbytes, /* "kbytes" */ RLIMIT_CORE, S_coredumpsize, /* "coredumpsize" */ 1024, S_kbytes, /* "kbytes" */ RLIMIT_NOFILE, S_descriptors, /* "descriptors" */ 1, S_, /* "" */ RLIMIT_VMEM, S_memorysize, /* "memorysize" */ 1024, S_kbytes, /* "kbytes" */ -1, 0, }; struct Bin B; struct whyle *whyles; bool chkstop; bool doneinp; bool intty; bool setintr; int shpgrp; int opgrp; off_t lineloc; tchar *evalp; tchar **evalvec; tchar *gointr; static int getval(struct limits *lp, tchar **v, rlim_t *); void islogin(void); int dolabel(void); void reexecute(struct command *kp); void preread_(void); void doagain(void); void toend(void); void wfree(void); void echo(tchar sep, tchar **v); void local_setenv(tchar *name, tchar *val); void local_unsetenv(tchar *name); void limtail(tchar *cp, tchar *str0); void plim(struct limits *lp, tchar hard); void search(); #define BUFSZ 1028 /* * C shell */ struct biltins * isbfunc(struct command *t) { tchar *cp = t->t_dcom[0]; struct biltins *bp, *bp1, *bp2; int dofg1(), dobg1(); static struct biltins label = { S_, dolabel, 0, 0 }; static struct biltins foregnd = { S_Pjob, dofg1, 0, 0 }; static struct biltins backgnd = { S_PjobAND, dobg1, 0, 0 }; #ifdef TRACE tprintf("TRACE- isbfunc()\n"); #endif if (lastchr(cp) == ':') { label.bname = cp; return (&label); } if (*cp == '%') { if (t->t_dflg & FAND) { t->t_dflg &= ~FAND; backgnd.bname = cp; return (&backgnd); } foregnd.bname = cp; return (&foregnd); } /* * Binary search * Bp1 is the beginning of the current search range. * Bp2 is one past the end. */ for (bp1 = bfunc, bp2 = bfunc + nbfunc; bp1 < bp2; ) { int i; bp = bp1 + (bp2 - bp1 >> 1); if ((i = *cp - *bp->bname) == 0 && (i = strcmp_(cp, bp->bname)) == 0) { return (bp); } if (i < 0) { bp2 = bp; } else { bp1 = bp + 1; } } return (0); } void func(struct command *t, struct biltins *bp) { int i; #ifdef TRACE tprintf("TRACE- func()\n"); #endif xechoit(t->t_dcom); setname(bp->bname); i = blklen(t->t_dcom) - 1; if (i < bp->minargs) { bferr("Too few arguments"); } if (i > bp->maxargs) { bferr("Too many arguments"); } (*bp->bfunct)(t->t_dcom, t); } int dolabel(void) { #ifdef TRACE tprintf("TRACE- dolabel()\n"); #endif return (0); } void doonintr(tchar **v) { tchar *cp; tchar *vv = v[1]; #ifdef TRACE tprintf("TRACE- doonintr()\n"); #endif if (parintr == SIG_IGN) { return; } if (setintr && intty) { bferr("Can't from terminal"); } cp = gointr, gointr = 0, xfree(cp); if (vv == 0) { if (setintr) { (void) sigblock(sigmask(SIGINT)); } else { (void) signal(SIGINT, SIG_DFL); } gointr = 0; } else if (eq((vv = strip(vv)), S_MINUS)) { (void) signal(SIGINT, SIG_IGN); gointr = S_MINUS; } else { gointr = savestr(vv); (void) signal(SIGINT, pintr); } } void donohup(void) { #ifdef TRACE tprintf("TRACE- donohup()\n"); #endif if (intty) { bferr("Can't from terminal"); } if (setintr == 0) { (void) signal(SIGHUP, SIG_IGN); #ifdef CC submit(getpid()); #endif } } void dozip(void) { ; } void prvars(void) { #ifdef TRACE tprintf("TRACE- prvars()\n"); #endif plist(&shvhed); } void doalias(tchar **v) { struct varent *vp; tchar *p; #ifdef TRACE tprintf("TRACE- doalias()\n"); #endif v++; p = *v++; if (p == 0) { plist(&aliases); } else if (*v == 0) { vp = adrof1(strip(p), &aliases); if (vp) { blkpr(vp->vec), printf("\n"); } } else { if (eq(p, S_alias) || eq(p, S_unalias)) { setname(p); bferr("Too dangerous to alias that"); } set1(strip(p), saveblk(v), &aliases); } } void unalias(tchar **v) { #ifdef TRACE tprintf("TRACE- unalias()\n"); #endif unset1(v, &aliases); } void dologout(void) { #ifdef TRACE tprintf("TRACE- dologout()\n"); #endif islogin(); goodbye(); } void dologin(tchar **v) { char *v_; /* work */ #ifdef TRACE tprintf("TRACE- dologin()\n"); #endif islogin(); rechist(); (void) signal(SIGTERM, parterm); if (v[1] != NULL) { v_ = tstostr(NULL, v[1]); /* No need to free */ } else { v_ = 0; } execl("/bin/login", "login", v_, 0); untty(); exit(1); } #ifdef NEWGRP void donewgrp(tchar **v) { char *v_; /* work */ #ifdef TRACE tprintf("TRACE- donewgrp()\n"); #endif if (chkstop == 0 && setintr) { panystop(0); } (void) signal(SIGTERM, parterm); if (v[1] != NULL) { v_ = tstostr(NOSTR, v[1]); /* No need to free */ } else { v_ = 0; } execl("/bin/newgrp", "newgrp", v_, 0); execl("/usr/bin/newgrp", "newgrp", v_, 0); untty(); exit(1); } #endif void islogin(void) { #ifdef TRACE tprintf("TRACE- islogin()\n"); #endif if (chkstop == 0 && setintr) { panystop(0); } if (loginsh) { return; } error("Not login shell"); } void doif(tchar **v, struct command *kp) { int i; tchar **vv; #ifdef TRACE tprintf("TRACE- doif()\n"); #endif v++; i = exp(&v); vv = v; if (*vv == NOSTR) { bferr("Empty if"); } if (eq(*vv, S_then)) { if (*++vv) { bferr("Improper then"); } setname(S_then); /* * If expression was zero, then scan to else, * otherwise just fall into following code. */ if (!i) { search(ZIF, 0); } return; } /* * Simple command attached to this if. * Left shift the node in this tree, munging it * so we can reexecute it. */ if (i) { lshift(kp->t_dcom, vv - kp->t_dcom); reexecute(kp); donefds(); } } /* * Reexecute a command, being careful not * to redo i/o redirection, which is already set up. */ void reexecute(struct command *kp) { #ifdef TRACE tprintf("TRACE- reexecute()\n"); #endif kp->t_dflg &= FSAVE; kp->t_dflg |= FREDO; /* * If tty is still ours to arbitrate, arbitrate it; * otherwise dont even set pgrp's as the jobs would * then have no way to get the tty (we can't give it * to them, and our parent wouldn't know their pgrp, etc. */ execute(kp, tpgrp > 0 ? tpgrp : -1); } void doelse(void) { #ifdef TRACE tprintf("TRACE- doelse()\n"); #endif search(ZELSE, 0); } void dogoto(tchar **v) { struct whyle *wp; tchar *lp; #ifdef TRACE tprintf("TRACE- dogoto()\n"); #endif /* * While we still can, locate any unknown ends of existing loops. * This obscure code is the WORST result of the fact that we * don't really parse. */ for (wp = whyles; wp; wp = wp->w_next) { if (wp->w_end == 0) { search(ZBREAK, 0); wp->w_end = btell(); } else { bseek(wp->w_end); } } search(ZGOTO, 0, lp = globone(v[1])); xfree(lp); /* * Eliminate loops which were exited. */ wfree(); } void doswitch(tchar **v) { tchar *cp, *lp; #ifdef TRACE tprintf("TRACE- doswitch()\n"); #endif v++; if (!*v || *(*v++) != '(') { goto syntax; } cp = **v == ')' ? S_ : *v++; if (*(*v++) != ')') { v--; } if (*v) { syntax: error("Syntax error"); } search(ZSWITCH, 0, lp = globone(cp)); xfree(lp); } void dobreak(void) { #ifdef TRACE tprintf("TRACE- dobreak()\n"); #endif if (whyles) { toend(); } else { bferr("Not in while/foreach"); } } void doexit(tchar **v) { #ifdef TRACE tprintf("TRACE- doexit()\n"); #endif if (chkstop == 0) { panystop(0); } /* * Don't DEMAND parentheses here either. */ v++; if (*v) { set(S_status, putn(exp(&v))); if (*v) { bferr("Expression syntax"); } } btoeof(); if (intty) { (void) close(SHIN); unsetfd(SHIN); } } void doforeach(tchar **v) { tchar *cp; struct whyle *nwp; #ifdef TRACE tprintf("TRACE- doforeach()\n"); #endif v++; cp = strip(*v); while (*cp && alnum(*cp)) { cp++; } if (*cp || strlen_(*v) >= MAX_VAR_LEN || !letter(**v)) { bferr("Invalid variable"); } cp = *v++; if (v[0][0] != '(' || v[blklen(v) - 1][0] != ')') { bferr("Words not ()'ed"); } v++; gflag = 0, tglob(v); v = glob(v); if (v == 0) { bferr("No match"); } nwp = (struct whyle *)xcalloc(1, sizeof (*nwp)); nwp->w_fe = nwp->w_fe0 = v; gargv = 0; nwp->w_start = btell(); nwp->w_fename = savestr(cp); nwp->w_next = whyles; whyles = nwp; /* * Pre-read the loop so as to be more * comprehensible to a terminal user. */ if (intty) { preread_(); } doagain(); } void dowhile(tchar **v) { int status; bool again = whyles != 0 && whyles->w_start == lineloc && whyles->w_fename == 0; #ifdef TRACE tprintf("TRACE- dowhile()\n"); #endif v++; /* * Implement prereading here also, taking care not to * evaluate the expression before the loop has been read up * from a terminal. */ if (intty && !again) { status = !exp0(&v, 1); } else { status = !exp(&v); } if (*v) { bferr("Expression syntax"); } if (!again) { struct whyle *nwp = (struct whyle *)xcalloc(1, sizeof (*nwp)); nwp->w_start = lineloc; nwp->w_end = 0; nwp->w_next = whyles; whyles = nwp; if (intty) { /* * The tty preread */ preread_(); doagain(); return; } } if (status) { /* We ain't gonna loop no more, no more! */ toend(); } } void preread_(void) { #ifdef TRACE tprintf("TRACE- preread()\n"); #endif whyles->w_end = -1; if (setintr) { (void) sigsetmask(sigblock(0) & ~sigmask(SIGINT)); } search(ZBREAK, 0); if (setintr) { (void) sigblock(sigmask(SIGINT)); } whyles->w_end = btell(); } void doend(void) { #ifdef TRACE tprintf("TRACE- doend()\n"); #endif if (!whyles) { bferr("Not in while/foreach"); } whyles->w_end = btell(); doagain(); } void docontin(void) { #ifdef TRACE tprintf("TRACE- docontin()\n"); #endif if (!whyles) { bferr("Not in while/foreach"); } doagain(); } void doagain(void) { #ifdef TRACE tprintf("TRACE- doagain()\n"); #endif /* Repeating a while is simple */ if (whyles->w_fename == 0) { bseek(whyles->w_start); return; } /* * The foreach variable list actually has a spurious word * ")" at the end of the w_fe list. Thus we are at the * of the list if one word beyond this is 0. */ if (!whyles->w_fe[1]) { dobreak(); return; } set(whyles->w_fename, savestr(*whyles->w_fe++)); bseek(whyles->w_start); } void dorepeat(tchar **v, struct command *kp) { int i, omask; #ifdef TRACE tprintf("TRACE- dorepeat()\n"); #endif i = getn(v[1]); if (setintr) { omask = sigblock(sigmask(SIGINT)) & ~sigmask(SIGINT); } lshift(v, 2); while (i > 0) { if (setintr) { (void) sigsetmask(omask); } reexecute(kp); --i; } donefds(); if (setintr) { (void) sigsetmask(omask); } } void doswbrk(void) { #ifdef TRACE tprintf("TRACE- doswbrk()\n"); #endif search(ZBRKSW, 0); } int srchx(tchar *cp) { struct srch *sp, *sp1, *sp2; int i; #ifdef TRACE tprintf("TRACE- srchx()\n"); #endif /* * Binary search * Sp1 is the beginning of the current search range. * Sp2 is one past the end. */ for (sp1 = srchn, sp2 = srchn + nsrchn; sp1 < sp2; ) { sp = sp1 + (sp2 - sp1 >> 1); if ((i = *cp - *sp->s_name) == 0 && (i = strcmp_(cp, sp->s_name)) == 0) { return (sp->s_value); } if (i < 0) { sp2 = sp; } else { sp1 = sp + 1; } } return (-1); } tchar Stype; tchar *Sgoal; /*VARARGS2*/ void search(type, level, goal) int type; int level; tchar *goal; { tchar wordbuf[BUFSIZ]; tchar *aword = wordbuf; tchar *cp; #ifdef TRACE tprintf("TRACE- search()\n"); #endif Stype = type; Sgoal = goal; if (type == ZGOTO) { bseek((off_t)0); } do { if (intty && fseekp == feobp) { printf("? "), flush(); } aword[0] = 0; (void) getword(aword); switch (srchx(aword)) { case ZELSE: if (level == 0 && type == ZIF) { return; } break; case ZIF: while (getword(aword)) { continue; } if ((type == ZIF || type == ZELSE) && eq(aword, S_then)) { level++; } break; case ZENDIF: if (type == ZIF || type == ZELSE) { level--; } break; case ZFOREACH: case ZWHILE: if (type == ZBREAK) { level++; } break; case ZEND: if (type == ZBREAK) { level--; } break; case ZSWITCH: if (type == ZSWITCH || type == ZBRKSW) { level++; } break; case ZENDSW: if (type == ZSWITCH || type == ZBRKSW) { level--; } break; case ZLABEL: if (type == ZGOTO && getword(aword) && eq(aword, goal)) { level = -1; } break; default: if (type != ZGOTO && (type != ZSWITCH || level != 0)) { break; } if (lastchr(aword) != ':') { break; } aword[strlen_(aword) - 1] = 0; if (type == ZGOTO && eq(aword, goal) || type == ZSWITCH && eq(aword, S_default)) { level = -1; } break; case ZCASE: if (type != ZSWITCH || level != 0) { break; } (void) getword(aword); if (lastchr(aword) == ':') { aword[strlen_(aword) - 1] = 0; } cp = strip(Dfix1(aword)); if (Gmatch(goal, cp)) { level = -1; } xfree(cp); break; case ZDEFAULT: if (type == ZSWITCH && level == 0) { level = -1; } break; } (void) getword(NOSTR); } while (level >= 0); } int getword(tchar *wp) { int found = 0; int c, d; #ifdef TRACE tprintf("TRACE- getword()\n"); #endif c = readc(1); d = 0; do { while (issp(c)) { c = readc(1); } if (c == '#') { do { c = readc(1); } while (c >= 0 && c != '\n'); } if (c < 0) { goto past; } if (c == '\n') { if (wp) { break; } return (0); } /* ( and ) form separate words */ if (c == '(' || c == ')') { return (1); } unreadc(c); found = 1; do { c = readc(1); if (c == '\\' && (c = readc(1)) == '\n') { c = ' '; } if (c == '\'' || c == '"') { if (d == 0) { d = c; } else if (d == c) { d = 0; } } if (c < 0) { goto past; } if (wp) { *wp++ = c; } } while ((d || !issp(c) && c != '(' && c != ')') && c != '\n'); } while (wp == 0); unreadc(c); if (found) { *--wp = 0; } return (found); past: switch (Stype) { case ZIF: bferr("then/endif not found"); case ZELSE: bferr("endif not found"); case ZBRKSW: case ZSWITCH: bferr("endsw not found"); case ZBREAK: bferr("end not found"); case ZGOTO: setname(Sgoal); bferr("label not found"); } /*NOTREACHED*/ return (0); } void toend(void) { #ifdef TRACE tprintf("TRACE- toend()\n"); #endif if (whyles->w_end == 0) { search(ZBREAK, 0); whyles->w_end = btell() - 1; } else { bseek(whyles->w_end); } wfree(); } void wfree(void) { long o = btell(); #ifdef TRACE tprintf("TRACE- wfree()\n"); #endif while (whyles) { struct whyle *wp = whyles; struct whyle *nwp = wp->w_next; if (o >= wp->w_start && (wp->w_end == 0 || o < wp->w_end)) { break; } if (wp->w_fe0) { blkfree(wp->w_fe0); } if (wp->w_fename) { xfree(wp->w_fename); } xfree((char *)wp); whyles = nwp; } } void doecho(tchar **v) { #ifdef TRACE tprintf("TRACE- doecho()\n"); #endif echo(' ', v); } void doglob(tchar **v) { #ifdef TRACE tprintf("TRACE- doglob()\n"); #endif echo(0, v); flush(); } void echo(tchar sep, tchar **v) { tchar *cp; int nonl = 0; #ifdef TRACE tprintf("TRACE- echo()\n"); #endif if (setintr) { (void) sigsetmask(sigblock(0) & ~sigmask(SIGINT)); } v++; if (*v == 0) { /* * echo command needs to have newline when there are no * flags or arguments. glob should have no newline. If * the separator is a blank, we are doing an echo. If the * separator is zero, we are globbing. */ if (sep == (tchar)' ') Putchar('\n'); return; } gflag = 0, tglob(v); if (gflag) { v = glob(v); if (v == 0) { bferr("No match"); } } /* check for -n arg, NOTE: it might be quoted */ if (sep == ' ' && *v && strlen_(*v) == 2 && ((**v&TRIM) == '-' && (*(*v + 1) & TRIM) == 'n' && (*(*v+2)&TRIM) == 0)) { nonl++, v++; } while (cp = *v++) { int c; while (c = *cp++) { Putchar(c | QUOTE); } if (*v) { Putchar(sep | QUOTE); } } if (sep && nonl == 0) { Putchar('\n'); } else { flush(); } if (setintr) { (void) sigblock(sigmask(SIGINT)); } if (gargv) { blkfree(gargv), gargv = 0; } } extern char **environ; /* * Check if the environment variable vp affects this csh's behavior * and therefore we should call setlocale() or not. * This function has two side effects when it returns 1: * variable islocalevar_catnum is set to the LC_xxx value. * variable islocalevar_catname is set to the string "LC_xxx" */ static int islocalevar_catnum; static char *islocalevar_catname; static bool islocalevar(tchar *vp) { static struct lcinfo { tchar * evname; /* The name of the env. var. */ } categories_we_care[] = { S_LANG, S_LC_ALL, S_LC_CTYPE, S_LC_MESSAGES, NOSTR /* assumption: LC_xxx >= 0 */ }; struct lcinfo *p = categories_we_care; do { if (strcmp_(vp, p->evname) == 0) { return (1); } } while (((++p)->evname) != NOSTR); return (0); } void dosetenv(tchar **v) { tchar *vp, *lp; #ifdef TRACE tprintf("TRACE- dosetenv()\n"); #endif v++; if ((vp = *v++) == 0) { char **ep; if (setintr) { (void) sigsetmask(sigblock(0) & ~ sigmask(SIGINT)); } for (ep = environ; *ep; ep++) { printf("%s\n", *ep); } return; } if ((lp = *v++) == 0) { lp = S_; /* "" */ } local_setenv(vp, lp = globone(lp)); if (eq(vp, S_PATH)) { importpath(lp); dohash(xhash); } else if (islocalevar(vp)) { if (!setlocale(LC_ALL, "")) { error("Locale could not be set properly"); } } xfree(lp); } void dounsetenv(tchar **v) { #ifdef TRACE tprintf("TRACE- dounsetenv()\n"); #endif v++; do { local_unsetenv(*v); if (islocalevar(*v++)) { setlocale(LC_ALL, ""); /* Hope no error! */ } } while (*v); } void local_setenv(tchar *name, tchar *val) { char **ep = environ; tchar *cp; char *dp; tchar *ep_; /* temporary */ char *blk[2], **oep = ep; #ifdef TRACE /* tprintf("TRACE- local_setenv(%t, %t)\n", name, val); */ /* printf("IN local_setenv args = (%t)\n", val); */ #endif for (; *ep; ep++) { #ifdef MBCHAR for (cp = name, dp = *ep; *cp && *dp; cp++) { /* * This loop compares two chars in different * representations, EUC (as char *) and wchar_t * (in tchar), and ends when they are different. */ wchar_t dwc; int n; n = mbtowc(&dwc, dp, MB_CUR_MAX); if (n <= 0) { break; /* Illegal multibyte. */ } dp += n; /* Advance to next multibyte char. */ if (dwc == (wchar_t)(*cp & TRIM)) { continue; } else { break; } } #else /* !MBCHAR */ for (cp = name, dp = *ep; *cp && (char)*cp == *dp; cp++, dp++) { continue; } #endif /* !MBCHAR */ if (*cp != 0 || *dp != '=') { continue; } cp = strspl(S_EQ, val); xfree(*ep); ep_ = strspl(name, cp); /* ep_ is xalloc'ed */ xfree(cp); /* * Trimming is not needed here. * trim(); */ *ep = tstostr(NULL, ep_); xfree(ep_); /* because temp. use */ return; } ep_ = strspl(name, S_EQ); /* ep_ is xalloc'ed */ blk[0] = tstostr(NULL, ep_); blk[1] = 0; xfree(ep_); environ = (char **)blkspl_((char **)environ, blk); xfree((void *)oep); local_setenv(name, val); } void local_unsetenv(tchar *name) { char **ep = environ; tchar *cp; char *dp; char **oep = ep; char *cp_; /* tmp use */ static int cnt = 0; /* delete counter */ #ifdef TRACE tprintf("TRACE- local_unsetenv()\n"); #endif for (; *ep; ep++) { #ifdef MBCHAR for (cp = name, dp = *ep; *cp && *dp; cp++) { /* * This loop compares two chars in different * representations, EUC (as char *) and wchar_t * (in tchar), and ends when they are different. */ wchar_t dwc; int n; n = mbtowc(&dwc, dp, MB_CUR_MAX); if (n <= 0) { break; /* Illegal multibyte. */ } dp += n; /* Advance to next multibyte char. */ if (dwc == (wchar_t)(*cp & TRIM)) { continue; } else { break; } } #else /* !MBCHAR */ for (cp = name, dp = *ep; *cp && (char)*cp == *dp; cp++, dp++) { continue; } #endif /* !MBCHAR */ if (*cp != 0 || *dp != '=') { continue; } cp_ = *ep; *ep = 0; environ = (char **)blkspl_((char **)environ, ep+1); *ep = cp_; xfree(cp_); xfree((void *)oep); return; } } void doumask(tchar **v) { tchar *cp = v[1]; int i; #ifdef TRACE tprintf("TRACE- dounmask()\n"); #endif if (cp == 0) { i = umask(0); (void) umask(i); printf("%o\n", i); return; } i = 0; while (digit(*cp) && *cp != '8' && *cp != '9') { i = i * 8 + *cp++ - '0'; } if (*cp || i < 0 || i > 0777) { bferr("Improper mask"); } (void) umask(i); } struct limits * findlim(tchar *cp) { struct limits *lp, *res; #ifdef TRACE tprintf("TRACE- findlim()\n"); #endif res = 0; for (lp = limits; lp->limconst >= 0; lp++) { if (prefix(cp, lp->limname)) { if (res) { bferr("Ambiguous"); } res = lp; } } if (res) { return (res); } bferr("No such limit"); /*NOTREACHED*/ } void dolimit(tchar **v) { struct limits *lp; rlim_t limit; tchar hard = 0; #ifdef TRACE tprintf("TRACE- dolimit()\n"); #endif v++; if (*v && eq(*v, S_h)) { hard = 1; v++; } if (*v == 0) { for (lp = limits; lp->limconst >= 0; lp++) { plim(lp, hard); } return; } lp = findlim(v[0]); if (v[1] == 0) { plim(lp, hard); return; } switch (getval(lp, v+1, &limit)) { case 0: error("Value specified for limit is too large"); return; case (-1): error("Numeric conversion failed"); return; default: if (setlim(lp, hard, limit) < 0) { error(NOSTR); } } } static int getval(struct limits *lp, tchar **v, rlim_t *retval) { rlim_t value, tmp, tmp2; tchar *cp = *v++; char chbuf[BUFSIZ * MB_LEN_MAX]; #ifdef TRACE tprintf("TRACE- getval()\n"); #endif tstostr(chbuf, cp); errno = 0; value = strtoull(chbuf, NULL, 0); /* * we must accept zero, but the conversion can fail and give us * zero as well...try to deal with it as gracefully as possible * by checking for EINVAL */ if (value == 0 && errno == EINVAL) return (-1); while (digit(*cp) || *cp == '.' || *cp == 'e' || *cp == 'E') { cp++; } if (*cp == 0) { if (*v == 0) { tmp = value * (rlim_t)lp->limdiv; /* Check for overflow */ if (tmp >= value) { *retval = tmp; return (1); } else { return (0); } } cp = *v; } switch (*cp) { case ':': if (lp->limconst != RLIMIT_CPU) { goto badscal; } tstostr(chbuf, cp + 1); tmp = strtoull(chbuf, NULL, 0); tmp2 = value * 60 + tmp; if (tmp2 >= value) { *retval = tmp2; return (1); } else { return (0); } case 'h': if (lp->limconst != RLIMIT_CPU) { goto badscal; } limtail(cp, S_hours); tmp = value * 3600; if (tmp < value) { return (0); } value = tmp; break; case 'm': if (lp->limconst == RLIMIT_CPU) { limtail(cp, S_minutes); tmp = value * 60; if (tmp < value) { return (0); } value = tmp; break; } case 'M': if (lp->limconst == RLIMIT_CPU) { goto badscal; } *cp = 'm'; limtail(cp, S_megabytes); tmp = value * 1024 * 1024; if (tmp < value) { return (0); } value = tmp; break; case 's': if (lp->limconst != RLIMIT_CPU) { goto badscal; } limtail(cp, S_seconds); break; case 'k': if (lp->limconst == RLIMIT_CPU) { goto badscal; } limtail(cp, S_kbytes); tmp = value * 1024; if (tmp < value) { return (0); } value = tmp; break; case 'u': limtail(cp, S_unlimited); *retval = RLIM_INFINITY; return (1); default: badscal: bferr("Improper or unknown scale factor"); } *retval = value; return (1); } void limtail(tchar *cp, tchar *str0) { tchar *str = str0; #ifdef TRACE tprintf("TRACE- limtail()\n"); #endif while (*cp && *cp == *str) { cp++, str++; } if (*cp) { error("Bad scaling; did you mean ``%t''?", str0); } } void plim(struct limits *lp, tchar hard) { struct rlimit rlim; char buf[BUFSZ]; char *pbuf; rlim_t limit; #ifdef TRACE tprintf("TRACE- plim()\n"); #endif printf("%t \t", lp->limname); (void) getrlimit(lp->limconst, &rlim); limit = hard ? rlim.rlim_max : rlim.rlim_cur; if (limit == RLIM_INFINITY) { printf("unlimited"); } else if (lp->limconst == RLIMIT_CPU) { psecs_ull(limit); } else { buf[BUFSZ - 1] = '\0'; pbuf = ulltostr((limit / lp->limdiv), &buf[BUFSZ - 1]); printf("%s %t", pbuf, lp->limscale); } printf("\n"); } void dounlimit(tchar **v) { struct limits *lp; int err = 0; tchar hard = 0; #ifdef TRACE tprintf("TRACE- dounlimit()\n"); #endif v++; if (*v && eq(*v, S_h)) { hard = 1; v++; } if (*v == 0) { for (lp = limits; lp->limconst >= 0; lp++) { if (setlim(lp, hard, RLIM_INFINITY) < 0) { err++; } } if (err) { error(NULL); } return; } while (*v) { lp = findlim(*v++); if (setlim(lp, hard, RLIM_INFINITY) < 0) { error(NULL); } } } int setlim(struct limits *lp, tchar hard, rlim_t limit) { struct rlimit rlim; #ifdef TRACE tprintf("TRACE- setlim()\n"); #endif (void) getrlimit(lp->limconst, &rlim); if (hard) { rlim.rlim_max = limit; } else if (limit == RLIM_INFINITY && geteuid() != 0) { rlim.rlim_cur = rlim.rlim_max; } else { rlim.rlim_cur = limit; } if (setrlimit(lp->limconst, &rlim) < 0) { printf("%t: %t: Can't %s%s limit\n", bname, lp->limname, limit == RLIM_INFINITY ? "remove" : "set", hard ? " hard" : ""); return (-1); } return (0); } void dosuspend() { int ctpgrp; void (*old)(); #ifdef TRACE tprintf("TRACE- dosuspend()\n"); #endif if (loginsh) { error("Can't suspend a login shell (yet)"); } if (getpid() == getsid(0)) { error("Can't suspend this shell"); } untty(); old = (void (*)())signal(SIGTSTP, SIG_DFL); (void) kill(0, SIGTSTP); /* the shell stops here */ (void) signal(SIGTSTP, old); if (tpgrp != -1) { retry: (void) ioctl(FSHTTY, TIOCGPGRP, (char *)&ctpgrp); if (ctpgrp != opgrp) { old = (void (*)())signal(SIGTTIN, SIG_DFL); (void) kill(0, SIGTTIN); (void) signal(SIGTTIN, old); goto retry; } (void) setpgid(0, shpgrp); (void) ioctl(FSHTTY, TIOCSPGRP, (char *)&shpgrp); } } void doeval(tchar **v) { tchar **oevalvec = evalvec; tchar *oevalp = evalp; jmp_buf osetexit; int reenter; tchar **gv = 0; #ifdef TRACE tprintf("TRACE- doeval()\n"); #endif v++; if (*v == 0) { return; } gflag = 0, tglob(v); if (gflag) { gv = v = glob(v); gargv = 0; if (v == 0) { error("No match"); } v = copyblk(v); } else { trim(v); } getexit(osetexit); reenter = 0; setexit(); reenter++; if (reenter == 1) { evalvec = v; evalp = 0; process(0); } evalvec = oevalvec; evalp = oevalp; doneinp = 0; if (gv) { blkfree(gv); } resexit(osetexit); if (reenter >= 2) { error(NULL); } } /* * Copyright 2005 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ /* Copyright (c) 1983, 1984, 1985, 1986, 1987, 1988, 1989 AT&T */ /* All Rights Reserved */ /* * Copyright (c) 1980 Regents of the University of California. * All rights reserved. The Berkeley Software License Agreement * specifies the terms and conditions for redistribution. */ #include "sh.h" #include "sh.tconst.h" #include #include #ifdef MBCHAR #include /* wcsetno() */ #include /* fnmatch() */ #endif /* MBCHAR */ /* * C Shell */ static long pargc; static long gnleft; static long pnleft; int globcnt; tchar *arginp; static tchar *pargs; tchar *gpath, *gpathp, *lastgpathp; int globbed; bool noglob; bool nonomatch; tchar *entp; static tchar *pargcp; tchar **sortbas; int sortscmp(tchar **, tchar **); void ginit(tchar **); void collect(tchar *); void acollect(tchar *); void expand(tchar *); void matchdir_(tchar *); void Gcat(tchar *, tchar *); void addpath(tchar); void tglob(tchar **); tchar **dobackp(tchar *, bool); void backeval(tchar *, bool); void psave(tchar); void pword(void); extern DIR *opendir_(tchar *); #define sort() qsort((char *)sortbas, &gargv[gargc] - sortbas, \ sizeof (*sortbas), (int (*)(const void *, \ const void *)) sortscmp), sortbas = &gargv[gargc] tchar ** glob(tchar **v) { tchar agpath[BUFSIZ]; tchar *agargv[GAVSIZ]; gpath = agpath; gpathp = gpath; *gpathp = 0; lastgpathp = &gpath[BUFSIZ - 2]; ginit(agargv); globcnt = 0; #ifdef TRACE tprintf("TRACE- glob()\n"); #endif #ifdef GDEBUG printf("glob entered: "); blkpr(v); printf("\n"); #endif noglob = adrof(S_noglob /* "noglob" */) != 0; nonomatch = adrof(S_nonomatch /* "nonomatch" */) != 0; globcnt = noglob | nonomatch; while (*v) collect(*v++); #ifdef GDEBUG printf("glob done, globcnt=%d, gflag=%d: ", globcnt, gflag); blkpr(gargv); printf("\n"); #endif if (globcnt == 0 && (gflag&1)) { blkfree(gargv), gargv = 0; return (0); } else return (gargv = copyblk(gargv)); } void ginit(tchar **agargv) { agargv[0] = 0; gargv = agargv; sortbas = agargv; gargc = 0; gnleft = NCARGS - 4; } void collect(tchar *as) { int i; #ifdef TRACE tprintf("TRACE- collect()\n"); #endif if (any('`', as)) { #ifdef GDEBUG printf("doing backp of %t\n", as); #endif (void) dobackp(as, 0); #ifdef GDEBUG printf("backp done, acollect'ing\n"); #endif /* * dobackp has the side effect of messing with * gflag, since it does more globbing, so check * if the results is still globbable */ tglob(pargv); for (i = 0; i < pargc; i++) if (noglob) { Gcat(pargv[i], S_ /* "" */); sortbas = &gargv[gargc]; } else acollect(pargv[i]); if (pargv) blkfree(pargv), pargv = 0; #ifdef GDEBUG printf("acollect done\n"); #endif } else if (noglob || eq(as, S_LBRA /* "{" */) || eq(as, S_BRABRA /* "{}" */)) { Gcat(as, S_ /* "" */); sort(); } else acollect(as); } void acollect(tchar *as) { long ogargc = gargc; #ifdef TRACE tprintf("TRACE- acollect()\n"); #endif gpathp = gpath; *gpathp = 0; globbed = 0; expand(as); if (gargc == ogargc) { if (nonomatch) { Gcat(as, S_ /* "" */); sort(); } } else sort(); } /* * String compare for qsort. Also used by filec code in sh.file.c. */ int sortscmp(tchar **a1, tchar **a2) { return (strcoll_(*a1, *a2)); } void expand(tchar *as) { tchar *cs; tchar *sgpathp, *oldcs; struct stat stb; #ifdef TRACE tprintf("TRACE- expand()\n"); #endif sgpathp = gpathp; cs = as; if (*cs == '~' && gpathp == gpath) { addpath('~'); for (cs++; alnum(*cs) || *cs == '-'; ) addpath(*cs++); if (!*cs || *cs == '/') { if (gpathp != gpath + 1) { *gpathp = 0; if (gethdir(gpath + 1)) /* * modified from %s to %t */ error("Unknown user: %t", gpath + 1); (void) strcpy_(gpath, gpath + 1); } else (void) strcpy_(gpath, value(S_home /* "home" */)); gpathp = strend(gpath); } } while (!isglob(*cs)) { if (*cs == 0) { if (!globbed) Gcat(gpath, S_ /* "" */); else if (lstat_(gpath, &stb) >= 0) { Gcat(gpath, S_ /* "" */); globcnt++; } goto endit; } addpath(*cs++); } oldcs = cs; while (cs > as && *cs != '/') cs--, gpathp--; if (*cs == '/') cs++, gpathp++; *gpathp = 0; if (*oldcs == '{') { (void) execbrc(cs, NOSTR); return; } matchdir_(cs); endit: gpathp = sgpathp; *gpathp = 0; } void matchdir_(tchar *pattern) { struct stat stb; struct dirent *dp; DIR *dirp; tchar curdir_[MAXNAMLEN+1]; int slproc = 0; #ifdef TRACE tprintf("TRACE- matchdir()\n"); #endif /* * BSD's opendir would open "." if argument is NULL, but not S5 */ if (*gpath == '\0') dirp = opendir_(S_DOT /* "." */); else dirp = opendir_(gpath); if (dirp == NULL) { if (globbed) return; goto patherr2; } if (fstat(dirp->dd_fd, &stb) < 0) goto patherr1; if (!isdir(stb)) { errno = ENOTDIR; goto patherr1; } while ((dp = readdir(dirp)) != NULL) { if (dp->d_ino == 0) continue; strtots(curdir_, dp->d_name); slproc = 0; if (match(curdir_, pattern, &slproc)) { Gcat(gpath, curdir_); globcnt++; } } unsetfd(dirp->dd_fd); closedir_(dirp); return; patherr1: unsetfd(dirp->dd_fd); closedir_(dirp); patherr2: Perror(gpath); } int execbrc(tchar *p, tchar *s) { tchar restbuf[BUFSIZ + 2]; tchar *pe, *pm, *pl; int brclev = 0; tchar *lm, savec, *sgpathp; int slproc = 0; #ifdef TRACE tprintf("TRACE- execbrc()\n"); #endif for (lm = restbuf; *p != '{'; *lm++ = *p++) continue; for (pe = ++p; *pe; pe++) switch (*pe) { case '{': brclev++; continue; case '}': if (brclev == 0) goto pend; brclev--; continue; case '[': for (pe++; *pe && *pe != ']'; pe++) continue; if (!*pe) error("Missing ]"); continue; } pend: if (brclev || !*pe) error("Missing }"); for (pl = pm = p; pm <= pe; pm++) switch (*pm & (QUOTE|TRIM)) { case '{': brclev++; continue; case '}': if (brclev) { brclev--; continue; } goto doit; case ',': if (brclev) continue; doit: savec = *pm; *pm = 0; (void) strcpy_(lm, pl); (void) strcat_(restbuf, pe + 1); *pm = savec; if (s == 0) { sgpathp = gpathp; expand(restbuf); gpathp = sgpathp; *gpathp = 0; } else if (amatch(s, restbuf, &slproc)) return (1); sort(); pl = pm + 1; continue; case '[': for (pm++; *pm && *pm != ']'; pm++) continue; if (!*pm) error("Missing ]"); continue; } return (0); } int match(tchar *s, tchar *p, int *slproc) { int c; tchar *sentp; tchar sglobbed = globbed; #ifdef TRACE tprintf("TRACE- match()\n"); #endif if (*s == '.' && *p != '.') return (0); sentp = entp; entp = s; c = amatch(s, p, slproc); entp = sentp; globbed = sglobbed; return (c); } int amatch(tchar *s, tchar *p, int *slproc) { int scc; int ok, lc; tchar *sgpathp; struct stat stb; int c, cc; #ifdef TRACE tprintf("TRACE- amatch()\n"); #endif globbed = 1; for (;;) { scc = *s++ & TRIM; switch (c = *p++) { case '{': return (execbrc(p - 1, s - 1)); case '[': ok = 0; lc = TRIM; while (cc = *p++) { if (cc == ']') { if (ok) break; return (0); } if (cc == '-') { #ifdef MBCHAR wchar_t rc = *p++; if (rc == ']') { p--; continue; } /* * Both ends of the char range * must belong to the same codeset. */ if (sh_bracket_exp(scc, lc, rc)) ok++; #else /* !MBCHAR */ if (lc <= scc && scc <= (int)*p++) ok++; #endif /* !MBCHAR */ } else if (scc == (lc = cc)) ok++; } if (cc == 0) error("Missing ]"); continue; case '*': if (!*p) return (1); if (*p == '/') { p++; goto slash; } else if (*p == '*') { s--; continue; } for (s--; *s; s++) if (amatch(s, p, slproc)) return (1); return (0); case 0: return (scc == 0); default: if ((c & TRIM) != scc) return (0); continue; case '?': if (scc == 0) return (0); continue; case '/': if (scc) return (0); slash: if (*slproc) /* Need to expand "/" only once */ return (0); else *slproc = 1; s = entp; sgpathp = gpathp; while (*s) addpath(*s++); addpath('/'); if (stat_(gpath, &stb) == 0 && isdir(stb)) if (*p == 0) { Gcat(gpath, S_ /* "" */); globcnt++; } else expand(p); gpathp = sgpathp; *gpathp = 0; return (0); } } } int Gmatch(tchar *s, tchar *p) { int scc; int ok, lc; int c, cc; #ifdef TRACE tprintf("TRACE- Gmatch()\n"); #endif for (;;) { scc = *s++ & TRIM; switch (c = *p++) { case '[': ok = 0; lc = TRIM; while (cc = *p++) { if (cc == ']') { if (ok) break; return (0); } if (cc == '-') { #ifdef MBCHAR wchar_t rc = *p++; /* * Both ends of the char range * must belong to the same codeset... */ if (sh_bracket_exp(scc, lc, rc)) ok++; #else /* !MBCHAR */ if (lc <= scc && scc <= (int)*p++) ok++; #endif /* !MBCHAR */ } else if (scc == (lc = cc)) ok++; } if (cc == 0) bferr("Missing ]"); continue; case '*': if (!*p) return (1); for (s--; *s; s++) if (Gmatch(s, p)) return (1); return (0); case 0: return (scc == 0); default: if ((c & TRIM) != scc) return (0); continue; case '?': if (scc == 0) return (0); continue; } } } void Gcat(tchar *s1, tchar *s2) { tchar *p, *q; int n; #ifdef TRACE tprintf("TRACE- Gcat()\n"); #endif for (p = s1; *p++; ) ; for (q = s2; *q++; ) ; gnleft -= (n = (p - s1) + (q - s2) - 1); if (gnleft <= 0 || ++gargc >= GAVSIZ) error("Arguments too long"); gargv[gargc] = 0; p = gargv[gargc - 1] = (tchar *) xalloc((unsigned)n*sizeof (tchar)); for (q = s1; *p++ = *q++; ) ; for (p--, q = s2; *p++ = *q++; ) ; } void addpath(tchar c) { #ifdef TRACE tprintf("TRACE- addpath()\n"); #endif if (gpathp >= lastgpathp) error("Pathname too long"); *gpathp++ = c & TRIM; *gpathp = 0; } void rscan(tchar **t, int (*f)(int)) { tchar *p; #ifdef TRACE tprintf("TRACE- rscan()\n"); #endif while (p = *t++) while (*p) (*f)(*p++); } void trim(tchar **t) { tchar *p; #ifdef TRACE tprintf("TRACE- trim()\n"); #endif while (p = *t++) while (*p) *p++ &= TRIM; } void tglob(tchar **t) { tchar *p, c; #ifdef TRACE tprintf("TRACE- tglob()\n"); #endif while (p = *t++) { if (*p == '~') gflag |= 2; else if (*p == '{' && (p[1] == '\0' || p[1] == '}' && p[2] == '\0')) continue; while (c = *p++) if (isglob(c)) gflag |= c == '{' ? 2 : 1; } } tchar * globone(tchar *str) { tchar *gv[2]; tchar **gvp; tchar *cp; #ifdef TRACE tprintf("TRACE- globone()\n"); #endif gv[0] = str; gv[1] = 0; gflag = 0; tglob(gv); if (gflag) { gvp = glob(gv); if (gvp == 0) { setname(str); bferr("No match"); } cp = *gvp++; if (cp == 0) cp = S_ /* "" */; else if (*gvp) { setname(str); bferr("Ambiguous"); } else cp = strip(cp); #if 0 if (cp == 0 || *gvp) { setname(str); bferr(cp ? "Ambiguous" : "No output"); } #endif xfree((char *)gargv); gargv = 0; } else { trim(gv); cp = savestr(gv[0]); } return (cp); } /* * Command substitute cp. If literal, then this is * a substitution from a << redirection, and so we should * not crunch blanks and tabs, separating words only at newlines. */ tchar ** dobackp(tchar *cp, bool literal) { tchar *lp, *rp; tchar *ep; tchar word[BUFSIZ]; tchar *apargv[GAVSIZ + 2]; #ifdef TRACE tprintf("TRACE- dobackp()\n"); #endif if (pargv) { blkfree(pargv); } pargv = apargv; pargv[0] = NOSTR; pargcp = pargs = word; pargc = 0; pnleft = BUFSIZ - 4; for (;;) { for (lp = cp; *lp != '`'; lp++) { if (*lp == 0) { if (pargcp != pargs) pword(); #ifdef GDEBUG printf("leaving dobackp\n"); #endif return (pargv = copyblk(pargv)); } psave(*lp); } lp++; for (rp = lp; *rp && *rp != '`'; rp++) if (*rp == '\\') { rp++; if (!*rp) goto oops; } if (!*rp) oops: error("Unmatched `"); ep = savestr(lp); ep[rp - lp] = 0; backeval(ep, literal); #ifdef GDEBUG printf("back from backeval\n"); #endif cp = rp + 1; } } void backeval(tchar *cp, bool literal) { int pvec[2]; int quoted = (literal || (cp[0] & QUOTE)) ? QUOTE : 0; tchar ibuf[BUFSIZ + MB_LEN_MAX]; /* read_ can return extra bytes */ int icnt = 0, c; tchar *ip; bool hadnl = 0; tchar *fakecom[2]; struct command faket; #ifdef TRACE tprintf("TRACE- backeval()\n"); #endif faket.t_dtyp = TCOM; faket.t_dflg = 0; faket.t_dlef = 0; faket.t_drit = 0; faket.t_dspr = 0; faket.t_dcom = fakecom; fakecom[0] = S_QPPPQ; /* "` ... `" */; fakecom[1] = 0; /* * We do the psave job to temporarily change the current job * so that the following fork is considered a separate job. * This is so that when backquotes are used in a * builtin function that calls glob the "current job" is not corrupted. * We only need one level of pushed jobs as long as we are sure to * fork here. */ psavejob(); /* * It would be nicer if we could integrate this redirection more * with the routines in sh.sem.c by doing a fake execute on a builtin * function that was piped out. */ mypipe(pvec); if (pfork(&faket, -1) == 0) { struct wordent paraml; struct command *t; tchar oHIST; new_process(); (void) close(pvec[0]); unsetfd(pvec[0]); (void) dmove(pvec[1], 1); (void) dmove(SHDIAG, 2); reinitdesc(0, NULL); arginp = cp; while (*cp) *cp++ &= TRIM; /* * disable history subsitution in sub-shell * of `` evaluation prevents possible * infinite recursion of `` evaluation */ oHIST = HIST; HIST = 0; (void) lex(¶ml); HIST = oHIST; if (err_msg) error("%s", gettext(err_msg)); alias(¶ml); t = syntax(paraml.next, ¶ml, 0); if (err_msg) error("%s", gettext(err_msg)); if (t) t->t_dflg |= FPAR; (void) signal(SIGTSTP, SIG_IGN); (void) signal(SIGTTIN, SIG_IGN); (void) signal(SIGTTOU, SIG_IGN); execute(t, -1); exitstat(); } xfree(cp); (void) close(pvec[1]); unsetfd(pvec[1]); do { int cnt = 0; for (;;) { if (icnt == 0) { ip = ibuf; icnt = read_(pvec[0], ip, BUFSIZ); if (icnt <= 0) { c = -1; break; } } if (hadnl) break; --icnt; c = (*ip++ & TRIM); if (c == 0) break; if (c == '\n') { /* * Continue around the loop one * more time, so that we can eat * the last newline without terminating * this word. */ hadnl = 1; continue; } if (!quoted && issp(c)) break; cnt++; psave(c | quoted); } /* * Unless at end-of-file, we will form a new word * here if there were characters in the word, or in * any case when we take text literally. If * we didn't make empty words here when literal was * set then we would lose blank lines. */ if (c != -1 && (cnt || literal)) { if (pargc == GAVSIZ) break; pword(); } hadnl = 0; } while (c >= 0); #ifdef GDEBUG printf("done in backeval, pvec: %d %d\n", pvec[0], pvec[1]); printf("also c = %c <%o>\n", (tchar) c, (tchar) c); #endif (void) close(pvec[0]); unsetfd(pvec[0]); pwait(); prestjob(); } void psave(tchar c) { #ifdef TRACE tprintf("TRACE- psave()\n"); #endif if (--pnleft <= 0) error("Word too long"); *pargcp++ = c; } void pword(void) { #ifdef TRACE tprintf("TRACE- pword()\n"); #endif psave(0); if (pargc == GAVSIZ) error("Too many words from ``"); pargv[pargc++] = savestr(pargs); pargv[pargc] = NOSTR; #ifdef GDEBUG printf("got word %t\n", pargv[pargc-1]); #endif pargcp = pargs; pnleft = BUFSIZ - 4; } /* * returns pathname of the form dir/file; * dir is a null-terminated string; */ char * makename(char *dir, char *file) { /* * Maximum length of a * file/dir name in ls-command; * dfile is static as this is returned * by makename(); */ static char dfile[MAXNAMLEN]; char *dp, *fp; dp = dfile; fp = dir; while (*fp) *dp++ = *fp++; if (dp > dfile && *(dp - 1) != '/') *dp++ = '/'; fp = file; while (*fp) *dp++ = *fp++; *dp = '\0'; /* * dfile points to the absolute pathname. We are * only interested in the last component. */ return (rindex(dfile, '/') + 1); } int sh_bracket_exp(tchar t_ch, tchar t_fch, tchar t_lch) { char t_char[MB_LEN_MAX + 1]; char t_patan[MB_LEN_MAX * 2 + 8]; char *p; int i; if ((t_ch == t_fch) || (t_ch == t_lch)) return (1); p = t_patan; if ((i = wctomb(t_char, (wchar_t)t_ch)) <= 0) return (0); t_char[i] = 0; *p++ = '['; if ((i = wctomb(p, (wchar_t)t_fch)) <= 0) return (0); p += i; *p++ = '-'; if ((i = wctomb(p, (wchar_t)t_lch)) <= 0) return (0); p += i; *p++ = ']'; *p = 0; if (fnmatch(t_patan, t_char, FNM_NOESCAPE)) return (0); return (1); } /* * Copyright 2006 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ /* Copyright (c) 1983, 1984, 1985, 1986, 1987, 1988, 1989 AT&T */ /* All Rights Reserved */ /* * Copyright (c) 1980 Regents of the University of California. * All rights reserved. The Berkeley Software License Agreement * specifies the terms and conditions for redistribution. */ #include /* MB_xxx, mbxxx(), wcxxx() etc. */ #include #include #include #include #include #include #include #include #include #include #include /* std sysV signal.h */ #include #include #include /* for MAXHOSTNAMELEN */ #include "signal.h" /* mainly BSD related signal.h */ #include "sh.local.h" #include "sh.char.h" #ifdef MBCHAR #if !defined(MB_LEN_MAX) || !defined(MB_CUR_MAX) Error: I need both ANSI macros! #endif #else #if !defined(MB_LEN_MAX) #define MB_LEN_MAX 1 #endif #if !defined(MB_CUR_MAX) #define MB_CUR_MAX 1 #endif #endif #ifndef MBCHAR /* Let's replace the ANSI functions with our own macro * for efficiency! */ #define mbtowc(pwc, pmb, n_is_ignored) ((*(pwc) = *(pmb)), 1) #define wctomb(pmb, wc) ((*(pmb) = ((char)wc)), 1) #endif /* !MBCHAR */ /* * C shell * * Bill Joy, UC Berkeley * October, 1978; May 1980 * * Jim Kulp, IIASA, Laxenburg Austria * April, 1980 */ /* * If we are setting the $cwd variable becuz we did a * cd, chdir, pushd, popd command, then set didchdir to * 1. This prevents globbing down when setting $cwd. * However, if the user set $cwd, we want the globbing * done; so, didchdir would be equal to 0 in that case. */ extern int didchdir; #define isdir(d) ((d.st_mode & S_IFMT) == S_IFDIR) typedef char bool; /* * tchar (Tagged CHARacter) is a place holder to keep a QUOTE bit and * a character. * For European language handling, lower 8 bits of tchar is used * to store a character. For other languages, especially Asian, 16 bits * are used to store a character. * Following typedef's assume short int is a 16-bit entity and long int is * a 32-bit entity. * The QUOTE bit tells whether the character is subject to further * interpretation such as history substitution, file mathing, command * subsitution. TRIM is a mask to strip off the QUOTE bit. */ #ifdef MBCHAR /* For multibyte character handling. */ typedef long int tchar; #define QUOTE 0x80000000 #define TRIM 0x7fffffff #else /* !MBCHAR */ /* European language requires only 8 bits. */ typedef unsigned short int tchar; #define QUOTE 0x8000 #define TRIM 0x00ff #endif /* !MBCHAR */ #define eq(a, b) (strcmp_(a, b) == 0) /* * Global flags */ extern bool chkstop; /* Warned of stopped jobs... allow exit */ extern bool didfds; /* Have setup i/o fd's for child */ extern bool doneinp; /* EOF indicator after reset from readc */ extern bool exiterr; /* Exit if error or non-zero exit status */ extern bool child; /* Child shell ... errors cause exit */ extern bool haderr; /* Reset was because of an error */ extern bool intty; /* Input is a tty */ extern bool cflg; /* invoked with -c option */ extern bool justpr; /* Just print because of :p hist mod */ extern bool loginsh; /* We are a loginsh -> .login/.logout */ extern bool neednote; /* Need to pnotify() */ extern bool noexec; /* Don't execute, just syntax check */ extern bool pjobs; /* want to print jobs if interrupted */ extern bool setintr; /* Set interrupts on/off -> Wait intr... */ extern bool havhash; /* path hashing is available */ extern bool havhash2; /* cdpath hashing is available */ #ifdef FILEC extern bool filec; /* doing filename expansion */ #endif /* * Global i/o info */ extern tchar *arginp; /* Argument input for sh -c and internal `xx` */ extern int onelflg; /* 2 -> need line for -t, 1 -> exit on read */ extern tchar *file; /* Name of shell file for $0 */ extern char *err_msg; /* Error message from scanner/parser */ extern struct timeval time0; /* Time at which the shell started */ /* * Miscellany */ extern tchar *doldol; /* Character pid for $$ */ extern int uid; /* Invokers uid */ extern int shpgrp; /* Pgrp of shell */ extern int tpgrp; /* Terminal process group */ /* If tpgrp is -1, leave tty alone! */ extern int opgrp; /* Initial pgrp and tty pgrp */ /* * These are declared here because they want to be * initialized in sh.init.c (to allow them to be made readonly) */ extern struct biltins { tchar *bname; int (*bfunct)(); short minargs, maxargs; } bfunc[]; extern int nbfunc; extern struct srch { tchar *s_name; short s_value; } srchn[]; extern int nsrchn; /* * To be able to redirect i/o for builtins easily, the shell moves the i/o * descriptors it uses away from 0,1,2. * Ideally these should be in units which are closed across exec's * (this saves work) but for version 6, this is not usually possible. * The desired initial values for these descriptors are defined in * sh.local.h. */ extern short SHIN; /* Current shell input (script) */ extern short SHOUT; /* Shell output */ extern short SHDIAG; /* Diagnostic output... shell errs go here */ extern short OLDSTD; /* Old standard input (def for cmds) */ /* * Error control * * Errors in scanning and parsing set up an error message to be printed * at the end and complete. Other errors always cause a reset. * Because of source commands and .cshrc we need nested error catches. */ extern jmp_buf reslab; #define setexit() ((void) setjmp(reslab)) #define reset() longjmp(reslab, 0) /* Should use structure assignment here */ #define getexit(a) copy((void *)(a), (void *)reslab, sizeof reslab) #define resexit(a) copy((void *)reslab, ((void *)(a)), sizeof reslab) extern tchar *gointr; /* Label for an onintr transfer */ extern void (*parintr)(); /* Parents interrupt catch */ extern void (*parterm)(); /* Parents terminate catch */ /* * Each level of input has a buffered input structure. * There are one or more blocks of buffered input for each level, * exactly one if the input is seekable and tell is available. * In other cases, the shell buffers enough blocks to keep all loops * in the buffer. */ extern struct Bin { off_t Bfseekp; /* Seek pointer */ off_t Bfbobp; /* Seekp of beginning of buffers */ off_t Bfeobp; /* Seekp of end of buffers */ short Bfblocks; /* Number of buffer blocks */ tchar **Bfbuf; /* The array of buffer blocks */ } B; #define fseekp B.Bfseekp #define fbobp B.Bfbobp #define feobp B.Bfeobp #define fblocks B.Bfblocks #define fbuf B.Bfbuf #define btell() fseekp #ifndef btell extern off_t btell(void); #endif /* * The shell finds commands in loops by reseeking the input * For whiles, in particular, it reseeks to the beginning of the * line the while was on; hence the while placement restrictions. */ extern off_t lineloc; #ifdef TELL extern bool cantell; /* Is current source tellable ? */ #endif /* * Input lines are parsed into doubly linked circular * lists of words of the following form. */ struct wordent { tchar *word; struct wordent *prev; struct wordent *next; }; /* * During word building, both in the initial lexical phase and * when expanding $ variable substitutions, expansion by `!' and `$' * must be inhibited when reading ahead in routines which are themselves * processing `!' and `$' expansion or after characters such as `\' or in * quotations. The following flags are passed to the getC routines * telling them which of these substitutions are appropriate for the * next character to be returned. */ #define DODOL 1 #define DOEXCL 2 #define DOALL DODOL|DOEXCL extern tchar *lap; /* * Parser structure * * Each command is parsed to a tree of command structures and * flags are set bottom up during this process, to be propagated down * as needed during the semantics/exeuction pass (sh.sem.c). */ struct command { short t_dtyp; /* Type of node */ short t_dflg; /* Flags, e.g. FAND|... */ union { tchar *T_dlef; /* Input redirect word */ struct command *T_dcar; /* Left part of list/pipe */ } L; union { tchar *T_drit; /* Output redirect word */ struct command *T_dcdr; /* Right part of list/pipe */ } R; #define t_dlef L.T_dlef #define t_dcar L.T_dcar #define t_drit R.T_drit #define t_dcdr R.T_dcdr tchar **t_dcom; /* Command/argument vector */ char *cfname; /* char pathname for execv */ char **cargs; /* char arg vec for execv */ struct command *t_dspr; /* Pointer to ()'d subtree */ short t_nice; }; #define TCOM 1 /* t_dcom t_drit */ #define TPAR 2 /* ( t_dspr ) t_drit */ #define TFIL 3 /* t_dlef | t_drit */ #define TLST 4 /* t_dlef ; t_drit */ #define TOR 5 /* t_dlef || t_drit */ #define TAND 6 /* t_dlef && t_drit */ #define FSAVE (FNICE|FTIME|FNOHUP) /* save these when re-doing */ #define FAND (1<<0) /* executes in background */ #define FCAT (1<<1) /* output is redirected >> */ #define FPIN (1<<2) /* input is a pipe */ #define FPOU (1<<3) /* output is a pipe */ #define FPAR (1<<4) /* don't fork, last ()ized cmd */ #define FINT (1<<5) /* should be immune from intr's */ /* spare */ #define FDIAG (1<<7) /* redirect unit 2 with unit 1 */ #define FANY (1<<8) /* output was ! */ #define FHERE (1<<9) /* input redirection is << */ #define FREDO (1<<10) /* reexec aft if, repeat,... */ #define FNICE (1<<11) /* t_nice is meaningful */ #define FNOHUP (1<<12) /* nohup this command */ #define FTIME (1<<13) /* time this command */ /* * The keywords for the parser */ #define ZBREAK 0 #define ZBRKSW 1 #define ZCASE 2 #define ZDEFAULT 3 #define ZELSE 4 #define ZEND 5 #define ZENDIF 6 #define ZENDSW 7 #define ZEXIT 8 #define ZFOREACH 9 #define ZGOTO 10 #define ZIF 11 #define ZLABEL 12 #define ZLET 13 #define ZSET 14 #define ZSWITCH 15 #define ZTEST 16 #define ZTHEN 17 #define ZWHILE 18 /* * Structure defining the existing while/foreach loops at this * source level. Loops are implemented by seeking back in the * input. For foreach (fe), the word list is attached here. */ extern struct whyle { off_t w_start; /* Point to restart loop */ off_t w_end; /* End of loop (0 if unknown) */ tchar **w_fe, **w_fe0; /* Current/initial wordlist for fe */ tchar *w_fename; /* Name for fe */ struct whyle *w_next; /* Next (more outer) loop */ } *whyles; /* * Variable structure * * Aliases and variables are stored in AVL balanced binary trees. */ extern struct varent { tchar **vec; /* Array of words which is the value */ tchar *v_name; /* Name of variable/alias */ struct varent *v_link[3]; /* The links, see below */ int v_bal; /* Balance factor */ } shvhed, aliases; #define v_left v_link[0] #define v_right v_link[1] #define v_parent v_link[2] struct varent *adrof1(); #define adrof(v) adrof1(v, &shvhed) #define value(v) value1(v, &shvhed) /* * MAX_VAR_LEN - maximum variable name defined by csh man page to be 128 */ #define MAX_VAR_LEN 128 /* * MAX_VREF_LEN - maximum variable reference $name[...] * it can be as big as a csh word, which is 1024 */ #define MAX_VREF_LEN 1024 /* * The following are for interfacing redo substitution in * aliases to the lexical routines. */ extern struct wordent *alhistp; /* Argument list (first) */ extern struct wordent *alhistt; /* Node after last in arg list */ extern tchar **alvec; /* The (remnants of) alias vector */ /* * Filename/command name expansion variables */ extern short gflag; /* After tglob -> is globbing needed? */ /* * A reasonable limit on number of arguments would seem to be * the maximum number of characters in an arg list / 6. * * XXX: With the new VM system, NCARGS has become enormous, making * it impractical to allocate arrays with NCARGS / 6 entries on * the stack. The proper fix is to revamp code elsewhere (in * sh.dol.c and sh.glob.c) to use a different technique for handling * command line arguments. In the meantime, we simply fall back * on using the old value of NCARGS. */ #ifdef notyet #define GAVSIZ (NCARGS / 6) #else /* notyet */ #define GAVSIZ (10240 / 6) #endif /* notyet */ /* * Variables for filename expansion */ extern tchar **gargv; /* Pointer to the (stack) arglist */ extern long gargc; /* Number args in gargv */ /* * Variables for command expansion. */ extern tchar **pargv; /* Pointer to the argv list space */ /* * History list * * Each history list entry contains an embedded wordlist * from the scanner, a number for the event, and a reference count * to aid in discarding old entries. * * Essentially "invisible" entries are put on the history list * when history substitution includes modifiers, and thrown away * at the next discarding since their event numbers are very negative. */ extern struct Hist { struct wordent Hlex; int Hnum; int Href; struct Hist *Hnext; } Histlist; extern struct wordent paraml; /* Current lexical word list */ extern int eventno; /* Next events number */ extern tchar HIST; /* history invocation character */ extern tchar HISTSUB; /* auto-substitute character */ extern void *xalloc(size_t); extern void *xcalloc(size_t, size_t); extern void *xrealloc(void *, size_t); extern void xfree(void *); extern void Putchar(tchar); extern void bferr(char *) __NORETURN; extern void error() __NORETURN; extern void exitstat(void) __NORETURN; extern tchar *Dfix1(tchar *); extern tchar **blkcpy(tchar **, tchar **); extern tchar **blkspl(tchar **, tchar **); extern char **blkspl_(char **, char **); extern tchar **copyblk(tchar **); extern tchar **dobackp(tchar *, bool); extern tchar *domod(tchar *, int); extern struct Hist *enthist(int, struct wordent *, bool); extern tchar *getenv_(tchar *); extern tchar *getenvs_(char *); extern tchar *getwd_(tchar *); extern tchar **glob(tchar **); extern tchar *globone(tchar *); extern tchar *index_(tchar *, tchar); extern struct biltins *isbfunc(struct command *); extern void pintr(void); extern void pchild(int); extern tchar *putn(int); extern tchar *rindex_(tchar *, tchar); extern tchar **saveblk(tchar **); extern tchar *savestr(tchar *); extern tchar *strcat_(tchar *, tchar *); extern int strlen_(tchar *); extern tchar *strcpy_(tchar *, tchar *); extern tchar *strend(tchar *); extern tchar *strip(tchar *); extern tchar *strspl(tchar *, tchar *); extern struct command *syntax(struct wordent *, struct wordent *, int); extern tchar *value1(tchar *, struct varent *); #define NOSTR ((tchar *) 0) /* * setname is a macro to copy the path in bname. (see sh.err.c) * Here we are dynamically reallocating the bname to the new length * to store the new path */ extern tchar *bname; #define setname(a) { \ bname = xrealloc(bname, (strlen_(a)+1) * sizeof (tchar)); \ strcpy_(bname, a); \ bname[strlen_(a)] = '\0'; \ } #ifdef VFORK extern tchar *Vsav; extern tchar **Vav; extern tchar *Vdp; #endif extern tchar **evalvec; extern tchar *evalp; /* Conversion functions between char and tchar strings. */ tchar *strtots(tchar *, char *); char *tstostr(char *, tchar *); #ifndef NULL #define NULL 0 #endif /* * Xhash is an array of HSHSIZ bits (HSHSIZ / 8 chars), which are used * to hash execs. If it is allocated (havhash true), then to tell * whether ``name'' is (possibly) present in the i'th component * of the variable path, you look at the bit in xhash indexed by * hash(hashname("name"), i). This is setup automatically * after .login is executed, and recomputed whenever ``path'' is * changed. * The two part hash function is designed to let texec() call the * more expensive hashname() only once and the simple hash() several * times (once for each path component checked). * Byte size is assumed to be 8. */ #define HSHSIZ (32*1024) /* 4k bytes */ #define HSHMASK (HSHSIZ - 1) #define HSHMUL 243 /* * The following two arrays are used for caching. xhash * is for caching path variable and xhash2 is for cdpath * variable. */ extern char xhash[HSHSIZ / 8]; extern char xhash2[HSHSIZ / 8]; #define hash(a, b) ((a) * HSHMUL + (b) & HSHMASK) #define bit(h, b) ((h)[(b) >> 3] & 1 << ((b) & 7)) /* bit test */ #define bis(h, b) ((h)[(b) >> 3] |= 1 << ((b) & 7)) /* bit set */ /* * Copyright 2005 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ /* Copyright (c) 1983, 1984, 1985, 1986, 1987, 1988, 1989 AT&T */ /* All Rights Reserved */ /* * Copyright (c) 1980 Regents of the University of California. * All rights reserved. The Berkeley Software License Agreement * specifies the terms and conditions for redistribution. */ #include "sh.h" #include "sh.tconst.h" struct Hist Histlist; int eventno; struct Hist *enthist(int, struct wordent *, bool); void hfree(struct Hist *); void dohist1(struct Hist *, int *, int, int); void phist(struct Hist *, int); /* * C shell */ void savehist(struct wordent *sp) { struct Hist *hp, *np; int histlen = 0; tchar *cp; #ifdef TRACE tprintf("TRACE- savehist()\n"); #endif /* throw away null lines */ if (sp->next->word[0] == '\n') return; cp = value(S_history /*"history"*/); if (*cp) { tchar *p = cp; while (*p) { if (!digit(*p)) { histlen = 0; break; } histlen = histlen * 10 + *p++ - '0'; } } for (hp = &Histlist; np = hp->Hnext;) if (eventno - np->Href >= histlen || histlen == 0) hp->Hnext = np->Hnext, hfree(np); else hp = np; (void) enthist(++eventno, sp, 1); } struct Hist * enthist(int event, struct wordent *lp, bool docopy) { struct Hist *np; #ifdef TRACE tprintf("TRACE- enthist()\n"); #endif np = (struct Hist *) xalloc(sizeof *np); np->Hnum = np->Href = event; if (docopy) copylex(&np->Hlex, lp); else { np->Hlex.next = lp->next; lp->next->prev = &np->Hlex; np->Hlex.prev = lp->prev; lp->prev->next = &np->Hlex; } np->Hnext = Histlist.Hnext; Histlist.Hnext = np; return (np); } void hfree(struct Hist *hp) { #ifdef TRACE tprintf("TRACE- hfree()\n"); #endif freelex(&hp->Hlex); xfree( (tchar *)hp); } void dohist(tchar **vp) { int n, rflg = 0, hflg = 0; #ifdef TRACE tprintf("TRACE- dohist()\n"); #endif if (getn(value(S_history /*"history"*/)) == 0) return; if (setintr) (void) sigsetmask(sigblock(0) & ~sigmask(SIGINT)); while (*++vp && **vp == '-') { tchar *vp2 = *vp; while (*++vp2) switch (*vp2) { case 'h': hflg++; break; case 'r': rflg++; break; case '-': /* ignore multiple '-'s */ break; default: printf("Unknown flag: -%c\n", *vp2); error("Usage: history [-rh] [# number of events]"); } } if (*vp) n = getn(*vp); else { n = getn(value(S_history /*"history"*/)); } dohist1(Histlist.Hnext, &n, rflg, hflg); } void dohist1(struct Hist *hp, int *np, int rflg, int hflg) { bool print = (*np) > 0; #ifdef TRACE tprintf("TRACE- dohist1()\n"); #endif top: if (hp == 0) return; (*np)--; hp->Href++; if (rflg == 0) { dohist1(hp->Hnext, np, rflg, hflg); if (print) phist(hp, hflg); return; } if (*np >= 0) phist(hp, hflg); hp = hp->Hnext; goto top; } void phist(struct Hist *hp, int hflg) { #ifdef TRACE tprintf("TRACE- phist()\n"); #endif if (hflg == 0) printf("%6d\t", hp->Hnum); prlex(&hp->Hlex); } /* * Copyright 1997 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ /* Copyright (c) 1983, 1984, 1985, 1986, 1987, 1988, 1989 AT&T */ /* All Rights Reserved */ /* * Copyright (c) 1980 Regents of the University of California. * All rights reserved. The Berkeley Software License Agreement * specifies the terms and conditions for redistribution. */ #include "sh.h" #include "sh.tconst.h" /* * C shell */ extern int doalias(); extern int dobg(); extern int dobreak(); extern int dochngd(); extern int docontin(); extern int dodirs(); extern int doecho(); extern int doelse(); extern int doend(); extern int doendif(); extern int doendsw(); extern int doeval(); extern int doexit(); extern int dofg(); extern int doforeach(); extern int doglob(); extern int dogoto(); extern int dohash(); extern int dorehash(); extern int dohist(); extern int doif(); extern int dojobs(); extern int dokill(); extern int dolet(); extern int dolimit(); extern int dologin(); extern int dologout(); #ifdef NEWGRP extern int donewgrp(); #endif extern int donice(); extern int donotify(); extern int donohup(); extern int doonintr(); extern int dopopd(); extern int dopushd(); extern int dorepeat(); extern int doset(); extern int dosetenv(); extern int dosource(); extern int dostop(); extern int dosuspend(); extern int doswbrk(); extern int doswitch(); extern int dotime(); extern int dounlimit(); extern int doumask(); extern int dowait(); extern int dowhile(); extern int dozip(); extern int execash(); extern int goodbye(); #ifdef VFORK extern int hashstat(); #endif extern int shift(); #ifdef OLDMALLOC extern int showall(); #endif extern int unalias(); extern int dounhash(); extern int unset(); extern int dounsetenv(); #define INF 1000 struct biltins bfunc[] = { S_AT, dolet, 0, INF, S_alias, doalias, 0, INF, #ifdef OLDMALLOC S_alloc, showall, 0, 1, #endif S_bg, dobg, 0, INF, S_break, dobreak, 0, 0, S_breaksw, doswbrk, 0, 0, #ifdef IIASA S_bye, goodbye, 0, 0, #endif S_case, dozip, 0, 1, S_cd, dochngd, 0, 1, S_chdir, dochngd, 0, 1, S_continue, docontin, 0, 0, S_default, dozip, 0, 0, S_dirs, dodirs, 0, 1, S_echo, doecho, 0, INF, S_else, doelse, 0, INF, S_end, doend, 0, 0, S_endif, dozip, 0, 0, S_endsw, dozip, 0, 0, S_eval, doeval, 0, INF, S_exec, execash, 1, INF, S_exit, doexit, 0, INF, S_fg, dofg, 0, INF, S_foreach, doforeach, 3, INF, #ifdef IIASA S_gd, dopushd, 0, 1, #endif S_glob, doglob, 0, INF, S_goto, dogoto, 1, 1, #ifdef VFORK S_hashstat, hashstat, 0, 0, #endif S_history, dohist, 0, 2, S_if, doif, 1, INF, S_jobs, dojobs, 0, 1, S_kill, dokill, 1, INF, S_limit, dolimit, 0, 3, S_login, dologin, 0, 1, S_logout, dologout, 0, 0, #ifdef NEWGRP S_newgrp, donewgrp, 1, 1, #endif S_nice, donice, 0, INF, S_nohup, donohup, 0, INF, S_notify, donotify, 0, INF, S_onintr, doonintr, 0, 2, S_popd, dopopd, 0, 1, S_pushd, dopushd, 0, 1, #ifdef IIASA S_rd, dopopd, 0, 1, #endif S_rehash, dorehash, 0, 0, S_repeat, dorepeat, 2, INF, S_set, doset, 0, INF, S_setenv, dosetenv, 0, 2, S_shift, shift, 0, 1, S_source, dosource, 1, 2, S_stop, dostop, 1, INF, S_suspend, dosuspend, 0, 0, S_switch, doswitch, 1, INF, S_time, dotime, 0, INF, S_umask, doumask, 0, 1, S_unalias, unalias, 1, INF, S_unhash, dounhash, 0, 0, S_unlimit, dounlimit, 0, INF, S_unset, unset, 1, INF, S_unsetenv, dounsetenv, 1, INF, S_wait, dowait, 0, 0, S_while, dowhile, 1, INF, }; int nbfunc = sizeof bfunc / sizeof *bfunc; #define ZBREAK 0 #define ZBRKSW 1 #define ZCASE 2 #define ZDEFAULT 3 #define ZELSE 4 #define ZEND 5 #define ZENDIF 6 #define ZENDSW 7 #define ZEXIT 8 #define ZFOREACH 9 #define ZGOTO 10 #define ZIF 11 #define ZLABEL 12 #define ZLET 13 #define ZSET 14 #define ZSWITCH 15 #define ZTEST 16 #define ZTHEN 17 #define ZWHILE 18 struct srch srchn[] = { S_AT, ZLET, S_break, ZBREAK, S_breaksw, ZBRKSW, S_case, ZCASE, S_default, ZDEFAULT, S_else, ZELSE, S_end, ZEND, S_endif, ZENDIF, S_endsw, ZENDSW, S_exit, ZEXIT, S_foreach, ZFOREACH, S_goto, ZGOTO, S_if, ZIF, S_label, ZLABEL, S_set, ZSET, S_switch, ZSWITCH, S_while, ZWHILE }; int nsrchn = sizeof srchn / sizeof *srchn; /* * Copyright 2006 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ /* Copyright (c) 1983, 1984, 1985, 1986, 1987, 1988, 1989 AT&T */ /* All Rights Reserved */ /* * Copyright (c) 1980 Regents of the University of California. * All rights reserved. The Berkeley Software License Agreement * specifies the terms and conditions for redistribution. */ #include /* for lseek prototype */ #include "sh.h" #include "sh.tconst.h" #include #include #define RAW O_RAW /* * C shell */ bool justpr; static int lastev; int onelflg; tchar **alvec; struct wordent *alhistp; struct wordent *alhistt; struct wordent paraml; /* * These lexical routines read input and form lists of words. * There is some involved processing here, because of the complications * of input buffering, and especially because of history substitution. */ tchar *word(void); tchar getC1(int); tchar *subword(tchar *, int, bool *); void getdol(void); void addla(tchar *); void getexcl(tchar); void noev(tchar *); void setexclp(tchar *); void unreadc(tchar); int readc(bool); struct wordent *dosub(int, struct wordent *, bool); struct Hist *findev(tchar *, bool); struct wordent *gethent(int); struct wordent *getsub(struct wordent *); /* * Peekc is a peek characer for getC, peekread for readc. * There is a subtlety here in many places... history routines * will read ahead and then insert stuff into the input stream. * If they push back a character then they must push it behind * the text substituted by the history substitution. On the other * hand in several places we need 2 peek characters. To make this * all work, the history routines read with getC, and make use both * of ungetC and unreadc. The key observation is that the state * of getC at the call of a history reference is such that calls * to getC from the history routines will always yield calls of * readc, unless this peeking is involved. That is to say that during * getexcl the variables lap, exclp, and exclnxt are all zero. * * Getdol invokes history substitution, hence the extra peek, peekd, * which it can ungetD to be before history substitutions. */ tchar peekc, peekd; tchar peekread; tchar *exclp; /* (Tail of) current word from ! subst */ struct wordent *exclnxt; /* The rest of the ! subst words */ int exclc; /* Count of remainig words in ! subst */ tchar *alvecp; /* "Globp" for alias resubstitution */ /* * Lex returns to its caller not only a wordlist (as a "var" parameter) * but also whether a history substitution occurred. This is used in * the main (process) routine to determine whether to echo, and also * when called by the alias routine to determine whether to keep the * argument list. */ bool hadhist; tchar getCtmp; #define getC(f) ((getCtmp = peekc) ? (peekc = 0, getCtmp) : getC1(f)) #define ungetC(c) peekc = c #define ungetD(c) peekd = c bool lex(struct wordent *hp) { struct wordent *wdp; int c; #ifdef TRACE tprintf("TRACE- lex()\n"); #endif lineloc = btell(); hp->next = hp->prev = hp; hp->word = S_ /* "" */; alvecp = 0, hadhist = 0; do c = readc(0); while (issp(c)); /* make sure history is enabled */ if (HIST && c == HISTSUB && intty) /* ^lef^rit from tty is short !:s^lef^rit */ getexcl(c); else unreadc(c); wdp = hp; /* * The following loop is written so that the links needed * by freelex will be ready and rarin to go even if it is * interrupted. */ do { struct wordent *new = (struct wordent *)xalloc(sizeof *wdp); new->word = 0; new->prev = wdp; new->next = hp; wdp->next = new; wdp = new; wdp->word = word(); } while (wdp->word[0] != '\n'); #ifdef TRACE tprintf("Exiting lex()\n"); #endif hp->prev = wdp; return (hadhist); } void prlex(struct wordent *sp0) { struct wordent *sp = sp0->next; #ifdef TRACE tprintf("TRACE- prlex()\n"); #endif for (;;) { printf("%t", sp->word); sp = sp->next; if (sp == sp0) break; if (sp->word[0] != '\n') Putchar(' '); } } void copylex(struct wordent *hp, struct wordent *fp) { struct wordent *wdp; #ifdef TRACE tprintf("TRACE- copylex()\n"); #endif wdp = hp; fp = fp->next; do { struct wordent *new = (struct wordent *)xalloc(sizeof *wdp); new->prev = wdp; new->next = hp; wdp->next = new; wdp = new; wdp->word = savestr(fp->word); fp = fp->next; } while (wdp->word[0] != '\n'); hp->prev = wdp; } void freelex(struct wordent *vp) { struct wordent *fp; #ifdef TRACE tprintf("TRACE- freelex()\n"); #endif while (vp->next != vp) { fp = vp->next; vp->next = fp->next; xfree(fp->word); xfree(fp); } vp->prev = vp; } tchar * word(void) { tchar c, c1; tchar *wp; tchar wbuf[BUFSIZ]; bool dolflg; int i; #ifdef TRACE tprintf("TRACE- word()\n"); #endif wp = wbuf; i = BUFSIZ - 4; loop: while (issp(c = getC(DOALL))) ; if (cmap(c, _META|_ESC)||isauxsp(c)) switch (c) { case '&': case '|': case '<': case '>': *wp++ = c; c1 = getC(DOALL); if (c1 == c) *wp++ = c1; else ungetC(c1); goto ret; case '#': if (intty) break; c = 0; do { c1 = c; c = getC(0); } while (c != '\n'); if (c1 == '\\') goto loop; /* fall into ... */ case ';': case '(': case ')': case '\n': *wp++ = c; goto ret; case '\\': c = getC(0); if (c == '\n') { if (onelflg == 1) onelflg = 2; goto loop; } if (c != HIST) *wp++ = '\\', --i; c |= QUOTE; } c1 = 0; dolflg = DOALL; for (;;) { if (c1) { if (c == c1) { c1 = 0; dolflg = DOALL; } else if (c == '\\') { c = getC(0); if (c == HIST) c |= QUOTE; else { if (c == '\n') #if 0 if (c1 == '`') c = ' '; else #endif c |= QUOTE; ungetC(c); c = '\\'; } } else if (c == '\n') { seterrc(gettext("Unmatched "), c1); ungetC(c); break; } } else if (cmap(c, _META|_Q|_Q1|_ESC)||isauxsp(c)) { if (c == '\\') { c = getC(0); if (c == '\n') { if (onelflg == 1) onelflg = 2; break; } if (c != HIST) *wp++ = '\\', --i; c |= QUOTE; } else if (cmap(c, _Q|_Q1)) { /* '"` */ c1 = c; dolflg = c == '"' ? DOALL : DOEXCL; } else if (c != '#' || !intty) { ungetC(c); break; } } if (--i > 0) { *wp++ = c; c = getC(dolflg); } else { seterr("Word too long"); wp = &wbuf[1]; break; } } ret: *wp = 0; #ifdef TRACE tprintf("word() returning:%t\n", wbuf); #endif return (savestr(wbuf)); } tchar getC1(int flag) { tchar c; top: if (c = peekc) { peekc = 0; return (c); } if (lap) { if ((c = *lap++) == 0) lap = 0; else { /* * don't quote things if there was an error (err!=0) * the input is original, not from a substitution and * therefore should not be quoted */ if (!err_msg && cmap(c, _META|_Q|_Q1)||isauxsp(c)) c |= QUOTE; return (c); } } if (c = peekd) { peekd = 0; return (c); } if (exclp) { if (c = *exclp++) return (c); if (exclnxt && --exclc >= 0) { exclnxt = exclnxt->next; setexclp(exclnxt->word); return (' '); } exclp = 0; exclnxt = 0; } if (exclnxt) { exclnxt = exclnxt->next; if (--exclc < 0) exclnxt = 0; else setexclp(exclnxt->word); goto top; } c = readc(0); if (c == '$' && (flag & DODOL)) { getdol(); goto top; } if (c == HIST && (flag & DOEXCL)) { getexcl(0); goto top; } return (c); } void getdol(void) { tchar *np, *p; tchar name[MAX_VREF_LEN]; int c; int sc; bool special = 0; #ifdef TRACE tprintf("TRACE- getdol()\n"); #endif np = name, *np++ = '$'; c = sc = getC(DOEXCL); if (isspnl(c)) { ungetD(c); ungetC('$' | QUOTE); return; } if (c == '{') *np++ = c, c = getC(DOEXCL); if (c == '#' || c == '?') special++, *np++ = c, c = getC(DOEXCL); *np++ = c; switch (c) { case '<': case '$': case '*': if (special) goto vsyn; goto ret; case '\n': ungetD(c); np--; goto vsyn; default: p = np; if (digit(c)) { /* make sure the variable names are MAX_VAR_LEN chars or less */ while (digit(c = getC(DOEXCL)) && (np - p) < MAX_VAR_LEN) { *np++ = c; } } else if (letter(c)) { while ((letter(c = getC(DOEXCL)) || digit(c)) && (np - p) < MAX_VAR_LEN) { *np++ = c; } } else goto vsyn; if ((np - p) > MAX_VAR_LEN) { seterr("Variable name too long"); goto ret; } } if (c == '[') { *np++ = c; do { c = getC(DOEXCL); if (c == '\n') { ungetD(c); np--; goto vsyn; } /* need to leave space for possible modifiers */ if (np >= &name[MAX_VREF_LEN - 8]) { seterr("Variable reference too long"); goto ret; } *np++ = c; } while (c != ']'); c = getC(DOEXCL); } if (c == ':') { *np++ = c, c = getC(DOEXCL); if (c == 'g') *np++ = c, c = getC(DOEXCL); *np++ = c; if (!any(c, S_htrqxe)) goto vsyn; } else ungetD(c); if (sc == '{') { c = getC(DOEXCL); if (c != '}') { ungetC(c); goto vsyn; } *np++ = c; } ret: *np = 0; addla(name); return; vsyn: seterr("Variable syntax"); goto ret; } void addla(tchar *cp) { tchar *buf; static tchar *labuf = NULL; int len = 0; #ifdef TRACE tprintf("TRACE- addla()\n"); #endif if (lap) { len = strlen_(lap); buf = xalloc((len+1) * sizeof (tchar)); (void) strcpy_(buf, lap); } len += strlen_(cp); /* len+5 is allow 4 additional charecters just to be safe */ labuf = xrealloc(labuf, (len+5) * sizeof (tchar)); (void) strcpy_(labuf, cp); if (lap) { (void) strcat_(labuf, buf); xfree(buf); } lap = labuf; } tchar lhsb[256]; tchar slhs[256]; tchar rhsb[512]; int quesarg; void getexcl(tchar sc) { struct wordent *hp, *ip; int left, right, dol; int c; #ifdef TRACE tprintf("TRACE- getexcl()\n"); #endif if (sc == 0) { sc = getC(0); if (sc != '{') { ungetC(sc); sc = 0; } } quesarg = -1; lastev = eventno; hp = gethent(sc); if (hp == 0) return; hadhist = 1; dol = 0; if (hp == alhistp) for (ip = hp->next->next; ip != alhistt; ip = ip->next) dol++; else for (ip = hp->next->next; ip != hp->prev; ip = ip->next) dol++; left = 0, right = dol; if (sc == HISTSUB) { ungetC('s'), unreadc(HISTSUB), c = ':'; goto subst; } c = getC(0); /* if (!any(c, ":^$*-%")) */ /* change needed for char -> tchar */ if (! (c == ':' || c == '^' || c == '$' || c == '*' || c == '-' || c == '%')) goto subst; left = right = -1; if (c == ':') { c = getC(0); unreadc(c); if (letter(c) || c == '&') { c = ':'; left = 0, right = dol; goto subst; } } else ungetC(c); if (!getsel(&left, &right, dol)) return; c = getC(0); if (c == '*') ungetC(c), c = '-'; if (c == '-') { if (!getsel(&left, &right, dol)) return; c = getC(0); } subst: exclc = right - left + 1; while (--left >= 0) hp = hp->next; if (sc == HISTSUB || c == ':') { do { hp = getsub(hp); c = getC(0); } while (c == ':'); } unreadc(c); if (sc == '{') { c = getC(0); if (c != '}') seterr("Bad ! form"); } exclnxt = hp; } struct wordent * getsub(struct wordent *en) { tchar *cp; int delim; int c; int sc; bool global = 0; tchar orhsb[(sizeof rhsb)/(sizeof rhsb[0])]; #ifdef TRACE tprintf("TRACE- getsub()\n"); #endif exclnxt = 0; sc = c = getC(0); if (c == 'g') global++, c = getC(0); switch (c) { case 'p': justpr++; goto ret; case 'x': case 'q': global++; /* fall into ... */ case 'h': case 'r': case 't': case 'e': break; case '&': if (slhs[0] == 0) { seterr("No prev sub"); goto ret; } (void) strcpy_(lhsb, slhs); break; #if 0 case '~': if (lhsb[0] == 0) goto badlhs; break; #endif case 's': delim = getC(0); if (alnum(delim) || isspnl(delim)) { unreadc(delim); bads: lhsb[0] = 0; seterr("Bad substitute"); goto ret; } cp = lhsb; for (;;) { c = getC(0); if (c == '\n') { unreadc(c); break; } if (c == delim) break; if (cp > &lhsb[(sizeof lhsb)/(sizeof lhsb[0]) - 2]) goto bads; if (c == '\\') { c = getC(0); if (c != delim && c != '\\') *cp++ = '\\'; } *cp++ = c; } if (cp != lhsb) *cp++ = 0; else if (lhsb[0] == 0) { /* badlhs: */ seterr("No prev lhs"); goto ret; } cp = rhsb; (void) strcpy_(orhsb, cp); for (;;) { c = getC(0); if (c == '\n') { unreadc(c); break; } if (c == delim) break; #if 0 if (c == '~') { if (&cp[strlen_(orhsb)] > &rhsb[(sizeof rhsb)/(sizeof rhsb[0]) - 2]) goto toorhs; (void) strcpy_(cp, orhsb); cp = strend(cp); continue; } #endif if (cp > &rhsb[(sizeof rhsb)/(sizeof rhsb[0]) - 2]) { /* toorhs: */ seterr("Rhs too long"); goto ret; } if (c == '\\') { c = getC(0); if (c != delim /* && c != '~' */) *cp++ = '\\'; } *cp++ = c; } *cp++ = 0; break; default: if (c == '\n') unreadc(c); seterrc(gettext("Bad ! modifier: "), c); goto ret; } (void) strcpy_(slhs, lhsb); if (exclc) en = dosub(sc, en, global); ret: return (en); } struct wordent * dosub(int sc, struct wordent *en, bool global) { struct wordent lex; bool didsub = 0; struct wordent *hp = &lex; struct wordent *wdp; int i = exclc; #ifdef TRACE tprintf("TRACE- dosub()\n"); #endif wdp = hp; while (--i >= 0) { struct wordent *new = (struct wordent *)xcalloc(1, sizeof *wdp); new->prev = wdp; new->next = hp; wdp->next = new; wdp = new; en = en->next; wdp->word = global || didsub == 0 ? subword(en->word, sc, &didsub) : savestr(en->word); } if (didsub == 0) seterr("Modifier failed"); hp->prev = wdp; return (&enthist(-1000, &lex, 0)->Hlex); } tchar * subword(tchar *cp, int type, bool *adid) { tchar wbuf[BUFSIZ]; tchar *wp, *mp, *np; int i; #ifdef TRACE tprintf("TRACE- subword()\n"); #endif switch (type) { case 'r': case 'e': case 'h': case 't': case 'q': case 'x': wp = domod(cp, type); if (wp == 0) return (savestr(cp)); *adid = 1; return (wp); default: wp = wbuf; i = BUFSIZ - 4; for (mp = cp; *mp; mp++) if (matchs(mp, lhsb)) { for (np = cp; np < mp; ) *wp++ = *np++, --i; for (np = rhsb; *np; np++) switch (*np) { case '\\': if (np[1] == '&') np++; /* fall into ... */ default: if (--i < 0) goto ovflo; *wp++ = *np; continue; case '&': i -= strlen_(lhsb); if (i < 0) goto ovflo; *wp = 0; (void) strcat_(wp, lhsb); wp = strend(wp); continue; } mp += strlen_(lhsb); i -= strlen_(mp); if (i < 0) { ovflo: seterr("Subst buf ovflo"); return (S_ /* "" */); } *wp = 0; (void) strcat_(wp, mp); *adid = 1; return (savestr(wbuf)); } return (savestr(cp)); } } tchar * domod(tchar *cp, int type) { tchar *wp, *xp; int c; #ifdef TRACE tprintf("TRACE- domod()\n"); #endif switch (type) { case 'x': case 'q': wp = savestr(cp); for (xp = wp; c = *xp; xp++) if (!issp(c) || type == 'q') *xp |= QUOTE; return (wp); case 'h': case 't': if (!any('/', cp)) return (type == 't' ? savestr(cp) : 0); wp = strend(cp); while (*--wp != '/') continue; if (type == 'h') xp = savestr(cp), xp[wp - cp] = 0; else xp = savestr(wp + 1); return (xp); case 'e': case 'r': wp = strend(cp); for (wp--; wp >= cp && *wp != '/'; wp--) if (*wp == '.') { if (type == 'e') xp = savestr(wp + 1); else xp = savestr(cp), xp[wp - cp] = 0; return (xp); } return (savestr(type == 'e' ? S_ /* "" */ : cp)); } return (0); } int matchs(tchar *str, tchar *pat) { #ifdef TRACE tprintf("TRACE- matchs()\n"); #endif while (*str && *pat && *str == *pat) str++, pat++; return (*pat == 0); } int getsel(int *al, int *ar, int dol) { int c = getC(0); int i; bool first = *al < 0; #ifdef TRACE tprintf("TRACE- getsel()\n"); #endif switch (c) { case '%': if (quesarg == -1) goto bad; if (*al < 0) *al = quesarg; *ar = quesarg; break; case '-': if (*al < 0) { *al = 0; *ar = dol - 1; unreadc(c); } return (1); case '^': if (*al < 0) *al = 1; *ar = 1; break; case '$': if (*al < 0) *al = dol; *ar = dol; break; case '*': if (*al < 0) *al = 1; *ar = dol; if (*ar < *al) { *ar = 0; *al = 1; return (1); } break; default: if (digit(c)) { i = 0; while (digit(c)) { i = i * 10 + c - '0'; c = getC(0); } if (i < 0) i = dol + 1; if (*al < 0) *al = i; *ar = i; } else if (*al < 0) *al = 0, *ar = dol; else *ar = dol - 1; unreadc(c); break; } if (first) { c = getC(0); unreadc(c); /* if (any(c, "-$*")) */ /* char -> tchar */ if (c == '-' || c == '$' || c == '*') return (1); } if (*al > *ar || *ar > dol) { bad: seterr("Bad ! arg selector"); return (0); } return (1); } struct wordent * gethent(int sc) { struct Hist *hp; tchar *np; int c; int event; bool back = 0; #ifdef TRACE tprintf("TRACE- gethent()\n"); #endif c = sc == HISTSUB ? HIST : getC(0); if (c == HIST) { if (alhistp) return (alhistp); event = eventno; goto skip; } switch (c) { case ':': case '^': case '$': case '*': case '%': ungetC(c); if (lastev == eventno && alhistp) return (alhistp); event = lastev; break; case '-': back = 1; c = getC(0); goto number; case '#': /* !# is command being typed in (mrh) */ return (¶ml); default: /* if (any(c, "(=~")) { */ if (c == '(' || c == '=' || c == '~') { unreadc(c); ungetC(HIST); return (0); } if (digit(c)) goto number; np = lhsb; /* while (!any(c, ": \t\\\n}")) { */ while (! (c == ':' || c == '\\' || isspnl(c) || c == '}')) { if (np < &lhsb[(sizeof lhsb)/(sizeof lhsb[0]) - 2]) *np++ = c; c = getC(0); } unreadc(c); if (np == lhsb) { ungetC(HIST); return (0); } *np++ = 0; hp = findev(lhsb, 0); if (hp) lastev = hp->Hnum; return (&hp->Hlex); case '?': np = lhsb; for (;;) { c = getC(0); if (c == '\n') { unreadc(c); break; } if (c == '?') break; if (np < &lhsb[(sizeof lhsb)/(sizeof lhsb[0]) - 2]) *np++ = c; } if (np == lhsb) { if (lhsb[0] == 0) { seterr("No prev search"); return (0); } } else *np++ = 0; hp = findev(lhsb, 1); if (hp) lastev = hp->Hnum; return (&hp->Hlex); number: event = 0; while (digit(c)) { event = event * 10 + c - '0'; c = getC(0); } if (back) event = eventno + (alhistp == 0) - (event ? event : 0); unreadc(c); break; } skip: for (hp = Histlist.Hnext; hp; hp = hp->Hnext) if (hp->Hnum == event) { hp->Href = eventno; lastev = hp->Hnum; return (&hp->Hlex); } np = putn(event); noev(np); return (0); } struct Hist * findev(tchar *cp, bool anyarg) { struct Hist *hp; #ifdef TRACE tprintf("TRACE- findev()\n"); #endif for (hp = Histlist.Hnext; hp; hp = hp->Hnext) { tchar *dp; tchar *p, *q; struct wordent *lp = hp->Hlex.next; int argno = 0; if (lp->word[0] == '\n') continue; if (!anyarg) { p = cp; q = lp->word; do if (!*p) return (hp); while (*p++ == *q++); continue; } do { for (dp = lp->word; *dp; dp++) { p = cp; q = dp; do if (!*p) { quesarg = argno; return (hp); } while (*p++ == *q++); } lp = lp->next; argno++; } while (lp->word[0] != '\n'); } noev(cp); return (0); } void noev(tchar *cp) { #ifdef TRACE tprintf("TRACE- noev()\n"); #endif seterr2(cp, ": Event not found"); } void setexclp(tchar *cp) { #ifdef TRACE tprintf("TRACE- setexclp()\n"); #endif if (cp && cp[0] == '\n') return; exclp = cp; } void unreadc(tchar c) { peekread = c; } int readc(bool wanteof) { int c; static int sincereal; if (c = peekread) { peekread = 0; return (c); } top: if (alvecp) { if (c = *alvecp++) return (c); if (*alvec) { alvecp = *alvec++; return (' '); } } if (alvec) { if (alvecp = *alvec) { alvec++; goto top; } /* Infinite source! */ return ('\n'); } if (evalp) { if (c = *evalp++) return (c); if (*evalvec) { evalp = *evalvec++; return (' '); } evalp = 0; } if (evalvec) { if (evalvec == (tchar **)1) { doneinp = 1; reset(); } if (evalp = *evalvec) { evalvec++; goto top; } evalvec = (tchar **)1; return ('\n'); } do { if (arginp == (tchar *) 1 || onelflg == 1) { if (wanteof) return (-1); exitstat(); } if (arginp) { if ((c = *arginp++) == 0) { arginp = (tchar *) 1; return ('\n'); } return (c); } reread: c = bgetc(); if (c < 0) { struct sgttyb tty; if (wanteof) return (-1); /* was isatty but raw with ignoreeof yields problems */ if (ioctl(SHIN, TIOCGETP, (char *)&tty) == 0 && (tty.sg_flags & RAW) == 0) { /* was 'short' for FILEC */ int ctpgrp; if (++sincereal > 25) goto oops; if (tpgrp != -1 && ioctl(FSHTTY, TIOCGPGRP, (char *)&ctpgrp) == 0 && tpgrp != ctpgrp) { (void) ioctl(FSHTTY, TIOCSPGRP, (char *)&tpgrp); (void) killpg(ctpgrp, SIGHUP); printf("Reset tty pgrp from %d to %d\n", ctpgrp, tpgrp); goto reread; } if (adrof(S_ignoreeof /* "ignoreeof" */)) { if (loginsh) printf("\nUse \"logout\" to logout.\n"); else printf("\nUse \"exit\" to leave csh.\n"); reset(); } if (chkstop == 0) { panystop(1); } } oops: doneinp = 1; reset(); } sincereal = 0; if (c == '\n' && onelflg) onelflg--; } while (c == 0); return (c); } static void expand_fbuf(void) { tchar **nfbuf = (tchar **)xcalloc((unsigned)(fblocks + 2), sizeof (tchar **)); if (fbuf) { (void) blkcpy(nfbuf, fbuf); xfree((char *)fbuf); } fbuf = nfbuf; fbuf[fblocks] = (tchar *)xcalloc(BUFSIZ + MB_LEN_MAX, sizeof (tchar)); fblocks++; } int bgetc(void) { int buf, off, c; #ifdef FILEC tchar ttyline[BUFSIZ + MB_LEN_MAX]; /* read_() can return extra bytes */ int roomleft; #endif #ifdef TELL if (cantell) { if (fseekp < fbobp || fseekp > feobp) { fbobp = feobp = fseekp; (void) lseek(SHIN, fseekp, 0); } if (fseekp == feobp) { fbobp = feobp; do c = read_(SHIN, fbuf[0], BUFSIZ); while (c < 0 && errno == EINTR); if (c <= 0) return (-1); feobp += c; } c = fbuf[0][fseekp - fbobp]; fseekp++; return (c); } #endif again: buf = (int)fseekp / BUFSIZ; if (buf >= fblocks) { expand_fbuf(); goto again; } if (fseekp >= feobp) { buf = (int)feobp / BUFSIZ; off = (int)feobp % BUFSIZ; #ifndef FILEC for (;;) { c = read_(SHIN, fbuf[buf] + off, BUFSIZ - off); #else roomleft = BUFSIZ - off; for (;;) { if (filec && intty) { c = tenex(ttyline, BUFSIZ); if (c > roomleft) { expand_fbuf(); copy(fbuf[buf] + off, ttyline, roomleft * sizeof (tchar)); copy(fbuf[buf + 1], ttyline + roomleft, (c - roomleft) * sizeof (tchar)); } else if (c > 0) { copy(fbuf[buf] + off, ttyline, c * sizeof (tchar)); } } else { c = read_(SHIN, fbuf[buf] + off, roomleft); if (c > roomleft) { expand_fbuf(); copy(fbuf[buf + 1], fbuf[buf] + off + roomleft, (c - roomleft) * sizeof (tchar)); } } #endif if (c >= 0) break; if (errno == EWOULDBLOCK) { int off = 0; (void) ioctl(SHIN, FIONBIO, (char *)&off); } else if (errno != EINTR) break; } if (c <= 0) return (-1); feobp += c; #ifndef FILEC goto again; #else if (filec && !intty) goto again; #endif } c = fbuf[buf][(int)fseekp % BUFSIZ]; fseekp++; return (c); } void bfree(void) { int sb, i; #ifdef TELL if (cantell) return; #endif if (whyles) return; sb = (int)(fseekp - 1) / BUFSIZ; if (sb > 0) { for (i = 0; i < sb; i++) xfree(fbuf[i]); (void) blkcpy(fbuf, &fbuf[sb]); fseekp -= BUFSIZ * sb; feobp -= BUFSIZ * sb; fblocks -= sb; } } void bseek(off_t l) { struct whyle *wp; fseekp = l; #ifdef TELL if (!cantell) { #endif if (!whyles) return; for (wp = whyles; wp->w_next; wp = wp->w_next) continue; if (wp->w_start > l) l = wp->w_start; #ifdef TELL } #endif } /* any similarity to bell telephone is purely accidental */ #ifndef btell off_t btell(void) { return (fseekp); } #endif void btoeof(void) { (void) lseek(SHIN, (off_t)0, 2); fseekp = feobp; wfree(); bfree(); } #ifdef TELL void settell(void) { cantell = 0; if (arginp || onelflg || intty) return; if (lseek(SHIN, (off_t)0, 1) < 0 || errno == ESPIPE) return; fbuf = (tchar **)xcalloc(2, sizeof (tchar **)); fblocks = 1; fbuf[0] = (tchar *)xcalloc(BUFSIZ + MB_LEN_MAX, sizeof (tchar)); fseekp = fbobp = feobp = lseek(SHIN, (off_t)0, 1); cantell = 1; } #endif /* * Copyright 2002 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ /* Copyright (c) 1983, 1984, 1985, 1986, 1987, 1988, 1989 AT&T */ /* All Rights Reserved */ /* * Copyright (c) 1980 Regents of the University of California. * All rights reserved. The Berkeley Software License Agreement * specifies the terms and conditions for redistribution. */ /* * This file defines certain local parameters * A symbol should be defined in Makefile for local conditional * compilation, e.g. IIASA or ERNIE, to be tested here and elsewhere. */ /* * Fundamental definitions which may vary from system to system. * * BUFSIZ The i/o buffering size; also limits word size * SHELLPATH Where the shell will live; initalizes $shell * MAILINTVL How often to mailcheck; more often is more expensive * OTHERSH Shell for scripts which don't start with # */ #define BUFSIZ 1024 /* default buffer size */ #define PATHSIZ 16384 /* allow longer PATH environment variables */ #define SHELLPATH "/bin/csh" #define OTHERSH "/bin/sh" #define FORKSLEEP 10 /* delay loop on non-interactive fork failure */ #define MAILINTVL 600 /* 10 minutes */ /* * The shell moves std in/out/diag and the old std input away from units * 0, 1, and 2 so that it is easy to set up these standards for invoked * commands. */ #define FSHTTY 15 /* /dev/tty when manip pgrps */ #define FSHIN 16 /* Preferred desc for shell input */ #define FSHOUT 17 /* ... shell output */ #define FSHDIAG 18 /* ... shell diagnostics */ #define FOLDSTD 19 /* ... old std input */ #ifdef IIASA #undef OTHERSH #endif #define copy(to, from, size) bcopy(from, to, size) #ifdef PROF #define exit(n) done(n) #endif /* * Copyright 2005 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ /* Copyright (c) 1983, 1984, 1985, 1986, 1987, 1988, 1989 AT&T */ /* All Rights Reserved */ /* * Copyright (c) 1980 Regents of the University of California. * All rights reserved. The Berkeley Software License Agreement * specifies the terms and conditions for redistribution. */ #include "sh.h" #include "sh.tconst.h" #include #include /* * C Shell */ tchar **blkcat(tchar **, tchar **); tchar **blkend(tchar **); int any(int c, tchar *s) { while (s && *s) if (*s++ == c) return (1); return (0); } int onlyread(tchar *cp) { extern char end[]; return ((char *)cp < end); } tchar * savestr(tchar *s) { tchar *n; tchar *p; if (s == 0) s = S_ /* "" */; #ifndef m32 for (p = s; *p++; ) ; n = p = (tchar *)xalloc((unsigned)(p - s)*sizeof (tchar)); while (*p++ = *s++) ; return (n); #else p = (tchar *) xalloc((strlen_(s) + 1)*sizeof (tchar)); strcpy_(p, s); return (p); #endif } static void * nomem(size_t i) { #ifdef debug static tchar *av[2] = {0, 0}; #endif child++; #ifndef debug error("Out of memory"); #ifdef lint i = i; #endif #else showall(av); printf("i=%d: Out of memory\n", i); chdir("/usr/bill/cshcore"); abort(); #endif return (0); /* fool lint */ } tchar ** blkend(tchar **up) { while (*up) up++; return (up); } void blkpr(tchar **av) { for (; *av; av++) { printf("%t", *av); if (av[1]) printf(" "); } } int blklen(tchar **av) { int i = 0; while (*av++) i++; return (i); } tchar ** blkcpy(tchar **oav, tchar **bv) { tchar **av = oav; while (*av++ = *bv++) continue; return (oav); } tchar ** blkcat(tchar **up, tchar **vp) { (void) blkcpy(blkend(up), vp); return (up); } void blkfree(tchar **av0) { tchar **av = av0; for (; *av; av++) xfree(*av); xfree(av0); } tchar ** saveblk(tchar **v) { tchar **newv = (tchar **)xcalloc((unsigned)(blklen(v) + 1), sizeof (tchar **)); tchar **onewv = newv; while (*v) *newv++ = savestr(*v++); return (onewv); } tchar * strspl(tchar *cp, tchar *dp) { tchar *ep; tchar *p, *q; #ifndef m32 for (p = cp; *p++; ) ; for (q = dp; *q++; ) ; ep = (tchar *) xalloc((unsigned)(((p - cp) + (q - dp) - 1))*sizeof (tchar)); for (p = ep, q = cp; *p++ = *q++; ) ; for (p--, q = dp; *p++ = *q++; ) ; #else int len1 = strlen_(cp); int len2 = strlen_(dp); ep = (tchar *)xalloc((unsigned)(len1 + len2 + 1)*sizeof (tchar)); strcpy_(ep, cp); strcat_(ep, dp); #endif return (ep); } tchar ** blkspl(tchar **up, tchar **vp) { tchar **wp = (tchar **)xcalloc((unsigned)(blklen(up) + blklen(vp) + 1), sizeof (tchar **)); (void) blkcpy(wp, up); return (blkcat(wp, vp)); } int lastchr(tchar *cp) { if (!*cp) return (0); while (cp[1]) cp++; return (*cp); } void donefds(void) { (void) close(0); (void) close(1); (void) close(2); /* * To avoid NIS+ functions to get hold of 0/1/2, * use descriptor 0, and dup it to 1 and 2. */ open("/dev/null", 0); dup(0); dup(0); didfds = 0; } /* * Move descriptor i to j. * If j is -1 then we just want to get i to a safe place, * i.e. to a unit > 2. This also happens in dcopy. */ int dmove(int i, int j) { int fd; if (i == j || i < 0) return (i); if (j >= 0) { fd = dup2(i, j); if (fd != -1) setfd(fd); } else j = dcopy(i, j); if (j != i) { (void) close(i); unsetfd(i); } return (j); } int dcopy(int i, int j) { int fd; if (i == j || i < 0 || j < 0 && i > 2) return (i); if (j >= 0) { fd = dup2(i, j); if (fd != -1) setfd(fd); return (j); } (void) close(j); unsetfd(j); return (renum(i, j)); } int renum(int i, int j) { int k = dup(i); if (k < 0) return (-1); if (j == -1 && k > 2) { setfd(k); return (k); } if (k != j) { j = renum(k, j); (void) close(k); /* no need ofr unsetfd() */ return (j); } return (k); } #ifndef copy void copy(tchar *to, tchar *from, int size) { if (size) do *to++ = *from++; while (--size != 0); } #endif /* * Left shift a command argument list, discarding * the first c arguments. Used in "shift" commands * as well as by commands like "repeat". */ void lshift(tchar **v, int c) { tchar **u = v; while (*u && --c >= 0) xfree((char *)*u++); (void) blkcpy(v, u); } int number(tchar *cp) { if (*cp == '-') { cp++; if (!digit(*cp++)) return (0); } while (*cp && digit(*cp)) cp++; return (*cp == 0); } tchar ** copyblk(tchar **v) { tchar **nv = (tchar **)xcalloc((unsigned)(blklen(v) + 1), sizeof (tchar **)); return (blkcpy(nv, v)); } tchar * strend(tchar *cp) { while (*cp) cp++; return (cp); } tchar * strip(tchar *cp) { tchar *dp = cp; while (*dp++ &= TRIM) continue; return (cp); } void udvar(tchar *name) { setname(name); bferr("Undefined variable"); } int prefix(tchar *sub, tchar *str) { for (;;) { if (*sub == 0) return (1); if (*str == 0) return (0); if (*sub++ != *str++) return (0); } } /* * blk*_ routines */ char ** blkend_(char **up) { while (*up) up++; return (up); } int blklen_(char **av) { int i = 0; while (*av++) i++; return (i); } char ** blkcpy_(char **oav, char **bv) { char **av = oav; while (*av++ = *bv++) continue; return (oav); } char ** blkcat_(char **up, char **vp) { (void) blkcpy_(blkend_(up), vp); return (up); } char ** blkspl_(char **up, char **vp) { char **wp = (char **)xcalloc((unsigned)(blklen_(up) + blklen_(vp) + 1), sizeof (char **)); (void) blkcpy_(wp, up); return (blkcat_(wp, vp)); } /* * If stack address was passed to free(), we have no good way to see if * they are really in the stack. Therefore, we record the bottom of heap, * and filter out the address not within heap's top(end) and bottom * (xalloc_bottom). */ extern char end[]; static char *xalloc_bottom; void * xalloc(size_t size) { char *rptr, *bp; if ((rptr = malloc(size)) == NULL) return (nomem(size)); bp = rptr + size; if (bp > xalloc_bottom) xalloc_bottom = bp; return (rptr); } void * xrealloc(void *ptr, size_t size) { char *rptr = ptr, *bp; if (ptr == NULL) return (xalloc(size)); if (rptr < end) { /* data area, but not in heap area. don't touch it */ oob: if (size == 0) return (NULL); rptr = xalloc(size); /* copy max size */ (void) memcpy(rptr, ptr, size); return (rptr); } if (rptr < xalloc_bottom) { /* address in the heap */ inb: if (size == 0) { free(ptr); return (NULL); } if ((rptr = realloc(ptr, size)) == NULL) return (nomem(size)); bp = rptr + size; if (bp > xalloc_bottom) xalloc_bottom = bp; return (rptr); } #if defined(__sparc) if (rptr > (char *)&rptr) { /* in the stack frame */ goto oob; } #endif /* * can be a memory block returned indirectly from * library functions. update bottom, and check it again. */ xalloc_bottom = sbrk(0); if (rptr <= xalloc_bottom) goto inb; else goto oob; /*NOTREACHED*/ } void xfree(void *ptr) { char *rptr = ptr; if (rptr < end) { return; } if (rptr < xalloc_bottom) { free(ptr); return; } #if defined(__sparc) if (rptr > (char *)&rptr) { /* in the stack frame */ return; } #endif xalloc_bottom = sbrk(0); if (rptr <= xalloc_bottom) { free(ptr); } } void * xcalloc(size_t i, size_t j) { char *cp; i *= j; cp = xalloc(i); (void) memset(cp, '\0', i); return (cp); } /* * Copyright 2005 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ /* Copyright (c) 1983, 1984, 1985, 1986, 1987, 1988, 1989 AT&T */ /* All Rights Reserved */ /* * Copyright (c) 1980 Regents of the University of California. * All rights reserved. The Berkeley Software License Agreement * specifies the terms and conditions for redistribution. */ #include "sh.h" #include "sh.tconst.h" /* * C shell */ void asyntax(struct wordent *, struct wordent *); void asyn0(struct wordent *, struct wordent *); void asyn3(struct wordent *, struct wordent *); void chr_blkfree(char **); struct command *syn0(struct wordent *, struct wordent *, int); struct command *syn1(struct wordent *, struct wordent *, int); struct command *syn1a(struct wordent *, struct wordent *, int); struct command *syn1b(struct wordent *, struct wordent *, int); struct command *syn2(struct wordent *, struct wordent *, int); struct command *syn3(struct wordent *, struct wordent *, int); struct wordent *freenod(struct wordent *, struct wordent *); /* * Perform aliasing on the word list lex * Do a (very rudimentary) parse to separate into commands. * If word 0 of a command has an alias, do it. * Repeat a maximum of 20 times. */ void alias(struct wordent *lex) { int aleft = 21; jmp_buf osetexit; #ifdef TRACE tprintf("TRACE- alias()\n"); #endif getexit(osetexit); setexit(); if (haderr) { resexit(osetexit); reset(); } if (--aleft == 0) error("Alias loop"); asyntax(lex->next, lex); resexit(osetexit); } void asyntax(struct wordent *p1, struct wordent *p2) { #ifdef TRACE tprintf("TRACE- asyntax()\n"); #endif while (p1 != p2) /* if (any(p1->word[0], ";&\n")) */ /* For char -> tchar */ if (p1->word[0] == ';' || p1->word[0] == '&' || p1->word[0] == '\n') p1 = p1->next; else { asyn0(p1, p2); return; } } void asyn0(struct wordent *p1, struct wordent *p2) { struct wordent *p; int l = 0; #ifdef TRACE tprintf("TRACE- asyn0()\n"); #endif for (p = p1; p != p2; p = p->next) switch (p->word[0]) { case '(': l++; continue; case ')': l--; if (l < 0) error("Too many )'s"); continue; case '>': if (p->next != p2 && eq(p->next->word, S_AND /* "&"*/)) p = p->next; continue; case '&': case '|': case ';': case '\n': if (l != 0) continue; asyn3(p1, p); asyntax(p->next, p2); return; } if (l == 0) asyn3(p1, p2); } void asyn3(struct wordent *p1, struct wordent *p2) { struct varent *ap; struct wordent alout; bool redid; #ifdef TRACE tprintf("TRACE- asyn3()\n"); #endif if (p1 == p2) return; if (p1->word[0] == '(') { for (p2 = p2->prev; p2->word[0] != ')'; p2 = p2->prev) if (p2 == p1) return; if (p2 == p1->next) return; asyn0(p1->next, p2); return; } ap = adrof1(p1->word, &aliases); if (ap == 0) return; alhistp = p1->prev; alhistt = p2; alvec = ap->vec; redid = lex(&alout); alhistp = alhistt = 0; alvec = 0; if (err_msg) { freelex(&alout); error("%s", gettext(err_msg)); } if (p1->word[0] && eq(p1->word, alout.next->word)) { tchar *cp = alout.next->word; alout.next->word = strspl(S_TOPBIT /* "\200" */, cp); xfree(cp); } p1 = freenod(p1, redid ? p2 : p1->next); if (alout.next != &alout) { p1->next->prev = alout.prev->prev; alout.prev->prev->next = p1->next; alout.next->prev = p1; p1->next = alout.next; xfree(alout.prev->word); xfree(alout.prev); } reset(); /* throw! */ } struct wordent * freenod(struct wordent *p1, struct wordent *p2) { struct wordent *retp = p1->prev; #ifdef TRACE tprintf("TRACE- freenod()\n"); #endif while (p1 != p2) { xfree(p1->word); p1 = p1->next; xfree(p1->prev); } retp->next = p2; p2->prev = retp; return (retp); } #define PHERE 1 #define PIN 2 #define POUT 4 #define PDIAG 8 /* * syntax * empty * syn0 */ struct command * syntax(struct wordent *p1, struct wordent *p2, int flags) { #ifdef TRACE tprintf("TRACE- syntax()\n"); #endif while (p1 != p2) /* if (any(p1->word[0], ";&\n")) */ /* for char -> tchar */ if (p1->word[0] == ';' || p1->word[0] == '&' || p1->word[0] == '\n') p1 = p1->next; else return (syn0(p1, p2, flags)); return (0); } /* * syn0 * syn1 * syn1 & syntax */ struct command * syn0(struct wordent *p1, struct wordent *p2, int flags) { struct wordent *p; struct command *t, *t1; int l; #ifdef TRACE tprintf("TRACE- syn0()\n"); #endif l = 0; for (p = p1; p != p2; p = p->next) switch (p->word[0]) { case '(': l++; continue; case ')': l--; if (l < 0) seterr("Too many )'s"); continue; case '|': if (p->word[1] == '|') continue; /* fall into ... */ case '>': if (p->next != p2 && eq(p->next->word, S_AND /* "&" */)) p = p->next; continue; case '&': if (l != 0) break; if (p->word[1] == '&') continue; t1 = syn1(p1, p, flags); if (t1->t_dtyp == TLST || t1->t_dtyp == TAND || t1->t_dtyp == TOR) { t = (struct command *)xcalloc(1, sizeof (*t)); t->t_dtyp = TPAR; t->t_dflg = FAND|FINT; t->t_dspr = t1; t1 = t; } else t1->t_dflg |= FAND|FINT; t = (struct command *)xcalloc(1, sizeof (*t)); t->t_dtyp = TLST; t->t_dflg = 0; t->t_dcar = t1; t->t_dcdr = syntax(p, p2, flags); return (t); } if (l == 0) return (syn1(p1, p2, flags)); seterr("Too many ('s"); return (0); } /* * syn1 * syn1a * syn1a ; syntax */ struct command * syn1(struct wordent *p1, struct wordent *p2, int flags) { struct wordent *p; struct command *t; int l; #ifdef TRACE tprintf("TRACE- syn1()\n"); #endif l = 0; for (p = p1; p != p2; p = p->next) switch (p->word[0]) { case '(': l++; continue; case ')': l--; continue; case ';': case '\n': if (l != 0) break; t = (struct command *)xcalloc(1, sizeof (*t)); t->t_dtyp = TLST; t->t_dcar = syn1a(p1, p, flags); t->t_dcdr = syntax(p->next, p2, flags); if (t->t_dcdr == 0) t->t_dcdr = t->t_dcar, t->t_dcar = 0; return (t); } return (syn1a(p1, p2, flags)); } /* * syn1a * syn1b * syn1b || syn1a */ struct command * syn1a(struct wordent *p1, struct wordent *p2, int flags) { struct wordent *p; struct command *t; int l = 0; #ifdef TRACE tprintf("TRACE- syn1a()\n"); #endif for (p = p1; p != p2; p = p->next) switch (p->word[0]) { case '(': l++; continue; case ')': l--; continue; case '|': if (p->word[1] != '|') continue; if (l == 0) { t = (struct command *)xcalloc(1, sizeof (*t)); t->t_dtyp = TOR; t->t_dcar = syn1b(p1, p, flags); t->t_dcdr = syn1a(p->next, p2, flags); t->t_dflg = 0; return (t); } continue; } return (syn1b(p1, p2, flags)); } /* * syn1b * syn2 * syn2 && syn1b */ struct command * syn1b(struct wordent *p1, struct wordent *p2, int flags) { struct wordent *p; struct command *t; int l = 0; #ifdef TRACE tprintf("TRACE- syn1b()\n"); #endif l = 0; for (p = p1; p != p2; p = p->next) switch (p->word[0]) { case '(': l++; continue; case ')': l--; continue; case '&': if (p->word[1] == '&' && l == 0) { t = (struct command *)xcalloc(1, sizeof (*t)); t->t_dtyp = TAND; t->t_dcar = syn2(p1, p, flags); t->t_dcdr = syn1b(p->next, p2, flags); t->t_dflg = 0; return (t); } continue; } return (syn2(p1, p2, flags)); } /* * syn2 * syn3 * syn3 | syn2 * syn3 |& syn2 */ struct command * syn2(struct wordent *p1, struct wordent *p2, int flags) { struct wordent *p, *pn; struct command *t; int l = 0; int f; #ifdef TRACE tprintf("TRACE- syn2()\n"); #endif for (p = p1; p != p2; p = p->next) switch (p->word[0]) { case '(': l++; continue; case ')': l--; continue; case '|': if (l != 0) continue; t = (struct command *)xcalloc(1, sizeof (*t)); f = flags | POUT; pn = p->next; if (pn != p2 && pn->word[0] == '&') { f |= PDIAG; t->t_dflg |= FDIAG; } t->t_dtyp = TFIL; t->t_dcar = syn3(p1, p, f); if (pn != p2 && pn->word[0] == '&') p = pn; t->t_dcdr = syn2(p->next, p2, flags | PIN); return (t); } return (syn3(p1, p2, flags)); } tchar RELPAR[] = {'<', '>', '(', ')', 0}; /* "<>()" */ /* * syn3 * ( syn0 ) [ < in ] [ > out ] * word word* [ < in ] [ > out ] * KEYWORD ( word* ) word* [ < in ] [ > out ] * * KEYWORD = (@ exit foreach if set switch test while) */ struct command * syn3(struct wordent *p1, struct wordent *p2, int flags) { struct wordent *p; struct wordent *lp, *rp; struct command *t; int l; tchar **av; int n, c; bool specp = 0; #ifdef TRACE tprintf("TRACE- syn3()\n"); #endif if (p1 != p2) { p = p1; again: switch (srchx(p->word)) { case ZELSE: p = p->next; if (p != p2) goto again; break; case ZEXIT: case ZFOREACH: case ZIF: case ZLET: case ZSET: case ZSWITCH: case ZWHILE: specp = 1; break; } } n = 0; l = 0; for (p = p1; p != p2; p = p->next) switch (p->word[0]) { case '(': if (specp) n++; l++; continue; case ')': if (specp) n++; l--; continue; case '>': case '<': if (l != 0) { if (specp) n++; continue; } if (p->next == p2) continue; if (any(p->next->word[0], RELPAR)) continue; n--; continue; default: if (!specp && l != 0) continue; n++; continue; } if (n < 0) n = 0; t = (struct command *)xcalloc(1, sizeof (*t)); av = (tchar **)xcalloc((unsigned)(n + 1), sizeof (tchar **)); t->t_dcom = av; n = 0; if (p2->word[0] == ')') t->t_dflg = FPAR; lp = 0; rp = 0; l = 0; for (p = p1; p != p2; p = p->next) { c = p->word[0]; switch (c) { case '(': if (l == 0) { if (lp != 0 && !specp) seterr("Badly placed ("); lp = p->next; } l++; goto savep; case ')': l--; if (l == 0) rp = p; goto savep; case '>': if (l != 0) goto savep; if (p->word[1] == '>') t->t_dflg |= FCAT; if (p->next != p2 && eq(p->next->word, S_AND /* "&" */)) { t->t_dflg |= FDIAG, p = p->next; if (flags & (POUT|PDIAG)) goto badout; } if (p->next != p2 && eq(p->next->word, S_EXAS /* "!" */)) t->t_dflg |= FANY, p = p->next; if (p->next == p2) { missfile: seterr("Missing name for redirect"); continue; } p = p->next; if (any(p->word[0], RELPAR)) goto missfile; if ((flags & POUT) && (flags & PDIAG) == 0 || t->t_drit) badout: seterr("Ambiguous output redirect"); else t->t_drit = savestr(p->word); continue; case '<': if (l != 0) goto savep; if (p->word[1] == '<') t->t_dflg |= FHERE; if (p->next == p2) goto missfile; p = p->next; if (any(p->word[0], RELPAR)) goto missfile; if ((flags & PHERE) && (t->t_dflg & FHERE)) seterr("Can't << within ()'s"); else if ((flags & PIN) || t->t_dlef) seterr("Ambiguous input redirect"); else t->t_dlef = savestr(p->word); continue; savep: if (!specp) continue; default: if (l != 0 && !specp) continue; if (err_msg == NULL) av[n] = savestr(p->word); n++; continue; } } if (lp != 0 && !specp) { if (n != 0) seterr("Badly placed ()'s"); t->t_dtyp = TPAR; t->t_dspr = syn0(lp, rp, PHERE); } else { if (n == 0) seterr("Invalid null command"); t->t_dtyp = TCOM; } return (t); } void freesyn(struct command *t) { #ifdef TRACE tprintf("TRACE- freesyn()\n"); #endif if (t == 0) return; switch (t->t_dtyp) { case TCOM: blkfree(t->t_dcom); if (t->cfname) xfree(t->cfname); if (t->cargs) chr_blkfree(t->cargs); goto lr; case TPAR: freesyn(t->t_dspr); /* fall into ... */ lr: xfree(t->t_dlef); xfree(t->t_drit); break; case TAND: case TOR: case TFIL: case TLST: freesyn(t->t_dcar), freesyn(t->t_dcdr); break; } xfree(t); } void chr_blkfree(char **vec) { char **av; for (av = vec; *av; av++) xfree(*av); xfree(vec); } /* * Copyright 2005 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ /* Copyright (c) 1983, 1984, 1985, 1986, 1987, 1988, 1989 AT&T */ /* All Rights Reserved */ /* * Copyright (c) 1980 Regents of the University of California. * All rights reserved. The Berkeley Software License Agreement * specifies the terms and conditions for redistribution. */ #include "sh.h" void p2dig_ull(unsigned long long); void p2dig_int(int); void flush(void); void Putchar(tchar); /* * C Shell */ void psecs_ull(unsigned long long l) { unsigned long long i; i = l / 3600; if (i) { printf("%llu:", i); i = l % 3600; p2dig_ull(i / 60); goto minsec; } i = l; printf("%llu", i / 60); minsec: i %= 60; printf(":"); p2dig_ull(i); } void psecs_int(int l) { int i; i = l / 3600; if (i) { printf("%d:", i); i = l % 3600; p2dig_int(i / 60); goto minsec; } i = l; printf("%d", i / 60); minsec: i %= 60; printf(":"); p2dig_int(i); } void p2dig_ull(unsigned long long i) { printf("%llu%llu", i / 10, i % 10); } void p2dig_int(int i) { printf("%d%d", i / 10, i % 10); } char linbuf[128]; char *linp = linbuf; #ifdef MBCHAR /* * putbyte() send a byte to SHOUT. No interpretation is done * except an un-QUOTE'd control character, which is displayed * as ^x. */ void putbyte(int c) { if ((c & QUOTE) == 0 && (c == 0177 || c < ' ' && c != '\t' && c != '\n')) { putbyte('^'); if (c == 0177) { c = '?'; } else { c |= 'A' - 1; } } c &= TRIM; *linp++ = c; if (c == '\n' || linp >= &linbuf[sizeof (linbuf) - 1 - MB_CUR_MAX]) { /* 'cause the next Putchar() call may overflow the buffer. */ flush(); } } /* * Putchar(tc) does what putbyte(c) do for a byte c. * Note that putbyte(c) just send the byte c (provided c is not * a control character) as it is, while Putchar(tc) may expand the * character tc to some byte sequnce that represents the character * in EUC form. */ void Putchar(tchar tc) { int n; if (isascii(tc&TRIM)) { putbyte((int)tc); return; } tc &= TRIM; n = wctomb(linp, tc); if (n == -1) { return; } linp += n; if (linp >= &linbuf[sizeof (linbuf) - 1 - MB_CUR_MAX]) { flush(); } } #else /* !MBCHAR */ /* * putbyte() send a byte to SHOUT. No interpretation is done * except an un-QUOTE'd control character, which is displayed * as ^x. */ void putbyte(int c) { if ((c & QUOTE) == 0 && (c == 0177 || c < ' ' && c != '\t' && c != '\n')) { putbyte('^'); if (c == 0177) { c = '?'; } else { c |= 'A' - 1; } } c &= TRIM; *linp++ = c; if (c == '\n' || linp >= &linbuf[sizeof (linbuf) - 2]) { flush(); } } /* * Putchar(tc) does what putbyte(c) do for a byte c. * For single-byte character only environment, there is no * difference between Putchar() and putbyte() though. */ void Putchar(tchar tc) { putbyte((int)tc); } #endif /* !MBCHAR */ void draino(void) { linp = linbuf; } void flush(void) { int unit; int lmode; if (linp == linbuf) { return; } if (haderr) { unit = didfds ? 2 : SHDIAG; } else { unit = didfds ? 1 : SHOUT; } #ifdef TIOCLGET if (didfds == 0 && ioctl(unit, TIOCLGET, (char *)&lmode) == 0 && lmode&LFLUSHO) { lmode = LFLUSHO; (void) ioctl(unit, TIOCLBIC, (char *)&lmode); (void) write(unit, "\n", 1); } #endif (void) write(unit, linbuf, linp - linbuf); linp = linbuf; } /* * Should not be needed. */ void write_string(char *s) { int unit; /* * First let's make it sure to flush out things. */ flush(); if (haderr) { unit = didfds ? 2 : SHDIAG; } else { unit = didfds ? 1 : SHOUT; } (void) write(unit, s, strlen(s)); } /* * Copyright 2006 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ /* Copyright (c) 1983, 1984, 1985, 1986, 1987, 1988, 1989 AT&T */ /* All Rights Reserved */ /* * Copyright (c) 1980 Regents of the University of California. * All rights reserved. The Berkeley Software License Agreement * specifies the terms and conditions for redistribution. */ #include "sh.h" #include "sh.dir.h" #include "sh.proc.h" #include "wait.h" #include "sh.tconst.h" /* * C Shell - functions that manage processes, handling hanging, termination */ bool neednote; bool pjobs; static struct process *pprevious; static short pmaxindex; static struct process proclist; static struct process *pcurrent; struct process *pcurrjob; static struct process *pholdjob; #define BIGINDEX 9 /* largest desirable job index */ void pjwait(struct process *); void pflush(struct process *); void pclrcurr(struct process *); void padd(struct command *); void pads(tchar *); void ptprint(struct process *); void pkill(tchar **, int); void pstart(struct process *, int); void okpcntl(void); struct process *pgetcurr(struct process *); struct process *pfind(tchar *); /* * pchild - called at interrupt level by the SIGCHLD signal * indicating that at least one child has terminated or stopped * thus at least one wait system call will definitely return a * childs status. Top level routines (like pwait) must be sure * to mask interrupts when playing with the proclist data structures! */ void pchild(int signal __unused) { struct process *pp; struct process *fp; int pid; union wait w; int jobflags; struct rusage ru; #ifdef TRACE tprintf("TRACE- pchile()\n"); #endif loop: pid = csh_wait3(&w, (setintr ? WNOHANG|WUNTRACED:WNOHANG), &ru); /* * SysV sends a SIGCHLD when the child process * receives a SIGCONT, and result of that action is ignored here */ if (w.w_status == WCONTFLG) return; if (pid <= 0) { if (errno == EINTR) { errno = 0; goto loop; } return; } for (pp = proclist.p_next; pp != PNULL; pp = pp->p_next) if (pid == pp->p_pid) goto found; goto loop; found: if (pid == atoi_(value(S_child /* "child" */))) unsetv(S_child /* "child" */); pp->p_flags &= ~(PRUNNING|PSTOPPED|PREPORTED); if (WIFSTOPPED(w)) { pp->p_flags |= PSTOPPED; pp->p_reason = w.w_stopsig; } else { if (pp->p_flags & (PTIME|PPTIME) || adrof(S_time /* "time" */)) (void) gettimeofday(&pp->p_etime, (struct timezone *)0); pp->p_rusage = ru; if (WIFSIGNALED(w)) { if (w.w_termsig == SIGINT) pp->p_flags |= PINTERRUPTED; else pp->p_flags |= PSIGNALED; if (w.w_coredump) pp->p_flags |= PDUMPED; pp->p_reason = w.w_termsig; } else { pp->p_reason = w.w_retcode; if (pp->p_reason != 0) pp->p_flags |= PAEXITED; else pp->p_flags |= PNEXITED; } } jobflags = 0; fp = pp; do { if ((fp->p_flags & (PPTIME|PRUNNING|PSTOPPED)) == 0 && !child && adrof(S_time /* "time" */) && fp->p_rusage.ru_utime.tv_sec+fp->p_rusage.ru_stime.tv_sec >= atoi_(value(S_time /* "time" */))) fp->p_flags |= PTIME; jobflags |= fp->p_flags; } while ((fp = fp->p_friends) != pp); pp->p_flags &= ~PFOREGND; if (pp == pp->p_friends && (pp->p_flags & PPTIME)) { pp->p_flags &= ~PPTIME; pp->p_flags |= PTIME; } if ((jobflags & (PRUNNING|PREPORTED)) == 0) { fp = pp; do { if (fp->p_flags&PSTOPPED) fp->p_flags |= PREPORTED; } while ((fp = fp->p_friends) != pp); while (fp->p_pid != fp->p_jobid) fp = fp->p_friends; if (jobflags&PSTOPPED) { if (pcurrent && pcurrent != fp) pprevious = pcurrent; pcurrent = fp; } else pclrcurr(fp); if (jobflags&PFOREGND) { if (jobflags & (PSIGNALED|PSTOPPED|PPTIME) || #ifdef IIASA jobflags & PAEXITED || #endif !eq(dcwd->di_name, fp->p_cwd->di_name)) { ; /* print in pjwait */ } } else { if (jobflags&PNOTIFY || adrof(S_notify /* "notify" */)) { write_string("\015\n"); flush(); (void) pprint(pp, NUMBER|NAME|REASON); if ((jobflags&PSTOPPED) == 0) pflush(pp); } else { fp->p_flags |= PNEEDNOTE; neednote++; } } } goto loop; } void pnote(void) { struct process *pp; int flags, omask; #ifdef TRACE tprintf("TRACE- pnote()\n"); #endif neednote = 0; for (pp = proclist.p_next; pp != PNULL; pp = pp->p_next) { if (pp->p_flags & PNEEDNOTE) { omask = sigblock(sigmask(SIGCHLD)); pp->p_flags &= ~PNEEDNOTE; flags = pprint(pp, NUMBER|NAME|REASON); if ((flags&(PRUNNING|PSTOPPED)) == 0) pflush(pp); (void) sigsetmask(omask); } } } /* * pwait - wait for current job to terminate, maintaining integrity * of current and previous job indicators. */ void pwait(void) { struct process *fp, *pp; int omask; #ifdef TRACE tprintf("TRACE- pwait()\n"); #endif /* * Here's where dead procs get flushed. */ omask = sigblock(sigmask(SIGCHLD)); for (pp = (fp = &proclist)->p_next; pp != PNULL; pp = (fp = pp)->p_next) if (pp->p_pid == 0) { fp->p_next = pp->p_next; xfree(pp->p_command); if (pp->p_cwd && --pp->p_cwd->di_count == 0) if (pp->p_cwd->di_next == 0) dfree(pp->p_cwd); xfree((tchar *)pp); pp = fp; } (void) sigsetmask(omask); pjwait(pcurrjob); } /* * pjwait - wait for a job to finish or become stopped * It is assumed to be in the foreground state (PFOREGND) */ void pjwait(struct process *pp) { struct process *fp; int jobflags, reason, omask; #ifdef TRACE tprintf("TRACE- pjwait()\n"); #endif while (pp->p_pid != pp->p_jobid) pp = pp->p_friends; fp = pp; do { if ((fp->p_flags&(PFOREGND|PRUNNING)) == PRUNNING) printf("BUG: waiting for background job!\n"); } while ((fp = fp->p_friends) != pp); /* * Now keep pausing as long as we are not interrupted (SIGINT), * and the target process, or any of its friends, are running */ fp = pp; omask = sigblock(sigmask(SIGCHLD)); for (;;) { jobflags = 0; do jobflags |= fp->p_flags; while ((fp = (fp->p_friends)) != pp); if ((jobflags & PRUNNING) == 0) break; /* * At this point, csh used to call: * sigpause(sigblock(0) &~ sigmask(SIGCHLD)); * expecting to receive a SIGCHLD signal from the * termination of the child and to invoke the * signal handler, pchild(), as a result. * * However, vfork() now causes a vfork()'d child to * have all of its active signal handlers reset to * SIG_DFL, to forstall parent memory corruption due * to race conditions with signal handling. * * If this instance of csh is itself a child of vfork(), * which can happen when the top-level csh performs a * command substitution inside an i/o redirection, like: * /bin/echo foo >`/bin/echo trash` * then we will never receive SIGCHLD. To accommodate * this, we wait until one of our children terminates * (without actually reaping the child) and call the * SIGCHLD signal handler (pchild()) directly. */ if (csh_wait_noreap() > 0) pchild(SIGCHLD); /* simulate receipt of SIGCHLD */ } (void) sigsetmask(omask); if (tpgrp > 0) /* get tty back */ (void) ioctl(FSHTTY, TIOCSPGRP, (char *)&tpgrp); if ((jobflags&(PSIGNALED|PSTOPPED|PTIME)) || !eq(dcwd->di_name, fp->p_cwd->di_name)) { if (jobflags&PSTOPPED) printf("\n"); (void) pprint(pp, AREASON|SHELLDIR); } if ((jobflags&(PINTERRUPTED|PSTOPPED)) && setintr && (!gointr || !eq(gointr, S_MINUS /* "-" */))) { if ((jobflags & PSTOPPED) == 0) pflush(pp); pintr1(0); /*NOTREACHED*/ } reason = 0; fp = pp; do { if (fp->p_reason) reason = fp->p_flags & (PSIGNALED|PINTERRUPTED) ? fp->p_reason | ABN_TERM : fp->p_reason; } while ((fp = fp->p_friends) != pp); set(S_status /* "status" */, putn(reason)); if (reason && exiterr) exitstat(); pflush(pp); } /* * dowait - wait for all processes to finish */ void dowait(void) { struct process *pp; int omask; #ifdef TRACE tprintf("TRACE- dowait()\n"); #endif pjobs++; omask = sigblock(sigmask(SIGCHLD)); loop: for (pp = proclist.p_next; pp; pp = pp->p_next) if (pp->p_pid && /* pp->p_pid == pp->p_jobid && */ pp->p_flags&PRUNNING) { sigpause(0); goto loop; } (void) sigsetmask(omask); pjobs = 0; } /* * pflushall - flush all jobs from list (e.g. at fork()) */ void pflushall(void) { struct process *pp; #ifdef TRACE tprintf("TRACE- pflush()\n"); #endif for (pp = proclist.p_next; pp != PNULL; pp = pp->p_next) if (pp->p_pid) pflush(pp); } /* * pflush - flag all process structures in the same job as the * the argument process for deletion. The actual free of the * space is not done here since pflush is called at interrupt level. */ void pflush(struct process *pp) { struct process *np; int index; #ifdef TRACE tprintf("TRACE- pflush()\n"); #endif if (pp->p_pid == 0) { printf("BUG: process flushed twice"); return; } while (pp->p_pid != pp->p_jobid) pp = pp->p_friends; pclrcurr(pp); if (pp == pcurrjob) pcurrjob = 0; index = pp->p_index; np = pp; do { np->p_index = np->p_pid = 0; np->p_flags &= ~PNEEDNOTE; } while ((np = np->p_friends) != pp); if (index == pmaxindex) { for (np = proclist.p_next, index = 0; np; np = np->p_next) if (np->p_index > (tchar)index) index = np->p_index; pmaxindex = index; } } /* * pclrcurr - make sure the given job is not the current or previous job; * pp MUST be the job leader */ void pclrcurr(struct process *pp) { #ifdef TRACE tprintf("TRACE- pclrcurr()\n"); #endif if (pp == pcurrent) if (pprevious != PNULL) { pcurrent = pprevious; pprevious = pgetcurr(pp); } else { pcurrent = pgetcurr(pp); pprevious = pgetcurr(pp); } else if (pp == pprevious) pprevious = pgetcurr(pp); } /* +4 here is 1 for '\0', 1 ea for << >& >> */ tchar command[PMAXLEN+4]; int cmdlen; tchar *cmdp; /* * palloc - allocate a process structure and fill it up. * an important assumption is made that the process is running. */ void palloc(int pid, struct command *t) { struct process *pp; int i; #ifdef TRACE tprintf("TRACE- palloc()\n"); #endif pp = (struct process *)xcalloc(1, sizeof (struct process)); pp->p_pid = pid; pp->p_flags = t->t_dflg & FAND ? PRUNNING : PRUNNING|PFOREGND; if (t->t_dflg & FTIME) pp->p_flags |= PPTIME; cmdp = command; cmdlen = 0; padd(t); *cmdp++ = 0; if (t->t_dflg & FPOU) { pp->p_flags |= PPOU; if (t->t_dflg & FDIAG) pp->p_flags |= PDIAG; } pp->p_command = savestr(command); if (pcurrjob) { struct process *fp; /* careful here with interrupt level */ pp->p_cwd = 0; pp->p_index = pcurrjob->p_index; pp->p_friends = pcurrjob; pp->p_jobid = pcurrjob->p_pid; for (fp = pcurrjob; fp->p_friends != pcurrjob; fp = fp->p_friends) ; fp->p_friends = pp; } else { pcurrjob = pp; pp->p_jobid = pid; pp->p_friends = pp; pp->p_cwd = dcwd; dcwd->di_count++; if (pmaxindex < BIGINDEX) pp->p_index = ++pmaxindex; else { struct process *np; for (i = 1; ; i++) { for (np = proclist.p_next; np; np = np->p_next) if (np->p_index == i) goto tryagain; pp->p_index = i; if (i > pmaxindex) pmaxindex = i; break; tryagain:; } } pprevious = pcurrent; pcurrent = pp; } pp->p_next = proclist.p_next; proclist.p_next = pp; (void) gettimeofday(&pp->p_btime, (struct timezone *)0); } void padd(struct command *t) { tchar **argp; #ifdef TRACE tprintf("TRACE- padd()\n"); #endif if (t == 0) return; switch (t->t_dtyp) { case TPAR: pads(S_LBRASP /* "( " */); padd(t->t_dspr); pads(S_SPRBRA /* " )" */); break; case TCOM: for (argp = t->t_dcom; *argp; argp++) { pads(*argp); if (argp[1]) pads(S_SP /* " " */); } break; case TOR: case TAND: case TFIL: case TLST: padd(t->t_dcar); switch (t->t_dtyp) { case TOR: pads(S_SPBARBARSP /* " || " */); break; case TAND: pads(S_SPANDANDSP /* " && " */); break; case TFIL: pads(S_SPBARSP /* " | " */); break; case TLST: pads(S_SEMICOLONSP /* "; " */); break; } padd(t->t_dcdr); return; } if ((t->t_dflg & FPIN) == 0 && t->t_dlef) { pads((t->t_dflg & FHERE) ? S_SPLESLESSP /* " << " */ : S_SPLESSP /* " < " */); pads(t->t_dlef); } if ((t->t_dflg & FPOU) == 0 && t->t_drit) { pads((t->t_dflg & FCAT) ? S_SPGTRGTRSP /* " >>" */ : S_SPGTR /* " >" */); if (t->t_dflg & FDIAG) pads(S_AND /* "&" */); pads(S_SP /* " " */); pads(t->t_drit); } } void pads(tchar *cp) { int i = strlen_(cp); #ifdef TRACE tprintf("TRACE- pads()\n"); #endif if (cmdlen >= PMAXLEN) return; if (cmdlen + i >= PMAXLEN) { (void) strcpy_(cmdp, S_SPPPP /* " ..." */); cmdlen = PMAXLEN; cmdp += 4; return; } (void) strcpy_(cmdp, cp); cmdp += i; cmdlen += i; } /* * psavejob - temporarily save the current job on a one level stack * so another job can be created. Used for { } in exp6 * and `` in globbing. */ void psavejob(void) { #ifdef TRACE tprintf("TRACE- psavejob()\n"); #endif pholdjob = pcurrjob; pcurrjob = PNULL; } /* * prestjob - opposite of psavejob. This may be missed if we are interrupted * somewhere, but pendjob cleans up anyway. */ void prestjob(void) { #ifdef TRACE tprintf("TRACE- prestjob()\n"); #endif pcurrjob = pholdjob; pholdjob = PNULL; } /* * pendjob - indicate that a job (set of commands) has been completed * or is about to begin. */ void pendjob(void) { struct process *pp, *tp; #ifdef TRACE tprintf("TRACE- pendjob()\n"); #endif if (pcurrjob && (pcurrjob->p_flags&(PFOREGND|PSTOPPED)) == 0) { pp = pcurrjob; while (pp->p_pid != pp->p_jobid) pp = pp->p_friends; printf("[%d]", pp->p_index); tp = pp; do { printf(" %d", pp->p_pid); pp = pp->p_friends; } while (pp != tp); printf("\n"); } pholdjob = pcurrjob = 0; } /* * pprint - print a job */ int pprint(struct process *pp, int flag) { int status, reason; struct process *tp; extern char *linp, linbuf[]; int jobflags, pstatus; char *format; #ifdef TRACE tprintf("TRACE- pprint()\n"); #endif while (pp->p_pid != pp->p_jobid) pp = pp->p_friends; if (pp == pp->p_friends && (pp->p_flags & PPTIME)) { pp->p_flags &= ~PPTIME; pp->p_flags |= PTIME; } tp = pp; status = reason = -1; jobflags = 0; do { jobflags |= pp->p_flags; pstatus = pp->p_flags & PALLSTATES; if (tp != pp && linp != linbuf && !(flag&FANCY) && (pstatus == status && pp->p_reason == reason || !(flag&REASON))) printf(" "); else { if (tp != pp && linp != linbuf) printf("\n"); if (flag&NUMBER) if (pp == tp) printf("[%d]%s %c ", pp->p_index, pp->p_index < 10 ? " " : "", pp == pcurrent ? '+' : (pp == pprevious ? (tchar) '-' : (tchar) ' ')); else printf(" "); if (flag&FANCY) printf("%5d ", pp->p_pid); if (flag&(REASON|AREASON)) { if (flag&NAME) format = "%-21s"; else format = "%s"; if (pstatus == status) if (pp->p_reason == reason) { printf(format, ""); goto prcomd; } else reason = pp->p_reason; else { status = pstatus; reason = pp->p_reason; } switch (status) { case PRUNNING: printf(format, "Running "); break; case PINTERRUPTED: case PSTOPPED: case PSIGNALED: if ((flag&(REASON|AREASON)) && reason != SIGINT && reason != SIGPIPE) printf(format, strsignal(pp->p_reason)); break; case PNEXITED: case PAEXITED: if (flag & REASON) if (pp->p_reason) printf("Exit %-16d", pp->p_reason); else printf(format, "Done"); break; default: printf("BUG: status=%-9o", status); } } } prcomd: if (flag&NAME) { printf("%t", pp->p_command); if (pp->p_flags & PPOU) printf(" |"); if (pp->p_flags & PDIAG) printf("&"); } if (flag&(REASON|AREASON) && pp->p_flags&PDUMPED) printf(" (core dumped)"); if (tp == pp->p_friends) { if (flag&ERSAND) printf(" &"); if (flag&JOBDIR && !eq(tp->p_cwd->di_name, dcwd->di_name)) { printf(" (wd: "); dtildepr(value(S_home /* "home" */), tp->p_cwd->di_name); printf(")"); } } if (pp->p_flags&PPTIME && !(status&(PSTOPPED|PRUNNING))) { if (linp != linbuf) printf("\n\t"); { static struct rusage zru; prusage(&zru, &pp->p_rusage, &pp->p_etime, &pp->p_btime); } } if (tp == pp->p_friends) { if (linp != linbuf) printf("\n"); if (flag&SHELLDIR && !eq(tp->p_cwd->di_name, dcwd->di_name)) { printf("(wd now: "); dtildepr(value(S_home /* "home" */), dcwd->di_name); printf(")\n"); } } } while ((pp = pp->p_friends) != tp); if (jobflags&PTIME && (jobflags&(PSTOPPED|PRUNNING)) == 0) { if (jobflags & NUMBER) printf(" "); ptprint(tp); } return (jobflags); } void ptprint(struct process *tp) { struct timeval tetime, diff; static struct timeval ztime; struct rusage ru; static struct rusage zru; struct process *pp = tp; #ifdef TRACE tprintf("TRACE- ptprint()\n"); #endif ru = zru; tetime = ztime; do { ruadd(&ru, &pp->p_rusage); tvsub(&diff, &pp->p_etime, &pp->p_btime); if (timercmp(&diff, &tetime, >)) tetime = diff; } while ((pp = pp->p_friends) != tp); prusage(&zru, &ru, &tetime, &ztime); } /* * dojobs - print all jobs */ void dojobs(tchar **v) { struct process *pp; int flag = NUMBER|NAME|REASON; int i; #ifdef TRACE tprintf("TRACE- dojobs()\n"); #endif if (chkstop) chkstop = 2; if (*++v) { if (v[1] || !eq(*v, S_DASHl /* "-l" */)) error("Usage: jobs [ -l ]"); flag |= FANCY|JOBDIR; } for (i = 1; i <= pmaxindex; i++) for (pp = proclist.p_next; pp; pp = pp->p_next) if (pp->p_index == i && pp->p_pid == pp->p_jobid) { pp->p_flags &= ~PNEEDNOTE; if (!(pprint(pp, flag) & (PRUNNING|PSTOPPED))) pflush(pp); break; } } /* * dofg - builtin - put the job into the foreground */ void dofg(tchar **v) { struct process *pp; #ifdef TRACE tprintf("TRACE- dofg()\n"); #endif okpcntl(); ++v; do { pp = pfind(*v); pstart(pp, 1); pjwait(pp); } while (*v && *++v); } /* * %... - builtin - put the job into the foreground */ void dofg1(tchar **v) { struct process *pp; #ifdef TRACE tprintf("TRACE- untty()\n"); #endif okpcntl(); pp = pfind(v[0]); pstart(pp, 1); pjwait(pp); } /* * dobg - builtin - put the job into the background */ void dobg(tchar **v) { struct process *pp; #ifdef TRACE tprintf("TRACE- dobg()\n"); #endif okpcntl(); ++v; do { pp = pfind(*v); pstart(pp, 0); } while (*v && *++v); } /* * %... & - builtin - put the job into the background */ void dobg1(tchar **v) { struct process *pp; #ifdef TRACE tprintf("TRACE- dobg1()\n"); #endif pp = pfind(v[0]); pstart(pp, 0); } /* * dostop - builtin - stop the job */ void dostop(tchar **v) { #ifdef TRACE tprintf("TRACE- dostop()\n"); #endif pkill(++v, SIGSTOP); } /* * dokill - builtin - superset of kill (1) */ void dokill(tchar **v) { int signum; tchar *name; #ifdef TRACE tprintf("TRACE- dokill()\n"); #endif v++; if (v[0] && v[0][0] == '-') { if (v[0][1] == 'l') { for (signum = 1; signum <= NSIG-1; signum++) { char sbuf[BUFSIZ]; if (sig2str(signum, sbuf) == 0) printf("%s ", sbuf); if (signum % 8 == 0) Putchar('\n'); } Putchar('\n'); return; } if (digit(v[0][1])) { if (chkalldigit_(v[0]+1) != 0) { setname(v[0]+1); bferr("Unknown signal; kill -l lists signals"); } signum = atoi_(v[0]+1); if (signum < 0 || signum > NSIG) bferr("Bad signal number"); } else { int signo; char sbuf[BUFSIZ]; name = &v[0][1]; tstostr(sbuf, name); if (str2sig(sbuf, &signo) == 0) { signum = signo; goto gotsig; } if (eq(name, S_IOT /* "IOT" */)) { signum = SIGABRT; goto gotsig; } setname(name); bferr("Unknown signal; kill -l lists signals"); } gotsig: v++; } else signum = SIGTERM; pkill(v, signum); } void pkill(tchar **v, int signum) { struct process *pp, *np; int jobflags = 0; int omask, pid, err = 0; tchar *cp; #ifdef TRACE tprintf("TRACE- pkill()\n"); #endif omask = sigmask(SIGCHLD); if (setintr) omask |= sigmask(SIGINT); omask = sigblock(omask) & ~omask; while (*v) { cp = globone(*v); if (*cp == '%') { np = pp = pfind(cp); do jobflags |= np->p_flags; while ((np = np->p_friends) != pp); switch (signum) { case SIGSTOP: case SIGTSTP: case SIGTTIN: case SIGTTOU: if ((jobflags & PRUNNING) == 0) { /* %s -> %t */ printf("%t: Already stopped\n", cp); err++; goto cont; } } if (killpg(pp->p_jobid, signum) < 0) { /* %s -> %t */ printf("%t: ", cp); printf("%s\n", strerror(errno)); err++; } if (signum == SIGTERM || signum == SIGHUP) (void) killpg(pp->p_jobid, SIGCONT); } else if (!(digit(*cp) || *cp == '-')) bferr("Arguments should be jobs or process id's"); else { pid = atoi_(cp); if (kill(pid, signum) < 0) { printf("%d: ", pid); printf("%s\n", strerror(errno)); err++; goto cont; } if (signum == SIGTERM || signum == SIGHUP) (void) kill(pid, SIGCONT); } cont: xfree(cp); v++; } (void) sigsetmask(omask); if (err) error(NULL); } /* * pstart - start the job in foreground/background */ void pstart(struct process *pp, int foregnd) { struct process *np; int omask, jobflags = 0; #ifdef TRACE tprintf("TRACE- pstart()\n"); #endif omask = sigblock(sigmask(SIGCHLD)); np = pp; do { jobflags |= np->p_flags; if (np->p_flags&(PRUNNING|PSTOPPED)) { np->p_flags |= PRUNNING; np->p_flags &= ~PSTOPPED; if (foregnd) np->p_flags |= PFOREGND; else np->p_flags &= ~PFOREGND; } } while ((np = np->p_friends) != pp); if (foregnd) pclrcurr(pp); else { if (pprevious && (pprevious->p_flags & PSTOPPED)) { pcurrent = pprevious; pprevious = pgetcurr(PNULL); } else { pcurrent = pgetcurr(pp); if (!pcurrent || (pcurrent->p_flags & PRUNNING)) pcurrent = pp; else pprevious = pp; } } (void) pprint(pp, foregnd ? NAME|JOBDIR : NUMBER|NAME|AMPERSAND); if (foregnd) (void) ioctl(FSHTTY, TIOCSPGRP, (char *)&pp->p_jobid); if (jobflags&PSTOPPED) (void) killpg(pp->p_jobid, SIGCONT); (void) sigsetmask(omask); } void panystop(int neednl) { struct process *pp; #ifdef TRACE tprintf("TRACE- panystop()\n"); #endif chkstop = 2; for (pp = proclist.p_next; pp; pp = pp->p_next) if (pp->p_flags & PSTOPPED) error("\nThere are stopped jobs" + 1 - neednl); } struct process * pfind(tchar *cp) { struct process *pp, *np; #ifdef TRACE tprintf("TRACE- pfind()\n"); #endif if (cp == 0 || cp[1] == 0 || eq(cp, S_PARCENTPARCENT /* "%%" */) || eq(cp, S_PARCENTPLUS /* "%+" */)) { if (pcurrent == PNULL) if ((pcurrent = pgetcurr(PNULL)) == PNULL) bferr("No current job"); return (pcurrent); } if (eq(cp, S_PARCENTMINUS /* "%-" */) || eq(cp, S_PARCENTSHARP /* "%#" */)) { if (pprevious == PNULL) bferr("No previous job"); return (pprevious); } if (digit(cp[1])) { int index = atoi_(cp+1); for (pp = proclist.p_next; pp; pp = pp->p_next) if (pp->p_index == index && pp->p_pid == pp->p_jobid) return (pp); bferr("No such job"); } np = PNULL; for (pp = proclist.p_next; pp; pp = pp->p_next) if (pp->p_pid == pp->p_jobid) { if (cp[1] == '?') { tchar *dp; for (dp = pp->p_command; *dp; dp++) { if (*dp != cp[2]) continue; if (prefix(cp+2, dp)) goto match; } } else if (prefix(cp+1, pp->p_command)) { match: if (np) bferr("Ambiguous"); np = pp; } } if (np) return (np); if (cp[1] == '?') bferr("No job matches pattern"); else bferr("No such job"); /*NOTREACHED*/ } /* * pgetcurr - find most recent job that is not pp, preferably stopped */ struct process * pgetcurr(struct process *pp) { struct process *np; struct process *xp = PNULL; #ifdef TRACE tprintf("TRACE- pgetcurr()\n"); #endif for (np = proclist.p_next; np; np = np->p_next) if (np != pcurrent && np != pp && np->p_pid && np->p_pid == np->p_jobid) { if (np->p_flags & PSTOPPED) return (np); if (xp == PNULL) xp = np; } return (xp); } /* * donotify - flag the job so as to report termination asynchronously */ void donotify(tchar **v) { struct process *pp; #ifdef TRACE tprintf("TRACE- donotify()\n"); #endif pp = pfind(*++v); pp->p_flags |= PNOTIFY; } /* * Do the fork and whatever should be done in the child side that * should not be done if we are not forking at all (like for simple builtin's) * Also do everything that needs any signals fiddled with in the parent side * * Wanttty tells whether process and/or tty pgrps are to be manipulated: * -1: leave tty alone; inherit pgrp from parent * 0: already have tty; manipulate process pgrps only * 1: want to claim tty; manipulate process and tty pgrps * It is usually just the value of tpgrp. * * argument: * t: command we are forking for */ int pfork(struct command *t, int wanttty) { int pid; bool ignint = 0; int pgrp, omask; int child_pid; #ifdef TRACE tprintf("TRACE- pfork()\n"); #endif /* * A child will be uninterruptible only under very special * conditions. Remember that the semantics of '&' is * implemented by disconnecting the process from the tty so * signals do not need to ignored just for '&'. * Thus signals are set to default action for children unless: * we have had an "onintr -" (then specifically ignored) * we are not playing with signals (inherit action) */ if (setintr) ignint = (tpgrp == -1 && (t->t_dflg&FINT)) || (gointr && eq(gointr, S_MINUS /* "-" */)); /* * Hold SIGCHLD until we have the process installed in our table. */ omask = sigblock(sigmask(SIGCHLD)); while ((pid = fork()) < 0) if (setintr == 0) sleep(FORKSLEEP); else { (void) sigsetmask(omask); error("Fork failed"); } /* * setup the process group */ if (pid == 0) child_pid = getpid(); else child_pid = pid; pgrp = pcurrjob ? pcurrjob->p_jobid : child_pid; if (pid == 0) { int sigttou; settimes(); pflushall(); pcurrjob = PNULL; child++; if (setintr) { setintr = 0; /* until I think otherwise */ /* * Children just get blown away on SIGINT, SIGQUIT * unless "onintr -" seen. */ (void) signal(SIGINT, ignint ? SIG_IGN : SIG_DFL); (void) signal(SIGQUIT, ignint ? SIG_IGN : SIG_DFL); if (wanttty >= 0) { /* make stoppable */ (void) signal(SIGTSTP, SIG_DFL); (void) signal(SIGTTIN, SIG_DFL); (void) signal(SIGTTOU, SIG_DFL); } (void) signal(SIGTERM, parterm); } else if (tpgrp == -1 && (t->t_dflg&FINT)) { (void) signal(SIGINT, SIG_IGN); (void) signal(SIGQUIT, SIG_IGN); } if (wanttty >= 0 && tpgrp >= 0) (void) setpgid(0, pgrp); if (wanttty > 0) { sigttou = sigblock(sigmask(SIGTTOU) | sigmask(SIGTTIN) | sigmask(SIGTSTP)); (void) ioctl(FSHTTY, TIOCSPGRP, (char *)&pgrp); sigsetmask(sigttou); } if (tpgrp > 0) tpgrp = 0; /* gave tty away */ /* * Nohup and nice apply only to TCOM's but it would be * nice (?!?) if you could say "nohup (foo;bar)" * Then the parser would have to know about nice/nohup/time */ if (t->t_dflg & FNOHUP) (void) signal(SIGHUP, SIG_IGN); if (t->t_dflg & FNICE) (void) setpriority(PRIO_PROCESS, 0, t->t_nice); } else { if (wanttty >= 0 && tpgrp >= 0) setpgid(pid, pgrp); palloc(pid, t); (void) sigsetmask(omask); } return (pid); } void okpcntl(void) { #ifdef TRACE tprintf("TRACE- okpcntl()\n"); #endif if (tpgrp == -1) error("No job control in this shell"); if (tpgrp == 0) error("No job control in subshells"); } void hupforegnd(void) { struct process *pp; int omask; omask = sigblock(sigmask(SIGCHLD)); for (pp = (&proclist)->p_next; pp != PNULL; pp = pp->p_next) if (pp->p_pid > 0) { if (pp->p_flags & PFOREGND) (void) kill(pp->p_pid, SIGHUP); } (void) sigsetmask(omask); } /* * Copyright 2005 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ /* Copyright (c) 1983, 1984, 1985, 1986, 1987, 1988, 1989 AT&T */ /* All Rights Reserved */ /* * Copyright (c) 1980 Regents of the University of California. * All rights reserved. The Berkeley Software License Agreement * specifies the terms and conditions for redistribution. */ /* * C shell - process structure declarations */ /* * Structure for each process the shell knows about: * allocated and filled by pcreate. * flushed by pflush; freeing always happens at top level * so the interrupt level has less to worry about. * processes are related to "friends" when in a pipeline; * p_friends links makes a circular list of such jobs */ struct process { struct process *p_next; /* next in global "proclist" */ struct process *p_friends; /* next in job list (or self) */ struct directory *p_cwd; /* cwd of the job (only in head) */ short unsigned p_flags; /* various job status flags */ tchar p_reason; /* reason for entering this state */ tchar p_index; /* shorthand job index */ int p_pid; int p_jobid; /* pid of job leader */ /* if a job is stopped/background p_jobid gives its pgrp */ struct timeval p_btime; /* begin time */ struct timeval p_etime; /* end time */ struct rusage p_rusage; tchar *p_command; /* first PMAXLEN chars of command */ }; /* added for status */ #define ABN_TERM 0200 /* flag values for p_flags */ #define PRUNNING (1<<0) /* running */ #define PSTOPPED (1<<1) /* stopped */ #define PNEXITED (1<<2) /* normally exited */ #define PAEXITED (1<<3) /* abnormally exited */ #define PSIGNALED (1<<4) /* terminated by a signal != SIGINT */ #define PALLSTATES (PRUNNING|PSTOPPED|PNEXITED|PAEXITED|PSIGNALED|PINTERRUPTED) #define PNOTIFY (1<<5) /* notify async when done */ #define PTIME (1<<6) /* job times should be printed */ #define PAWAITED (1<<7) /* top level is waiting for it */ #define PFOREGND (1<<8) /* started in shells pgrp */ #define PDUMPED (1<<9) /* process dumped core */ #define PDIAG (1<<10) /* diagnostic output also piped out */ #define PPOU (1<<11) /* piped output */ #define PREPORTED (1<<12) /* status has been reported */ #define PINTERRUPTED (1<<13) /* job stopped via interrupt signal */ #define PPTIME (1<<14) /* time individual process */ #define PNEEDNOTE (1<<15) /* notify as soon as practical */ #define PNULL (struct process *)0 #define PMAXLEN 80 /* defines for arguments to pprint */ #define NUMBER 01 #define NAME 02 #define REASON 04 #define AMPERSAND 010 #define FANCY 020 #define SHELLDIR 040 /* print shell's dir if not the same */ #define JOBDIR 0100 /* print job's dir if not the same */ #define AREASON 0200 extern struct process *pcurrjob; /* current job */ /* * Copyright 2005 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ /* Copyright (c) 1983, 1984, 1985, 1986, 1987, 1988, 1989 AT&T */ /* All Rights Reserved */ /* * Copyright (c) 1980 Regents of the University of California. * All rights reserved. The Berkeley Software License Agreement * specifies the terms and conditions for redistribution. */ #include #include #include "sh.h" #include "sh.proc.h" #include "sh.tconst.h" /* * C shell */ void doio(struct command *, int *, int *); void mypipe(int *); void chkclob(tchar *); /* * Return true if there is a back-quote (`) anywhere in the argument list. * Its presence would cause glob() to be invoked in the child process * and this would cause chaos if the child is created with vfork(). */ static bool AnyBquote(struct command *t) { tchar **pp; tchar *p; if (noexec) return (0); for (pp = t->t_dcom; p = *pp++;) { if (any('`', p)) return (1); } return (0); } /*VARARGS 1*/ void execute(t, wanttty, pipein, pipeout) struct command *t; int wanttty, *pipein, *pipeout; { bool forked = 0; struct biltins *bifunc; int pid = 0; int pv[2]; extern int globcnt; #ifdef TRACE tprintf("TRACE- execute()\n"); #endif if (t == 0) return; if ((t->t_dflg & FAND) && wanttty > 0) wanttty = 0; switch (t->t_dtyp) { case TCOM: if (t->t_dcom[0][0] == (tchar)S_TOPBIT[0]) (void) strcpy_(t->t_dcom[0], t->t_dcom[0] + 1); if ((t->t_dflg & FREDO) == 0) Dfix(t); /* $ " ' \ */ if (t->t_dcom[0] == 0) return; /* fall into... */ case TPAR: if (t->t_dflg & FPOU) mypipe(pipeout); /* * Must do << early so parent will know * where input pointer should be. * If noexec then this is all we do. */ if (t->t_dflg & FHERE) { (void) close(0); unsetfd(0); heredoc(t->t_dlef); if (noexec) { (void) close(0); unsetfd(0); } } if (noexec) break; set(S_status, S_0); /* * This mess is the necessary kludge to handle the prefix * builtins: nice, nohup, time. These commands can also * be used by themselves, and this is not handled here. * This will also work when loops are parsed. */ while (t->t_dtyp == TCOM) if (eq(t->t_dcom[0], S_nice /*"nice"*/)) if (t->t_dcom[1]) /*if (any(t->t_dcom[1][0], "+-"))*/ if (t->t_dcom[1][0] == '+' || t->t_dcom[1][0] == '-') if (t->t_dcom[2]) { setname(S_nice /*"nice"*/); t->t_nice = getn(t->t_dcom[1]); lshift(t->t_dcom, 2); t->t_dflg |= FNICE; } else break; else { t->t_nice = 4; lshift(t->t_dcom, 1); t->t_dflg |= FNICE; } else break; else if (eq(t->t_dcom[0], S_nohup /*"nohup"*/)) if (t->t_dcom[1]) { t->t_dflg |= FNOHUP; lshift(t->t_dcom, 1); } else break; else if (eq(t->t_dcom[0], S_time /*"time"*/)) if (t->t_dcom[1]) { t->t_dflg |= FTIME; lshift(t->t_dcom, 1); } else break; else break; /* * Check if we have a builtin function and remember which one. */ bifunc = t->t_dtyp == TCOM ? isbfunc(t) : (struct biltins *) 0; /* * We fork only if we are timed, or are not the end of * a parenthesized list and not a simple builtin function. * Simple meaning one that is not pipedout, niced, nohupped, * or &'d. * It would be nice(?) to not fork in some of these cases. */ if (((t->t_dflg & FTIME) || (t->t_dflg & FPAR) == 0 && (!bifunc || t->t_dflg & (FPOU|FAND|FNICE|FNOHUP)))) #ifdef VFORK if (t->t_dtyp == TPAR || t->t_dflg&(FREDO|FAND) || bifunc || AnyBquote(t)) #endif { forked++; pid = pfork(t, wanttty); } #ifdef VFORK else { void vffree(); struct sv { int mask, child, setintr, haderr, didfds; int SHIN, SHOUT, SHDIAG, OLDSTD, tpgrp; struct sigvec sigv; } sv; /* * Prepare for the vfork by saving everything * that the child corrupts before it exec's. * Note that in some signal implementations * which keep the signal info in user space * (e.g. Sun's) it will also be necessary to * save and restore the current sigvec's for * the signals the child touches before it * exec's. */ sv.mask = sigblock(sigmask(SIGCHLD)); sv.child = child; sv.setintr = setintr; sv.haderr = haderr; sv.didfds = didfds; sv.SHIN = SHIN; sv.SHOUT = SHOUT; sv.SHDIAG = SHDIAG; sv.OLDSTD = OLDSTD; sv.tpgrp = tpgrp; Vsav = Vdp = 0; Vav = 0; (void) sigvec(SIGINT, (struct sigvec *)0, &sv.sigv); pid = vfork(); if (pid < 0) { (void) sigsetmask(sv.mask); error("Vfork failed"); } forked++; if (pid) { /* parent */ int ppid; closelog(); child = sv.child; setintr = sv.setintr; haderr = sv.haderr; didfds = sv.didfds; SHIN = sv.SHIN; SHOUT = sv.SHOUT; SHDIAG = sv.SHDIAG; OLDSTD = sv.OLDSTD; tpgrp = sv.tpgrp; xfree(Vsav); Vsav = 0; xfree(Vdp); Vdp = 0; xfree( (tchar *)Vav); Vav = 0; /* this is from pfork() */ ppid = pcurrjob ? pcurrjob->p_jobid : pid; if (wanttty >= 0 && tpgrp >= 0) setpgid (ppid, ppid); palloc(pid, t); /* * Restore SIGINT handler. */ (void) sigvec(SIGINT, &sv.sigv, (struct sigvec *)0); (void) sigsetmask(sv.mask); } else { /* child */ /* this is from pfork() */ int pgrp; bool ignint = 0; int sigttou; if (setintr) ignint = (tpgrp == -1 && (t->t_dflg&FINT)) || gointr && eq(gointr, S_MINUS/*"-"*/); pgrp = pcurrjob ? pcurrjob->p_jobid : getpid(); child++; if (setintr) { setintr = 0; #ifdef notdef (void) signal(SIGCHLD, SIG_DFL); #endif (void) signal(SIGINT, ignint ? SIG_IGN : vffree); (void) signal(SIGQUIT, ignint ? SIG_IGN : SIG_DFL); if (wanttty >= 0) { (void) signal(SIGTSTP, SIG_DFL); (void) signal(SIGTTIN, SIG_DFL); (void) signal(SIGTTOU, SIG_DFL); } (void) signal(SIGTERM, parterm); } else if (tpgrp == -1 && (t->t_dflg&FINT)) { (void) signal(SIGINT, SIG_IGN); (void) signal(SIGQUIT, SIG_IGN); } if (wanttty >= 0 && tpgrp >= 0) (void) setpgid(0, pgrp); if (wanttty > 0) { sigttou = sigblock ( sigmask(SIGTTOU) | sigmask(SIGTTIN) | sigmask(SIGTSTP)); (void) ioctl(FSHTTY, TIOCSPGRP, (tchar *)&pgrp); sigsetmask (sigttou); } if (tpgrp > 0) tpgrp = 0; if (t->t_dflg & FNOHUP) (void) signal(SIGHUP, SIG_IGN); if (t->t_dflg & FNICE) (void) setpriority(PRIO_PROCESS, 0, t->t_nice); } } #endif if (pid != 0) { /* * It would be better if we could wait for the * whole job when we knew the last process * had been started. Pwait, in fact, does * wait for the whole job anyway, but this test * doesn't really express our intentions. */ if (didfds==0 && t->t_dflg&FPIN) { (void) close(pipein[0]); unsetfd(pipein[0]); (void) close(pipein[1]); unsetfd(pipein[1]); } if ((t->t_dflg & (FPOU|FAND)) == 0) pwait(); break; } doio(t, pipein, pipeout); if (t->t_dflg & FPOU) { (void) close(pipeout[0]); (void) unsetfd(pipeout[0]); (void) close(pipeout[1]); (void) unsetfd(pipeout[1]); } /* * Perform a builtin function. * If we are not forked, arrange for possible stopping */ if (bifunc) { func(t, bifunc); if (forked) exitstat(); break; } if (t->t_dtyp != TPAR) { doexec(t); /*NOTREACHED*/ } /* * For () commands must put new 0,1,2 in FSH* and recurse */ OLDSTD = dcopy(0, FOLDSTD); SHOUT = dcopy(1, FSHOUT); SHDIAG = dcopy(2, FSHDIAG); (void) close(SHIN); (void) unsetfd(SHIN); SHIN = -1; didfds = 0; wanttty = -1; t->t_dspr->t_dflg |= t->t_dflg & FINT; execute(t->t_dspr, wanttty); exitstat(); case TFIL: t->t_dcar->t_dflg |= FPOU | (t->t_dflg & (FPIN|FAND|FDIAG|FINT)); execute(t->t_dcar, wanttty, pipein, pv); t->t_dcdr->t_dflg |= FPIN | (t->t_dflg & (FPOU|FAND|FPAR|FINT)); if (wanttty > 0) wanttty = 0; /* got tty already */ execute(t->t_dcdr, wanttty, pv, pipeout); break; case TLST: if (t->t_dcar) { t->t_dcar->t_dflg |= t->t_dflg & FINT; execute(t->t_dcar, wanttty); /* * In strange case of A&B make a new job after A */ if (t->t_dcar->t_dflg&FAND && t->t_dcdr && (t->t_dcdr->t_dflg&FAND) == 0) pendjob(); } if (t->t_dcdr) { t->t_dcdr->t_dflg |= t->t_dflg & (FPAR|FINT); execute(t->t_dcdr, wanttty); } break; case TOR: case TAND: if (t->t_dcar) { t->t_dcar->t_dflg |= t->t_dflg & FINT; execute(t->t_dcar, wanttty); if ((getn(value(S_status/*"status"*/)) == 0) != (t->t_dtyp == TAND)) return; } if (t->t_dcdr) { t->t_dcdr->t_dflg |= t->t_dflg & (FPAR|FINT); execute(t->t_dcdr, wanttty); } break; } /* * Fall through for all breaks from switch * * If there will be no more executions of this * command, flush all file descriptors. * Places that turn on the FREDO bit are responsible * for doing donefds after the last re-execution */ if (didfds && !(t->t_dflg & FREDO)) donefds(); /* * If glob() was called and arguments list is not yet * free'ed, free them here. */ if (gargv) { blkfree(gargv); gargv = 0; globcnt = 0; } } #ifdef VFORK void vffree(void) { tchar **v; #ifdef TRACE tprintf("TRACE- vffree()\n"); #endif if (v = gargv) gargv = 0, xfree( (tchar *)v); if (v = pargv) pargv = 0, xfree( (tchar *)v); _exit(1); } #endif /* * Perform io redirection. * We may or maynot be forked here. */ void doio(struct command *t, int *pipein, int *pipeout) { tchar *cp, *dp; int flags = t->t_dflg; int fd; #ifdef TRACE tprintf("TRACE- doio()\n"); #endif if (didfds || (flags & FREDO)) return; if ((flags & FHERE) == 0) { /* FHERE already done */ (void) close(0); (void) unsetfd(0); if (cp = t->t_dlef) { dp = Dfix1(cp); cp = globone(dp); xfree(dp); xfree(cp); if (open_(cp, 0) < 0) Perror(cp); } else if (flags & FPIN) { fd = dup(pipein[0]); if (fd != -1) setfd(fd); (void) close(pipein[0]); (void) unsetfd(pipein[0]); (void) close(pipein[1]); (void) unsetfd(pipein[1]); } else if ((flags & FINT) && tpgrp == -1) { (void) close(0); /* no need for unsetfd */ (void) open("/dev/null", 0); /* no need for setfd */ } else { fd = dup(OLDSTD); if (fd != -1) setfd(fd); } } (void) close(1); (void) unsetfd(1); if (cp = t->t_drit) { dp = Dfix1(cp); cp = globone(dp); xfree(dp); if ((flags & FCAT) && open_(cp, 1) >= 0) (void) lseek(1, (off_t)0, 2); else { if (!(flags & FANY) && adrof(S_noclobber/*"noclobber"*/)) { if (flags & FCAT) Perror(cp); chkclob(cp); } if (creat_(cp, 0666) < 0) Perror(cp); } xfree(cp); } else if (flags & FPOU) { fd = dup(pipeout[1]); if (fd != -1) setfd (fd); } else { fd = dup(SHOUT); if (fd != -1) setfd(fd); } (void) close(2); (void) unsetfd(2); if (flags & FDIAG) { fd = dup(1); if (fd != -1) setfd(fd); } else { fd = dup(SHDIAG); if (fd != -1) setfd(fd); } didfds = 1; } void mypipe(int *pv) { #ifdef TRACE tprintf("TRACE- mypipe()\n"); #endif if (pipe(pv) < 0) goto oops; setfd(pv[0]); setfd(pv[1]); pv[0] = dmove(pv[0], -1); pv[1] = dmove(pv[1], -1); if (pv[0] >= 0 && pv[1] >= 0) return; oops: error("Can't make pipe"); } void chkclob(tchar *cp) { struct stat stb; unsigned short type; #ifdef TRACE tprintf("TRACE- chkclob()\n"); #endif if (stat_(cp, &stb) < 0) return; type = stb.st_mode & S_IFMT; if (type == S_IFCHR || type == S_IFIFO) return; error("%t: File exists", cp); } /* * Copyright 2005 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ /* Copyright (c) 1983, 1984, 1985, 1986, 1987, 1988, 1989 AT&T */ /* All Rights Reserved */ /* * Copyright (c) 1980 Regents of the University of California. * All rights reserved. The Berkeley Software License Agreement * specifies the terms and conditions for redistribution. */ #include "sh.h" #include "sh.tconst.h" /* * C Shell */ void asx(tchar *, int, tchar *); void putn1(int); void set(tchar *, tchar *); void set1(tchar *, tchar **, struct varent *); void setq(tchar *, tchar **, struct varent *); void unset1(tchar *[], struct varent *); void unsetv1(struct varent *); void exportpath(tchar **); void balance(struct varent *, int, int); tchar *operate(tchar, tchar *, tchar *); tchar *getinx(tchar *, int *); tchar *xset(tchar *, tchar ***); struct varent *getvx(tchar *, int); void doset(tchar **v) { tchar *p; tchar *vp, op; tchar **vecp; bool hadsub; int subscr; tchar *retp; #ifdef TRACE tprintf("TRACE- doset()\n"); #endif v++; p = *v++; if (p == 0) { prvars(); return; } do { hadsub = 0; /* * check for proper variable syntax * must be alphanumeric, start with a letter and * be at most 20 characters */ for (vp = p; alnum(*p); p++) continue; if (vp == p || !letter(*vp)) goto setsyn; if ((p - vp) > MAX_VAR_LEN) bferr("Variable name too long"); if (*p == '[') { hadsub++; p = getinx(p, &subscr); } if (op = *p) { *p++ = 0; if (*p == 0 && *v && **v == '(') p = *v++; } else if (*v && eq(*v, S_EQ /* "=" */)) { op = '=', v++; if (*v) p = *v++; } if (op && op != '=') setsyn: bferr("Syntax error"); if (eq(p, S_LPAR /* "(" */)) { tchar **e = v; if (hadsub) goto setsyn; for (;;) { if (!*e) bferr("Missing )"); if (**e == ')') break; e++; } p = *e; *e = 0; vecp = saveblk(v); set1(vp, vecp, &shvhed); *e = p; v = e + 1; } else if (hadsub) { retp = savestr(p); asx(vp, subscr, retp); xfree(retp); retp = 0; } else set(vp, savestr(p)); if (eq(vp, S_path /* "path" */)) { exportpath(adrof(S_path /* "path" */)->vec); dohash(xhash); } else if (eq(vp, S_histchars /* "histchars" */)) { tchar *p = value(S_histchars /* "histchars" */); HIST = *p++; HISTSUB = *p; } else if (eq(vp, S_user /* "user" */)) local_setenv(S_USER /* "USER" */, value(vp)); else if (eq(vp, S_term /* "term" */)) local_setenv(S_TERM /* "TERM" */, value(vp)); else if (eq(vp, S_home /* "home" */)) local_setenv(S_HOME /* "HOME" */, value(vp)); #ifdef FILEC else if (eq(vp, S_filec /* "filec" */)) filec = 1; else if (eq(vp, S_cdpath /* "cdpath" */)) dohash(xhash2); #endif } while (p = *v++); } tchar * getinx(tchar *cp, int *ip) { #ifdef TRACE tprintf("TRACE- getinx()\n"); #endif *ip = 0; *cp++ = 0; while (*cp && digit(*cp)) *ip = *ip * 10 + *cp++ - '0'; if (*cp++ != ']') bferr("Subscript error"); return (cp); } void asx(tchar *vp, int subscr, tchar *p) { struct varent *v = getvx(vp, subscr); #ifdef TRACE tprintf("TRACE- asx()\n"); #endif xfree(v->vec[subscr - 1]); v->vec[subscr - 1] = globone(p); } struct varent * getvx(tchar *vp, int subscr) { struct varent *v = adrof(vp); #ifdef TRACE tprintf("TRACE- getvx()\n"); #endif if (v == 0) udvar(vp); if (subscr < 1 || subscr > blklen(v->vec)) bferr("Subscript out of range"); return (v); } tchar plusplus[2] = { '1', 0 }; void dolet(tchar **v) { tchar *p; tchar *vp, c, op; bool hadsub; int subscr; v++; p = *v++; if (p == 0) { prvars(); return; } do { hadsub = 0; for (vp = p; alnum(*p); p++) continue; if (vp == p || !letter(*vp)) goto letsyn; if (*p == '[') { hadsub++; p = getinx(p, &subscr); } if (*p == 0 && *v) p = *v++; if (op = *p) *p++ = 0; else goto letsyn; vp = savestr(vp); if (op == '=') { c = '='; p = xset(p, &v); } else { c = *p++; /* if (any(c, "+-")) { */ if (c == '+' || c == '-') { if (c != op || *p) goto letsyn; p = plusplus; } else { /* if (any(op, "<>")) { */ if (op == '<' || op == '>') { if (c != op) goto letsyn; c = *p++; letsyn: bferr("Syntax error"); } if (c != '=') goto letsyn; p = xset(p, &v); } } if (op == '=') if (hadsub) asx(vp, subscr, p); else set(vp, p); else if (hadsub) #ifndef V6 /* avoid bug in vax CC */ { struct varent *gv = getvx(vp, subscr); asx(vp, subscr, operate(op, gv->vec[subscr - 1], p)); } #else asx(vp, subscr, operate(op, getvx(vp, subscr)->vec[subscr - 1], p)); #endif else set(vp, operate(op, value(vp), p)); if (eq(vp, S_path /* "path" */)) { exportpath(adrof(S_path /* "path" */)->vec); dohash(xhash); } if (eq(vp, S_cdpath /* "cdpath" */)) dohash(xhash2); xfree(vp); if (c != '=') xfree(p); } while (p = *v++); } tchar * xset(tchar *cp, tchar ***vp) { tchar *dp; #ifdef TRACE tprintf("TRACE- xset()\n"); #endif if (*cp) { dp = savestr(cp); --(*vp); xfree(**vp); **vp = dp; } return (putn(exp(vp))); } tchar * operate(tchar op, tchar *vp, tchar *p) { tchar opr[2]; tchar *vec[5]; tchar **v = vec; tchar **vecp = v; int i; if (op != '=') { if (*vp) *v++ = vp; opr[0] = op; opr[1] = 0; *v++ = opr; if (op == '<' || op == '>') *v++ = opr; } *v++ = p; *v++ = 0; i = exp(&vecp); if (*vecp) bferr("Expression syntax"); return (putn(i)); } static tchar *putp; tchar * putn(int n) { static tchar number[15]; #ifdef TRACE tprintf("TRACE- putn()\n"); #endif putp = number; if (n < 0) { n = -n; *putp++ = '-'; } if (sizeof (int) == 2 && n == -32768) { *putp++ = '3'; n = 2768; #ifdef pdp11 } #else } else if (sizeof (int) == 4 && n == 0x80000000) { *putp++ = '2'; n = 147483648; } #endif putn1(n); *putp = 0; return (savestr(number)); } void putn1(int n) { #ifdef TRACE tprintf("TRACE- putn1()\n"); #endif if (n > 9) putn1(n / 10); *putp++ = n % 10 + '0'; } int getn(tchar *cp) { int n; int sign; #ifdef TRACE tprintf("TRACE- getn()\n"); #endif sign = 0; if (cp[0] == '+' && cp[1]) cp++; if (*cp == '-') { sign++; cp++; if (!digit(*cp)) goto badnum; } n = 0; while (digit(*cp)) n = n * 10 + *cp++ - '0'; if (*cp) goto badnum; return (sign ? -n : n); badnum: bferr("Badly formed number"); return (0); } tchar * value1(tchar *var, struct varent *head) { struct varent *vp; #ifdef TRACE tprintf("TRACE- value1()\n"); #endif vp = adrof1(var, head); return (vp == 0 || vp->vec[0] == 0 ? S_ /* "" */ : vp->vec[0]); } struct varent * madrof(tchar *pat, struct varent *vp) { struct varent *vp1; #ifdef TRACE tprintf("TRACE- madrof()\n"); #endif for (; vp; vp = vp->v_right) { if (vp->v_left && (vp1 = madrof(pat, vp->v_left))) return vp1; if (Gmatch(vp->v_name, pat)) return vp; } return vp; } struct varent * adrof1(tchar *name, struct varent *v) { int cmp; #ifdef TRACE tprintf("TRACE- adrof1()\n"); #endif v = v->v_left; while (v && ((cmp = *name - *v->v_name) || (cmp = strcmp_(name, v->v_name)))) if (cmp < 0) v = v->v_left; else v = v->v_right; return v; } /* * The caller is responsible for putting value in a safe place */ void set(tchar *var, tchar *val) { tchar **vec = (tchar **)xalloc(2 * sizeof (tchar **)); #ifdef TRACE tprintf("TRACE- set()\n"); #endif vec[0] = onlyread(val) ? savestr(val) : val; vec[1] = 0; set1(var, vec, &shvhed); } void set1(tchar *var, tchar **vec, struct varent *head) { tchar **oldv = vec; #ifdef TRACE tprintf("TRACE- set1()\n"); #endif gflag = 0; /* * If setting cwd variable via "set cwd=/tmp/something" * then do globbing. But if we are setting the cwd * becuz of a cd, chdir, pushd, popd, do not do globbing. */ if ((!(eq(var, S_cwd))) || (eq(var, S_cwd) && (didchdir == 0))) { tglob(oldv); } if (gflag) { vec = glob(oldv); if (vec == 0) { bferr("No match"); blkfree(oldv); return; } blkfree(oldv); gargv = 0; } setq(var, vec, head); } void setq(tchar *name, tchar **vec, struct varent *p) { struct varent *c; int f; #ifdef TRACE tprintf("TRACE- setq()\n"); #endif f = 0; /* tree hangs off the header's left link */ while (c = p->v_link[f]) { if ((f = *name - *c->v_name) == 0 && (f = strcmp_(name, c->v_name)) == 0) { blkfree(c->vec); goto found; } p = c; f = f > 0; } p->v_link[f] = c = (struct varent *)xalloc(sizeof (struct varent)); c->v_name = savestr(name); c->v_bal = 0; c->v_left = c->v_right = 0; c->v_parent = p; balance(p, f, 0); found: trim(c->vec = vec); } void unset(tchar *v[]) { #ifdef TRACE tprintf("TRACE- unset()\n"); #endif unset1(v, &shvhed); if (adrof(S_histchars /* "histchars" */) == 0) { HIST = '!'; HISTSUB = '^'; } #ifdef FILEC if (adrof(S_filec /* "filec" */) == 0) filec = 0; #endif } void unset1(tchar *v[], struct varent *head) { struct varent *vp; int cnt; #ifdef TRACE tprintf("TRACE- unset1()\n"); #endif while (*++v) { cnt = 0; while (vp = madrof(*v, head->v_left)) unsetv1(vp), cnt++; if (cnt == 0) setname(*v); } } void unsetv(tchar *var) { struct varent *vp; #ifdef TRACE tprintf("TRACE- unsetv()\n"); #endif if ((vp = adrof1(var, &shvhed)) == 0) udvar(var); unsetv1(vp); } void unsetv1(struct varent *p) { struct varent *c, *pp; int f; #ifdef TRACE tprintf("TRACE- unsetv1()\n"); #endif /* * Free associated memory first to avoid complications. */ blkfree(p->vec); xfree(p->v_name); /* * If p is missing one child, then we can move the other * into where p is. Otherwise, we find the predecessor * of p, which is guaranteed to have no right child, copy * it into p, and move it's left child into it. */ if (p->v_right == 0) c = p->v_left; else if (p->v_left == 0) c = p->v_right; else { for (c = p->v_left; c->v_right; c = c->v_right) ; p->v_name = c->v_name; p->vec = c->vec; p = c; c = p->v_left; } /* * Move c into where p is. */ pp = p->v_parent; f = pp->v_right == p; if (pp->v_link[f] = c) c->v_parent = pp; /* * Free the deleted node, and rebalance. */ xfree(p); balance(pp, f, 1); } void setNS(tchar *cp) { #ifdef TRACE tprintf("TRACE- setNS()\n"); #endif set(cp, S_ /* "" */); } void shift(tchar **v) { struct varent *argv; tchar *name; #ifdef TRACE tprintf("TRACE- shift()\n"); #endif v++; name = *v; if (name == 0) name = S_argv /* "argv" */; else (void) strip(name); argv = adrof(name); if (argv == 0) udvar(name); if (argv->vec[0] == 0) bferr("No more words"); lshift(argv->vec, 1); } void exportpath(tchar **val) { tchar exppath[PATHSIZ]; #ifdef TRACE tprintf("TRACE- exportpath()\n"); #endif exppath[0] = 0; if (val) while (*val) { if (strlen_(*val) + strlen_(exppath) + 2 > PATHSIZ) { printf("Warning: ridiculously long PATH truncated\n"); break; } (void) strcat_(exppath, *val++); if (*val == 0 || eq(*val, S_RPAR /* ")" */)) break; (void) strcat_(exppath, S_COLON /* ":" */); } local_setenv(S_PATH /* "PATH" */, exppath); } /* macros to do single rotations on node p */ #define rright(p) (\ t = (p)->v_left,\ (t)->v_parent = (p)->v_parent,\ ((p)->v_left = t->v_right) ? (t->v_right->v_parent = (p)) : 0,\ (t->v_right = (p))->v_parent = t,\ (p) = t) #define rleft(p) (\ t = (p)->v_right,\ (t)->v_parent = (p)->v_parent,\ ((p)->v_right = t->v_left) ? (t->v_left->v_parent = (p)) : 0,\ (t->v_left = (p))->v_parent = t,\ (p) = t) /* * Rebalance a tree, starting at p and up. * F == 0 means we've come from p's left child. * D == 1 means we've just done a delete, otherwise an insert. */ void balance(struct varent *p, int f, int d) { struct varent *pp; struct varent *t; /* used by the rotate macros */ int ff; #ifdef TRACE tprintf("TRACE- balance()\n"); #endif /* * Ok, from here on, p is the node we're operating on; * pp is it's parent; f is the branch of p from which we have come; * ff is the branch of pp which is p. */ for (; pp = p->v_parent; p = pp, f = ff) { ff = pp->v_right == p; if (f ^ d) { /* right heavy */ switch (p->v_bal) { case -1: /* was left heavy */ p->v_bal = 0; break; case 0: /* was balanced */ p->v_bal = 1; break; case 1: /* was already right heavy */ switch (p->v_right->v_bal) { case 1: /* sigle rotate */ pp->v_link[ff] = rleft(p); p->v_left->v_bal = 0; p->v_bal = 0; break; case 0: /* single rotate */ pp->v_link[ff] = rleft(p); p->v_left->v_bal = 1; p->v_bal = -1; break; case -1: /* double rotate */ rright(p->v_right); pp->v_link[ff] = rleft(p); p->v_left->v_bal = p->v_bal < 1 ? 0 : -1; p->v_right->v_bal = p->v_bal > -1 ? 0 : 1; p->v_bal = 0; break; } break; } } else { /* left heavy */ switch (p->v_bal) { case 1: /* was right heavy */ p->v_bal = 0; break; case 0: /* was balanced */ p->v_bal = -1; break; case -1: /* was already left heavy */ switch (p->v_left->v_bal) { case -1: /* single rotate */ pp->v_link[ff] = rright(p); p->v_right->v_bal = 0; p->v_bal = 0; break; case 0: /* signle rotate */ pp->v_link[ff] = rright(p); p->v_right->v_bal = -1; p->v_bal = 1; break; case 1: /* double rotate */ rleft(p->v_left); pp->v_link[ff] = rright(p); p->v_left->v_bal = p->v_bal < 1 ? 0 : -1; p->v_right->v_bal = p->v_bal > -1 ? 0 : 1; p->v_bal = 0; break; } break; } } /* * If from insert, then we terminate when p is balanced. * If from delete, then we terminate when p is unbalanced. */ if ((p->v_bal == 0) ^ d) break; } } void plist(struct varent *p) { struct varent *c; int len; #ifdef TRACE tprintf("TRACE- plist()\n"); #endif if (setintr) (void) sigsetmask(sigblock(0) & ~ sigmask(SIGINT)); for (;;) { while (p->v_left) p = p->v_left; x: if (p->v_parent == 0) /* is it the header? */ return; len = blklen(p->vec); printf("%t", p->v_name); Putchar('\t'); if (len != 1) Putchar('('); blkpr(p->vec); if (len != 1) Putchar(')'); Putchar('\n'); if (p->v_right) { p = p->v_right; continue; } do { c = p; p = p->v_parent; } while (p->v_right == c); goto x; } } /* * Copyright 2006 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ /* Copyright (c) 1983, 1984, 1985, 1986, 1987, 1988, 1989 AT&T */ /* All Rights Reserved */ /* * Copyright (c) 1980 Regents of the University of California. * All rights reserved. The Berkeley Software License Agreement * specifies the terms and conditions for redistribution. */ /* * This module provides with system/library function substitutes for tchar * datatype. This also includes two conversion functions between tchar and * char arrays. * * T. Kurosaka, Palo Alto, California, USA * March 1989 * * Implementation Notes: * Many functions defined here use a "char" buffer chbuf[]. In the * first attempt, there used to be only one chbuf defined as static * (private) variable and shared by these functions. csh linked with that * version of this file misbehaved in interpreting "eval `tset ....`". * (in general, builtin function with back-quoted expression). * This bug seemed to be caused by sharing of chbuf * by these functions simultanously (thru vfork() mechanism?). We could not * identify which two functions interfere each other so we decided to * have each of these function its private instance of chbuf. * The size of chbuf[] might be much bigger than necessary for some functions. */ #ifdef DBG #include /* For needs stderr defined. */ #else /* !DBG */ #define NDEBUG /* Disable assert(). */ #endif /* !DBG */ #include #include "sh.h" #ifdef MBCHAR #include /* For wcsetno() */ #endif #include /* MAXPATHLEN */ #include #include bool cflg; /* * strtots(to, from): convert a char string 'from' into a tchar buffer 'to'. * 'to' is assumed to have the enough size to hold the conversion result. * When 'to' is NOSTR(=(tchar *)0), strtots() attempts to allocate a space * automatically using xalloc(). It is caller's responsibility to * free the space allocated in this way, by calling xfree(ptr). * In either case, strtots() returns the pointer to the conversion * result (i.e. 'to', if 'to' wasn't NOSTR, or the allocated space.). * When a conversion or allocateion failed, NOSTR is returned. */ tchar * strtots(tchar *to, char *from) { int i; if (to == NOSTR) { /* Need to xalloc(). */ int i; i = mbstotcs(NOSTR, from, 0); if (i < 0) { return (NOSTR); } /* Allocate space for the resulting tchar array. */ to = (tchar *)xalloc(i * sizeof (tchar)); } i = mbstotcs(to, from, INT_MAX); if (i < 0) { return (NOSTR); } return (to); } char * tstostr(char *to, tchar *from) { tchar *ptc; wchar_t wc; char *pmb; int len; if (to == (char *)NULL) { /* Need to xalloc(). */ int i; int i1; char junk[MB_LEN_MAX]; /* Get sum of byte counts for each char in from. */ i = 0; ptc = from; while (wc = (wchar_t)((*ptc++)&TRIM)) { if ((i1 = wctomb(junk, wc)) <= 0) { i1 = 1; } i += i1; } /* Allocate that much. */ to = (char *)xalloc(i + 1); } ptc = from; pmb = to; while (wc = (wchar_t)((*ptc++)&TRIM)) { if ((len = wctomb(pmb, wc)) <= 0) { *pmb = (unsigned char)wc; len = 1; } pmb += len; } *pmb = (char)0; return (to); } /* * mbstotcs(to, from, tosize) is similar to strtots() except that * this returns # of tchars of the resulting tchar string. * When NULL is give as the destination, no real conversion is carried out, * and the function reports how many tchar characters would be made in * the converted result including the terminating 0. * tchar *to; - Destination buffer, or NULL. * char *from; - Source string. * int tosize; - Size of to, in terms of # of tchars. */ int mbstotcs(tchar *to, char *from, int tosize) { tchar *ptc = to; char *pmb = from; wchar_t wc; int chcnt = 0; int j; /* Just count how many tchar would be in the result. */ if (to == (tchar *)NULL) { while (*pmb) { if ((j = mbtowc(&wc, pmb, MB_CUR_MAX)) <= 0) { j = 1; } pmb += j; chcnt++; } chcnt++; /* For terminator. */ return (chcnt); /* # of chars including terminating zero. */ } else { /* Do the real conversion. */ while (*pmb) { if ((j = mbtowc(&wc, pmb, MB_CUR_MAX)) <= 0) { wc = (unsigned char)*pmb; j = 1; } pmb += j; *(ptc++) = (tchar)wc; if (++chcnt >= tosize) { break; } } /* Terminate with zero only when space is left. */ if (chcnt < tosize) { *ptc = (tchar)0; ++chcnt; } return (chcnt); /* # of chars including terminating zero. */ } } /* tchar version of STRING functions. */ /* * Returns the number of * non-NULL tchar elements in tchar string argument. */ int strlen_(tchar *s) { int n; n = 0; while (*s++) { n++; } return (n); } /* * Concatenate tchar string s2 on the end of s1. S1's space must be large * enough. Return s1. */ tchar * strcat_(tchar *s1, tchar *s2) { tchar *os1; os1 = s1; while (*s1++) ; --s1; while (*s1++ = *s2++) ; return (os1); } /* * Compare tchar strings: s1>s2: >0 s1==s2: 0 s1 0) w += p_col; } return (w); #else /* !MBCHAR --- one char always occupies one column. */ return (strlen_(ts)); #endif } /* * Two getenv() substitute functions. They differ in the type of arguments. * BUGS: Both returns the pointer to an allocated space where the env var's * values is stored. This space is freed automatically on the successive * call of either function. Therefore the caller must copy the contents * if it needs to access two env vars. There is an arbitary limitation * on the number of chars of a env var name. */ #define LONGEST_ENVVARNAME 256 /* Too big? */ tchar * getenv_(tchar *name_) { char name[LONGEST_ENVVARNAME * MB_LEN_MAX]; assert(strlen_(name_) < LONGEST_ENVVARNAME); return (getenvs_(tstostr(name, name_))); } tchar * getenvs_(char *name) { static tchar *pbuf = (tchar *)NULL; char *val; if (pbuf) { xfree(pbuf); pbuf = NOSTR; } val = getenv(name); if (val == (char *)NULL) { return (NOSTR); } return (pbuf = strtots(NOSTR, val)); } /* Followings are the system call interface for tchar strings. */ /* * creat() and open() replacement. * BUGS: An unusually long file name could be dangerous. */ int creat_(tchar *name_, int mode) { int fd; char chbuf[MAXPATHLEN * MB_LEN_MAX]; /* General use buffer. */ tstostr(chbuf, name_); fd = creat((char *)chbuf, mode); if (fd != -1) { setfd(fd); } return (fd); } /*VARARGS2*/ int open_(path_, flags, mode) tchar *path_; int flags; int mode; /* May be omitted. */ { char chbuf[MAXPATHLEN * MB_LEN_MAX]; /* General use buffer. */ int fd; tstostr(chbuf, path_); fd = open((char *)chbuf, flags, mode); if (fd != -1) { setfd(fd); } return (fd); } /* * mkstemp replacement */ int mkstemp_(tchar *name_) { int fd; char chbuf[MAXPATHLEN * MB_LEN_MAX]; /* General use buffer. */ tstostr(chbuf, name_); fd = mkstemp((char *)chbuf); if (fd != -1) { setfd(fd); strtots(name_, chbuf); } return (fd); } /* * read() and write() reaplacement. * int d; * tchar *buf; - where the result be stored. Not NULL terminated. * int nchreq; - # of tchars requrested. */ int read_(int d, tchar *buf, int nchreq) { unsigned char chbuf[BUFSIZ * MB_LEN_MAX]; /* General use buffer. */ #ifdef MBCHAR /* * We would have to read more than tchar bytes * when there are multibyte characters in the file. */ int i, j, fflags; unsigned char *s; /* Byte being scanned for a multibyte char. */ /* Points to the pos where next read() to read the data into. */ unsigned char *p; tchar *t; wchar_t wc; int b_len; int nchread = 0; /* Count how many bytes has been read. */ int nbytread = 0; /* Total # of bytes read. */ /* # of bytes needed to complete the last char just read. */ int delta; unsigned char *q; /* q points to the first invalid byte. */ int mb_cur_max = MB_CUR_MAX; #ifdef DBG tprintf("Entering read_(d=%d, buf=0x%x, nchreq=%d);\n", d, buf, nchreq); #endif /* DBG */ /* * Step 1: We collect the exact number of bytes that make * nchreq characters into chbuf. * We must be careful not to read too many bytes as we * cannot push back such over-read bytes. * The idea we use here is that n multibyte characters are stored * in no less than n but less than n*MB_CUR_MAX bytes. */ assert(nchreq <= BUFSIZ); delta = 0; p = s = chbuf; t = buf; while (nchread < nchreq) { int m; /* # of bytes to try to read this time. */ int k; /* # of bytes successfully read. */ retry: /* * Let's say the (N+1)'th byte bN is actually the first * byte of a three-byte character c. * In that case, p, s, q look like this: * * /-- already read--\ /-- not yet read --\ * chbuf[]: b0 b1 ..... bN bN+1 bN+2 bN+2 ... * ^ ^ ^ * | | | * p s q * \----------/ * c hasn't been completed * * Just after the next read(), p and q will be adavanced to: * * /-- already read-----------------------\ /-- not yet - * chbuf[]: b0 b1 ..... bN bN+1 bN+2 bN+2 ... bX bX+1 bX+2... * ^ ^ ^ * | | | * s p q * \----------/ * c has been completed * but hasn't been scanned */ m = nchreq - nchread; assert(p + m < chbuf + sizeof (chbuf)); k = read(d, p, m); /* * when child sets O_NDELAY or O_NONBLOCK on stdin * and exits and we are interactive then turn the modes off * and retry */ if (k == 0) { if ((intty && !onelflg && !cflg) && ((fflags = fcntl(d, F_GETFL, 0)) & O_NDELAY)) { fflags &= ~O_NDELAY; fcntl(d, F_SETFL, fflags); goto retry; } } else if (k < 0) { if (errno == EAGAIN) { fflags = fcntl(d, F_GETFL, 0); fflags &= ~O_NONBLOCK; fcntl(d, F_SETFL, fflags); goto retry; } return (-1); } nbytread += k; q = p + k; delta = 0; /* Try scaning characters in s..q-1 */ while (s < q) { /* Convert the collected bytes into tchar array. */ if (*s == 0) { /* NUL is treated as a normal char here. */ *t++ = 0; s++; nchread++; continue; } if ((b_len = q - s) > mb_cur_max) { b_len = mb_cur_max; } if ((j = mbtowc(&wc, (char *)s, b_len)) <= 0) { if (mb_cur_max > 1 && b_len < mb_cur_max) { /* * Needs more byte to complete this char * In order to read() more than delta * bytes. */ break; } wc = (unsigned char)*s; j = 1; } *t++ = wc; nchread++; s += j; } if (k < m) { /* We've read as many bytes as possible. */ while (s < q) { if ((b_len = q - s) > mb_cur_max) { b_len = mb_cur_max; } if ((j = mbtowc(&wc, (char *)s, b_len)) <= 0) { wc = (unsigned char)*s; j = 1; } *t++ = wc; nchread++; s += j; } return (nchread); } p = q; } if (mb_cur_max == 1 || (delta = q - s) == 0) { return (nchread); } /* * We may have (MB_CUR_MAX - 1) unread data in the buffer. * Here, the last converted data was an illegal character which was * treated as one byte character. We don't know at this point * whether or not the remaining data is in legal sequence. * We first attempt to convert the remaining data. */ do { if ((j = mbtowc(&wc, (char *)s, delta)) <= 0) break; *t++ = wc; nchread++; s += j; delta -= j; } while (delta > 0); if (delta == 0) return (nchread); /* * There seem to be ugly sequence in the buffer. Fill up till * mb_cur_max and see if we can get a right sequence. */ while (delta < mb_cur_max) { assert((q + 1) < (chbuf + sizeof (chbuf))); if (read(d, q, 1) != 1) break; delta++; q++; if (mbtowc(&wc, (char *)s, delta) > 0) { *t = wc; return (nchread + 1); } } /* * no luck. we have filled MB_CUR_MAX bytes in the buffer. * Ideally we should return with leaving such data off and * put them into a local buffer for next read, but we don't * have such. * So, stop reading further, and treat them as all single * byte characters. */ while (s < q) { b_len = q - s; if ((j = mbtowc(&wc, (char *)s, b_len)) <= 0) { wc = (unsigned char)*s; j = 1; } *t++ = wc; nchread++; s += j; } return (nchread); #else /* !MBCHAR */ /* One byte always represents one tchar. Easy! */ int i; unsigned char *s; tchar *t; int nchread; #ifdef DBG tprintf("Entering read_(d=%d, buf=0x%x, nchreq=%d);\n", d, buf, nchreq); #endif /* DBG */ assert(nchreq <= BUFSIZ); retry: nchread = read(d, (char *)chbuf, nchreq); /* * when child sets O_NDELAY or O_NONBLOCK on stdin * and exits and we are interactive then turn the modes off * and retry */ if (nchread == 0) { if ((intty && !onelflg && !cflg) && ((fflags = fcntl(d, F_GETFL, 0)) & O_NDELAY)) { fflags &= ~O_NDELAY; fcntl(d, F_SETFL, fflags); goto retry; } } else if (nchread < 0) { if (errno == EAGAIN) { fflags = fcntl(d, F_GETFL, 0); fflags &= ~O_NONBLOCK; fcntl(d, F_SETFL, fflags); goto retry; } len = 0; } else { for (i = 0, t = buf, s = chbuf; i < nchread; ++i) { *t++ = ((tchar)*s++); } } return (nchread); #endif } /* * BUG: write_() returns -1 on failure, or # of BYTEs it has written. * For consistency and symmetry, it should return the number of * characters it has actually written, but that is technically * difficult although not impossible. Anyway, the return * value of write() has never been used by the original csh, * so this bug should be OK. */ int write_(int d, tchar *buf, int nch) { unsigned char chbuf[BUFSIZ*MB_LEN_MAX]; /* General use buffer. */ #ifdef MBCHAR tchar *pt; unsigned char *pc; wchar_t wc; int i, j; #ifdef DBG tprintf("Entering write_(d=%d, buf=0x%x, nch=%d);\n", d, buf, nch); /* Hope printf() doesn't call write_() itself! */ #endif /* DBG */ assert(nch * MB_CUR_MAX < sizeof (chbuf)); i = nch; pt = buf; pc = chbuf; while (i--) { /* * Convert to tchar string. * NUL is treated as normal char here. */ wc = (wchar_t)((*pt++)&TRIM); if (wc == (wchar_t)0) { *pc++ = 0; } else { if ((j = wctomb((char *)pc, wc)) <= 0) { *pc = (unsigned char)wc; j = 1; } pc += j; } } return (write(d, chbuf, pc - chbuf)); #else /* !MBCHAR */ /* One byte always represents one tchar. Easy! */ int i; unsigned char *s; tchar *t; #ifdef DBG tprintf("Entering write_(d=%d, buf=0x%x, nch=%d);\n", d, buf, nch); /* Hope printf() doesn't call write_() itself! */ #endif /* DBG */ assert(nch <= sizeof (chbuf)); for (i = 0, t = buf, s = chbuf; i < nch; ++i) { *s++ = (char)((*t++)&0xff); } return (write(d, (char *)chbuf, nch)); #endif } #undef chbuf #include #include /* satruct stat */ #include /* DIR */ extern DIR *Dirp; int stat_(tchar *path, struct stat *buf) { char chbuf[MAXPATHLEN * MB_LEN_MAX]; /* General use buffer. */ tstostr(chbuf, path); return (stat((char *)chbuf, buf)); } int lstat_(tchar *path, struct stat *buf) { char chbuf[MAXPATHLEN * MB_LEN_MAX]; /* General use buffer. */ tstostr(chbuf, path); return (lstat((char *)chbuf, buf)); } int chdir_(tchar *path) { char chbuf[MAXPATHLEN * MB_LEN_MAX]; /* General use buffer. */ tstostr(chbuf, path); return (chdir((char *)chbuf)); } tchar * getwd_(tchar *path) { char chbuf[MAXPATHLEN * MB_LEN_MAX]; /* General use buffer. */ int rc; rc = (int)getwd((char *)chbuf); if (rc == 0) { return (0); } else { return (strtots(path, chbuf)); } } int unlink_(tchar *path) { char chbuf[MAXPATHLEN * MB_LEN_MAX]; /* General use buffer. */ tstostr(chbuf, path); return (unlink((char *)chbuf)); } DIR * opendir_(tchar *dirname) { char chbuf[MAXPATHLEN * MB_LEN_MAX]; /* General use buffer. */ extern DIR *opendir(); DIR *dir; dir = opendir(tstostr(chbuf, dirname)); if (dir != NULL) { setfd(dir->dd_fd); } return (Dirp = dir); } int closedir_(DIR *dirp) { int ret; extern int closedir(); ret = closedir(dirp); Dirp = NULL; return (ret); } int gethostname_(tchar *name, int namelen) { char chbuf[BUFSIZ * MB_LEN_MAX]; /* General use buffer. */ assert(namelen < BUFSIZ); if (gethostname((char *)chbuf, sizeof (chbuf)) != 0) { return (-1); } if (mbstotcs(name, chbuf, namelen) < 0) { return (-1); } return (0); /* Succeeded. */ } int readlink_(tchar *path, tchar *buf, int bufsiz) { char chbuf[MAXPATHLEN * MB_LEN_MAX]; /* General use buffer. */ char chpath[MAXPATHLEN + 1]; int i; tstostr(chpath, path); i = readlink(chpath, (char *)chbuf, sizeof (chbuf)); if (i < 0) { return (-1); } chbuf[i] = (char)0; /* readlink() doesn't put NULL. */ i = mbstotcs(buf, chbuf, bufsiz); if (i < 0) { return (-1); } return (i - 1); /* Return # of tchars EXCLUDING the terminating NULL. */ } /* checks that it's a number */ int chkalldigit_(tchar *str) { char chbuf[BUFSIZ * MB_LEN_MAX]; /* General use buffer. */ char *c = chbuf; (void) tstostr(chbuf, str); while (*c) if (!isdigit(*(c++))) return (-1); return (0); } int atoi_(tchar *str) { char chbuf[BUFSIZ * MB_LEN_MAX]; /* General use buffer. */ tstostr(chbuf, str); return (atoi((char *)chbuf)); } tchar * simple(tchar *s) { tchar *sname = s; while (1) { if (any('/', sname)) { while (*sname++ != '/') ; } else { return (sname); } } } /* * Copyright 2006 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ /* Copyright (c) 1983, 1984, 1985, 1986, 1987, 1988, 1989 AT&T */ /* All Rights Reserved */ /* * Copyright (c) 1980 Regents of the University of California. * All rights reserved. The Berkeley Software License Agreement * specifies the terms and conditions for redistribution. */ /* * C shell */ /* * These tchar constants used to be defined as * character string constants. */ #include "sh.h" tchar S_[] = {0}; tchar S_0[]={'0', 0}; tchar S_1[]={'1', 0}; tchar S_AND[] = {'&', 0}; /* & */ tchar S_ANDAND[] = {'&', '&', 0}; /* && */ tchar S_AST[]={'*', 0}; tchar S_AT[] = { '@', 0 }; tchar S_BAR[] = {'|', 0}; /* | */ tchar S_BARBAR[] = {'|','|', 0}; /* || */ tchar S_BRABRA[] = {'{', '}', 0}; /* {} */ tchar S_BRAPPPBRA[] = {'{', ' ', '.', '.', '.', ' ', '}', 0}; /* { ... } */ tchar S_COLON[] = {':', 0}; /*:*/ tchar S_DASHl[] = {'-', 'l', 0}; /*-l */ tchar S_DELIM[] = {' ','\'','"','\t',';','&','<','>','(',')','|','`',0}; tchar S_DOT[] = {'.', 0}; tchar S_DOTDOTSLA[]={'.', '.', '/', 0}; tchar S_DOTSLA[]={'.', '/', 0}; tchar S_EQ[] = {'=', 0}; /*=*/ tchar S_EXAS[] = {'!', 0}; /* ! */ tchar S_HAT[] = {'^', 0}; /* ^ */ tchar S_HOME[] = {'H','O','M','E',0};/*HOME*/ tchar S_IOT[] = {'I', 'O', 'T', 0}; /*IOT*/ tchar S_LANG[]={'L', 'A', 'N', 'G', 0}; /*LANG*/ tchar S_LBRA[] = {'{', 0}; /* { */ tchar S_LBRASP[] = {'(', ' ', 0}; /*( */ tchar S_LC_ALL[]={'L', 'C', '_', 'A', 'L', 'L', 0}; /*LC_ALL*/ tchar S_LC_CTYPE[]={'L', 'C', '_', 'C', 'T', 'Y', 'P', 'E', 0}; /*LC_CTYPE*/ tchar S_LC_MESSAGES[]={'L', 'C', '_', 'M', 'E', 'S', 'S', 'A', 'G', 'E', 'S', 0}; /*LC_MESSAGES*/ tchar S_LESLES[]={'<', '<', 0}; tchar S_LPAR[] = {'(', 0}; /* ( */ tchar S_MINUS[] = {'-',0};/*"-"*/ tchar S_MINl[]={'-', 'l', 0}; tchar S_NDOThistory[] = {'~','/','.','h','i','s','t','o','r','y',0}; tchar S_OTHERSH[] = {'/','b','i','n','/','s','h',0}; tchar S_PARCENTMINUS[] = {'%', '-', 0}; /*%-*/ tchar S_PARCENTPARCENT[] = {'%', '%', 0}; /*%%*/ tchar S_PARCENTPLUS[] = {'%', '+', 0}; /*%+*/ tchar S_PARCENTSHARP[] = {'%', '#', 0}; /*%#*/ tchar S_PATH[] = {'P','A','T','H',0};/*"PATH"*/ tchar S_PERSENTSP[] = {'%',' ',0}; tchar S_PWD[]={'P', 'W', 'D', 0}; tchar S_Pjob[] = {'%','j','o','b', 0}; /*"%job"*/ tchar S_PjobAND[] = {'%','j','o','b',' ','&',0}; /*"%job &"*/ tchar S_QPPPQ[] = {'`', ' ', '.', '.', '.', ' ', '`', 0}; /*` ... `*/ tchar S_RBRA[] = {'}', 0}; /* } */ tchar S_RPAR[] = {')', 0}; /*)*/ tchar S_SEMICOLONSP[] = {';', ' ', 0}; /* | */ tchar S_SHARPSP[] = {'#',' ',0}; tchar S_SHELLPATH[] = {'/','b','i','n','/','c','s','h',0}; tchar S_SLADOTcshrc[] = {'/','.','c','s','h','r','c', 0}; tchar S_SLADOThistory[] = {'/','.','h','i','s','t','o','r','y', 0}; tchar S_SLADOTlogin[] = {'/','.','l','o','g','i','n', 0}; tchar S_SLADOTlogout[] = {'/','.','l','o','g','o','u','t', 0}; tchar S_SLASH[] = {'/', 0}; /* "/" */ tchar S_SP[] = {' ', 0}; /* */ tchar S_SPANDANDSP[] = {' ', '&', '&', ' ', 0}; /* && */ tchar S_SPBARBARSP[] = {' ', '|', '|', ' ', 0}; /* || */ tchar S_SPBARSP[] = {' ', '|', ' ', 0}; /* | */ tchar S_SPGTRGTRSP[] = {' ', '>', '>', ' ', 0}; /* >> */ tchar S_SPGTR[] = {' ', '>',0}; /* > */ tchar S_SPLESLESSP[] = {' ', '<', '<', ' ', 0}; /* << */ tchar S_SPLESSP[] = {' ', '<', ' ', 0}; /* < */ tchar S_SPPPP[] = {' ', '.', '.', '.', 0}; /* ... */ tchar S_SPRBRA[] = {' ', ')', 0}; /* )*/ tchar S_SUNW_VARLEN[] = {'S', 'U', 'N', 'W', '_', 'V', 'A', 'R', 'L', 'E', 'N', 0}; tchar S_TERM[] = {'T','E','R','M',0};/*TERM*/ tchar S_TIL[] = {'~', 0}; /* ~ */ tchar S_TOPBIT[] = {(tchar)QUOTE, 0}; /* Was "\200". A hack! */ tchar S_USAGEFORMAT[] = {'%','U','u',' ','%','S','s',' ','%','E',' ','%','P', ' ','%','X','+','%','D','k',' ','%','I','+','%','O','i','o',' ','%','F','p','f','+', '%','W','w',0}; tchar S_USER[] = {'U','S','E','R',0};/*USER*/ tchar S_alias[] = { 'a','l','i','a','s', 0 }; tchar S_alloc[] = { 'a','l','l','o','c', 0}; tchar S_aout[] = {'a','.','o','u','t',0}; tchar S_argv[]={'a', 'r', 'g', 'v', 0}; tchar S_bg[] = { 'b','g', 0}; tchar S_bin[] = {'/','b','i','n',0}; tchar S_break[] = { 'b','r','e','a','k', 0}; tchar S_breaksw[] = { 'b','r','e','a','k','s','w', 0}; tchar S_bye[] = { 'b','y','e', 0}; tchar S_case[] = { 'c','a','s','e', 0}; tchar S_cd[] = { 'c','d', 0}; tchar S_cdpath[]={'c', 'd', 'p', 'a', 't', 'h', 0}; tchar S_chdir[] = { 'c','h','d','i','r', 0}; tchar S_child[] = {'c', 'h', 'i', 'l', 'd', 0}; /*child */ tchar S_continue[] = { 'c','o','n','t','i','n','u','e', 0}; tchar S_coredumpsize[] = {'c','o','r','e','d','u','m','p','s','i','z','e',0};/*"coredumpsize"*/ tchar S_cputime[] = {'c','p','u','t','i','m','e',0};/*"cputime"*/ tchar S_csh[]={'c', 's', 'h', 0}; tchar S_cwd[]={'c', 'w', 'd', 0}; tchar S_datasize[] = {'d','a','t','a','s','i','z','e',0};/*"datasize"*/ tchar S_default[] = { 'd','e','f','a','u','l','t', 0 }; tchar S_descriptors[] = {'d', 'e', 's', 'c', 'r', 'i', 'p', 't', 'o', 'r', 's', 0}; tchar S_dirs[] = { 'd','i','r','s', 0 }; tchar S_echo[] = {'e','c','h','o', 0}; tchar S_else[] = { 'e','l','s','e', 0 }; tchar S_end[] = { 'e','n','d', 0 }; tchar S_endif[] = { 'e','n','d','i','f', 0 }; tchar S_endsw[] = { 'e','n','d','s','w', 0 }; tchar S_erwxfdzo[] = {'e', 'r', 'w', 'x', 'f', 'd', 'z', 'o', 0}; /* erwxfdzo */ tchar S_eval[] = { 'e','v','a','l', 0 }; tchar S_exec[] = { 'e','x','e','c', 0 }; tchar S_exit[] = { 'e','x','i','t', 0 }; tchar S_fg[] = { 'f','g', 0 }; tchar S_fignore[] = {'f','i','g','n','o','r','e',0}; tchar S_filec[] = {'f','i','l','e','c',0};/*filec*/ tchar S_filesize[] = {'f','i','l','e','s','i','z','e',0};/*"filesize"*/ tchar S_foreach[] = { 'f','o','r','e','a','c','h', 0 }; tchar S_gd[] = { 'g','d', 0 }; tchar S_glob[] = { 'g','l','o','b', 0 }; tchar S_goto[] = { 'g','o','t','o', 0 }; tchar S_h[] = {'-','h',0}; tchar S_hardpaths[]={'h', 'a', 'r', 'd', 'p', 'a', 't', 'h', 's', 0}; tchar S_hashstat[] = { 'h','a','s','h','s','t','a','t', 0 }; tchar S_histchars[] = {'h','i','s','t','c','h','a','r','s',0}; /*histchars*/ tchar S_history[] = {'h','i','s','t','o','r','y',0}; tchar S_home[]={'h', 'o', 'm', 'e', 0}; tchar S_hours[] = {'h','o','u','r','s',0};/*"hours"*/ tchar S_htrqxe[]={'h', 't', 'r', 'q', 'x', 'e', 0}; tchar S_if[] = { 'i','f', 0 }; tchar S_ignoreeof[] = {'i','g','n','o','r','e','e','o','f',0}; /*"ignoreeof"*/ tchar S_jobs[] = {'j','o','b','s', 0}; tchar S_kbytes[] = {'k','b','y','t','e','s',0};/*"kbytes"*/ tchar S_kill[] = { 'k','i','l','l', 0 }; tchar S_label[] = { 'l','a','b','e','l', 0 }; tchar S_limit[] = { 'l','i','m','i','t', 0 }; tchar S_login[] = { 'l','o','g','i','n', 0 }; tchar S_logout[] = { 'l','o','g','o','u','t', 0 }; tchar S_mail[] = {'m','a','i','l', 0}; tchar S_megabytes[] = {'m','e','g','a','b','y','t','e','s',0};/*"megabytes"*/ tchar S_memorysize[] = {'m','e','m','o','r','y','s','i','z','e',0};/*"memorysize"*/ tchar S_minutes[]={'m','i','n','u','t','e','s',0};/*"minutes"*/ tchar S_n[] = {'-','n',0};/*"-n"*/ tchar S_newgrp[] = { 'n','e','w','g','r','p', 0 }; tchar S_nice[] = { 'n','i','c','e', 0 }; tchar S_nobeep[] = {'n', 'o', 'b', 'e', 'e', 'p', 0}; tchar S_noclobber[] = {'n','o','c','l','o','b','b','e','r',0};/*noclobber*/ tchar S_noglob[] = {'n', 'o', 'g', 'l', 'o', 'b', 0}; /*noglob */ tchar S_nohup[] = {'n', 'o', 'h', 'u', 'p', 0}; /*nohup */ tchar S_nonomatch[] = {'n', 'o', 'n', 'o', 'm', 'a', 't', 'c', 'h', 0}; /*nonomatch */ tchar S_notify[] = {'n', 'o', 't', 'i', 'f', 'y', 0}; /*nofify */ tchar S_onintr[] = { 'o','n','i','n','t','r', 0 }; tchar S_path[] = {'p','a','t','h', 0}; /*path*/ tchar S_popd[] = { 'p','o','p','d', 0 }; tchar S_prompt[] = {'p','r','o','m','p','t', 0}; tchar S_pushd[] = { 'p','u','s','h','d', 0 }; tchar S_rd[] = { 'r','d', 0 }; tchar S_rehash[] = { 'r','e','h','a','s','h', 0 }; tchar S_repeat[] = { 'r','e','p','e','a','t', 0 }; tchar S_savehist[] = {'s','a','v','e','h','i','s','t', 0}; tchar S_seconds[] = {'s','e','c','o','n','d','s',0};/*"seconds"*/ tchar S_set[] = { 's','e','t', 0 }; tchar S_setenv[] = { 's','e','t','e','n','v', 0 }; tchar S_shell[] = {'s','h','e','l','l', 0}; tchar S_shift[] = { 's','h','i','f','t', 0 }; tchar S_source[] = {'s','o','u','r','c','e',0}; tchar S_stacksize[] = {'s','t','a','c','k','s','i','z','e',0};/*"stacksize"*/ tchar S_status[]={'s', 't', 'a', 't', 'u', 's', 0}; tchar S_stop[] = { 's','t','o','p', 0 }; tchar S_suspend[] = { 's','u','s','p','e','n','d', 0 }; tchar S_switch[] = { 's','w','i','t','c','h', 0 }; tchar S_term[] = {'t','e','r','m', 0}; tchar S_then[] = {'t','h','e','n',0}; /*"then"*/ tchar S_time[] = {'t', 'i', 'm', 'e', 0}; /*time*/ tchar S_umask[] = { 'u','m','a','s','k', 0 }; tchar S_unalias[] = { 'u','n','a','l','i','a','s', 0 }; tchar S_unhash[] = { 'u','n','h','a','s','h', 0 }; tchar S_unlimit[] = { 'u','n','l','i','m','i','t', 0 }; tchar S_unlimited[] = {'u','n','l','i','m','i','t','e','d',0};/*"unlimited"*/ tchar S_unset[] = { 'u','n','s','e','t', 0 }; tchar S_unsetenv[] = { 'u','n','s','e','t','e','n','v', 0 }; tchar S_user[] = {'u','s','e','r', 0}; tchar S_usrbin[] = {'/','u','s','r','/','b','i','n',0}; tchar S_usrucb[] = {'/','u','s','r','/','u','c','b',0}; tchar S_verbose[] = {'v','e','r','b','o','s','e', 0}; tchar S_wait[] = { 'w','a','i','t', 0 }; tchar S_while[] = { 'w','h','i','l','e', 0 }; /* Dummy search path for just absolute search when no path */ tchar *justabs[] = { S_ /* "" */, 0 }; /* * Copyright 2005 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ /* Copyright (c) 1983, 1984, 1985, 1986, 1987, 1988, 1989 AT&T */ /* All Rights Reserved */ /* * Copyright (c) 1980 Regents of the University of California. * All rights reserved. The Berkeley Software License Agreement * specifies the terms and conditions for redistribution. */ #include "sh.h" #include "sh.tconst.h" struct timeval time0; static struct rusage ru0; void ruadd(struct rusage *ru, struct rusage *ru2); void prusage(struct rusage *r0, struct rusage *r1, struct timeval *e, struct timeval *b); void pdeltat(struct timeval *t1, struct timeval *t0); void tvadd(struct timeval *tsum, struct timeval *t0); void tvsub(struct timeval *tdiff, struct timeval *t1, struct timeval *t0); /* * C Shell - routines handling process timing and niceing */ void settimes(void) { struct rusage ruch; #ifdef TRACE tprintf("TRACE- settimes()\n"); #endif (void) gettimeofday(&time0, (struct timezone *)0); (void) getrusage(RUSAGE_SELF, &ru0); (void) getrusage(RUSAGE_CHILDREN, &ruch); ruadd(&ru0, &ruch); } /* * dotime is only called if it is truly a builtin function and not a * prefix to another command */ void dotime(void) { struct timeval timedol; struct rusage ru1, ruch; #ifdef TRACE tprintf("TRACE- dotime()\n"); #endif (void) getrusage(RUSAGE_SELF, &ru1); (void) getrusage(RUSAGE_CHILDREN, &ruch); ruadd(&ru1, &ruch); (void) gettimeofday(&timedol, (struct timezone *)0); prusage(&ru0, &ru1, &timedol, &time0); } /* * donice is only called when it's on the line by itself or with a +- value */ void donice(tchar **v) { tchar *cp; int nval; #ifdef TRACE tprintf("TRACE- donice()\n"); #endif v++; cp = *v++; if (cp == 0) { nval = 4; } else if (*v == 0 && (cp[0] == '+' || cp[0] == '-')) { nval = getn(cp); } (void) setpriority(PRIO_PROCESS, 0, nval); } void ruadd(struct rusage *ru, struct rusage *ru2) { long *lp, *lp2; int cnt; /* * The SunOS 4.x has ru_first and ru_last #defines * as below. * The SVR4/POSIX does not have these defined for * struct rusage * The #defines below are here so that the original csh logic * for ruadd remains clear now that there is no longer a private copy * of the old */ #define ru_first ru_ixrss #define ru_last ru_nivcsw #ifdef TRACE tprintf("TRACE- ruadd()\n"); #endif tvadd(&ru->ru_utime, &ru2->ru_utime); tvadd(&ru->ru_stime, &ru2->ru_stime); if (ru2->ru_maxrss > ru->ru_maxrss) { ru->ru_maxrss = ru2->ru_maxrss; } cnt = &ru->ru_last - &ru->ru_first + 1; lp = &ru->ru_first; lp2 = &ru2->ru_first; do { *lp++ += *lp2++; } while (--cnt > 0); } void prusage(struct rusage *r0, struct rusage *r1, struct timeval *e, struct timeval *b) { #define pgtok(p) ((p * pgsize) / 1024) static int pgsize; time_t t = (r1->ru_utime.tv_sec - r0->ru_utime.tv_sec) * 100 + (r1->ru_utime.tv_usec - r0->ru_utime.tv_usec) / 10000 + (r1->ru_stime.tv_sec - r0->ru_stime.tv_sec) * 100 + (r1->ru_stime.tv_usec - r0->ru_stime.tv_usec) / 10000; tchar *cp; int i; struct varent *vp = adrof(S_time); int ms = (e->tv_sec - b->tv_sec) * 100 + (e->tv_usec - b->tv_usec) / 10000; #ifdef TRACE tprintf("TRACE- prusage()\n"); #endif if (pgsize == 0) { pgsize = getpagesize(); } cp = S_USAGEFORMAT; /* "%Uu %Ss %E %P %X+%Dk %I+%Oio %Fpf+%Ww" */ if (vp && vp->vec[0] && vp->vec[1]) { cp = vp->vec[1]; } for (; *cp; cp++) { if (*cp != '%') { Putchar(*cp); } else if (cp[1]) { switch (*++cp) { case 'U': pdeltat(&r1->ru_utime, &r0->ru_utime); break; case 'S': pdeltat(&r1->ru_stime, &r0->ru_stime); break; case 'E': psecs_int(ms / 100); break; case 'P': printf("%d%%", (int)(t * 100 / ((ms ? ms : 1)))); break; case 'W': i = r1->ru_nswap - r0->ru_nswap; printf("%d", i); break; case 'X': printf("%d", t == 0 ? 0 : pgtok((r1->ru_ixrss - r0->ru_ixrss) / t)); break; case 'D': printf("%d", t == 0 ? 0 : pgtok((r1->ru_idrss + r1->ru_isrss- (r0->ru_idrss + r0->ru_isrss)) / t)); break; case 'K': printf("%d", t == 0 ? 0 : pgtok(((r1->ru_ixrss + r1->ru_isrss + r1->ru_idrss) - (r0->ru_ixrss + r0->ru_idrss + r0->ru_isrss)) / t)); break; case 'M': printf("%d", r1->ru_maxrss / 2); break; case 'F': printf("%d", r1->ru_majflt - r0->ru_majflt); break; case 'R': printf("%d", r1->ru_minflt - r0->ru_minflt); break; case 'I': printf("%d", r1->ru_inblock - r0->ru_inblock); break; case 'O': printf("%d", r1->ru_oublock - r0->ru_oublock); break; } } } Putchar('\n'); #undef pgtok } void pdeltat(struct timeval *t1, struct timeval *t0) { struct timeval td; #ifdef TRACE tprintf("TRACE- pdeltat()\n"); #endif tvsub(&td, t1, t0); /* change printf formats */ printf("%d.%01d", td.tv_sec, td.tv_usec / 100000); } void tvadd(struct timeval *tsum, struct timeval *t0) { #ifdef TRACE tprintf("TRACE- tvadd()\n"); #endif tsum->tv_sec += t0->tv_sec; tsum->tv_usec += t0->tv_usec; if (tsum->tv_usec > 1000000) { tsum->tv_sec++; tsum->tv_usec -= 1000000; } } void tvsub(struct timeval *tdiff, struct timeval *t1, struct timeval *t0) { #ifdef TRACE tprintf("TRACE- tvsub()\n"); #endif tdiff->tv_sec = t1->tv_sec - t0->tv_sec; tdiff->tv_usec = t1->tv_usec - t0->tv_usec; if (tdiff->tv_usec < 0) { tdiff->tv_sec--; tdiff->tv_usec += 1000000; } } /* * Copyright 2005 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ /* * * Copyright (c) 1983, 1984, 1985, 1986, 1987, 1988, 1989 AT&T * All Rights Reserved * * Copyright (c) 1980 Regents of the University of California. * All rights reserved. The Berkeley Software License Agreement * specifies the terms and conditions for redistribution. */ /* * This file holds definitions relevant to the wait system call. * Some of the options here are available only through the ``wait3'' * entry point; the old entry point with one argument has more fixed * semantics, never returning status of unstopped children, hanging until * a process terminates if any are outstanding, and never returns * detailed information about process resource utilization (). */ #ifndef _sys_wait_h #define _sys_wait_h #include /* included for _LITTLE_ENDIAN define */ /* * Structure of the information in the first word returned by both * wait and wait3. If w_stopval==WSTOPPED, then the second structure * describes the information returned, else the first. See WUNTRACED below. */ union wait { int w_status; /* used in syscall */ /* * Terminated process status. */ struct { #if defined(_LITTLE_ENDIAN) unsigned short w_Termsig:7; /* termination signal */ unsigned short w_Coredump:1; /* core dump indicator */ unsigned short w_Retcode:8; /* exit code if w_termsig==0 */ #elif defined(_BIG_ENDIAN) unsigned short w_Fill1:16; /* high 16 bits unused */ unsigned short w_Retcode:8; /* exit code if w_termsig==0 */ unsigned short w_Coredump:1; /* core dump indicator */ unsigned short w_Termsig:7; /* termination signal */ #else #error NO ENDIAN defined. #endif } w_T; /* * Stopped process status. Returned * only for traced children unless requested * with the WUNTRACED option bit. */ struct { #if defined(_LITTLE_ENDIAN) unsigned short w_Stopval:8; /* == W_STOPPED if stopped */ unsigned short w_Stopsig:8; /* signal that stopped us */ #elif defined(_BIG_ENDIAN) unsigned short w_Fill2:16; /* high 16 bits unused */ unsigned short w_Stopsig:8; /* signal that stopped us */ unsigned short w_Stopval:8; /* == W_STOPPED if stopped */ #else #error NO ENDIAN defined. #endif } w_S; }; #define w_termsig w_T.w_Termsig #define w_coredump w_T.w_Coredump #define w_retcode w_T.w_Retcode #define w_stopval w_S.w_Stopval #define w_stopsig w_S.w_Stopsig #define WSTOPPED 0177 /* value of s.stopval if process is stopped */ #define WCONTFLG 0177777 /* value of w.w_status due to SIGCONT, sysV */ /* * Option bits for the second argument of wait3. WNOHANG causes the * wait to not hang if there are no stopped or terminated processes, rather * returning an error indication in this case (pid==0). WUNTRACED * indicates that the caller should receive status about untraced children * which stop due to signals. If children are stopped and a wait without * this option is done, it is as though they were still running... nothing * about them is returned. */ #define WNOHANG 1 /* dont hang in wait */ #define WUNTRACED 2 /* tell about stopped, untraced children */ #define WIFSTOPPED(x) ((x).w_stopval == WSTOPPED) #define WIFSIGNALED(x) ((x).w_stopval != WSTOPPED && (x).w_termsig != 0) #define WIFEXITED(x) ((x).w_stopval != WSTOPPED && (x).w_termsig == 0) extern pid_t csh_wait3(union wait *w, int options, struct rusage *rp); extern pid_t csh_wait_noreap(void); #endif /* _sys_wait_h */ /* * Copyright 2006 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ /* Copyright (c) 1983, 1984, 1985, 1986, 1987, 1988, 1989 AT&T */ /* All Rights Reserved */ /* * Copyright (c) 1980 Regents of the University of California. * All rights reserved. The Berkeley Software License Agreement * specifies the terms and conditions for redistribution. */ /* * Compatibility lib for BSD's wait3(). It is not * binary compatible, since BSD's WNOHANG and WUNTRACED * carry different #define values. */ #include #include #include #include #include #include #include #include #include /* * Since sysV does not support rusage as in BSD, an approximate approach * is: * ... * call times * call waitid * if ( a child is found ) * call times again * rusage ~= diff in the 2 times call * ... * */ /* * XXX: There is now a wait3 function in libc which should be used instead * of this local version of wait3. With the addition of a wait3 prototype * in as per the X/Open XPG4v2 specification, compilation of * the csh utility will result in warnings, hence the renaming of the local * version. Using the libc wait3 rather than the local version results in * a failure with csh, however, this version should eventually be dropped * in favor of the libc wait3 with appropriate updates made to sh.proc.c * to account for the difference in implementation of the local versus * the libc versions. This should probably be done as part of an overall * effort to rid csh of local versions of functions now in libc. */ static int wstat(int code, int status); pid_t csh_wait3(int *status, int options, struct rusage *rp) { struct tms before_tms; struct tms after_tms; siginfo_t info; int error; if (rp) memset((void *)rp, 0, sizeof (struct rusage)); memset((void *)&info, 0, sizeof (siginfo_t)); if (times(&before_tms) == -1) return (-1); /* errno is set by times() */ /* * BSD's wait3() only supports WNOHANG & WUNTRACED */ options |= (WNOHANG|WUNTRACED|WEXITED|WSTOPPED|WTRAPPED|WCONTINUED); error = waitid(P_ALL, 0, &info, options); if (error == 0) { clock_t diffu; /* difference in usertime (ticks) */ clock_t diffs; /* difference in systemtime (ticks) */ if ((options & WNOHANG) && (info.si_pid == 0)) return (0); /* no child found */ if (rp) { if (times(&after_tms) == -1) return (-1); /* errno set by times() */ /* * The system/user time is an approximation only !!! */ diffu = after_tms.tms_cutime - before_tms.tms_cutime; diffs = after_tms.tms_cstime - before_tms.tms_cstime; rp->ru_utime.tv_sec = diffu/HZ; rp->ru_utime.tv_usec = ((diffu % HZ) * 1000000) / HZ; rp->ru_stime.tv_sec = diffs/HZ; rp->ru_stime.tv_usec = ((diffs % HZ) * 1000000) / HZ; } *status = wstat(info.si_code, info.si_status); return (info.si_pid); } else { return (-1); /* error number is set by waitid() */ } } /* * Convert the status code to old style wait status */ static int wstat(int code, int status) { int stat = (status & 0377); switch (code) { case CLD_EXITED: stat <<= 8; break; case CLD_DUMPED: stat |= WCOREFLG; break; case CLD_KILLED: break; case CLD_TRAPPED: case CLD_STOPPED: stat <<= 8; stat |= WSTOPFLG; break; case CLD_CONTINUED: stat = WCONTFLG; break; } return (stat); } pid_t csh_wait_noreap(void) { siginfo_t info; if (waitid(P_ALL, 0, &info, WEXITED | WTRAPPED | WSTOPPED | WCONTINUED | WNOWAIT) != 0) return (-1); return (info.si_pid); }